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