17a286c47SDai Ngo /*
27a286c47SDai Ngo  * CDDL HEADER START
37a286c47SDai Ngo  *
47a286c47SDai Ngo  * The contents of this file are subject to the terms of the
57a286c47SDai Ngo  * Common Development and Distribution License (the "License").
67a286c47SDai Ngo  * You may not use this file except in compliance with the License.
77a286c47SDai Ngo  *
87a286c47SDai Ngo  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97a286c47SDai Ngo  * or http://www.opensolaris.org/os/licensing.
107a286c47SDai Ngo  * See the License for the specific language governing permissions
117a286c47SDai Ngo  * and limitations under the License.
127a286c47SDai Ngo  *
137a286c47SDai Ngo  * When distributing Covered Code, include this CDDL HEADER in each
147a286c47SDai Ngo  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157a286c47SDai Ngo  * If applicable, add the following below this CDDL HEADER, with the
167a286c47SDai Ngo  * fields enclosed by brackets "[]" replaced with your own identifying
177a286c47SDai Ngo  * information: Portions Copyright [yyyy] [name of copyright owner]
187a286c47SDai Ngo  *
197a286c47SDai Ngo  * CDDL HEADER END
207a286c47SDai Ngo  */
217a286c47SDai Ngo /*
227a286c47SDai Ngo  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237a286c47SDai Ngo  * Use is subject to license terms.
247a286c47SDai Ngo  */
257a286c47SDai Ngo 
266a1a5bb9SJohn Levon /*
27*44bf619dSJohn Levon  * Copyright 2019 Joyent, Inc.
286a1a5bb9SJohn Levon  */
296a1a5bb9SJohn Levon 
307a286c47SDai Ngo /*
317a286c47SDai Ngo  * Reparsed daemon
327a286c47SDai Ngo  */
337a286c47SDai Ngo 
347a286c47SDai Ngo #include <stdio.h>
357a286c47SDai Ngo #include <stdio_ext.h>
367a286c47SDai Ngo #include <stdlib.h>
377a286c47SDai Ngo #include <unistd.h>
387a286c47SDai Ngo #include <signal.h>
397a286c47SDai Ngo #include <sys/types.h>
407a286c47SDai Ngo #include <sys/stat.h>
417a286c47SDai Ngo #include <fcntl.h>
427a286c47SDai Ngo #include <memory.h>
437a286c47SDai Ngo #include <alloca.h>
447a286c47SDai Ngo #include <ucontext.h>
457a286c47SDai Ngo #include <errno.h>
467a286c47SDai Ngo #include <syslog.h>
477a286c47SDai Ngo #include <string.h>
487a286c47SDai Ngo #include <strings.h>
497a286c47SDai Ngo #include <door.h>
507a286c47SDai Ngo #include <wait.h>
517a286c47SDai Ngo #include <libintl.h>
527a286c47SDai Ngo #include <locale.h>
537a286c47SDai Ngo #include <sys/param.h>
547a286c47SDai Ngo #include <sys/systeminfo.h>
557a286c47SDai Ngo #include <sys/thread.h>
567a286c47SDai Ngo #include <rpc/xdr.h>
577a286c47SDai Ngo #include <priv.h>
587a286c47SDai Ngo #include <sys/fs_reparse.h>
597a286c47SDai Ngo #include <priv_utils.h>
607a286c47SDai Ngo #include <rpcsvc/daemon_utils.h>
617a286c47SDai Ngo 
627a286c47SDai Ngo #define	REPARSED_CMD_OPTS	"v"
637a286c47SDai Ngo #define	DOOR_RESULT_BUFSZ	(MAXPATHLEN + sizeof (reparsed_door_res_t))
647a286c47SDai Ngo #define	SAFETY_BUFFER		8*1024
657a286c47SDai Ngo 
667a286c47SDai Ngo static char *MyName;
677a286c47SDai Ngo static int verbose = 0;
687a286c47SDai Ngo 
697a286c47SDai Ngo static int start_reparsed_svcs();
707a286c47SDai Ngo static void daemonize(void);
717a286c47SDai Ngo static void reparsed_door_call_error(int error, int buflen);
727a286c47SDai Ngo static void reparsed_doorfunc(void *cookie, char *argp, size_t arg_size,
737a286c47SDai Ngo 			door_desc_t *dp, uint_t n_desc);
747a286c47SDai Ngo 
757a286c47SDai Ngo static void
usage()767a286c47SDai Ngo usage()
777a286c47SDai Ngo {
787a286c47SDai Ngo 	syslog(LOG_ERR, "Usage: %s", MyName);
797a286c47SDai Ngo 	syslog(LOG_ERR, "\t[-v]\t\tverbose error messages)");
807a286c47SDai Ngo 	exit(1);
817a286c47SDai Ngo }
827a286c47SDai Ngo 
837a286c47SDai Ngo static void
warn_hup(int i)847a286c47SDai Ngo warn_hup(int i)
857a286c47SDai Ngo {
867a286c47SDai Ngo 	syslog(LOG_ERR, "SIGHUP received: ignored");
877a286c47SDai Ngo 	(void) signal(SIGHUP, warn_hup);
887a286c47SDai Ngo }
897a286c47SDai Ngo 
907a286c47SDai Ngo /*
917a286c47SDai Ngo  * Processing for daemonization
927a286c47SDai Ngo  */
937a286c47SDai Ngo static void
daemonize(void)947a286c47SDai Ngo daemonize(void)
957a286c47SDai Ngo {
967a286c47SDai Ngo 	switch (fork()) {
977a286c47SDai Ngo 	case -1:
987a286c47SDai Ngo 		syslog(LOG_ERR, "reparsed: can't fork - errno %d", errno);
997a286c47SDai Ngo 		exit(2);
1007a286c47SDai Ngo 		/* NOTREACHED */
1017a286c47SDai Ngo 	case 0:		/* child */
1027a286c47SDai Ngo 		break;
1037a286c47SDai Ngo 
1047a286c47SDai Ngo 	default:	/* parent */
1057a286c47SDai Ngo 		_exit(0);
1067a286c47SDai Ngo 	}
1077a286c47SDai Ngo 	(void) chdir("/");
1087a286c47SDai Ngo 
1097a286c47SDai Ngo 	/*
1107a286c47SDai Ngo 	 * Close stdin, stdout, and stderr.
1117a286c47SDai Ngo 	 * Open again to redirect input+output
1127a286c47SDai Ngo 	 */
1137a286c47SDai Ngo 	(void) close(0);
1147a286c47SDai Ngo 	(void) close(1);
1157a286c47SDai Ngo 	(void) close(2);
1167a286c47SDai Ngo 	(void) open("/dev/null", O_RDONLY);
1177a286c47SDai Ngo 	(void) open("/dev/null", O_WRONLY);
1187a286c47SDai Ngo 	(void) dup(1);
1197a286c47SDai Ngo 	(void) setsid();
1207a286c47SDai Ngo }
1217a286c47SDai Ngo 
1227a286c47SDai Ngo int
main(int argc,char * argv[])1237a286c47SDai Ngo main(int argc, char *argv[])
1247a286c47SDai Ngo {
1257a286c47SDai Ngo 	pid_t pid;
1267a286c47SDai Ngo 	int c, error;
1277a286c47SDai Ngo 	struct rlimit rlset;
1287a286c47SDai Ngo 	char *defval;
1297a286c47SDai Ngo 
1307a286c47SDai Ngo 	/*
1317a286c47SDai Ngo 	 * There is no check for non-global zone and Trusted Extensions.
1327a286c47SDai Ngo 	 * Reparsed works in both of these environments as long as the
1337a286c47SDai Ngo 	 * services that use reparsed are supported.
1347a286c47SDai Ngo 	 */
1357a286c47SDai Ngo 
1367a286c47SDai Ngo 	MyName = argv[0];
1377a286c47SDai Ngo 	if (geteuid() != 0) {
1387a286c47SDai Ngo 		syslog(LOG_ERR, "%s must be run as root", MyName);
1397a286c47SDai Ngo 		exit(1);
1407a286c47SDai Ngo 	}
1417a286c47SDai Ngo 
1427a286c47SDai Ngo 	while ((c = getopt(argc, argv, REPARSED_CMD_OPTS)) != EOF) {
1437a286c47SDai Ngo 		switch (c) {
1447a286c47SDai Ngo 		case 'v':
1457a286c47SDai Ngo 			verbose++;
1467a286c47SDai Ngo 			break;
1477a286c47SDai Ngo 		default:
1487a286c47SDai Ngo 			usage();
1497a286c47SDai Ngo 		}
1507a286c47SDai Ngo 	}
1517a286c47SDai Ngo 
1527a286c47SDai Ngo 	daemonize();
1537a286c47SDai Ngo 	openlog(MyName, LOG_PID | LOG_NDELAY, LOG_DAEMON);
1547a286c47SDai Ngo 
1557a286c47SDai Ngo 	(void) _create_daemon_lock(REPARSED, DAEMON_UID, DAEMON_GID);
1567a286c47SDai Ngo 	(void) enable_extended_FILE_stdio(-1, -1);
1577a286c47SDai Ngo 	switch (_enter_daemon_lock(REPARSED)) {
1587a286c47SDai Ngo 	case 0:
1597a286c47SDai Ngo 		break;
1607a286c47SDai Ngo 	case -1:
1617a286c47SDai Ngo 		syslog(LOG_ERR, "Error locking for %s", REPARSED);
1627a286c47SDai Ngo 		exit(2);
1637a286c47SDai Ngo 	default:
1647a286c47SDai Ngo 		/* daemon was already running */
1657a286c47SDai Ngo 		exit(0);
1667a286c47SDai Ngo 	}
1677a286c47SDai Ngo 
1687a286c47SDai Ngo 	(void) signal(SIGHUP, warn_hup);
1697a286c47SDai Ngo 
1707a286c47SDai Ngo 	/*
1717a286c47SDai Ngo 	 * Make the process a privilege aware daemon.
1727a286c47SDai Ngo 	 * Only "basic" privileges are required.
1737a286c47SDai Ngo 	 *
1747a286c47SDai Ngo 	 */
1757a286c47SDai Ngo 	if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, 0, 0,
1767a286c47SDai Ngo 	    (char *)NULL) == -1) {
1777a286c47SDai Ngo 		syslog(LOG_ERR, "should be run with sufficient privileges");
1787a286c47SDai Ngo 		exit(3);
1797a286c47SDai Ngo 	}
1807a286c47SDai Ngo 
1817a286c47SDai Ngo 	/*
1827a286c47SDai Ngo 	 * Clear basic privileges not required by reparsed.
1837a286c47SDai Ngo 	 */
1847a286c47SDai Ngo 	__fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION,
1857a286c47SDai Ngo 	    PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL);
1867a286c47SDai Ngo 
1877a286c47SDai Ngo 	return (start_reparsed_svcs());
1887a286c47SDai Ngo }
1897a286c47SDai Ngo 
190*44bf619dSJohn Levon __NORETURN static void
reparsed_door_call_error(int error,int buflen)1917a286c47SDai Ngo reparsed_door_call_error(int error, int buflen)
1927a286c47SDai Ngo {
1937a286c47SDai Ngo 	reparsed_door_res_t rpd_res;
1947a286c47SDai Ngo 
1957a286c47SDai Ngo 	memset(&rpd_res, 0, sizeof (reparsed_door_res_t));
1967a286c47SDai Ngo 	rpd_res.res_status = error;
1977a286c47SDai Ngo 	rpd_res.res_len = buflen;
1986a1a5bb9SJohn Levon 	(void) door_return((char *)&rpd_res,
1996a1a5bb9SJohn Levon 	    sizeof (reparsed_door_res_t), NULL, 0);
2007a286c47SDai Ngo 
2017a286c47SDai Ngo 	(void) door_return(NULL, 0, NULL, 0);
2026a1a5bb9SJohn Levon 	abort();
2037a286c47SDai Ngo }
2047a286c47SDai Ngo 
2057a286c47SDai Ngo /*
2067a286c47SDai Ngo  *  reparsed_doorfunc
2077a286c47SDai Ngo  *
2087a286c47SDai Ngo  *  argp:  "service_type:service_data" string
2097a286c47SDai Ngo  *  dp & n_desc: not used.
2107a286c47SDai Ngo  */
2117a286c47SDai Ngo static void
reparsed_doorfunc(void * cookie,char * argp,size_t arg_size,door_desc_t * dp,uint_t n_desc)2127a286c47SDai Ngo reparsed_doorfunc(void *cookie, char *argp, size_t arg_size,
2137a286c47SDai Ngo     door_desc_t *dp, uint_t n_desc)
2147a286c47SDai Ngo {
2157a286c47SDai Ngo 	int err;
2167a286c47SDai Ngo 	size_t bufsz;
2177a286c47SDai Ngo 	char *svc_type, *svc_data;
2187a286c47SDai Ngo 	char *cp, *buf, *sbuf, res_buf[DOOR_RESULT_BUFSZ];
2197a286c47SDai Ngo 	reparsed_door_res_t *resp;
2207a286c47SDai Ngo 
2217a286c47SDai Ngo 	if ((argp == NULL) || (arg_size == 0)) {
2227a286c47SDai Ngo 		reparsed_door_call_error(EINVAL, 0);
2237a286c47SDai Ngo 		/* NOTREACHED */
2247a286c47SDai Ngo 	}
2257a286c47SDai Ngo 
2267a286c47SDai Ngo 	if (verbose)
2277a286c47SDai Ngo 		syslog(LOG_NOTICE, "reparsed_door: [%s, %d]", argp, arg_size);
2287a286c47SDai Ngo 
2297a286c47SDai Ngo 	if ((svc_type = strdup(argp)) == NULL) {
2307a286c47SDai Ngo 		reparsed_door_call_error(ENOMEM, 0);
2317a286c47SDai Ngo 		/* NOTREACHED */
2327a286c47SDai Ngo 	}
2337a286c47SDai Ngo 
2347a286c47SDai Ngo 	/*
2357a286c47SDai Ngo 	 * Door argument string comes in "service_type:service_data" format.
2367a286c47SDai Ngo 	 * Need to break it into separate "service_type" and "service_data"
2377a286c47SDai Ngo 	 * string before passing them to reparse_deref() to process them.
2387a286c47SDai Ngo 	 */
2397a286c47SDai Ngo 	if ((cp = strchr(svc_type, ':')) == NULL) {
2407a286c47SDai Ngo 		free(svc_type);
2417a286c47SDai Ngo 		reparsed_door_call_error(EINVAL, 0);
2427a286c47SDai Ngo 		/* NOTREACHED */
2437a286c47SDai Ngo 	}
2447a286c47SDai Ngo 	*cp++ = '\0';
2457a286c47SDai Ngo 	svc_data = cp;
2467a286c47SDai Ngo 
2477a286c47SDai Ngo 	/*
2487a286c47SDai Ngo 	 * Setup buffer for reparse_deref(). 'bufsz' is the actual
2497a286c47SDai Ngo 	 * buffer size to hold the result returned by reparse_deref().
2507a286c47SDai Ngo 	 */
2517a286c47SDai Ngo 	resp = (reparsed_door_res_t *)res_buf;
2527a286c47SDai Ngo 	buf = resp->res_data;
2537a286c47SDai Ngo 	bufsz = sizeof (res_buf) - sizeof (reparsed_door_res_t);
2547a286c47SDai Ngo 
2557a286c47SDai Ngo 	/*
2567a286c47SDai Ngo 	 * reparse_deref() calls the service type plugin library to process
2577a286c47SDai Ngo 	 * the service data. The plugin library function should understand
2587a286c47SDai Ngo 	 * the context of the service data and should be the one to XDR the
2597a286c47SDai Ngo 	 * results before returning it to the caller.
2607a286c47SDai Ngo 	 */
2617a286c47SDai Ngo 	err = reparse_deref(svc_type, svc_data, buf, &bufsz);
2627a286c47SDai Ngo 
2637a286c47SDai Ngo 	if (verbose)
2647a286c47SDai Ngo 		syslog(LOG_NOTICE,
2657a286c47SDai Ngo 		    "reparsed_deref(svc_type: %s, data: %s, size: %d) -> %d",
2667a286c47SDai Ngo 		    svc_type, svc_data, bufsz, err);
2677a286c47SDai Ngo 
2687a286c47SDai Ngo 	switch (err) {
2697a286c47SDai Ngo 	case 0:
2707a286c47SDai Ngo 		break;
2717a286c47SDai Ngo 
2727a286c47SDai Ngo 	case EOVERFLOW:
2737a286c47SDai Ngo 		/*
2747a286c47SDai Ngo 		 * bufsz was returned with size needed by reparse_deref().
2757a286c47SDai Ngo 		 *
2767a286c47SDai Ngo 		 * We cannot use malloc() here because door_return() never
2777a286c47SDai Ngo 		 * returns, and memory allocated by malloc() would get leaked.
2787a286c47SDai Ngo 		 */
2797a286c47SDai Ngo 		sbuf = alloca(bufsz + sizeof (reparsed_door_res_t));
2807a286c47SDai Ngo 		if (sbuf == NULL || stack_inbounds(buf) == 0 ||
2817a286c47SDai Ngo 		    stack_inbounds(buf + sizeof (reparsed_door_res_t) +
2827a286c47SDai Ngo 		    SAFETY_BUFFER - 1) == 0) {
2837a286c47SDai Ngo 			free(svc_type);
2847a286c47SDai Ngo 			reparsed_door_call_error(ENOMEM, 0);
2857a286c47SDai Ngo 			/* NOTREACHED */
2867a286c47SDai Ngo 		}
2877a286c47SDai Ngo 
2887a286c47SDai Ngo 		resp = (reparsed_door_res_t *)sbuf;
2897a286c47SDai Ngo 		if ((err = reparse_deref(svc_type, svc_data, resp->res_data,
2907a286c47SDai Ngo 		    &bufsz)) == 0)
2917a286c47SDai Ngo 			break;
2927a286c47SDai Ngo 
2937a286c47SDai Ngo 		/* fall through */
2947a286c47SDai Ngo 
2957a286c47SDai Ngo 	default:
2967a286c47SDai Ngo 		free(svc_type);
2977a286c47SDai Ngo 		reparsed_door_call_error(err, 0);
2987a286c47SDai Ngo 		/* NOTREACHED */
2997a286c47SDai Ngo 	}
3007a286c47SDai Ngo 
3017a286c47SDai Ngo 	free(svc_type);
3027a286c47SDai Ngo 
3037a286c47SDai Ngo 	if (verbose)
3047a286c47SDai Ngo 		syslog(LOG_NOTICE, "reparsed_door_return <buf=%s> size=%d",
3057a286c47SDai Ngo 		    buf, bufsz);
3067a286c47SDai Ngo 
3077a286c47SDai Ngo 	resp->res_status = 0;
3087a286c47SDai Ngo 	resp->res_len = bufsz;
3097a286c47SDai Ngo 	(void) door_return((char *)resp, bufsz + sizeof (reparsed_door_res_t),
3107a286c47SDai Ngo 	    NULL, 0);
3117a286c47SDai Ngo 
3127a286c47SDai Ngo 	(void) door_return(NULL, 0, NULL, 0);
3137a286c47SDai Ngo 	/* NOTREACHED */
3147a286c47SDai Ngo }
3157a286c47SDai Ngo 
3167a286c47SDai Ngo static int
start_reparsed_svcs()3177a286c47SDai Ngo start_reparsed_svcs()
3187a286c47SDai Ngo {
3197a286c47SDai Ngo 	int doorfd;
3207a286c47SDai Ngo 	int dfd;
3217a286c47SDai Ngo 
3227a286c47SDai Ngo 	if ((doorfd = door_create(reparsed_doorfunc, NULL,
3237a286c47SDai Ngo 	    DOOR_REFUSE_DESC|DOOR_NO_CANCEL)) == -1) {
3247a286c47SDai Ngo 		syslog(LOG_ERR, "Unable to create door");
3257a286c47SDai Ngo 		return (1);
3267a286c47SDai Ngo 	}
3277a286c47SDai Ngo 
3287a286c47SDai Ngo 	/*
3297a286c47SDai Ngo 	 * Create a file system path for the door
3307a286c47SDai Ngo 	 */
3317a286c47SDai Ngo 	if ((dfd = open(REPARSED_DOOR, O_RDWR|O_CREAT|O_TRUNC,
3327a286c47SDai Ngo 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
3337a286c47SDai Ngo 		syslog(LOG_ERR, "unable to open %s", REPARSED_DOOR);
3347a286c47SDai Ngo 		(void) close(doorfd);
3357a286c47SDai Ngo 		return (1);
3367a286c47SDai Ngo 	}
3377a286c47SDai Ngo 
3387a286c47SDai Ngo 	/*
3397a286c47SDai Ngo 	 * Clean up any stale associations
3407a286c47SDai Ngo 	 */
3417a286c47SDai Ngo 	(void) fdetach(REPARSED_DOOR);
3427a286c47SDai Ngo 
3437a286c47SDai Ngo 	/*
3447a286c47SDai Ngo 	 * Register in the kernel namespace for door_ki_open().
3457a286c47SDai Ngo 	 */
3467a286c47SDai Ngo 	if (fattach(doorfd, REPARSED_DOOR) == -1) {
3477a286c47SDai Ngo 		syslog(LOG_ERR, "Unable to fattach door %s", REPARSED_DOOR);
3487a286c47SDai Ngo 		(void) close(doorfd);
3497a286c47SDai Ngo 		(void) close(dfd);
3507a286c47SDai Ngo 		return (1);
3517a286c47SDai Ngo 	}
3527a286c47SDai Ngo 	(void) close(dfd);
3537a286c47SDai Ngo 
3547a286c47SDai Ngo 	/*
3557a286c47SDai Ngo 	 * Wait for incoming calls
3567a286c47SDai Ngo 	 */
3577a286c47SDai Ngo 	while (1)
3587a286c47SDai Ngo 		(void) pause();
3597a286c47SDai Ngo }
360