1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
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.
33 *
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
39 * capable.
40 *
41 * - A request to update service's IPfilter configuration is made for
42 * actions that affect service's configuration or running state. The
43 * actions are:
44 * - enable/disable
45 * - refresh/restart
46 * - maintenance/clear maintenance
47 *
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
52 * becomes available.
53 *
54 * - The program responsible for updating service's IPfilter configuration
55 * is /lib/svc/method/ipfilter. This program is called as:
56 *
57 * /lib/svc/method/ipfilter fw_update fmri
58 *
59 * where fmri the instance fmri of the service to be updated.
60 */
61
62 #include <stdio.h>
63 #include <unistd.h>
64 #include <stdlib.h>
65 #include <assert.h>
66 #include <errno.h>
67 #include <sys/types.h>
68 #include <sys/stat.h>
69 #include <sys/wait.h>
70 #include <fcntl.h>
71 #include <umem.h>
72 #include <libscf.h>
73 #include <libscf_priv.h>
74 #include <signal.h>
75 #include <string.h>
76 #include <syslog.h>
77
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"
81
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"
87
88 #define SCF_PROPERTY_ISRPC "isrpc"
89
90 #define MAX_RETRY 7
91 #define DEV_NULL "/dev/null"
92
93 static scf_handle_t *h;
94 static ssize_t max_scf_fmri_size;
95 static ssize_t max_scf_name_size;
96
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;
102
103 static char *scratch_fmri;
104 static char *scratch_name;
105
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
110 };
111 #define ALL_PROPS_CNT 7
112
113 static const char *maint_props[] = {
114 SCF_PROPERTY_REFRESH, SCF_PROPERTY_RESTART, SCF_PROPERTY_MAINT_OFF };
115 #define MAINT_PROPS_CNT 3
116
117 static int ipfilter_update(const char *);
118
119 static int
daemonize_self(void)120 daemonize_self(void)
121 {
122 pid_t pid;
123 int fd;
124
125 (void) close(STDIN_FILENO);
126
127 if ((fd = open(DEV_NULL, O_RDONLY)) == -1) {
128 (void) printf("Could not open /dev/null: %s\n",
129 strerror(errno));
130 } else if (fd != STDIN_FILENO) {
131 (void) dup2(fd, STDIN_FILENO);
132 (void) close(fd);
133 }
134 (void) dup2(STDERR_FILENO, STDOUT_FILENO);
135 closefrom(3);
136
137 if ((pid = fork1()) < 0) {
138 (void) printf("fork() failed: %s\n", strerror(errno));
139 return (1);
140 }
141
142 if (pid != 0)
143 exit(0);
144
145 (void) setsid();
146 (void) chdir("/");
147
148 return (0);
149 }
150
151 static void
repository_rebind(scf_handle_t * hndl)152 repository_rebind(scf_handle_t *hndl)
153 {
154 int c = 0;
155
156 (void) scf_handle_unbind(hndl);
157 while ((scf_handle_bind(hndl)) != 0) {
158 if (c > MAX_RETRY) {
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 "
164 "properly\n");
165
166 exit(1);
167 } else {
168 c++;
169 }
170
171 (void) sleep(1);
172 }
173 }
174
175 static void
repository_notify_setup(scf_handle_t * h)176 repository_notify_setup(scf_handle_t *h)
177 {
178 for (;;) {
179 if (_scf_notify_add_pgtype(h, SCF_GROUP_FRAMEWORK) ==
180 SCF_SUCCESS)
181 break;
182
183 switch (scf_error()) {
184 case SCF_ERROR_CONNECTION_BROKEN:
185 repository_rebind(h);
186 break;
187
188 case SCF_ERROR_NO_RESOURCES:
189 (void) sleep(1);
190 break;
191
192 default:
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()));
197 abort();
198 }
199 }
200 }
201
202 /*
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).
208 */
209 static void
repository_setup()210 repository_setup()
211 {
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");
217 }
218 }
219
220 static int
pg_get_prop_value(const scf_propertygroup_t * pg,const char * pname,scf_value_t * v)221 pg_get_prop_value(const scf_propertygroup_t *pg, const char *pname,
222 scf_value_t *v)
223 {
224 if (pg == NULL || pname == NULL || v == NULL)
225 return (-1);
226
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:
232 break;
233
234 default:
235 syslog(LOG_ERR | LOG_DAEMON,
236 "scf_pg_get_property failed for %s: %s\n",
237 pname, scf_strerror(scf_error()));
238 }
239 return (-1);
240 }
241 return (0);
242 }
243
244 static int
is_correct_event(const char * fmri,const scf_propertygroup_t * pg,const boolean_t isrpc)245 is_correct_event(const char *fmri, const scf_propertygroup_t *pg,
246 const boolean_t isrpc)
247 {
248 char *state = NULL;
249 const char **proplist = all_props;
250 int prop_cnt = ALL_PROPS_CNT;
251
252 int i, ret = 0;
253
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()));
257 return (-1);
258 }
259
260 /*
261 * We care about enable, disable, and refresh since that's
262 * when we activate, deactivate, or change firewall policy.
263 *
264 * - enable/disable -> change in "general" or "general_ovr"
265 * - refresh/restart -> change in "restarter_actions"
266 */
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);
270 return (1);
271 }
272
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()));
276 return (-1);
277 }
278
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;
283 }
284
285 /*
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.
289 */
290 if (isrpc) {
291 ret = 1;
292 goto out;
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],
296 scratch_v) == 0) {
297 syslog(LOG_DEBUG | LOG_DAEMON, "Action: %s/%s",
298 scratch_name, proplist[i]);
299
300 ret = 1;
301 goto out;
302 }
303 }
304 }
305
306 out:
307 if (state)
308 free(state);
309
310 return (ret);
311 }
312
313 static int
ipfilter_update(const char * fmri)314 ipfilter_update(const char *fmri)
315 {
316 pid_t pid;
317 int status, ret = 0;
318
319 syslog(LOG_DEBUG | LOG_DAEMON, "ipfilter_update: %s\n", fmri);
320
321 /*
322 * Start refresh in another process
323 */
324 if ((pid = fork1()) < 0) {
325 syslog(LOG_ERR | LOG_DAEMON, "Couldn't fork to refresh "
326 "ipfilter for %s: %s", fmri, strerror(errno));
327 ret = 1;
328 goto out;
329 }
330
331 if (pid == 0) {
332 if (execl(IPF_UPDATE_CMD, IPF_UPDATE_CMD, "fw_update", fmri,
333 NULL) == -1)
334 syslog(LOG_ERR | LOG_DAEMON, "execl() failed for "
335 "%s: %s", fmri, strerror(errno));
336
337 exit(1);
338 }
339
340 /*
341 * Parent - only one update at a time.
342 */
343 (void) wait(&status);
344 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
345 ret = 1;
346
347 out:
348 if (ret == 1)
349 syslog(LOG_ERR | LOG_DAEMON, "Firewall update failed "
350 "for: %s\n", fmri);
351
352 return (ret);
353 }
354
355 /*
356 * Determine whether a given instance is a RPC service. Repository and
357 * libscf errors are treated as if the service isn't an RPC service,
358 * returning B_FALSE to indicate validation failure.
359 */
360 static boolean_t
service_is_rpc(const scf_instance_t * inst)361 service_is_rpc(const scf_instance_t *inst)
362 {
363 scf_snapshot_t *lsnap = NULL;
364 uint8_t isrpc;
365
366 if (scf_instance_get_snapshot(inst, SCF_SNAPSHOT_RUNNING, snap) != 0) {
367 syslog(LOG_DEBUG | LOG_DAEMON,
368 "Could not get running snapshot, using editing value\n");
369 } else {
370 lsnap = snap;
371 }
372
373 if (scf_instance_get_pg_composed(inst, lsnap, SCF_PG_INETD,
374 scratch_pg) == -1) {
375 switch (scf_error()) {
376 case SCF_ERROR_NOT_FOUND:
377 case SCF_ERROR_DELETED:
378 break;
379
380 default:
381 syslog(LOG_ERR | LOG_DAEMON,
382 "scf_instance_get_pg_composed failed: %s\n",
383 scf_strerror(scf_error()));
384 return (B_FALSE);
385 }
386
387 if (scf_instance_get_pg_composed(inst, lsnap,
388 SCF_PG_FW_CONTEXT, scratch_pg) == -1) {
389 switch (scf_error()) {
390 case SCF_ERROR_NOT_FOUND:
391 case SCF_ERROR_DELETED:
392 break;
393
394 default:
395 syslog(LOG_ERR | LOG_DAEMON,
396 "scf_instance_get_pg_composed failed: %s\n",
397 scf_strerror(scf_error()));
398 }
399 return (B_FALSE);
400 }
401 }
402
403 if (pg_get_prop_value(scratch_pg, SCF_PROPERTY_ISRPC, scratch_v) == -1)
404 return (B_FALSE);
405
406 if (scf_value_get_boolean(scratch_v, &isrpc) == -1) {
407 syslog(LOG_ERR | LOG_DAEMON, "scf_value_get_boolean failed: "
408 "%s\n", scf_strerror(scf_error()));
409 return (B_FALSE);
410 }
411
412 if (isrpc)
413 return (B_TRUE);
414 else
415 return (B_FALSE);
416 }
417
418 static int
instance_has_firewall(scf_instance_t * inst)419 instance_has_firewall(scf_instance_t *inst)
420 {
421 scf_snapshot_t *lsnap = NULL;
422
423 if (scf_instance_get_snapshot(inst, SCF_SNAPSHOT_RUNNING, snap) == -1) {
424 switch (scf_error()) {
425 case SCF_ERROR_CONNECTION_BROKEN:
426 syslog(LOG_ERR | LOG_DAEMON,
427 "scf_instance_get_snapshot failed: %s\n",
428 scf_strerror(scf_error()));
429 repository_setup();
430 return (-1);
431
432 case SCF_ERROR_DELETED:
433 default:
434 /*
435 * If running snapshot is not available for
436 * other reasons, fall back to current values.
437 */
438 syslog(LOG_DEBUG | LOG_DAEMON, "Could not get "
439 "running snapshot, using current value\n");
440 }
441 } else {
442 lsnap = snap;
443 }
444
445 /*
446 * Update service's IPfilter configuration if either
447 * SCF_PG_FW_CONTEXT or SCF_PG_FW_CONFIG exists.
448 */
449 if (scf_instance_get_pg_composed(inst, lsnap, SCF_PG_FW_CONTEXT,
450 scratch_pg) == 0) {
451 return (1);
452 } else {
453 switch (scf_error()) {
454 case SCF_ERROR_NOT_FOUND:
455 case SCF_ERROR_DELETED:
456 break;
457
458 case SCF_ERROR_CONNECTION_BROKEN:
459 repository_setup();
460 /* FALLTHROUGH */
461 default:
462 syslog(LOG_ERR | LOG_DAEMON,
463 "scf_instance_get_pg_composed failed: %s\n",
464 scf_strerror(scf_error()));
465 return (-1);
466 }
467 }
468
469 if (scf_instance_get_pg_composed(inst, lsnap, SCF_PG_FW_CONFIG,
470 scratch_pg) == -1) {
471 /*
472 * It's either a non-firewall service or a failure to
473 * read firewall pg, just continue and listen for
474 * future events.
475 */
476 switch (scf_error()) {
477 case SCF_ERROR_NOT_FOUND:
478 case SCF_ERROR_DELETED:
479 return (0);
480
481 case SCF_ERROR_CONNECTION_BROKEN:
482 repository_setup();
483 /* FALLTHROUGH */
484 default:
485 syslog(LOG_ERR | LOG_DAEMON,
486 "scf_instance_get_pg_composed failed: %s\n",
487 scf_strerror(scf_error()));
488 return (-1);
489 }
490 }
491 return (1);
492 }
493
494 static int
repository_event_process(scf_propertygroup_t * pg)495 repository_event_process(scf_propertygroup_t *pg)
496 {
497 boolean_t isrpc = B_FALSE;
498 int res;
499
500 /*
501 * Figure out it's a firewall capable instance and call ipfilter_update
502 * if it is.
503 */
504 if (scf_pg_get_parent_instance(pg, inst) == -1) {
505 /* Not an error if pg doesn't belong to a valid instance */
506 if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
507 return (0);
508 }
509
510 syslog(LOG_ERR | LOG_DAEMON, "scf_pg_get_parent_instance "
511 "failed: %s\n", scf_strerror(scf_error()));
512
513 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
514 repository_setup();
515
516 return (1);
517 }
518
519 if (scf_instance_to_fmri(inst, scratch_fmri, max_scf_fmri_size) == -1) {
520 syslog(LOG_ERR | LOG_DAEMON, "scf_instance_to_fmri "
521 "failed: %s\n", scf_strerror(scf_error()));
522
523 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
524 repository_setup();
525
526 return (1);
527 }
528
529 if (strcmp(scratch_fmri, IPFILTER_FMRI) == 0) {
530 return (0);
531 }
532
533 isrpc = service_is_rpc(inst);
534
535 /*
536 * If it's not an event we're interested in, returns success.
537 */
538 res = is_correct_event(scratch_fmri, pg, isrpc);
539 if (res == -1) {
540 syslog(LOG_ERR | LOG_DAEMON,
541 "is_correct_event failed for %s.\n", scratch_fmri);
542 return (1);
543 } else if (res == 0) {
544 return (0);
545 }
546
547 /*
548 * Proceed only if instance has firewall policy.
549 */
550 res = instance_has_firewall(inst);
551 if (res == -1) {
552 syslog(LOG_ERR | LOG_DAEMON,
553 "instance_has_firewall failed for %s.\n", scratch_fmri);
554 return (1);
555 } else if (res == 0) {
556 return (0);
557 }
558
559 if (ipfilter_update(scratch_fmri) == -1) {
560 return (1);
561 }
562
563 return (0);
564 }
565
566 static int
repository_event_wait()567 repository_event_wait()
568 {
569 scf_propertygroup_t *pg;
570 char *fmri, *scratch;
571 const char *inst_name, *pg_name;
572 ssize_t res;
573
574 if ((fmri = umem_alloc(max_scf_fmri_size, UMEM_DEFAULT)) == NULL) {
575 syslog(LOG_ERR | LOG_DAEMON, "Out of memory");
576 return (1);
577 }
578
579 if ((scratch = umem_alloc(max_scf_fmri_size, UMEM_DEFAULT)) == NULL) {
580 syslog(LOG_ERR | LOG_DAEMON, "Out of memory");
581 return (1);
582 }
583
584 if ((pg = scf_pg_create(h)) == NULL) {
585 syslog(LOG_ERR | LOG_DAEMON, "scf_pg_create failed: %s\n",
586 scf_strerror(scf_error()));
587 return (1);
588 }
589
590 repository_notify_setup(h);
591
592 for (;;) {
593 /*
594 * Calling _scf_notify_wait which will block this thread
595 * until it's notified of a framework event.
596 *
597 * Note: fmri is only set on delete events.
598 */
599 res = _scf_notify_wait(pg, fmri, max_scf_fmri_size);
600 if (res < 0) {
601 syslog(LOG_ERR | LOG_DAEMON, "_scf_notify_wait "
602 "failed: %s\n", scf_strerror(scf_error()));
603 repository_setup();
604 } else if (res == 0) {
605 if (repository_event_process(pg))
606 syslog(LOG_ERR | LOG_DAEMON, "Service may have "
607 "incorrect IPfilter configuration\n");
608 } else {
609 /*
610 * The received event is a deletion of a service,
611 * instance or pg. If it's a deletion of an instance,
612 * update the instance's IPfilter configuration.
613 */
614 syslog(LOG_DEBUG | LOG_DAEMON, "Deleted: %s", fmri);
615
616 (void) strlcpy(scratch, fmri, max_scf_fmri_size);
617 if (scf_parse_svc_fmri(scratch, NULL, NULL, &inst_name,
618 &pg_name, NULL) != SCF_SUCCESS)
619 continue;
620
621 if (inst_name != NULL && pg_name == NULL) {
622 (void) ipfilter_update(fmri);
623 }
624 }
625 }
626
627 /*NOTREACHED*/
628 }
629
630 int
main()631 main()
632 {
633 if (daemonize_self() == 1)
634 return (1);
635
636 max_scf_fmri_size = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH) + 1;
637 max_scf_name_size = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
638
639 assert(max_scf_fmri_size > 0);
640 assert(max_scf_name_size > 0);
641
642 if ((h = scf_handle_create(SCF_VERSION)) == NULL) {
643 syslog(LOG_ERR | LOG_DAEMON, "scf_handle_create failed: %s\n",
644 scf_strerror(scf_error()));
645 return (1);
646 }
647
648 repository_rebind(h);
649
650 scratch_fmri = umem_alloc(max_scf_fmri_size, UMEM_DEFAULT);
651 scratch_name = umem_alloc(max_scf_name_size, UMEM_DEFAULT);
652
653 if (scratch_fmri == NULL || scratch_name == NULL) {
654 syslog(LOG_ERR | LOG_DAEMON, "Out of memory");
655 return (1);
656 }
657
658 inst = scf_instance_create(h);
659 snap = scf_snapshot_create(h);
660 scratch_pg = scf_pg_create(h);
661 scratch_prop = scf_property_create(h);
662 scratch_v = scf_value_create(h);
663
664 if (inst == NULL || snap == NULL || scratch_pg == NULL ||
665 scratch_prop == NULL || scratch_v == NULL) {
666 syslog(LOG_ERR | LOG_DAEMON, "Initialization failed: %s\n",
667 scf_strerror(scf_error()));
668 return (1);
669 }
670
671 return (repository_event_wait());
672 }
673