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