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