xref: /illumos-gate/usr/src/cmd/ipf/svc/ipfd.c (revision eb1a3463)
1*eb1a3463STruong Nguyen /*
2*eb1a3463STruong Nguyen  * CDDL HEADER START
3*eb1a3463STruong Nguyen  *
4*eb1a3463STruong Nguyen  * The contents of this file are subject to the terms of the
5*eb1a3463STruong Nguyen  * Common Development and Distribution License (the "License").
6*eb1a3463STruong Nguyen  * You may not use this file except in compliance with the License.
7*eb1a3463STruong Nguyen  *
8*eb1a3463STruong Nguyen  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*eb1a3463STruong Nguyen  * or http://www.opensolaris.org/os/licensing.
10*eb1a3463STruong Nguyen  * See the License for the specific language governing permissions
11*eb1a3463STruong Nguyen  * and limitations under the License.
12*eb1a3463STruong Nguyen  *
13*eb1a3463STruong Nguyen  * When distributing Covered Code, include this CDDL HEADER in each
14*eb1a3463STruong Nguyen  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*eb1a3463STruong Nguyen  * If applicable, add the following below this CDDL HEADER, with the
16*eb1a3463STruong Nguyen  * fields enclosed by brackets "[]" replaced with your own identifying
17*eb1a3463STruong Nguyen  * information: Portions Copyright [yyyy] [name of copyright owner]
18*eb1a3463STruong Nguyen  *
19*eb1a3463STruong Nguyen  * CDDL HEADER END
20*eb1a3463STruong Nguyen  */
21*eb1a3463STruong Nguyen 
22*eb1a3463STruong Nguyen /*
23*eb1a3463STruong Nguyen  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24*eb1a3463STruong Nguyen  * Use is subject to license terms.
25*eb1a3463STruong Nguyen  */
26*eb1a3463STruong Nguyen 
27*eb1a3463STruong Nguyen /*
28*eb1a3463STruong Nguyen  * This file delivers svc.ipfd, the daemon that monitors changes to
29*eb1a3463STruong Nguyen  * firewall capable services and requests IPfilter configuration update
30*eb1a3463STruong Nguyen  * on behalf of the service. Essentially, the daemon listens for
31*eb1a3463STruong Nguyen  * service changes and forks the program that update a service's
32*eb1a3463STruong Nguyen  * IPfilter configuration.
33*eb1a3463STruong Nguyen  *
34*eb1a3463STruong Nguyen  * - A firewall capable SMF service can restrict network access to its
35*eb1a3463STruong Nguyen  *   service by providing a firewall policy that can be translated into
36*eb1a3463STruong Nguyen  *   a set of IPfilter rules. The mentioned firewall policy is stored in
37*eb1a3463STruong Nguyen  *   firewall_config and firewall_context property groups. If one of these
38*eb1a3463STruong Nguyen  *   two property groups exist, the service is considered to be firewall
39*eb1a3463STruong Nguyen  *   capable.
40*eb1a3463STruong Nguyen  *
41*eb1a3463STruong Nguyen  * - A request to update service's IPfilter configuration is made for
42*eb1a3463STruong Nguyen  *   actions that affect service's configuration or running state. The
43*eb1a3463STruong Nguyen  *   actions are:
44*eb1a3463STruong Nguyen  *	- enable/disable
45*eb1a3463STruong Nguyen  *	- refresh/restart
46*eb1a3463STruong Nguyen  *	- maintenance/clear maintenance
47*eb1a3463STruong Nguyen  *
48*eb1a3463STruong Nguyen  * Lacking a generic SMF mechanism to observe service state changes, the
49*eb1a3463STruong Nguyen  * daemon observe change events by listening to changes to 'general',
50*eb1a3463STruong Nguyen  * 'general_ovr', and 'restarter_actions' property groups. This is not a
51*eb1a3463STruong Nguyen  * stable interface and should be replaced when a SMF supported mechanism
52*eb1a3463STruong Nguyen  * becomes available.
53*eb1a3463STruong Nguyen  *
54*eb1a3463STruong Nguyen  * - The program responsible for updating service's IPfilter configuration
55*eb1a3463STruong Nguyen  *   is /lib/svc/method/ipfilter. This program is called as:
56*eb1a3463STruong Nguyen  *
57*eb1a3463STruong Nguyen  *   /lib/svc/method/ipfilter fw_update fmri
58*eb1a3463STruong Nguyen  *
59*eb1a3463STruong Nguyen  *   where fmri the instance fmri of the service to be updated.
60*eb1a3463STruong Nguyen  */
61*eb1a3463STruong Nguyen 
62*eb1a3463STruong Nguyen #include <stdio.h>
63*eb1a3463STruong Nguyen #include <unistd.h>
64*eb1a3463STruong Nguyen #include <stdlib.h>
65*eb1a3463STruong Nguyen #include <assert.h>
66*eb1a3463STruong Nguyen #include <errno.h>
67*eb1a3463STruong Nguyen #include <sys/types.h>
68*eb1a3463STruong Nguyen #include <sys/stat.h>
69*eb1a3463STruong Nguyen #include <sys/wait.h>
70*eb1a3463STruong Nguyen #include <fcntl.h>
71*eb1a3463STruong Nguyen #include <umem.h>
72*eb1a3463STruong Nguyen #include <libscf.h>
73*eb1a3463STruong Nguyen #include <libscf_priv.h>
74*eb1a3463STruong Nguyen #include <signal.h>
75*eb1a3463STruong Nguyen #include <string.h>
76*eb1a3463STruong Nguyen #include <syslog.h>
77*eb1a3463STruong Nguyen 
78*eb1a3463STruong Nguyen #define	IPFILTER_FMRI		"svc:/network/ipfilter:default"
79*eb1a3463STruong Nguyen #define	RPCBIND_FMRI		"svc:/network/rpc/bind:default"
80*eb1a3463STruong Nguyen #define	IPF_UPDATE_CMD		"/lib/svc/method/ipfilter"
81*eb1a3463STruong Nguyen 
82*eb1a3463STruong Nguyen #define	SCF_SNAPSHOT_RUNNING	"running"
83*eb1a3463STruong Nguyen #define	SCF_PG_FW_CONTEXT	"firewall_context"
84*eb1a3463STruong Nguyen #define	SCF_PG_FW_CONFIG	"firewall_config"
85*eb1a3463STruong Nguyen #define	SCF_PG_REFRESH		"refresh"
86*eb1a3463STruong Nguyen #define	SCF_PG_INETD		"inetd"
87*eb1a3463STruong Nguyen 
88*eb1a3463STruong Nguyen #define	SCF_PROPERTY_ISRPC	"isrpc"
89*eb1a3463STruong Nguyen 
90*eb1a3463STruong Nguyen #define	MAX_RETRY		7
91*eb1a3463STruong Nguyen #define	DEV_NULL		"/dev/null"
92*eb1a3463STruong Nguyen 
93*eb1a3463STruong Nguyen static scf_handle_t *h;
94*eb1a3463STruong Nguyen static ssize_t max_scf_fmri_size;
95*eb1a3463STruong Nguyen static ssize_t max_scf_name_size;
96*eb1a3463STruong Nguyen 
97*eb1a3463STruong Nguyen static scf_instance_t *inst;
98*eb1a3463STruong Nguyen static scf_snapshot_t *snap;
99*eb1a3463STruong Nguyen static scf_propertygroup_t *scratch_pg;
100*eb1a3463STruong Nguyen static scf_property_t *scratch_prop;
101*eb1a3463STruong Nguyen static scf_value_t *scratch_v;
102*eb1a3463STruong Nguyen 
103*eb1a3463STruong Nguyen static char *scratch_fmri;
104*eb1a3463STruong Nguyen static char *scratch_name;
105*eb1a3463STruong Nguyen 
106*eb1a3463STruong Nguyen static const char *all_props[] = {
107*eb1a3463STruong Nguyen 	SCF_PROPERTY_REFRESH, SCF_PROPERTY_RESTART, SCF_PROPERTY_MAINT_ON,
108*eb1a3463STruong Nguyen 	SCF_PROPERTY_MAINT_ON_IMMEDIATE, SCF_PROPERTY_MAINT_ON_IMMTEMP,
109*eb1a3463STruong Nguyen 	SCF_PROPERTY_MAINT_ON_TEMPORARY, SCF_PROPERTY_MAINT_OFF
110*eb1a3463STruong Nguyen };
111*eb1a3463STruong Nguyen #define	ALL_PROPS_CNT		7
112*eb1a3463STruong Nguyen 
113*eb1a3463STruong Nguyen static const char *maint_props[] = {
114*eb1a3463STruong Nguyen 	SCF_PROPERTY_REFRESH, SCF_PROPERTY_RESTART, SCF_PROPERTY_MAINT_OFF };
115*eb1a3463STruong Nguyen #define	MAINT_PROPS_CNT		3
116*eb1a3463STruong Nguyen 
117*eb1a3463STruong Nguyen static int ipfilter_update(const char *);
118*eb1a3463STruong Nguyen 
119*eb1a3463STruong Nguyen static int
daemonize_self(void)120*eb1a3463STruong Nguyen daemonize_self(void)
121*eb1a3463STruong Nguyen {
122*eb1a3463STruong Nguyen 	pid_t pid;
123*eb1a3463STruong Nguyen 	int fd;
124*eb1a3463STruong Nguyen 
125*eb1a3463STruong Nguyen 	(void) close(STDIN_FILENO);
126*eb1a3463STruong Nguyen 
127*eb1a3463STruong Nguyen 	if ((fd = open(DEV_NULL, O_RDONLY)) == -1) {
128*eb1a3463STruong Nguyen 		(void) printf("Could not open /dev/null: %s\n",
129*eb1a3463STruong Nguyen 		    strerror(errno));
130*eb1a3463STruong Nguyen 	} else if (fd != STDIN_FILENO) {
131*eb1a3463STruong Nguyen 		(void) dup2(fd, STDIN_FILENO);
132*eb1a3463STruong Nguyen 		(void) close(fd);
133*eb1a3463STruong Nguyen 	}
134*eb1a3463STruong Nguyen 	(void) dup2(STDERR_FILENO, STDOUT_FILENO);
135*eb1a3463STruong Nguyen 	closefrom(3);
136*eb1a3463STruong Nguyen 
137*eb1a3463STruong Nguyen 	if ((pid = fork1()) < 0) {
138*eb1a3463STruong Nguyen 		(void) printf("fork() failed: %s\n", strerror(errno));
139*eb1a3463STruong Nguyen 		return (1);
140*eb1a3463STruong Nguyen 	}
141*eb1a3463STruong Nguyen 
142*eb1a3463STruong Nguyen 	if (pid != 0)
143*eb1a3463STruong Nguyen 		exit(0);
144*eb1a3463STruong Nguyen 
145*eb1a3463STruong Nguyen 	(void) setsid();
146*eb1a3463STruong Nguyen 	(void) chdir("/");
147*eb1a3463STruong Nguyen 
148*eb1a3463STruong Nguyen 	return (0);
149*eb1a3463STruong Nguyen }
150*eb1a3463STruong Nguyen 
151*eb1a3463STruong Nguyen static void
repository_rebind(scf_handle_t * hndl)152*eb1a3463STruong Nguyen repository_rebind(scf_handle_t *hndl)
153*eb1a3463STruong Nguyen {
154*eb1a3463STruong Nguyen 	int c = 0;
155*eb1a3463STruong Nguyen 
156*eb1a3463STruong Nguyen 	(void) scf_handle_unbind(hndl);
157*eb1a3463STruong Nguyen 	while ((scf_handle_bind(hndl)) != 0) {
158*eb1a3463STruong Nguyen 		if (c > MAX_RETRY) {
159*eb1a3463STruong Nguyen 			syslog(LOG_ERR | LOG_DAEMON, "Repository access "
160*eb1a3463STruong Nguyen 			    "unavailable. Couldn't bind handle: %s\n",
161*eb1a3463STruong Nguyen 			    scf_strerror(scf_error()));
162*eb1a3463STruong Nguyen 			syslog(LOG_ERR | LOG_DAEMON, "Service specific"
163*eb1a3463STruong Nguyen 			    "IPfilter configuration may not be updated "
164*eb1a3463STruong Nguyen 			    "properly\n");
165*eb1a3463STruong Nguyen 
166*eb1a3463STruong Nguyen 			exit(1);
167*eb1a3463STruong Nguyen 		} else {
168*eb1a3463STruong Nguyen 			c++;
169*eb1a3463STruong Nguyen 		}
170*eb1a3463STruong Nguyen 
171*eb1a3463STruong Nguyen 		(void) sleep(1);
172*eb1a3463STruong Nguyen 	}
173*eb1a3463STruong Nguyen }
174*eb1a3463STruong Nguyen 
175*eb1a3463STruong Nguyen static void
repository_notify_setup(scf_handle_t * h)176*eb1a3463STruong Nguyen repository_notify_setup(scf_handle_t *h)
177*eb1a3463STruong Nguyen {
178*eb1a3463STruong Nguyen 	for (;;) {
179*eb1a3463STruong Nguyen 		if (_scf_notify_add_pgtype(h, SCF_GROUP_FRAMEWORK) ==
180*eb1a3463STruong Nguyen 		    SCF_SUCCESS)
181*eb1a3463STruong Nguyen 			break;
182*eb1a3463STruong Nguyen 
183*eb1a3463STruong Nguyen 		switch (scf_error()) {
184*eb1a3463STruong Nguyen 		case SCF_ERROR_CONNECTION_BROKEN:
185*eb1a3463STruong Nguyen 			repository_rebind(h);
186*eb1a3463STruong Nguyen 			break;
187*eb1a3463STruong Nguyen 
188*eb1a3463STruong Nguyen 		case SCF_ERROR_NO_RESOURCES:
189*eb1a3463STruong Nguyen 			(void) sleep(1);
190*eb1a3463STruong Nguyen 			break;
191*eb1a3463STruong Nguyen 
192*eb1a3463STruong Nguyen 		default:
193*eb1a3463STruong Nguyen 			syslog(LOG_ERR | LOG_DAEMON,
194*eb1a3463STruong Nguyen 			    "Abort: Couldn't set up repository notification "
195*eb1a3463STruong Nguyen 			    "for pg type %s: %s\n", SCF_GROUP_FRAMEWORK,
196*eb1a3463STruong Nguyen 			    scf_strerror(scf_error()));
197*eb1a3463STruong Nguyen 			abort();
198*eb1a3463STruong Nguyen 		}
199*eb1a3463STruong Nguyen 	}
200*eb1a3463STruong Nguyen }
201*eb1a3463STruong Nguyen 
202*eb1a3463STruong Nguyen /*
203*eb1a3463STruong Nguyen  * If the repository connection is lost, rebind and re-setup repository
204*eb1a3463STruong Nguyen  * notification. During the repository connection outage, services that
205*eb1a3463STruong Nguyen  * changed states wouldn't get the corresponding firewall update. To make
206*eb1a3463STruong Nguyen  * we're not out of sync, update the entire system firewall configuration,
207*eb1a3463STruong Nguyen  * invoke ipfilter_update(IPFILTER_FMRI).
208*eb1a3463STruong Nguyen  */
209*eb1a3463STruong Nguyen static void
repository_setup()210*eb1a3463STruong Nguyen repository_setup()
211*eb1a3463STruong Nguyen {
212*eb1a3463STruong Nguyen 	repository_rebind(h);
213*eb1a3463STruong Nguyen 	repository_notify_setup(h);
214*eb1a3463STruong Nguyen 	if (ipfilter_update(IPFILTER_FMRI) == -1) {
215*eb1a3463STruong Nguyen 		syslog(LOG_ERR | LOG_DAEMON,
216*eb1a3463STruong Nguyen 		    "Failed to reconfigure system firewall.\n");
217*eb1a3463STruong Nguyen 	}
218*eb1a3463STruong Nguyen }
219*eb1a3463STruong Nguyen 
220*eb1a3463STruong Nguyen static int
pg_get_prop_value(const scf_propertygroup_t * pg,const char * pname,scf_value_t * v)221*eb1a3463STruong Nguyen pg_get_prop_value(const scf_propertygroup_t *pg, const char *pname,
222*eb1a3463STruong Nguyen     scf_value_t *v)
223*eb1a3463STruong Nguyen {
224*eb1a3463STruong Nguyen 	if (pg == NULL || pname == NULL || v == NULL)
225*eb1a3463STruong Nguyen 		return (-1);
226*eb1a3463STruong Nguyen 
227*eb1a3463STruong Nguyen 	if (scf_pg_get_property(pg, pname, scratch_prop) == -1 ||
228*eb1a3463STruong Nguyen 	    scf_property_get_value(scratch_prop, v) == -1) {
229*eb1a3463STruong Nguyen 		switch (scf_error()) {
230*eb1a3463STruong Nguyen 		case SCF_ERROR_NOT_FOUND:
231*eb1a3463STruong Nguyen 		case SCF_ERROR_DELETED:
232*eb1a3463STruong Nguyen 			break;
233*eb1a3463STruong Nguyen 
234*eb1a3463STruong Nguyen 		default:
235*eb1a3463STruong Nguyen 			syslog(LOG_ERR | LOG_DAEMON,
236*eb1a3463STruong Nguyen 			    "scf_pg_get_property failed for %s: %s\n",
237*eb1a3463STruong Nguyen 			    pname, scf_strerror(scf_error()));
238*eb1a3463STruong Nguyen 		}
239*eb1a3463STruong Nguyen 		return (-1);
240*eb1a3463STruong Nguyen 	}
241*eb1a3463STruong Nguyen 	return (0);
242*eb1a3463STruong Nguyen }
243*eb1a3463STruong Nguyen 
244*eb1a3463STruong Nguyen static int
is_correct_event(const char * fmri,const scf_propertygroup_t * pg,const boolean_t isrpc)245*eb1a3463STruong Nguyen is_correct_event(const char *fmri, const scf_propertygroup_t *pg,
246*eb1a3463STruong Nguyen     const boolean_t isrpc)
247*eb1a3463STruong Nguyen {
248*eb1a3463STruong Nguyen 	char *state = NULL;
249*eb1a3463STruong Nguyen 	const char **proplist = all_props;
250*eb1a3463STruong Nguyen 	int prop_cnt = ALL_PROPS_CNT;
251*eb1a3463STruong Nguyen 
252*eb1a3463STruong Nguyen 	int i, ret = 0;
253*eb1a3463STruong Nguyen 
254*eb1a3463STruong Nguyen 	if (scf_pg_get_name(pg, scratch_name, max_scf_name_size) < 0) {
255*eb1a3463STruong Nguyen 		syslog(LOG_ERR | LOG_DAEMON, "scf_pg_get_name failed: %s\n",
256*eb1a3463STruong Nguyen 		    scf_strerror(scf_error()));
257*eb1a3463STruong Nguyen 		return (-1);
258*eb1a3463STruong Nguyen 	}
259*eb1a3463STruong Nguyen 
260*eb1a3463STruong Nguyen 	/*
261*eb1a3463STruong Nguyen 	 * We care about enable, disable, and refresh since that's
262*eb1a3463STruong Nguyen 	 * when we activate, deactivate, or change firewall policy.
263*eb1a3463STruong Nguyen 	 *
264*eb1a3463STruong Nguyen 	 *  - enable/disable -> change in "general" or "general_ovr"
265*eb1a3463STruong Nguyen 	 *  - refresh/restart -> change in "restarter_actions"
266*eb1a3463STruong Nguyen 	 */
267*eb1a3463STruong Nguyen 	if (strcmp(scratch_name, SCF_PG_GENERAL) == 0 ||
268*eb1a3463STruong Nguyen 	    strcmp(scratch_name, SCF_PG_GENERAL_OVR) == 0) {
269*eb1a3463STruong Nguyen 		syslog(LOG_DEBUG | LOG_DAEMON, "Action: %s", scratch_name);
270*eb1a3463STruong Nguyen 		return (1);
271*eb1a3463STruong Nguyen 	}
272*eb1a3463STruong Nguyen 
273*eb1a3463STruong Nguyen 	if ((state = smf_get_state(fmri)) == NULL) {
274*eb1a3463STruong Nguyen 		syslog(LOG_ERR | LOG_DAEMON, "smf_get_state failed for %s: "
275*eb1a3463STruong Nguyen 		    "%s\n", fmri, scf_strerror(scf_error()));
276*eb1a3463STruong Nguyen 		return (-1);
277*eb1a3463STruong Nguyen 	}
278*eb1a3463STruong Nguyen 
279*eb1a3463STruong Nguyen 	syslog(LOG_DEBUG | LOG_DAEMON, "%s STATE: %s \n", fmri, state);
280*eb1a3463STruong Nguyen 	if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) {
281*eb1a3463STruong Nguyen 		proplist = maint_props;
282*eb1a3463STruong Nguyen 		prop_cnt = MAINT_PROPS_CNT;
283*eb1a3463STruong Nguyen 	}
284*eb1a3463STruong Nguyen 
285*eb1a3463STruong Nguyen 	/*
286*eb1a3463STruong Nguyen 	 * Only concerned with refresh, restart, and maint on|off actions.
287*eb1a3463STruong Nguyen 	 * RPC services are restarted whenever rpc/bind restarts so it's
288*eb1a3463STruong Nguyen 	 * an automatic valid event for RPC services.
289*eb1a3463STruong Nguyen 	 */
290*eb1a3463STruong Nguyen 	if (isrpc) {
291*eb1a3463STruong Nguyen 		ret = 1;
292*eb1a3463STruong Nguyen 		goto out;
293*eb1a3463STruong Nguyen 	} else if (strcmp(scratch_name, SCF_PG_RESTARTER_ACTIONS) == 0) {
294*eb1a3463STruong Nguyen 		for (i = 0; i < prop_cnt; i++) {
295*eb1a3463STruong Nguyen 			if (pg_get_prop_value(pg, proplist[i],
296*eb1a3463STruong Nguyen 			    scratch_v) == 0) {
297*eb1a3463STruong Nguyen 				syslog(LOG_DEBUG | LOG_DAEMON, "Action: %s/%s",
298*eb1a3463STruong Nguyen 				    scratch_name, proplist[i]);
299*eb1a3463STruong Nguyen 
300*eb1a3463STruong Nguyen 				ret = 1;
301*eb1a3463STruong Nguyen 				goto out;
302*eb1a3463STruong Nguyen 			}
303*eb1a3463STruong Nguyen 		}
304*eb1a3463STruong Nguyen 	}
305*eb1a3463STruong Nguyen 
306*eb1a3463STruong Nguyen out:
307*eb1a3463STruong Nguyen 	if (state)
308*eb1a3463STruong Nguyen 		free(state);
309*eb1a3463STruong Nguyen 
310*eb1a3463STruong Nguyen 	return (ret);
311*eb1a3463STruong Nguyen }
312*eb1a3463STruong Nguyen 
313*eb1a3463STruong Nguyen static int
ipfilter_update(const char * fmri)314*eb1a3463STruong Nguyen ipfilter_update(const char *fmri)
315*eb1a3463STruong Nguyen {
316*eb1a3463STruong Nguyen 	pid_t pid;
317*eb1a3463STruong Nguyen 	int status, ret = 0;
318*eb1a3463STruong Nguyen 
319*eb1a3463STruong Nguyen 	syslog(LOG_DEBUG | LOG_DAEMON, "ipfilter_update: %s\n", fmri);
320*eb1a3463STruong Nguyen 
321*eb1a3463STruong Nguyen 	/*
322*eb1a3463STruong Nguyen 	 * Start refresh in another process
323*eb1a3463STruong Nguyen 	 */
324*eb1a3463STruong Nguyen 	if ((pid = fork1()) < 0) {
325*eb1a3463STruong Nguyen 		syslog(LOG_ERR | LOG_DAEMON, "Couldn't fork to refresh "
326*eb1a3463STruong Nguyen 		    "ipfilter for %s: %s", fmri, strerror(errno));
327*eb1a3463STruong Nguyen 		ret = 1;
328*eb1a3463STruong Nguyen 		goto out;
329*eb1a3463STruong Nguyen 	}
330*eb1a3463STruong Nguyen 
331*eb1a3463STruong Nguyen 	if (pid == 0) {
332*eb1a3463STruong Nguyen 		if (execl(IPF_UPDATE_CMD, IPF_UPDATE_CMD, "fw_update", fmri,
333*eb1a3463STruong Nguyen 		    NULL) == -1)
334*eb1a3463STruong Nguyen 			syslog(LOG_ERR | LOG_DAEMON, "execl() failed for "
335*eb1a3463STruong Nguyen 			    "%s: %s", fmri, strerror(errno));
336*eb1a3463STruong Nguyen 
337*eb1a3463STruong Nguyen 		exit(1);
338*eb1a3463STruong Nguyen 	}
339*eb1a3463STruong Nguyen 
340*eb1a3463STruong Nguyen 	/*
341*eb1a3463STruong Nguyen 	 * Parent - only one update at a time.
342*eb1a3463STruong Nguyen 	 */
343*eb1a3463STruong Nguyen 	(void) wait(&status);
344*eb1a3463STruong Nguyen 	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
345*eb1a3463STruong Nguyen 		ret = 1;
346*eb1a3463STruong Nguyen 
347*eb1a3463STruong Nguyen out:
348*eb1a3463STruong Nguyen 	if (ret == 1)
349*eb1a3463STruong Nguyen 		syslog(LOG_ERR | LOG_DAEMON, "Firewall update failed "
350*eb1a3463STruong Nguyen 		    "for: %s\n", fmri);
351*eb1a3463STruong Nguyen 
352*eb1a3463STruong Nguyen 	return (ret);
353*eb1a3463STruong Nguyen }
354*eb1a3463STruong Nguyen 
355*eb1a3463STruong Nguyen /*
356*eb1a3463STruong Nguyen  * Determine whether a given instance is a RPC service. Repository and
357*eb1a3463STruong Nguyen  * libscf errors are treated as if the service isn't an RPC service,
358*eb1a3463STruong Nguyen  * returning B_FALSE to indicate validation failure.
359*eb1a3463STruong Nguyen  */
360*eb1a3463STruong Nguyen static boolean_t
service_is_rpc(const scf_instance_t * inst)361*eb1a3463STruong Nguyen service_is_rpc(const scf_instance_t *inst)
362*eb1a3463STruong Nguyen {
363*eb1a3463STruong Nguyen 	scf_snapshot_t *lsnap = NULL;
364*eb1a3463STruong Nguyen 	uint8_t	isrpc;
365*eb1a3463STruong Nguyen 
366*eb1a3463STruong Nguyen 	if (scf_instance_get_snapshot(inst, SCF_SNAPSHOT_RUNNING, snap) != 0) {
367*eb1a3463STruong Nguyen 		syslog(LOG_DEBUG | LOG_DAEMON,
368*eb1a3463STruong Nguyen 		    "Could not get running snapshot, using editing value\n");
369*eb1a3463STruong Nguyen 	} else {
370*eb1a3463STruong Nguyen 		lsnap = snap;
371*eb1a3463STruong Nguyen 	}
372*eb1a3463STruong Nguyen 
373*eb1a3463STruong Nguyen 	if (scf_instance_get_pg_composed(inst, lsnap, SCF_PG_INETD,
374*eb1a3463STruong Nguyen 	    scratch_pg) == -1) {
375*eb1a3463STruong Nguyen 		switch (scf_error()) {
376*eb1a3463STruong Nguyen 		case SCF_ERROR_NOT_FOUND:
377*eb1a3463STruong Nguyen 		case SCF_ERROR_DELETED:
378*eb1a3463STruong Nguyen 			break;
379*eb1a3463STruong Nguyen 
380*eb1a3463STruong Nguyen 		default:
381*eb1a3463STruong Nguyen 			syslog(LOG_ERR | LOG_DAEMON,
382*eb1a3463STruong Nguyen 			    "scf_instance_get_pg_composed failed: %s\n",
383*eb1a3463STruong Nguyen 			    scf_strerror(scf_error()));
384*eb1a3463STruong Nguyen 			return (B_FALSE);
385*eb1a3463STruong Nguyen 		}
386*eb1a3463STruong Nguyen 
387*eb1a3463STruong Nguyen 		if (scf_instance_get_pg_composed(inst, lsnap,
388*eb1a3463STruong Nguyen 		    SCF_PG_FW_CONTEXT, scratch_pg) == -1) {
389*eb1a3463STruong Nguyen 			switch (scf_error()) {
390*eb1a3463STruong Nguyen 			case SCF_ERROR_NOT_FOUND:
391*eb1a3463STruong Nguyen 			case SCF_ERROR_DELETED:
392*eb1a3463STruong Nguyen 				break;
393*eb1a3463STruong Nguyen 
394*eb1a3463STruong Nguyen 			default:
395*eb1a3463STruong Nguyen 				syslog(LOG_ERR | LOG_DAEMON,
396*eb1a3463STruong Nguyen 				    "scf_instance_get_pg_composed failed: %s\n",
397*eb1a3463STruong Nguyen 				    scf_strerror(scf_error()));
398*eb1a3463STruong Nguyen 			}
399*eb1a3463STruong Nguyen 			return (B_FALSE);
400*eb1a3463STruong Nguyen 		}
401*eb1a3463STruong Nguyen 	}
402*eb1a3463STruong Nguyen 
403*eb1a3463STruong Nguyen 	if (pg_get_prop_value(scratch_pg, SCF_PROPERTY_ISRPC, scratch_v) == -1)
404*eb1a3463STruong Nguyen 		return (B_FALSE);
405*eb1a3463STruong Nguyen 
406*eb1a3463STruong Nguyen 	if (scf_value_get_boolean(scratch_v, &isrpc) == -1) {
407*eb1a3463STruong Nguyen 		syslog(LOG_ERR | LOG_DAEMON, "scf_value_get_boolean failed: "
408*eb1a3463STruong Nguyen 		    "%s\n", scf_strerror(scf_error()));
409*eb1a3463STruong Nguyen 		return (B_FALSE);
410*eb1a3463STruong Nguyen 	}
411*eb1a3463STruong Nguyen 
412*eb1a3463STruong Nguyen 	if (isrpc)
413*eb1a3463STruong Nguyen 		return (B_TRUE);
414*eb1a3463STruong Nguyen 	else
415*eb1a3463STruong Nguyen 		return (B_FALSE);
416*eb1a3463STruong Nguyen }
417*eb1a3463STruong Nguyen 
418*eb1a3463STruong Nguyen static int
instance_has_firewall(scf_instance_t * inst)419*eb1a3463STruong Nguyen instance_has_firewall(scf_instance_t *inst)
420*eb1a3463STruong Nguyen {
421*eb1a3463STruong Nguyen 	scf_snapshot_t *lsnap = NULL;
422*eb1a3463STruong Nguyen 
423*eb1a3463STruong Nguyen 	if (scf_instance_get_snapshot(inst, SCF_SNAPSHOT_RUNNING, snap) == -1) {
424*eb1a3463STruong Nguyen 		switch (scf_error()) {
425*eb1a3463STruong Nguyen 		case SCF_ERROR_CONNECTION_BROKEN:
426*eb1a3463STruong Nguyen 			syslog(LOG_ERR | LOG_DAEMON,
427*eb1a3463STruong Nguyen 			    "scf_instance_get_snapshot failed: %s\n",
428*eb1a3463STruong Nguyen 			    scf_strerror(scf_error()));
429*eb1a3463STruong Nguyen 			repository_setup();
430*eb1a3463STruong Nguyen 			return (-1);
431*eb1a3463STruong Nguyen 
432*eb1a3463STruong Nguyen 		case SCF_ERROR_DELETED:
433*eb1a3463STruong Nguyen 		default:
434*eb1a3463STruong Nguyen 			/*
435*eb1a3463STruong Nguyen 			 * If running snapshot is not available for
436*eb1a3463STruong Nguyen 			 * other reasons, fall back to current values.
437*eb1a3463STruong Nguyen 			 */
438*eb1a3463STruong Nguyen 			syslog(LOG_DEBUG | LOG_DAEMON, "Could not get "
439*eb1a3463STruong Nguyen 			    "running snapshot, using current value\n");
440*eb1a3463STruong Nguyen 		}
441*eb1a3463STruong Nguyen 	} else {
442*eb1a3463STruong Nguyen 		lsnap = snap;
443*eb1a3463STruong Nguyen 	}
444*eb1a3463STruong Nguyen 
445*eb1a3463STruong Nguyen 	/*
446*eb1a3463STruong Nguyen 	 * Update service's IPfilter configuration if either
447*eb1a3463STruong Nguyen 	 * SCF_PG_FW_CONTEXT or SCF_PG_FW_CONFIG exists.
448*eb1a3463STruong Nguyen 	 */
449*eb1a3463STruong Nguyen 	if (scf_instance_get_pg_composed(inst, lsnap, SCF_PG_FW_CONTEXT,
450*eb1a3463STruong Nguyen 	    scratch_pg) == 0) {
451*eb1a3463STruong Nguyen 		return (1);
452*eb1a3463STruong Nguyen 	} else {
453*eb1a3463STruong Nguyen 		switch (scf_error()) {
454*eb1a3463STruong Nguyen 		case SCF_ERROR_NOT_FOUND:
455*eb1a3463STruong Nguyen 		case SCF_ERROR_DELETED:
456*eb1a3463STruong Nguyen 			break;
457*eb1a3463STruong Nguyen 
458*eb1a3463STruong Nguyen 		case SCF_ERROR_CONNECTION_BROKEN:
459*eb1a3463STruong Nguyen 			repository_setup();
460*eb1a3463STruong Nguyen 			/* FALLTHROUGH */
461*eb1a3463STruong Nguyen 		default:
462*eb1a3463STruong Nguyen 			syslog(LOG_ERR | LOG_DAEMON,
463*eb1a3463STruong Nguyen 			    "scf_instance_get_pg_composed failed: %s\n",
464*eb1a3463STruong Nguyen 			    scf_strerror(scf_error()));
465*eb1a3463STruong Nguyen 			return (-1);
466*eb1a3463STruong Nguyen 		}
467*eb1a3463STruong Nguyen 	}
468*eb1a3463STruong Nguyen 
469*eb1a3463STruong Nguyen 	if (scf_instance_get_pg_composed(inst, lsnap, SCF_PG_FW_CONFIG,
470*eb1a3463STruong Nguyen 	    scratch_pg) == -1) {
471*eb1a3463STruong Nguyen 		/*
472*eb1a3463STruong Nguyen 		 * It's either a non-firewall service or a failure to
473*eb1a3463STruong Nguyen 		 * read firewall pg, just continue and listen for
474*eb1a3463STruong Nguyen 		 * future events.
475*eb1a3463STruong Nguyen 		 */
476*eb1a3463STruong Nguyen 		switch (scf_error()) {
477*eb1a3463STruong Nguyen 		case SCF_ERROR_NOT_FOUND:
478*eb1a3463STruong Nguyen 		case SCF_ERROR_DELETED:
479*eb1a3463STruong Nguyen 			return (0);
480*eb1a3463STruong Nguyen 
481*eb1a3463STruong Nguyen 		case SCF_ERROR_CONNECTION_BROKEN:
482*eb1a3463STruong Nguyen 			repository_setup();
483*eb1a3463STruong Nguyen 			/* FALLTHROUGH */
484*eb1a3463STruong Nguyen 		default:
485*eb1a3463STruong Nguyen 			syslog(LOG_ERR | LOG_DAEMON,
486*eb1a3463STruong Nguyen 			    "scf_instance_get_pg_composed failed: %s\n",
487*eb1a3463STruong Nguyen 			    scf_strerror(scf_error()));
488*eb1a3463STruong Nguyen 			return (-1);
489*eb1a3463STruong Nguyen 		}
490*eb1a3463STruong Nguyen 	}
491*eb1a3463STruong Nguyen 	return (1);
492*eb1a3463STruong Nguyen }
493*eb1a3463STruong Nguyen 
494*eb1a3463STruong Nguyen static int
repository_event_process(scf_propertygroup_t * pg)495*eb1a3463STruong Nguyen repository_event_process(scf_propertygroup_t *pg)
496*eb1a3463STruong Nguyen {
497*eb1a3463STruong Nguyen 	boolean_t isrpc = B_FALSE;
498*eb1a3463STruong Nguyen 	int res;
499*eb1a3463STruong Nguyen 
500*eb1a3463STruong Nguyen 	/*
501*eb1a3463STruong Nguyen 	 * Figure out it's a firewall capable instance and call ipfilter_update
502*eb1a3463STruong Nguyen 	 * if it is.
503*eb1a3463STruong Nguyen 	 */
504*eb1a3463STruong Nguyen 	if (scf_pg_get_parent_instance(pg, inst) == -1) {
505*eb1a3463STruong Nguyen 		/* Not an error if pg doesn't belong to a valid instance */
506*eb1a3463STruong Nguyen 		if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
507*eb1a3463STruong Nguyen 			return (0);
508*eb1a3463STruong Nguyen 		}
509*eb1a3463STruong Nguyen 
510*eb1a3463STruong Nguyen 		syslog(LOG_ERR | LOG_DAEMON, "scf_pg_get_parent_instance "
511*eb1a3463STruong Nguyen 		    "failed: %s\n", scf_strerror(scf_error()));
512*eb1a3463STruong Nguyen 
513*eb1a3463STruong Nguyen 		if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
514*eb1a3463STruong Nguyen 			repository_setup();
515*eb1a3463STruong Nguyen 
516*eb1a3463STruong Nguyen 		return (1);
517*eb1a3463STruong Nguyen 	}
518*eb1a3463STruong Nguyen 
519*eb1a3463STruong Nguyen 	if (scf_instance_to_fmri(inst, scratch_fmri, max_scf_fmri_size) == -1) {
520*eb1a3463STruong Nguyen 		syslog(LOG_ERR | LOG_DAEMON, "scf_instance_to_fmri "
521*eb1a3463STruong Nguyen 		    "failed: %s\n", scf_strerror(scf_error()));
522*eb1a3463STruong Nguyen 
523*eb1a3463STruong Nguyen 		if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
524*eb1a3463STruong Nguyen 			repository_setup();
525*eb1a3463STruong Nguyen 
526*eb1a3463STruong Nguyen 		return (1);
527*eb1a3463STruong Nguyen 	}
528*eb1a3463STruong Nguyen 
529*eb1a3463STruong Nguyen 	if (strcmp(scratch_fmri, IPFILTER_FMRI) == 0) {
530*eb1a3463STruong Nguyen 		return (0);
531*eb1a3463STruong Nguyen 	}
532*eb1a3463STruong Nguyen 
533*eb1a3463STruong Nguyen 	isrpc = service_is_rpc(inst);
534*eb1a3463STruong Nguyen 
535*eb1a3463STruong Nguyen 	/*
536*eb1a3463STruong Nguyen 	 * If it's not an event we're interested in, returns success.
537*eb1a3463STruong Nguyen 	 */
538*eb1a3463STruong Nguyen 	res = is_correct_event(scratch_fmri, pg, isrpc);
539*eb1a3463STruong Nguyen 	if (res == -1) {
540*eb1a3463STruong Nguyen 		syslog(LOG_ERR | LOG_DAEMON,
541*eb1a3463STruong Nguyen 		    "is_correct_event failed for %s.\n", scratch_fmri);
542*eb1a3463STruong Nguyen 		return (1);
543*eb1a3463STruong Nguyen 	} else if (res == 0) {
544*eb1a3463STruong Nguyen 		return (0);
545*eb1a3463STruong Nguyen 	}
546*eb1a3463STruong Nguyen 
547*eb1a3463STruong Nguyen 	/*
548*eb1a3463STruong Nguyen 	 * Proceed only if instance has firewall policy.
549*eb1a3463STruong Nguyen 	 */
550*eb1a3463STruong Nguyen 	res = instance_has_firewall(inst);
551*eb1a3463STruong Nguyen 	if (res == -1) {
552*eb1a3463STruong Nguyen 		syslog(LOG_ERR | LOG_DAEMON,
553*eb1a3463STruong Nguyen 		    "instance_has_firewall failed for %s.\n", scratch_fmri);
554*eb1a3463STruong Nguyen 		return (1);
555*eb1a3463STruong Nguyen 	} else if (res == 0) {
556*eb1a3463STruong Nguyen 		return (0);
557*eb1a3463STruong Nguyen 	}
558*eb1a3463STruong Nguyen 
559*eb1a3463STruong Nguyen 	if (ipfilter_update(scratch_fmri) == -1) {
560*eb1a3463STruong Nguyen 		return (1);
561*eb1a3463STruong Nguyen 	}
562*eb1a3463STruong Nguyen 
563*eb1a3463STruong Nguyen 	return (0);
564*eb1a3463STruong Nguyen }
565*eb1a3463STruong Nguyen 
566*eb1a3463STruong Nguyen static int
repository_event_wait()567*eb1a3463STruong Nguyen repository_event_wait()
568*eb1a3463STruong Nguyen {
569*eb1a3463STruong Nguyen 	scf_propertygroup_t *pg;
570*eb1a3463STruong Nguyen 	char *fmri, *scratch;
571*eb1a3463STruong Nguyen 	const char *inst_name, *pg_name;
572*eb1a3463STruong Nguyen 	ssize_t res;
573*eb1a3463STruong Nguyen 
574*eb1a3463STruong Nguyen 	if ((fmri = umem_alloc(max_scf_fmri_size, UMEM_DEFAULT)) == NULL) {
575*eb1a3463STruong Nguyen 		syslog(LOG_ERR | LOG_DAEMON, "Out of memory");
576*eb1a3463STruong Nguyen 		return (1);
577*eb1a3463STruong Nguyen 	}
578*eb1a3463STruong Nguyen 
579*eb1a3463STruong Nguyen 	if ((scratch = umem_alloc(max_scf_fmri_size, UMEM_DEFAULT)) == NULL) {
580*eb1a3463STruong Nguyen 		syslog(LOG_ERR | LOG_DAEMON, "Out of memory");
581*eb1a3463STruong Nguyen 		return (1);
582*eb1a3463STruong Nguyen 	}
583*eb1a3463STruong Nguyen 
584*eb1a3463STruong Nguyen 	if ((pg = scf_pg_create(h)) == NULL) {
585*eb1a3463STruong Nguyen 		syslog(LOG_ERR | LOG_DAEMON, "scf_pg_create failed: %s\n",
586*eb1a3463STruong Nguyen 		    scf_strerror(scf_error()));
587*eb1a3463STruong Nguyen 		return (1);
588*eb1a3463STruong Nguyen 	}
589*eb1a3463STruong Nguyen 
590*eb1a3463STruong Nguyen 	repository_notify_setup(h);
591*eb1a3463STruong Nguyen 
592*eb1a3463STruong Nguyen 	for (;;) {
593*eb1a3463STruong Nguyen 		/*
594*eb1a3463STruong Nguyen 		 * Calling _scf_notify_wait which will block this thread
595*eb1a3463STruong Nguyen 		 * until it's notified of a framework event.
596*eb1a3463STruong Nguyen 		 *
597*eb1a3463STruong Nguyen 		 * Note: fmri is only set on delete events.
598*eb1a3463STruong Nguyen 		 */
599*eb1a3463STruong Nguyen 		res = _scf_notify_wait(pg, fmri, max_scf_fmri_size);
600*eb1a3463STruong Nguyen 		if (res < 0) {
601*eb1a3463STruong Nguyen 			syslog(LOG_ERR | LOG_DAEMON, "_scf_notify_wait "
602*eb1a3463STruong Nguyen 			    "failed: %s\n", scf_strerror(scf_error()));
603*eb1a3463STruong Nguyen 			repository_setup();
604*eb1a3463STruong Nguyen 		} else if (res == 0) {
605*eb1a3463STruong Nguyen 			if (repository_event_process(pg))
606*eb1a3463STruong Nguyen 				syslog(LOG_ERR | LOG_DAEMON, "Service may have "
607*eb1a3463STruong Nguyen 				    "incorrect IPfilter configuration\n");
608*eb1a3463STruong Nguyen 		} else {
609*eb1a3463STruong Nguyen 			/*
610*eb1a3463STruong Nguyen 			 * The received event is a deletion of a service,
611*eb1a3463STruong Nguyen 			 * instance or pg. If it's a deletion of an instance,
612*eb1a3463STruong Nguyen 			 * update the instance's IPfilter configuration.
613*eb1a3463STruong Nguyen 			 */
614*eb1a3463STruong Nguyen 			syslog(LOG_DEBUG | LOG_DAEMON, "Deleted: %s", fmri);
615*eb1a3463STruong Nguyen 
616*eb1a3463STruong Nguyen 			(void) strlcpy(scratch, fmri, max_scf_fmri_size);
617*eb1a3463STruong Nguyen 			if (scf_parse_svc_fmri(scratch, NULL, NULL, &inst_name,
618*eb1a3463STruong Nguyen 			    &pg_name, NULL) != SCF_SUCCESS)
619*eb1a3463STruong Nguyen 				continue;
620*eb1a3463STruong Nguyen 
621*eb1a3463STruong Nguyen 			if (inst_name != NULL && pg_name == NULL) {
622*eb1a3463STruong Nguyen 				(void) ipfilter_update(fmri);
623*eb1a3463STruong Nguyen 			}
624*eb1a3463STruong Nguyen 		}
625*eb1a3463STruong Nguyen 	}
626*eb1a3463STruong Nguyen 
627*eb1a3463STruong Nguyen 	/*NOTREACHED*/
628*eb1a3463STruong Nguyen }
629*eb1a3463STruong Nguyen 
630*eb1a3463STruong Nguyen int
main()631*eb1a3463STruong Nguyen main()
632*eb1a3463STruong Nguyen {
633*eb1a3463STruong Nguyen 	if (daemonize_self() == 1)
634*eb1a3463STruong Nguyen 		return (1);
635*eb1a3463STruong Nguyen 
636*eb1a3463STruong Nguyen 	max_scf_fmri_size = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH) + 1;
637*eb1a3463STruong Nguyen 	max_scf_name_size = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
638*eb1a3463STruong Nguyen 
639*eb1a3463STruong Nguyen 	assert(max_scf_fmri_size > 0);
640*eb1a3463STruong Nguyen 	assert(max_scf_name_size > 0);
641*eb1a3463STruong Nguyen 
642*eb1a3463STruong Nguyen 	if ((h = scf_handle_create(SCF_VERSION)) == NULL) {
643*eb1a3463STruong Nguyen 		syslog(LOG_ERR | LOG_DAEMON, "scf_handle_create failed: %s\n",
644*eb1a3463STruong Nguyen 		    scf_strerror(scf_error()));
645*eb1a3463STruong Nguyen 		return (1);
646*eb1a3463STruong Nguyen 	}
647*eb1a3463STruong Nguyen 
648*eb1a3463STruong Nguyen 	repository_rebind(h);
649*eb1a3463STruong Nguyen 
650*eb1a3463STruong Nguyen 	scratch_fmri = umem_alloc(max_scf_fmri_size, UMEM_DEFAULT);
651*eb1a3463STruong Nguyen 	scratch_name = umem_alloc(max_scf_name_size, UMEM_DEFAULT);
652*eb1a3463STruong Nguyen 
653*eb1a3463STruong Nguyen 	if (scratch_fmri == NULL || scratch_name == NULL) {
654*eb1a3463STruong Nguyen 		syslog(LOG_ERR | LOG_DAEMON, "Out of memory");
655*eb1a3463STruong Nguyen 		return (1);
656*eb1a3463STruong Nguyen 	}
657*eb1a3463STruong Nguyen 
658*eb1a3463STruong Nguyen 	inst = scf_instance_create(h);
659*eb1a3463STruong Nguyen 	snap = scf_snapshot_create(h);
660*eb1a3463STruong Nguyen 	scratch_pg = scf_pg_create(h);
661*eb1a3463STruong Nguyen 	scratch_prop = scf_property_create(h);
662*eb1a3463STruong Nguyen 	scratch_v = scf_value_create(h);
663*eb1a3463STruong Nguyen 
664*eb1a3463STruong Nguyen 	if (inst == NULL || snap == NULL || scratch_pg == NULL ||
665*eb1a3463STruong Nguyen 	    scratch_prop == NULL || scratch_v == NULL) {
666*eb1a3463STruong Nguyen 		syslog(LOG_ERR | LOG_DAEMON, "Initialization failed: %s\n",
667*eb1a3463STruong Nguyen 		    scf_strerror(scf_error()));
668*eb1a3463STruong Nguyen 		return (1);
669*eb1a3463STruong Nguyen 	}
670*eb1a3463STruong Nguyen 
671*eb1a3463STruong Nguyen 	return (repository_event_wait());
672*eb1a3463STruong Nguyen }
673