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 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 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 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 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 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 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 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 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 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 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 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 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