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