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