/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* This file delivers svc.ipfd, the daemon that monitors changes to
* firewall capable services and requests IPfilter configuration update
* on behalf of the service. Essentially, the daemon listens for
* service changes and forks the program that update a service's
* IPfilter configuration.
*
* - A firewall capable SMF service can restrict network access to its
* service by providing a firewall policy that can be translated into
* a set of IPfilter rules. The mentioned firewall policy is stored in
* firewall_config and firewall_context property groups. If one of these
* two property groups exist, the service is considered to be firewall
* capable.
*
* - A request to update service's IPfilter configuration is made for
* actions that affect service's configuration or running state. The
* actions are:
* - maintenance/clear maintenance
*
* Lacking a generic SMF mechanism to observe service state changes, the
* daemon observe change events by listening to changes to 'general',
* 'general_ovr', and 'restarter_actions' property groups. This is not a
* stable interface and should be replaced when a SMF supported mechanism
* becomes available.
*
* - The program responsible for updating service's IPfilter configuration
*
*
* where fmri the instance fmri of the service to be updated.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <umem.h>
#include <libscf.h>
#include <libscf_priv.h>
#include <signal.h>
#include <string.h>
#include <syslog.h>
static scf_handle_t *h;
static char *scratch_fmri;
static char *scratch_name;
static const char *all_props[] = {
};
static const char *maint_props[] = {
static int ipfilter_update(const char *);
static int
daemonize_self(void)
{
int fd;
(void) close(STDIN_FILENO);
} else if (fd != STDIN_FILENO) {
}
closefrom(3);
return (1);
}
if (pid != 0)
exit(0);
(void) setsid();
(void) chdir("/");
return (0);
}
static void
{
int c = 0;
(void) scf_handle_unbind(hndl);
while ((scf_handle_bind(hndl)) != 0) {
if (c > MAX_RETRY) {
"unavailable. Couldn't bind handle: %s\n",
scf_strerror(scf_error()));
"IPfilter configuration may not be updated "
"properly\n");
exit(1);
} else {
c++;
}
(void) sleep(1);
}
}
static void
{
for (;;) {
if (_scf_notify_add_pgtype(h, SCF_GROUP_FRAMEWORK) ==
break;
switch (scf_error()) {
break;
case SCF_ERROR_NO_RESOURCES:
(void) sleep(1);
break;
default:
"Abort: Couldn't set up repository notification "
"for pg type %s: %s\n", SCF_GROUP_FRAMEWORK,
scf_strerror(scf_error()));
abort();
}
}
}
/*
* If the repository connection is lost, rebind and re-setup repository
* notification. During the repository connection outage, services that
* changed states wouldn't get the corresponding firewall update. To make
* we're not out of sync, update the entire system firewall configuration,
* invoke ipfilter_update(IPFILTER_FMRI).
*/
static void
{
"Failed to reconfigure system firewall.\n");
}
}
static int
scf_value_t *v)
{
return (-1);
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_DELETED:
break;
default:
"scf_pg_get_property failed for %s: %s\n",
}
return (-1);
}
return (0);
}
static int
{
int i, ret = 0;
scf_strerror(scf_error()));
return (-1);
}
/*
* We care about enable, disable, and refresh since that's
* when we activate, deactivate, or change firewall policy.
*
*/
return (1);
}
return (-1);
}
}
/*
* Only concerned with refresh, restart, and maint on|off actions.
* an automatic valid event for RPC services.
*/
if (isrpc) {
ret = 1;
goto out;
for (i = 0; i < prop_cnt; i++) {
scratch_v) == 0) {
scratch_name, proplist[i]);
ret = 1;
goto out;
}
}
}
out:
if (state)
return (ret);
}
static int
{
/*
* Start refresh in another process
*/
ret = 1;
goto out;
}
if (pid == 0) {
NULL) == -1)
exit(1);
}
/*
* Parent - only one update at a time.
*/
ret = 1;
out:
if (ret == 1)
"for: %s\n", fmri);
return (ret);
}
/*
* Determine whether a given instance is a RPC service. Repository and
* libscf errors are treated as if the service isn't an RPC service,
* returning B_FALSE to indicate validation failure.
*/
static boolean_t
{
"Could not get running snapshot, using editing value\n");
} else {
}
scratch_pg) == -1) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_DELETED:
break;
default:
"scf_instance_get_pg_composed failed: %s\n",
scf_strerror(scf_error()));
return (B_FALSE);
}
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_DELETED:
break;
default:
"scf_instance_get_pg_composed failed: %s\n",
scf_strerror(scf_error()));
}
return (B_FALSE);
}
}
return (B_FALSE);
return (B_FALSE);
}
if (isrpc)
return (B_TRUE);
else
return (B_FALSE);
}
static int
{
switch (scf_error()) {
"scf_instance_get_snapshot failed: %s\n",
scf_strerror(scf_error()));
return (-1);
case SCF_ERROR_DELETED:
default:
/*
* If running snapshot is not available for
* other reasons, fall back to current values.
*/
"running snapshot, using current value\n");
}
} else {
}
/*
* Update service's IPfilter configuration if either
* SCF_PG_FW_CONTEXT or SCF_PG_FW_CONFIG exists.
*/
scratch_pg) == 0) {
return (1);
} else {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_DELETED:
break;
/* FALLTHROUGH */
default:
"scf_instance_get_pg_composed failed: %s\n",
scf_strerror(scf_error()));
return (-1);
}
}
scratch_pg) == -1) {
/*
* It's either a non-firewall service or a failure to
* read firewall pg, just continue and listen for
* future events.
*/
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_DELETED:
return (0);
/* FALLTHROUGH */
default:
"scf_instance_get_pg_composed failed: %s\n",
scf_strerror(scf_error()));
return (-1);
}
}
return (1);
}
static int
{
int res;
/*
* Figure out it's a firewall capable instance and call ipfilter_update
* if it is.
*/
/* Not an error if pg doesn't belong to a valid instance */
if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
return (0);
}
if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
return (1);
}
if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
return (1);
}
return (0);
}
/*
* If it's not an event we're interested in, returns success.
*/
if (res == -1) {
"is_correct_event failed for %s.\n", scratch_fmri);
return (1);
} else if (res == 0) {
return (0);
}
/*
* Proceed only if instance has firewall policy.
*/
if (res == -1) {
"instance_has_firewall failed for %s.\n", scratch_fmri);
return (1);
} else if (res == 0) {
return (0);
}
return (1);
}
return (0);
}
static int
{
return (1);
}
return (1);
}
scf_strerror(scf_error()));
return (1);
}
for (;;) {
/*
* Calling _scf_notify_wait which will block this thread
* until it's notified of a framework event.
*
* Note: fmri is only set on delete events.
*/
if (res < 0) {
} else if (res == 0) {
if (repository_event_process(pg))
"incorrect IPfilter configuration\n");
} else {
/*
* The received event is a deletion of a service,
* instance or pg. If it's a deletion of an instance,
* update the instance's IPfilter configuration.
*/
continue;
(void) ipfilter_update(fmri);
}
}
}
/*NOTREACHED*/
}
int
main()
{
if (daemonize_self() == 1)
return (1);
assert(max_scf_fmri_size > 0);
assert(max_scf_name_size > 0);
scf_strerror(scf_error()));
return (1);
}
return (1);
}
inst = scf_instance_create(h);
snap = scf_snapshot_create(h);
scratch_pg = scf_pg_create(h);
scratch_v = scf_value_create(h);
scf_strerror(scf_error()));
return (1);
}
return (repository_event_wait());
}