1*7a286c47SDai Ngo /* 2*7a286c47SDai Ngo * CDDL HEADER START 3*7a286c47SDai Ngo * 4*7a286c47SDai Ngo * The contents of this file are subject to the terms of the 5*7a286c47SDai Ngo * Common Development and Distribution License (the "License"). 6*7a286c47SDai Ngo * You may not use this file except in compliance with the License. 7*7a286c47SDai Ngo * 8*7a286c47SDai Ngo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*7a286c47SDai Ngo * or http://www.opensolaris.org/os/licensing. 10*7a286c47SDai Ngo * See the License for the specific language governing permissions 11*7a286c47SDai Ngo * and limitations under the License. 12*7a286c47SDai Ngo * 13*7a286c47SDai Ngo * When distributing Covered Code, include this CDDL HEADER in each 14*7a286c47SDai Ngo * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*7a286c47SDai Ngo * If applicable, add the following below this CDDL HEADER, with the 16*7a286c47SDai Ngo * fields enclosed by brackets "[]" replaced with your own identifying 17*7a286c47SDai Ngo * information: Portions Copyright [yyyy] [name of copyright owner] 18*7a286c47SDai Ngo * 19*7a286c47SDai Ngo * CDDL HEADER END 20*7a286c47SDai Ngo */ 21*7a286c47SDai Ngo /* 22*7a286c47SDai Ngo * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23*7a286c47SDai Ngo * Use is subject to license terms. 24*7a286c47SDai Ngo */ 25*7a286c47SDai Ngo 26*7a286c47SDai Ngo /* 27*7a286c47SDai Ngo * Reparsed daemon 28*7a286c47SDai Ngo */ 29*7a286c47SDai Ngo 30*7a286c47SDai Ngo #include <stdio.h> 31*7a286c47SDai Ngo #include <stdio_ext.h> 32*7a286c47SDai Ngo #include <stdlib.h> 33*7a286c47SDai Ngo #include <unistd.h> 34*7a286c47SDai Ngo #include <signal.h> 35*7a286c47SDai Ngo #include <sys/types.h> 36*7a286c47SDai Ngo #include <sys/stat.h> 37*7a286c47SDai Ngo #include <fcntl.h> 38*7a286c47SDai Ngo #include <memory.h> 39*7a286c47SDai Ngo #include <alloca.h> 40*7a286c47SDai Ngo #include <ucontext.h> 41*7a286c47SDai Ngo #include <errno.h> 42*7a286c47SDai Ngo #include <syslog.h> 43*7a286c47SDai Ngo #include <string.h> 44*7a286c47SDai Ngo #include <strings.h> 45*7a286c47SDai Ngo #include <door.h> 46*7a286c47SDai Ngo #include <wait.h> 47*7a286c47SDai Ngo #include <libintl.h> 48*7a286c47SDai Ngo #include <locale.h> 49*7a286c47SDai Ngo #include <sys/param.h> 50*7a286c47SDai Ngo #include <sys/systeminfo.h> 51*7a286c47SDai Ngo #include <sys/thread.h> 52*7a286c47SDai Ngo #include <rpc/xdr.h> 53*7a286c47SDai Ngo #include <priv.h> 54*7a286c47SDai Ngo #include <sys/fs_reparse.h> 55*7a286c47SDai Ngo #include <priv_utils.h> 56*7a286c47SDai Ngo #include <rpcsvc/daemon_utils.h> 57*7a286c47SDai Ngo 58*7a286c47SDai Ngo #define REPARSED_CMD_OPTS "v" 59*7a286c47SDai Ngo #define DOOR_RESULT_BUFSZ (MAXPATHLEN + sizeof (reparsed_door_res_t)) 60*7a286c47SDai Ngo #define SAFETY_BUFFER 8*1024 61*7a286c47SDai Ngo 62*7a286c47SDai Ngo static char *MyName; 63*7a286c47SDai Ngo static int verbose = 0; 64*7a286c47SDai Ngo 65*7a286c47SDai Ngo static int start_reparsed_svcs(); 66*7a286c47SDai Ngo static void daemonize(void); 67*7a286c47SDai Ngo static void reparsed_door_call_error(int error, int buflen); 68*7a286c47SDai Ngo static void reparsed_doorfunc(void *cookie, char *argp, size_t arg_size, 69*7a286c47SDai Ngo door_desc_t *dp, uint_t n_desc); 70*7a286c47SDai Ngo 71*7a286c47SDai Ngo static void 72*7a286c47SDai Ngo usage() 73*7a286c47SDai Ngo { 74*7a286c47SDai Ngo syslog(LOG_ERR, "Usage: %s", MyName); 75*7a286c47SDai Ngo syslog(LOG_ERR, "\t[-v]\t\tverbose error messages)"); 76*7a286c47SDai Ngo exit(1); 77*7a286c47SDai Ngo } 78*7a286c47SDai Ngo 79*7a286c47SDai Ngo static void 80*7a286c47SDai Ngo warn_hup(int i) 81*7a286c47SDai Ngo { 82*7a286c47SDai Ngo syslog(LOG_ERR, "SIGHUP received: ignored"); 83*7a286c47SDai Ngo (void) signal(SIGHUP, warn_hup); 84*7a286c47SDai Ngo } 85*7a286c47SDai Ngo 86*7a286c47SDai Ngo /* 87*7a286c47SDai Ngo * Processing for daemonization 88*7a286c47SDai Ngo */ 89*7a286c47SDai Ngo static void 90*7a286c47SDai Ngo daemonize(void) 91*7a286c47SDai Ngo { 92*7a286c47SDai Ngo switch (fork()) { 93*7a286c47SDai Ngo case -1: 94*7a286c47SDai Ngo syslog(LOG_ERR, "reparsed: can't fork - errno %d", errno); 95*7a286c47SDai Ngo exit(2); 96*7a286c47SDai Ngo /* NOTREACHED */ 97*7a286c47SDai Ngo case 0: /* child */ 98*7a286c47SDai Ngo break; 99*7a286c47SDai Ngo 100*7a286c47SDai Ngo default: /* parent */ 101*7a286c47SDai Ngo _exit(0); 102*7a286c47SDai Ngo } 103*7a286c47SDai Ngo (void) chdir("/"); 104*7a286c47SDai Ngo 105*7a286c47SDai Ngo /* 106*7a286c47SDai Ngo * Close stdin, stdout, and stderr. 107*7a286c47SDai Ngo * Open again to redirect input+output 108*7a286c47SDai Ngo */ 109*7a286c47SDai Ngo (void) close(0); 110*7a286c47SDai Ngo (void) close(1); 111*7a286c47SDai Ngo (void) close(2); 112*7a286c47SDai Ngo (void) open("/dev/null", O_RDONLY); 113*7a286c47SDai Ngo (void) open("/dev/null", O_WRONLY); 114*7a286c47SDai Ngo (void) dup(1); 115*7a286c47SDai Ngo (void) setsid(); 116*7a286c47SDai Ngo } 117*7a286c47SDai Ngo 118*7a286c47SDai Ngo int 119*7a286c47SDai Ngo main(int argc, char *argv[]) 120*7a286c47SDai Ngo { 121*7a286c47SDai Ngo pid_t pid; 122*7a286c47SDai Ngo int c, error; 123*7a286c47SDai Ngo struct rlimit rlset; 124*7a286c47SDai Ngo char *defval; 125*7a286c47SDai Ngo 126*7a286c47SDai Ngo /* 127*7a286c47SDai Ngo * There is no check for non-global zone and Trusted Extensions. 128*7a286c47SDai Ngo * Reparsed works in both of these environments as long as the 129*7a286c47SDai Ngo * services that use reparsed are supported. 130*7a286c47SDai Ngo */ 131*7a286c47SDai Ngo 132*7a286c47SDai Ngo MyName = argv[0]; 133*7a286c47SDai Ngo if (geteuid() != 0) { 134*7a286c47SDai Ngo syslog(LOG_ERR, "%s must be run as root", MyName); 135*7a286c47SDai Ngo exit(1); 136*7a286c47SDai Ngo } 137*7a286c47SDai Ngo 138*7a286c47SDai Ngo while ((c = getopt(argc, argv, REPARSED_CMD_OPTS)) != EOF) { 139*7a286c47SDai Ngo switch (c) { 140*7a286c47SDai Ngo case 'v': 141*7a286c47SDai Ngo verbose++; 142*7a286c47SDai Ngo break; 143*7a286c47SDai Ngo default: 144*7a286c47SDai Ngo usage(); 145*7a286c47SDai Ngo } 146*7a286c47SDai Ngo } 147*7a286c47SDai Ngo 148*7a286c47SDai Ngo daemonize(); 149*7a286c47SDai Ngo openlog(MyName, LOG_PID | LOG_NDELAY, LOG_DAEMON); 150*7a286c47SDai Ngo 151*7a286c47SDai Ngo (void) _create_daemon_lock(REPARSED, DAEMON_UID, DAEMON_GID); 152*7a286c47SDai Ngo (void) enable_extended_FILE_stdio(-1, -1); 153*7a286c47SDai Ngo switch (_enter_daemon_lock(REPARSED)) { 154*7a286c47SDai Ngo case 0: 155*7a286c47SDai Ngo break; 156*7a286c47SDai Ngo case -1: 157*7a286c47SDai Ngo syslog(LOG_ERR, "Error locking for %s", REPARSED); 158*7a286c47SDai Ngo exit(2); 159*7a286c47SDai Ngo default: 160*7a286c47SDai Ngo /* daemon was already running */ 161*7a286c47SDai Ngo exit(0); 162*7a286c47SDai Ngo } 163*7a286c47SDai Ngo 164*7a286c47SDai Ngo (void) signal(SIGHUP, warn_hup); 165*7a286c47SDai Ngo 166*7a286c47SDai Ngo /* 167*7a286c47SDai Ngo * Make the process a privilege aware daemon. 168*7a286c47SDai Ngo * Only "basic" privileges are required. 169*7a286c47SDai Ngo * 170*7a286c47SDai Ngo */ 171*7a286c47SDai Ngo if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, 0, 0, 172*7a286c47SDai Ngo (char *)NULL) == -1) { 173*7a286c47SDai Ngo syslog(LOG_ERR, "should be run with sufficient privileges"); 174*7a286c47SDai Ngo exit(3); 175*7a286c47SDai Ngo } 176*7a286c47SDai Ngo 177*7a286c47SDai Ngo /* 178*7a286c47SDai Ngo * Clear basic privileges not required by reparsed. 179*7a286c47SDai Ngo */ 180*7a286c47SDai Ngo __fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION, 181*7a286c47SDai Ngo PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL); 182*7a286c47SDai Ngo 183*7a286c47SDai Ngo return (start_reparsed_svcs()); 184*7a286c47SDai Ngo } 185*7a286c47SDai Ngo 186*7a286c47SDai Ngo static void 187*7a286c47SDai Ngo reparsed_door_call_error(int error, int buflen) 188*7a286c47SDai Ngo { 189*7a286c47SDai Ngo reparsed_door_res_t rpd_res; 190*7a286c47SDai Ngo 191*7a286c47SDai Ngo memset(&rpd_res, 0, sizeof (reparsed_door_res_t)); 192*7a286c47SDai Ngo rpd_res.res_status = error; 193*7a286c47SDai Ngo rpd_res.res_len = buflen; 194*7a286c47SDai Ngo door_return((char *)&rpd_res, sizeof (reparsed_door_res_t), NULL, 0); 195*7a286c47SDai Ngo 196*7a286c47SDai Ngo (void) door_return(NULL, 0, NULL, 0); 197*7a286c47SDai Ngo /* NOTREACHED */ 198*7a286c47SDai Ngo } 199*7a286c47SDai Ngo 200*7a286c47SDai Ngo /* 201*7a286c47SDai Ngo * reparsed_doorfunc 202*7a286c47SDai Ngo * 203*7a286c47SDai Ngo * argp: "service_type:service_data" string 204*7a286c47SDai Ngo * dp & n_desc: not used. 205*7a286c47SDai Ngo */ 206*7a286c47SDai Ngo static void 207*7a286c47SDai Ngo reparsed_doorfunc(void *cookie, char *argp, size_t arg_size, 208*7a286c47SDai Ngo door_desc_t *dp, uint_t n_desc) 209*7a286c47SDai Ngo { 210*7a286c47SDai Ngo int err; 211*7a286c47SDai Ngo size_t bufsz; 212*7a286c47SDai Ngo char *svc_type, *svc_data; 213*7a286c47SDai Ngo char *cp, *buf, *sbuf, res_buf[DOOR_RESULT_BUFSZ]; 214*7a286c47SDai Ngo reparsed_door_res_t *resp; 215*7a286c47SDai Ngo 216*7a286c47SDai Ngo if ((argp == NULL) || (arg_size == 0)) { 217*7a286c47SDai Ngo reparsed_door_call_error(EINVAL, 0); 218*7a286c47SDai Ngo /* NOTREACHED */ 219*7a286c47SDai Ngo } 220*7a286c47SDai Ngo 221*7a286c47SDai Ngo if (verbose) 222*7a286c47SDai Ngo syslog(LOG_NOTICE, "reparsed_door: [%s, %d]", argp, arg_size); 223*7a286c47SDai Ngo 224*7a286c47SDai Ngo if ((svc_type = strdup(argp)) == NULL) { 225*7a286c47SDai Ngo reparsed_door_call_error(ENOMEM, 0); 226*7a286c47SDai Ngo /* NOTREACHED */ 227*7a286c47SDai Ngo } 228*7a286c47SDai Ngo 229*7a286c47SDai Ngo /* 230*7a286c47SDai Ngo * Door argument string comes in "service_type:service_data" format. 231*7a286c47SDai Ngo * Need to break it into separate "service_type" and "service_data" 232*7a286c47SDai Ngo * string before passing them to reparse_deref() to process them. 233*7a286c47SDai Ngo */ 234*7a286c47SDai Ngo if ((cp = strchr(svc_type, ':')) == NULL) { 235*7a286c47SDai Ngo free(svc_type); 236*7a286c47SDai Ngo reparsed_door_call_error(EINVAL, 0); 237*7a286c47SDai Ngo /* NOTREACHED */ 238*7a286c47SDai Ngo } 239*7a286c47SDai Ngo *cp++ = '\0'; 240*7a286c47SDai Ngo svc_data = cp; 241*7a286c47SDai Ngo 242*7a286c47SDai Ngo /* 243*7a286c47SDai Ngo * Setup buffer for reparse_deref(). 'bufsz' is the actual 244*7a286c47SDai Ngo * buffer size to hold the result returned by reparse_deref(). 245*7a286c47SDai Ngo */ 246*7a286c47SDai Ngo resp = (reparsed_door_res_t *)res_buf; 247*7a286c47SDai Ngo buf = resp->res_data; 248*7a286c47SDai Ngo bufsz = sizeof (res_buf) - sizeof (reparsed_door_res_t); 249*7a286c47SDai Ngo 250*7a286c47SDai Ngo /* 251*7a286c47SDai Ngo * reparse_deref() calls the service type plugin library to process 252*7a286c47SDai Ngo * the service data. The plugin library function should understand 253*7a286c47SDai Ngo * the context of the service data and should be the one to XDR the 254*7a286c47SDai Ngo * results before returning it to the caller. 255*7a286c47SDai Ngo */ 256*7a286c47SDai Ngo err = reparse_deref(svc_type, svc_data, buf, &bufsz); 257*7a286c47SDai Ngo 258*7a286c47SDai Ngo if (verbose) 259*7a286c47SDai Ngo syslog(LOG_NOTICE, 260*7a286c47SDai Ngo "reparsed_deref(svc_type: %s, data: %s, size: %d) -> %d", 261*7a286c47SDai Ngo svc_type, svc_data, bufsz, err); 262*7a286c47SDai Ngo 263*7a286c47SDai Ngo switch (err) { 264*7a286c47SDai Ngo case 0: 265*7a286c47SDai Ngo break; 266*7a286c47SDai Ngo 267*7a286c47SDai Ngo case EOVERFLOW: 268*7a286c47SDai Ngo /* 269*7a286c47SDai Ngo * bufsz was returned with size needed by reparse_deref(). 270*7a286c47SDai Ngo * 271*7a286c47SDai Ngo * We cannot use malloc() here because door_return() never 272*7a286c47SDai Ngo * returns, and memory allocated by malloc() would get leaked. 273*7a286c47SDai Ngo */ 274*7a286c47SDai Ngo sbuf = alloca(bufsz + sizeof (reparsed_door_res_t)); 275*7a286c47SDai Ngo if (sbuf == NULL || stack_inbounds(buf) == 0 || 276*7a286c47SDai Ngo stack_inbounds(buf + sizeof (reparsed_door_res_t) + 277*7a286c47SDai Ngo SAFETY_BUFFER - 1) == 0) { 278*7a286c47SDai Ngo free(svc_type); 279*7a286c47SDai Ngo reparsed_door_call_error(ENOMEM, 0); 280*7a286c47SDai Ngo /* NOTREACHED */ 281*7a286c47SDai Ngo } 282*7a286c47SDai Ngo 283*7a286c47SDai Ngo resp = (reparsed_door_res_t *)sbuf; 284*7a286c47SDai Ngo if ((err = reparse_deref(svc_type, svc_data, resp->res_data, 285*7a286c47SDai Ngo &bufsz)) == 0) 286*7a286c47SDai Ngo break; 287*7a286c47SDai Ngo 288*7a286c47SDai Ngo /* fall through */ 289*7a286c47SDai Ngo 290*7a286c47SDai Ngo default: 291*7a286c47SDai Ngo free(svc_type); 292*7a286c47SDai Ngo reparsed_door_call_error(err, 0); 293*7a286c47SDai Ngo /* NOTREACHED */ 294*7a286c47SDai Ngo } 295*7a286c47SDai Ngo 296*7a286c47SDai Ngo free(svc_type); 297*7a286c47SDai Ngo 298*7a286c47SDai Ngo if (verbose) 299*7a286c47SDai Ngo syslog(LOG_NOTICE, "reparsed_door_return <buf=%s> size=%d", 300*7a286c47SDai Ngo buf, bufsz); 301*7a286c47SDai Ngo 302*7a286c47SDai Ngo resp->res_status = 0; 303*7a286c47SDai Ngo resp->res_len = bufsz; 304*7a286c47SDai Ngo (void) door_return((char *)resp, bufsz + sizeof (reparsed_door_res_t), 305*7a286c47SDai Ngo NULL, 0); 306*7a286c47SDai Ngo 307*7a286c47SDai Ngo (void) door_return(NULL, 0, NULL, 0); 308*7a286c47SDai Ngo /* NOTREACHED */ 309*7a286c47SDai Ngo } 310*7a286c47SDai Ngo 311*7a286c47SDai Ngo static int 312*7a286c47SDai Ngo start_reparsed_svcs() 313*7a286c47SDai Ngo { 314*7a286c47SDai Ngo int doorfd; 315*7a286c47SDai Ngo int dfd; 316*7a286c47SDai Ngo 317*7a286c47SDai Ngo if ((doorfd = door_create(reparsed_doorfunc, NULL, 318*7a286c47SDai Ngo DOOR_REFUSE_DESC|DOOR_NO_CANCEL)) == -1) { 319*7a286c47SDai Ngo syslog(LOG_ERR, "Unable to create door"); 320*7a286c47SDai Ngo return (1); 321*7a286c47SDai Ngo } 322*7a286c47SDai Ngo 323*7a286c47SDai Ngo /* 324*7a286c47SDai Ngo * Create a file system path for the door 325*7a286c47SDai Ngo */ 326*7a286c47SDai Ngo if ((dfd = open(REPARSED_DOOR, O_RDWR|O_CREAT|O_TRUNC, 327*7a286c47SDai Ngo S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) { 328*7a286c47SDai Ngo syslog(LOG_ERR, "unable to open %s", REPARSED_DOOR); 329*7a286c47SDai Ngo (void) close(doorfd); 330*7a286c47SDai Ngo return (1); 331*7a286c47SDai Ngo } 332*7a286c47SDai Ngo 333*7a286c47SDai Ngo /* 334*7a286c47SDai Ngo * Clean up any stale associations 335*7a286c47SDai Ngo */ 336*7a286c47SDai Ngo (void) fdetach(REPARSED_DOOR); 337*7a286c47SDai Ngo 338*7a286c47SDai Ngo /* 339*7a286c47SDai Ngo * Register in the kernel namespace for door_ki_open(). 340*7a286c47SDai Ngo */ 341*7a286c47SDai Ngo if (fattach(doorfd, REPARSED_DOOR) == -1) { 342*7a286c47SDai Ngo syslog(LOG_ERR, "Unable to fattach door %s", REPARSED_DOOR); 343*7a286c47SDai Ngo (void) close(doorfd); 344*7a286c47SDai Ngo (void) close(dfd); 345*7a286c47SDai Ngo return (1); 346*7a286c47SDai Ngo } 347*7a286c47SDai Ngo (void) close(dfd); 348*7a286c47SDai Ngo 349*7a286c47SDai Ngo /* 350*7a286c47SDai Ngo * Wait for incoming calls 351*7a286c47SDai Ngo */ 352*7a286c47SDai Ngo /*CONSTCOND*/ 353*7a286c47SDai Ngo while (1) 354*7a286c47SDai Ngo (void) pause(); 355*7a286c47SDai Ngo 356*7a286c47SDai Ngo syslog(LOG_ERR, "Door server exited"); 357*7a286c47SDai Ngo return (10); 358*7a286c47SDai Ngo } 359