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
usage()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
warn_hup(int i)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
daemonize(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
main(int argc,char * argv[])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
reparsed_door_call_error(int error,int buflen)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
reparsed_doorfunc(void * cookie,char * argp,size_t arg_size,door_desc_t * dp,uint_t n_desc)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
start_reparsed_svcs()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