4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * This file delivers svc.ipfd, the daemon that monitors changes to
29 * firewall capable services and requests IPfilter configuration update
30 * on behalf of the service. Essentially, the daemon listens for
31 * service changes and forks the program that update a service's
32 * IPfilter configuration.
34 * - A firewall capable SMF service can restrict network access to its
35 * service by providing a firewall policy that can be translated into
36 * a set of IPfilter rules. The mentioned firewall policy is stored in
37 * firewall_config and firewall_context property groups. If one of these
38 * two property groups exist, the service is considered to be firewall
41 * - A request to update service's IPfilter configuration is made for
42 * actions that affect service's configuration or running state. The
46 * - maintenance/clear maintenance
48 * Lacking a generic SMF mechanism to observe service state changes, the
49 * daemon observe change events by listening to changes to 'general',
50 * 'general_ovr', and 'restarter_actions' property groups. This is not a
51 * stable interface and should be replaced when a SMF supported mechanism
54 * - The program responsible for updating service's IPfilter configuration
55 * is /lib/svc/method/ipfilter. This program is called as:
57 * /lib/svc/method/ipfilter fw_update fmri
59 * where fmri the instance fmri of the service to be updated.
67 #include <sys/types.h>
73 #include <libscf_priv.h>
78 #define IPFILTER_FMRI "svc:/network/ipfilter:default"
79 #define RPCBIND_FMRI "svc:/network/rpc/bind:default"
80 #define IPF_UPDATE_CMD "/lib/svc/method/ipfilter"
82 #define SCF_SNAPSHOT_RUNNING "running"
83 #define SCF_PG_FW_CONTEXT "firewall_context"
84 #define SCF_PG_FW_CONFIG "firewall_config"
85 #define SCF_PG_REFRESH "refresh"
86 #define SCF_PG_INETD "inetd"
88 #define SCF_PROPERTY_ISRPC "isrpc"
91 #define DEV_NULL "/dev/null"
93 static scf_handle_t
*h
;
94 static ssize_t max_scf_fmri_size
;
95 static ssize_t max_scf_name_size
;
97 static scf_instance_t
*inst
;
98 static scf_snapshot_t
*snap
;
99 static scf_propertygroup_t
*scratch_pg
;
100 static scf_property_t
*scratch_prop
;
101 static scf_value_t
*scratch_v
;
103 static char *scratch_fmri
;
104 static char *scratch_name
;
106 static const char *all_props
[] = {
107 SCF_PROPERTY_REFRESH
, SCF_PROPERTY_RESTART
, SCF_PROPERTY_MAINT_ON
,
108 SCF_PROPERTY_MAINT_ON_IMMEDIATE
, SCF_PROPERTY_MAINT_ON_IMMTEMP
,
109 SCF_PROPERTY_MAINT_ON_TEMPORARY
, SCF_PROPERTY_MAINT_OFF
111 #define ALL_PROPS_CNT 7
113 static const char *maint_props
[] = {
114 SCF_PROPERTY_REFRESH
, SCF_PROPERTY_RESTART
, SCF_PROPERTY_MAINT_OFF
};
115 #define MAINT_PROPS_CNT 3
117 static int ipfilter_update(const char *);
125 (void) close(STDIN_FILENO
);
127 if ((fd
= open(DEV_NULL
, O_RDONLY
)) == -1) {
128 (void) printf("Could not open /dev/null: %s\n",
130 } else if (fd
!= STDIN_FILENO
) {
131 (void) dup2(fd
, STDIN_FILENO
);
134 (void) dup2(STDERR_FILENO
, STDOUT_FILENO
);
137 if ((pid
= fork1()) < 0) {
138 (void) printf("fork() failed: %s\n", strerror(errno
));
152 repository_rebind(scf_handle_t
*hndl
)
156 (void) scf_handle_unbind(hndl
);
157 while ((scf_handle_bind(hndl
)) != 0) {
159 syslog(LOG_ERR
| LOG_DAEMON
, "Repository access "
160 "unavailable. Couldn't bind handle: %s\n",
161 scf_strerror(scf_error()));
162 syslog(LOG_ERR
| LOG_DAEMON
, "Service specific"
163 "IPfilter configuration may not be updated "
176 repository_notify_setup(scf_handle_t
*h
)
179 if (_scf_notify_add_pgtype(h
, SCF_GROUP_FRAMEWORK
) ==
183 switch (scf_error()) {
184 case SCF_ERROR_CONNECTION_BROKEN
:
185 repository_rebind(h
);
188 case SCF_ERROR_NO_RESOURCES
:
193 syslog(LOG_ERR
| LOG_DAEMON
,
194 "Abort: Couldn't set up repository notification "
195 "for pg type %s: %s\n", SCF_GROUP_FRAMEWORK
,
196 scf_strerror(scf_error()));
203 * If the repository connection is lost, rebind and re-setup repository
204 * notification. During the repository connection outage, services that
205 * changed states wouldn't get the corresponding firewall update. To make
206 * we're not out of sync, update the entire system firewall configuration,
207 * invoke ipfilter_update(IPFILTER_FMRI).
212 repository_rebind(h
);
213 repository_notify_setup(h
);
214 if (ipfilter_update(IPFILTER_FMRI
) == -1) {
215 syslog(LOG_ERR
| LOG_DAEMON
,
216 "Failed to reconfigure system firewall.\n");
221 pg_get_prop_value(const scf_propertygroup_t
*pg
, const char *pname
,
224 if (pg
== NULL
|| pname
== NULL
|| v
== NULL
)
227 if (scf_pg_get_property(pg
, pname
, scratch_prop
) == -1 ||
228 scf_property_get_value(scratch_prop
, v
) == -1) {
229 switch (scf_error()) {
230 case SCF_ERROR_NOT_FOUND
:
231 case SCF_ERROR_DELETED
:
235 syslog(LOG_ERR
| LOG_DAEMON
,
236 "scf_pg_get_property failed for %s: %s\n",
237 pname
, scf_strerror(scf_error()));
245 is_correct_event(const char *fmri
, const scf_propertygroup_t
*pg
,
246 const boolean_t isrpc
)
249 const char **proplist
= all_props
;
250 int prop_cnt
= ALL_PROPS_CNT
;
254 if (scf_pg_get_name(pg
, scratch_name
, max_scf_name_size
) < 0) {
255 syslog(LOG_ERR
| LOG_DAEMON
, "scf_pg_get_name failed: %s\n",
256 scf_strerror(scf_error()));
261 * We care about enable, disable, and refresh since that's
262 * when we activate, deactivate, or change firewall policy.
264 * - enable/disable -> change in "general" or "general_ovr"
265 * - refresh/restart -> change in "restarter_actions"
267 if (strcmp(scratch_name
, SCF_PG_GENERAL
) == 0 ||
268 strcmp(scratch_name
, SCF_PG_GENERAL_OVR
) == 0) {
269 syslog(LOG_DEBUG
| LOG_DAEMON
, "Action: %s", scratch_name
);
273 if ((state
= smf_get_state(fmri
)) == NULL
) {
274 syslog(LOG_ERR
| LOG_DAEMON
, "smf_get_state failed for %s: "
275 "%s\n", fmri
, scf_strerror(scf_error()));
279 syslog(LOG_DEBUG
| LOG_DAEMON
, "%s STATE: %s \n", fmri
, state
);
280 if (strcmp(state
, SCF_STATE_STRING_MAINT
) == 0) {
281 proplist
= maint_props
;
282 prop_cnt
= MAINT_PROPS_CNT
;
286 * Only concerned with refresh, restart, and maint on|off actions.
287 * RPC services are restarted whenever rpc/bind restarts so it's
288 * an automatic valid event for RPC services.
293 } else if (strcmp(scratch_name
, SCF_PG_RESTARTER_ACTIONS
) == 0) {
294 for (i
= 0; i
< prop_cnt
; i
++) {
295 if (pg_get_prop_value(pg
, proplist
[i
],
297 syslog(LOG_DEBUG
| LOG_DAEMON
, "Action: %s/%s",
298 scratch_name
, proplist
[i
]);
313 ipfilter_update(const char *fmri
)
318 syslog(LOG_DEBUG
| LOG_DAEMON
, "ipfilter_update: %s\n", fmri
);
321 * Start refresh in another process
323 if ((pid
= fork1()) < 0) {
324 syslog(LOG_ERR
| LOG_DAEMON
, "Couldn't fork to refresh "
325 "ipfilter for %s: %s", fmri
, strerror(errno
));
331 if (execl(IPF_UPDATE_CMD
, IPF_UPDATE_CMD
, "fw_update", fmri
,
333 syslog(LOG_ERR
| LOG_DAEMON
, "execl() failed for "
334 "%s: %s", fmri
, strerror(errno
));
340 * Parent - only one update at a time.
342 (void) wait(&status
);
343 if (!WIFEXITED(status
) || WEXITSTATUS(status
) != 0)
348 syslog(LOG_ERR
| LOG_DAEMON
, "Firewall update failed "
355 * Determine whether a given instance is a RPC service. Repository and
356 * libscf errors are treated as if the service isn't an RPC service,
357 * returning B_FALSE to indicate validation failure.
360 service_is_rpc(const scf_instance_t
*inst
)
362 scf_snapshot_t
*lsnap
= NULL
;
365 if (scf_instance_get_snapshot(inst
, SCF_SNAPSHOT_RUNNING
, snap
) != 0) {
366 syslog(LOG_DEBUG
| LOG_DAEMON
,
367 "Could not get running snapshot, using editing value\n");
372 if (scf_instance_get_pg_composed(inst
, lsnap
, SCF_PG_INETD
,
374 switch (scf_error()) {
375 case SCF_ERROR_NOT_FOUND
:
376 case SCF_ERROR_DELETED
:
380 syslog(LOG_ERR
| LOG_DAEMON
,
381 "scf_instance_get_pg_composed failed: %s\n",
382 scf_strerror(scf_error()));
386 if (scf_instance_get_pg_composed(inst
, lsnap
,
387 SCF_PG_FW_CONTEXT
, scratch_pg
) == -1) {
388 switch (scf_error()) {
389 case SCF_ERROR_NOT_FOUND
:
390 case SCF_ERROR_DELETED
:
394 syslog(LOG_ERR
| LOG_DAEMON
,
395 "scf_instance_get_pg_composed failed: %s\n",
396 scf_strerror(scf_error()));
402 if (pg_get_prop_value(scratch_pg
, SCF_PROPERTY_ISRPC
, scratch_v
) == -1)
405 if (scf_value_get_boolean(scratch_v
, &isrpc
) == -1) {
406 syslog(LOG_ERR
| LOG_DAEMON
, "scf_value_get_boolean failed: "
407 "%s\n", scf_strerror(scf_error()));
418 instance_has_firewall(scf_instance_t
*inst
)
420 scf_snapshot_t
*lsnap
= NULL
;
422 if (scf_instance_get_snapshot(inst
, SCF_SNAPSHOT_RUNNING
, snap
) == -1) {
423 switch (scf_error()) {
424 case SCF_ERROR_CONNECTION_BROKEN
:
425 syslog(LOG_ERR
| LOG_DAEMON
,
426 "scf_instance_get_snapshot failed: %s\n",
427 scf_strerror(scf_error()));
431 case SCF_ERROR_DELETED
:
434 * If running snapshot is not available for
435 * other reasons, fall back to current values.
437 syslog(LOG_DEBUG
| LOG_DAEMON
, "Could not get "
438 "running snapshot, using current value\n");
445 * Update service's IPfilter configuration if either
446 * SCF_PG_FW_CONTEXT or SCF_PG_FW_CONFIG exists.
448 if (scf_instance_get_pg_composed(inst
, lsnap
, SCF_PG_FW_CONTEXT
,
452 switch (scf_error()) {
453 case SCF_ERROR_NOT_FOUND
:
454 case SCF_ERROR_DELETED
:
457 case SCF_ERROR_CONNECTION_BROKEN
:
461 syslog(LOG_ERR
| LOG_DAEMON
,
462 "scf_instance_get_pg_composed failed: %s\n",
463 scf_strerror(scf_error()));
468 if (scf_instance_get_pg_composed(inst
, lsnap
, SCF_PG_FW_CONFIG
,
471 * It's either a non-firewall service or a failure to
472 * read firewall pg, just continue and listen for
475 switch (scf_error()) {
476 case SCF_ERROR_NOT_FOUND
:
477 case SCF_ERROR_DELETED
:
480 case SCF_ERROR_CONNECTION_BROKEN
:
484 syslog(LOG_ERR
| LOG_DAEMON
,
485 "scf_instance_get_pg_composed failed: %s\n",
486 scf_strerror(scf_error()));
494 repository_event_process(scf_propertygroup_t
*pg
)
496 boolean_t isrpc
= B_FALSE
;
500 * Figure out it's a firewall capable instance and call ipfilter_update
503 if (scf_pg_get_parent_instance(pg
, inst
) == -1) {
504 /* Not an error if pg doesn't belong to a valid instance */
505 if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED
) {
509 syslog(LOG_ERR
| LOG_DAEMON
, "scf_pg_get_parent_instance "
510 "failed: %s\n", scf_strerror(scf_error()));
512 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN
)
518 if (scf_instance_to_fmri(inst
, scratch_fmri
, max_scf_fmri_size
) == -1) {
519 syslog(LOG_ERR
| LOG_DAEMON
, "scf_instance_to_fmri "
520 "failed: %s\n", scf_strerror(scf_error()));
522 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN
)
528 if (strcmp(scratch_fmri
, IPFILTER_FMRI
) == 0) {
532 isrpc
= service_is_rpc(inst
);
535 * If it's not an event we're interested in, returns success.
537 res
= is_correct_event(scratch_fmri
, pg
, isrpc
);
539 syslog(LOG_ERR
| LOG_DAEMON
,
540 "is_correct_event failed for %s.\n", scratch_fmri
);
542 } else if (res
== 0) {
547 * Proceed only if instance has firewall policy.
549 res
= instance_has_firewall(inst
);
551 syslog(LOG_ERR
| LOG_DAEMON
,
552 "instance_has_firewall failed for %s.\n", scratch_fmri
);
554 } else if (res
== 0) {
558 if (ipfilter_update(scratch_fmri
) == -1) {
566 repository_event_wait()
568 scf_propertygroup_t
*pg
;
569 char *fmri
, *scratch
;
570 const char *inst_name
, *pg_name
;
573 if ((fmri
= umem_alloc(max_scf_fmri_size
, UMEM_DEFAULT
)) == NULL
) {
574 syslog(LOG_ERR
| LOG_DAEMON
, "Out of memory");
578 if ((scratch
= umem_alloc(max_scf_fmri_size
, UMEM_DEFAULT
)) == NULL
) {
579 syslog(LOG_ERR
| LOG_DAEMON
, "Out of memory");
583 if ((pg
= scf_pg_create(h
)) == NULL
) {
584 syslog(LOG_ERR
| LOG_DAEMON
, "scf_pg_create failed: %s\n",
585 scf_strerror(scf_error()));
589 repository_notify_setup(h
);
593 * Calling _scf_notify_wait which will block this thread
594 * until it's notified of a framework event.
596 * Note: fmri is only set on delete events.
598 res
= _scf_notify_wait(pg
, fmri
, max_scf_fmri_size
);
600 syslog(LOG_ERR
| LOG_DAEMON
, "_scf_notify_wait "
601 "failed: %s\n", scf_strerror(scf_error()));
603 } else if (res
== 0) {
604 if (repository_event_process(pg
))
605 syslog(LOG_ERR
| LOG_DAEMON
, "Service may have "
606 "incorrect IPfilter configuration\n");
609 * The received event is a deletion of a service,
610 * instance or pg. If it's a deletion of an instance,
611 * update the instance's IPfilter configuration.
613 syslog(LOG_DEBUG
| LOG_DAEMON
, "Deleted: %s", fmri
);
615 (void) strlcpy(scratch
, fmri
, max_scf_fmri_size
);
616 if (scf_parse_svc_fmri(scratch
, NULL
, NULL
, &inst_name
,
617 &pg_name
, NULL
) != SCF_SUCCESS
)
620 if (inst_name
!= NULL
&& pg_name
== NULL
) {
621 (void) ipfilter_update(fmri
);
632 if (daemonize_self() == 1)
635 max_scf_fmri_size
= scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH
) + 1;
636 max_scf_name_size
= scf_limit(SCF_LIMIT_MAX_NAME_LENGTH
) + 1;
638 assert(max_scf_fmri_size
> 0);
639 assert(max_scf_name_size
> 0);
641 if ((h
= scf_handle_create(SCF_VERSION
)) == NULL
) {
642 syslog(LOG_ERR
| LOG_DAEMON
, "scf_handle_create failed: %s\n",
643 scf_strerror(scf_error()));
647 repository_rebind(h
);
649 scratch_fmri
= umem_alloc(max_scf_fmri_size
, UMEM_DEFAULT
);
650 scratch_name
= umem_alloc(max_scf_name_size
, UMEM_DEFAULT
);
652 if (scratch_fmri
== NULL
|| scratch_name
== NULL
) {
653 syslog(LOG_ERR
| LOG_DAEMON
, "Out of memory");
657 inst
= scf_instance_create(h
);
658 snap
= scf_snapshot_create(h
);
659 scratch_pg
= scf_pg_create(h
);
660 scratch_prop
= scf_property_create(h
);
661 scratch_v
= scf_value_create(h
);
663 if (inst
== NULL
|| snap
== NULL
|| scratch_pg
== NULL
||
664 scratch_prop
== NULL
|| scratch_v
== NULL
) {
665 syslog(LOG_ERR
| LOG_DAEMON
, "Initialization failed: %s\n",
666 scf_strerror(scf_error()));
670 return (repository_event_wait());