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 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 /*
30 * The BrandZ Linux thunking library.
31 *
32 * The interfaces defined in this file form the client side of a bridge
33 * to allow native Solaris process to access Linux services. Currently
34 * the Linux services that is made accessible by these interfaces here
35 * are:
36 * - Linux host <-> address naming services
37 * - Linux service <-> port naming services
38 * - Linux syslog
39 *
40 * Currently, to use this library it must be LD_PRELOADed into the
41 * application that needs to access Linux services. Once loaded
42 * Linux services are accessed by the client application in two
43 * different ways:
44 *
45 * - Direct library calls:
46 * lxt_gethostbyname_r
47 * lxt_gethostbyaddr_r
48 * lxt_getservbyname_r
49 * lxt_getservbyport_r
50 * lxt_debug
51 *
52 * These library functions are used by the BrandZ lx name services
53 * translation library (lx_nametoaddr.so) to handle libnsl.so name
54 * service requests.
55 *
56 * - Intercepted library calls:
57 * openlog(3c)
58 * syslog(3c)
59 * vsyslog(3c)
60 * closelog(3c)
61 *
62 * Via the LD_PRELOAD mechanism this library interposes itself on
63 * these interfaces and when the application calls these interfaces
64 * (either directly or indirectly via any libraries the program may
65 * be linked against) this library intercepts the request and passes
66 * it onto a Linux process to handle the request.
67 *
68 * Once this library receives a request that needs to be serviced by a
69 * Linux process, it packs up that request and attempts to send it
70 * to a doors server. The door server interfaces are defined in
71 * lx_thunk_server.h. If the doors server is not running or not
72 * responding, this library will attempt to spawn a new doors server
73 * by forking and executing the following shell script (which runs as
74 * a native /bin/sh Linux process):
75 * /native/usr/lib/brand/lx/lx_thunk
76 *
77 * Notes:
78 * - This library also intercepts the following system calls:
79 * close(2) - We intercept close(2) to prevent the caller from
80 * accidentally closing any of the file descriptors we
81 * need to do our work.
82 *
83 * setppriv(2) - We intercept setppriv(2) to prevent a process
84 * from dropping any of the privileges we'll need to create
85 * a new lx_thunk server process and to deal with service
86 * requests.
87 *
88 * - To facilitate the running of native Solaris programs and libraries
89 * when this library is preloaded into an application it will chroot()
90 * into /native. This way the Solaris application and libraries can
91 * access files via their expected paths and we can avoid having to
92 * either do path mapping or modifying all libraries to make them
93 * aware of "/native" so that they can pre-pend it to all their
94 * filesystem operations.
95 *
96 * - This library can only be used with processes that are initially
97 * run by root in a zone. The reason is that we use the chroot()
98 * system call and this requires the PRIV_PROC_CHROOT privilege,
99 * which non-root users don't have.
100 */
101
102 #include <alloca.h>
103 #include <assert.h>
104 #include <dlfcn.h>
105 #include <door.h>
106 #include <errno.h>
107 #include <fcntl.h>
108 #include <netdb.h>
109 #include <netdir.h>
110 #include <priv.h>
111 #include <stdarg.h>
112 #include <stdio.h>
113 #include <stdlib.h>
114 #include <string.h>
115 #include <strings.h>
116 #include <synch.h>
117 #include <sys/brand.h>
118 #include <sys/fcntl.h>
119 #include <sys/lx_thunk_server.h>
120 #include <sys/lx_thunk.h>
121 #include <sys/mman.h>
122 #include <sys/priv_impl.h>
123 #include <sys/stat.h>
124 #include <sys/syscall.h>
125 #include <sys/types.h>
126 #include <sys/wait.h>
127 #include <thread.h>
128 #include <unistd.h>
129 #include <sys/varargs.h>
130
131 #define LXT_DOOR_DIR "/tmp"
132 #define LXT_DOOR_PREFIX "lxt"
133 #define LXT_MSG_MAXLEN (128 + MAXPATHLEN)
134
135 #pragma init(init)
136
137 typedef uintptr_t (*fp1_t)(uintptr_t);
138 typedef uintptr_t (*fp3_t)(uintptr_t, uintptr_t, uintptr_t);
139
140 static char *lxt_debug_path = NULL; /* debug output file path */
141 static char lxt_debug_path_buf[MAXPATHLEN];
142 static int root_fd;
143 static int debug_fd = -1;
144
145 void lxt_debug(const char *msg, ...);
146
147 void
148 init(void)
149 {
150 if (getenv("LX_DEBUG") != NULL) {
151
152 /* check if there's a debug log file specified */
153 lxt_debug_path = getenv("LX_DEBUG_FILE");
154 if (lxt_debug_path == NULL) {
155 /* send all debugging output to /dev/tty */
156 lxt_debug_path = "/dev/tty";
157 }
158
159 (void) strlcpy(lxt_debug_path_buf, lxt_debug_path,
160 sizeof (lxt_debug_path_buf));
161 lxt_debug_path = lxt_debug_path_buf;
162
163 /*
164 * Open the debugging output file. We need to open it
165 * and hold it open because we're going to call chroot()
166 * in just a second, so we won't be able to open it later.
167 */
168 if ((debug_fd = open(lxt_debug_path,
169 O_WRONLY|O_APPEND|O_CREAT|O_NDELAY|O_NOCTTY,
170 0666)) != -1) {
171 (void) fchmod(debug_fd, 0666);
172 }
173 }
174 lxt_debug("lxt_init: executing native process");
175
176 /* Get a fd that points to the root directory */
177 if ((root_fd = open("/", O_RDONLY)) < 0) {
178 lxt_debug("lxt_init(): "
179 "failed to open root directory: %s", strerror(errno));
180 exit(-1);
181 }
182
183 /*
184 * Now, so that we can avoid having to do path mapping,
185 * just chdir() and chroot() into /native.
186 */
187 if (chdir("/native") != 0) {
188 lxt_debug("lxt_init(): "
189 "failed to chdir to /native: %s", strerror(errno));
190 exit(-1);
191 }
192 if (chroot("/native") != 0) {
193 lxt_debug("lxt_init(): "
194 "failed to chroot to /native: %s", strerror(errno));
195 exit(-1);
196 }
197 }
198
199 /*
200 * Linux Thunking Interfaces - Client Side
201 */
202 static mutex_t lxt_door_lock = DEFAULTMUTEX;
203 static int lxt_door_fd = -1;
204
205 static void
206 lxt_server_exec(int fifo_wr, int fifo_rd)
207 {
208 extern const char **environ;
209 char *nullist[] = { NULL };
210
211 lxt_debug("lxt_server_exec: server starting");
212
213 /*
214 * First we need to dup our fifos to the file descriptors
215 * the brand library is expecting them to be at.
216 */
217
218 /* Check if the write fifo needs to be moved aside */
219 if ((fifo_wr == LXT_SERVER_FIFO_RD_FD) &&
220 ((fifo_wr = dup(fifo_wr)) < 0))
221 return;
222
223 /* Check if the read fifo needs to be moved aside */
224 if ((fifo_rd == LXT_SERVER_FIFO_WR_FD) &&
225 ((fifo_rd = dup(fifo_rd)) < 0))
226 return;
227
228 if ((fifo_wr != LXT_SERVER_FIFO_WR_FD) &&
229 (dup2(fifo_wr, LXT_SERVER_FIFO_WR_FD) < 0))
230 return;
231 if ((fifo_rd != LXT_SERVER_FIFO_RD_FD) &&
232 (dup2(fifo_rd, LXT_SERVER_FIFO_RD_FD) < 0))
233 return;
234
235 /*
236 * We're about to execute a native Linux process.
237 * Since we've been loaded into a Solaris process with
238 * LD_PRELOAD and LD_LIBRARY_PATH we should clear these
239 * variables from the environment before calling exec.
240 */
241 (void) unsetenv("LD_PRELOAD");
242 (void) unsetenv("LD_LIBRARY_PATH");
243
244 /*
245 * Now we need to exec the thunk server process. This is a
246 * branded Linux process that will act as a doors server and
247 * service our requests to perform native Linux operations.
248 * Since we're currently running as a native Solaris process
249 * to start up the server we'll use the brand system call to
250 * the kernel that the target of the exec will be a branded
251 * process.
252 */
253 lxt_debug("lxt_server_exec: execing as Linux process");
254 (void) syscall(SYS_brand, B_EXEC_BRAND,
255 LXT_SERVER_BINARY, nullist, environ);
256 }
257
258
259 static void *
260 lxt_door_waitpid(void *arg)
261 {
262 pid_t child_pid = (pid_t)(uintptr_t)arg;
263 int stat;
264
265 (void) waitpid(child_pid, &stat, 0);
266 return (NULL);
267 }
268
269 static char *
270 lxt_door_mkfifo()
271 {
272 char *path;
273
274 for (;;) {
275 path = tempnam(LXT_DOOR_DIR, LXT_DOOR_PREFIX);
276 if (path == NULL)
277 return (NULL);
278 if (mkfifo(path, S_IWUSR | S_IRUSR) != 0) {
279 if (errno != EEXIST) {
280 free(path);
281 return (NULL);
282 }
283 /* This file path exists, pick a new name. */
284 free(path);
285 continue;
286 }
287 /* We successfully created the fifo */
288 break;
289 }
290 return (path);
291 }
292
293 static void
294 lxt_door_init()
295 {
296 char *fifo1_path = NULL, *fifo2_path = NULL;
297 char fifo1_path_native[MAXPATHLEN];
298 int fifo1_rd = -1, fifo1_wr = -1;
299 int fifo2_rd = -1, fifo2_wr = -1;
300 int junk;
301 pid_t child_pid;
302 thread_t tid;
303
304 lxt_debug("lxt_door_init: preparint to start server");
305
306 /* Create two new fifos. */
307 if (((fifo1_path = lxt_door_mkfifo()) == NULL) ||
308 ((fifo2_path = lxt_door_mkfifo()) == NULL))
309 goto fail;
310
311 (void) snprintf(fifo1_path_native, sizeof (fifo1_path_native),
312 "/native%s", fifo1_path);
313
314 /*
315 * Open both fifos for reading and writing. We have to open
316 * the read side of the fifo first (because the write side will
317 * fail to open if there is no reader) and we have to use the
318 * O_NONBLOCK flag (because the read open with hang without it).
319 */
320 if (((fifo1_rd = open(fifo1_path, O_RDONLY | O_NONBLOCK)) < 0) ||
321 ((fifo1_wr = open(fifo1_path, O_WRONLY)) < 0) ||
322 ((fifo2_rd = open(fifo2_path, O_RDONLY | O_NONBLOCK)) < 0) ||
323 ((fifo2_wr = open(fifo2_path, O_WRONLY)) < 0))
324 goto fail;
325
326 /*
327 * Now we have to close the read side of fifo1 and fifo2 and re-open
328 * them without the O_NONBLOCK flag. This is because we're using
329 * the fifos for synchronization and when we actually try to read
330 * from them we want to block.
331 */
332 (void) close(fifo1_rd);
333 if ((fifo1_rd = open(fifo1_path, O_RDONLY)) < 0)
334 goto fail;
335 (void) close(fifo2_rd);
336 if ((fifo2_rd = open(fifo2_path, O_RDONLY)) < 0)
337 goto fail;
338
339 /*
340 * Once fifo2 is opened no one will ever need to open it again
341 * so delete it now.
342 */
343 (void) unlink(fifo2_path);
344 free(fifo2_path);
345 fifo2_path = NULL;
346
347 /* Attempt to fork and start the door server */
348 lxt_debug("lxt_door_init: starting server");
349 switch (child_pid = fork1()) {
350 case -1:
351 /* fork1() failed. */
352 goto fail;
353 case 0:
354 /* Child process - new door server. */
355 (void) close(fifo1_rd);
356 (void) close(fifo2_wr);
357
358 /* Need to chroot back to the real root directory */
359 if (fchroot(root_fd) != 0) {
360 lxt_debug("lxt_server_exec: "
361 "failed fchroot(\"/\"): %s", strerror(errno));
362 exit(-1);
363 }
364 (void) close(root_fd);
365
366 /* Start the server */
367 lxt_server_exec(fifo1_wr, fifo2_rd);
368 lxt_debug("lxt_server_exec: server init failed");
369 exit(-1);
370 /*NOTREACHED*/
371 }
372 /* Parent process - door client. */
373
374 /*
375 * fifo2 is used to send the door path to the child.
376 * (We can't simply pass it via the address space since the
377 * child will need to exec.) We'll write the name of the door
378 * file to fifo2 before we close the read end of the fifo2 so
379 * that if the child has exited for some reason we won't get
380 * a SIGPIPE. Note that we're reusing the name of fifo1 as
381 * the door path. Also note that we've pre-pended /native
382 * to the fifo/door path. The reason is that we're chroot'ed
383 * to /native, but when the thunking server executes it will
384 * be chroot'ed back to the real root directory.
385 */
386 (void) write(fifo2_wr,
387 fifo1_path_native, strlen(fifo1_path_native) + 1);
388 (void) close(fifo2_wr);
389 (void) close(fifo2_rd);
390
391 /*
392 * Start up a thread that will perfom a waitpid() on the child
393 * door server process. We do this because if the calling
394 * application that is using our interfaces is forking it's own
395 * children and using wait(), then it won't expect to see our
396 * children. We take advantage of the fact that if there are
397 * wait() and a waitpid() calls in progress at the same time
398 * when a child exists, preference will be given to any
399 * waitpid() calls that are explicity waiting for that child.
400 * There is of course a window of time where the child could
401 * exit after we've forked it but before we've called waitpid()
402 * where another wait() in this process could collect the result.
403 * There's nothing we can really do to prevent this short of
404 * stopping all the other threads in this process.
405 */
406 (void) thr_create(NULL, 0,
407 lxt_door_waitpid, (void *)(uintptr_t)child_pid, THR_DAEMON, &tid);
408
409 /*
410 * fifo1 is used for the child process to signal us that the
411 * door server is ready to take requests.
412 */
413 (void) close(fifo1_wr);
414 (void) read(fifo1_rd, &junk, 1);
415 (void) close(fifo1_rd);
416
417 /* If there was a door that was open, close it now. */
418
419 if (lxt_door_fd >= 0)
420 (void) close(lxt_door_fd);
421 /*
422 * The server should be started up by now and fattach()ed the door
423 * server to the fifo/door path. so if we re-open that path now we
424 * should get a fd to the door server.
425 */
426 lxt_door_fd = open(fifo1_path, O_RDWR);
427
428 lxt_debug("lxt_door_init: new server door = %d", lxt_door_fd);
429
430 /* We don't need the fifo/door anymore so delete it. */
431 (void) unlink(fifo1_path);
432 free(fifo1_path);
433 return;
434
435 fail:
436 if (fifo1_path != NULL)
437 (void) unlink(fifo1_path);
438 if (fifo2_path != NULL)
439 (void) unlink(fifo2_path);
440 if (fifo1_rd != -1)
441 (void) close(fifo1_rd);
442 if (fifo1_wr != -1)
443 (void) close(fifo1_wr);
444 if (fifo2_rd != -1)
445 (void) close(fifo2_rd);
446 if (fifo2_wr != -1)
447 (void) close(fifo2_wr);
448 }
449
450 static int
451 lxt_door_call(door_arg_t *door_arg, int lock_held)
452 {
453 int fd;
454
455 if (!lock_held)
456 (void) mutex_lock(&lxt_door_lock);
457
458 /* Get a copy of lxt_door_fd */
459 fd = lxt_door_fd;
460
461 if (!lock_held)
462 (void) mutex_unlock(&lxt_door_lock);
463
464 if (fd == -1) {
465 lxt_debug("lxt_door_call: no door available");
466 return (-1);
467 }
468
469 if (door_call(fd, door_arg) != 0) {
470 lxt_debug("lxt_door_call: call failed");
471 return (-1);
472 }
473 if (door_arg->rbuf == NULL) {
474 lxt_debug("lxt_door_call: call returned NULL");
475 return (-1);
476 }
477 return (0);
478 }
479
480 static int
481 lxt_door_request(door_arg_t *door_arg)
482 {
483 door_arg_t door_ping;
484 lxt_server_arg_t ping_request, *ping_result;
485 int rv, ping_success = 0;
486
487 /* First just try the door call. */
488 lxt_debug("lxt_door_request: calling server");
489 if (lxt_door_call(door_arg, 0) == 0)
490 return (0);
491
492 /* Prepare a door server ping request. */
493 bzero(&door_ping, sizeof (door_ping));
494 bzero(&ping_request, sizeof (ping_request));
495 door_ping.data_ptr = (char *)&ping_request;
496 door_ping.data_size = sizeof (ping_request);
497 ping_request.lxt_sa_op = LXT_SERVER_OP_PING;
498
499 (void) mutex_lock(&lxt_door_lock);
500
501 /* Ping the doors server. */
502 lxt_debug("lxt_door_request: pinging server");
503 if (lxt_door_call(&door_ping, 1) == 0) {
504 /*LINTED*/
505 ping_result = (lxt_server_arg_t *)door_ping.rbuf;
506 ping_success = ping_result->lxt_sa_success;
507 (void) munmap(door_ping.rbuf, door_ping.rsize);
508 }
509
510 if (!ping_success) {
511 /* The server is not responding so start up a new one. */
512 lxt_door_init();
513 }
514 (void) mutex_unlock(&lxt_door_lock);
515
516 /* Retry the original request */
517 lxt_debug("lxt_door_request: calling server, retry");
518 if ((rv = lxt_door_call(door_arg, 0)) == 0)
519 return (0);
520 return (rv);
521 }
522
523 static struct hostent *
524 lxt_gethost(int op, const char *token, int token_len, int type,
525 struct hostent *result, char *buf, int buf_len, int *h_errnop)
526 {
527 door_arg_t door_arg;
528 lxt_gethost_arg_t *data;
529 lxt_server_arg_t *request;
530 int request_size, errno_tmp, i;
531
532 lxt_debug("lxt_gethost: request caught");
533
534 request_size = sizeof (*request) + sizeof (*data) +
535 token_len + buf_len - 1;
536 if ((request = calloc(1, request_size)) == NULL) {
537 lxt_debug("lxt_gethost: calloc() failed");
538 *h_errnop = TRY_AGAIN;
539 return (NULL);
540 }
541 /*LINTED*/
542 data = (lxt_gethost_arg_t *)&request->lxt_sa_data[0];
543
544 /* Initialize the server request. */
545 request->lxt_sa_op = op;
546 data->lxt_gh_type = type;
547 data->lxt_gh_token_len = token_len;
548 data->lxt_gh_buf_len = buf_len;
549 data->lxt_gh_storage_len = token_len + token_len;
550 bcopy(token, &data->lxt_gh_storage[0], token_len);
551
552 /* Initialize door_call() arguments. */
553 bzero(&door_arg, sizeof (door_arg));
554 door_arg.data_ptr = (char *)request;
555 door_arg.data_size = request_size;
556
557 if (lxt_door_request(&door_arg) != 0) {
558 lxt_debug("lxt_gethost: door_call() failed");
559 /* Don't know what caused the error so clear errno. */
560 errno = 0;
561 *h_errnop = ND_SYSTEM;
562 free(request);
563 return (NULL);
564 }
565
566 free(request);
567
568 if (door_arg.rbuf == NULL) {
569 lxt_debug("lxt_gethost: door_call() returned NULL");
570 /* Don't know what caused the error so clear errno. */
571 errno = 0;
572 *h_errnop = ND_SYSTEM;
573 return (NULL);
574 }
575
576 /*LINTED*/
577 request = (lxt_server_arg_t *)door_arg.rbuf;
578 /*LINTED*/
579 data = (lxt_gethost_arg_t *)&request->lxt_sa_data[0];
580
581 /* Check if the remote procedure call failed */
582 if (!request->lxt_sa_success) {
583 lxt_debug("lxt_gethost: remote function call failed");
584 errno_tmp = request->lxt_sa_errno;
585 *h_errnop = data->lxt_gh_h_errno;
586 (void) munmap(door_arg.rbuf, door_arg.rsize);
587 errno = errno_tmp;
588 return (NULL);
589 }
590
591 /* Copy out the results and output buffer. */
592 bcopy(&data->lxt_gh_result, result, sizeof (*result));
593 bcopy(&data->lxt_gh_storage[token_len], buf, buf_len);
594 (void) munmap(door_arg.rbuf, door_arg.rsize);
595
596 /* Now go through the results and convert all offsets to pointers */
597 result->h_name = LXT_OFFSET_TO_PTR(result->h_name, buf);
598 result->h_aliases = LXT_OFFSET_TO_PTR(result->h_aliases, buf);
599 result->h_addr_list = LXT_OFFSET_TO_PTR(result->h_addr_list, buf);
600 for (i = 0; result->h_aliases[i] != NULL; i++) {
601 result->h_aliases[i] =
602 LXT_OFFSET_TO_PTR(result->h_aliases[i], buf);
603 }
604 for (i = 0; result->h_addr_list[i] != NULL; i++) {
605 result->h_addr_list[i] =
606 LXT_OFFSET_TO_PTR(result->h_addr_list[i], buf);
607 }
608
609 return (result);
610 }
611
612 static struct servent *
613 lxt_getserv(int op, const char *token, const int token_len, const char *proto,
614 struct servent *result, char *buf, int buf_len)
615 {
616 door_arg_t door_arg;
617 lxt_getserv_arg_t *data;
618 lxt_server_arg_t *request;
619 int request_size, errno_tmp, i;
620
621 lxt_debug("lxt_getserv: request caught");
622
623 request_size = sizeof (*request) + sizeof (*data) +
624 token_len + buf_len - 1;
625 if ((request = calloc(1, request_size)) == NULL) {
626 lxt_debug("lxt_getserv: calloc() failed");
627 return (NULL);
628 }
629 /*LINTED*/
630 data = (lxt_getserv_arg_t *)&request->lxt_sa_data[0];
631
632 /* Initialize the server request. */
633 request->lxt_sa_op = op;
634 data->lxt_gs_token_len = token_len;
635 data->lxt_gs_buf_len = buf_len;
636 data->lxt_gs_storage_len = token_len + token_len;
637 bcopy(token, &data->lxt_gs_storage[0], token_len);
638
639 bzero(data->lxt_gs_proto, sizeof (data->lxt_gs_proto));
640 if (proto != NULL)
641 (void) strncpy(data->lxt_gs_proto, proto,
642 sizeof (data->lxt_gs_proto));
643
644 /* Initialize door_call() arguments. */
645 bzero(&door_arg, sizeof (door_arg));
646 door_arg.data_ptr = (char *)request;
647 door_arg.data_size = request_size;
648
649 /* Call the doors server */
650 if (lxt_door_request(&door_arg) != 0) {
651 lxt_debug("lxt_getserv: door_call() failed");
652 /* Don't know what caused the error so clear errno */
653 errno = 0;
654 free(request);
655 return (NULL);
656 }
657 free(request);
658
659 if (door_arg.rbuf == NULL) {
660 lxt_debug("lxt_getserv: door_call() returned NULL");
661 /* Don't know what caused the error so clear errno */
662 errno = 0;
663 return (NULL);
664 }
665 /*LINTED*/
666 request = (lxt_server_arg_t *)door_arg.rbuf;
667 /*LINTED*/
668 data = (lxt_getserv_arg_t *)&request->lxt_sa_data[0];
669
670 /* Check if the remote procedure call failed */
671 if (!request->lxt_sa_success) {
672 lxt_debug("lxt_getserv: remote function call failed");
673 errno_tmp = request->lxt_sa_errno;
674 (void) munmap(door_arg.rbuf, door_arg.rsize);
675 errno = errno_tmp;
676 return (NULL);
677 }
678
679 /* Copy out the results and output buffer. */
680 bcopy(&data->lxt_gs_result, result, sizeof (*result));
681 bcopy(&data->lxt_gs_storage[token_len], buf, buf_len);
682 (void) munmap(door_arg.rbuf, door_arg.rsize);
683
684 /*
685 * Now go through the results and convert all offsets to pointers.
686 * See the comments in lxt_server_getserv() for why we need
687 * to subtract 1 from each offset.
688 */
689 result->s_name = LXT_OFFSET_TO_PTR(result->s_name, buf);
690 result->s_proto = LXT_OFFSET_TO_PTR(result->s_proto, buf);
691 result->s_aliases = LXT_OFFSET_TO_PTR(result->s_aliases, buf);
692 for (i = 0; result->s_aliases[i] != NULL; i++) {
693 result->s_aliases[i] =
694 LXT_OFFSET_TO_PTR(result->s_aliases[i], buf);
695 }
696
697 return (result);
698 }
699
700 static void
701 lxt_openlog(const char *ident, int logopt, int facility)
702 {
703 door_arg_t door_arg;
704 lxt_openlog_arg_t *data;
705 lxt_server_arg_t *request;
706 int request_size;
707
708 request_size = sizeof (*request) + sizeof (*data);
709 if ((request = calloc(1, request_size)) == NULL) {
710 lxt_debug("lxt_openlog: calloc() failed");
711 return;
712 }
713 /*LINTED*/
714 data = (lxt_openlog_arg_t *)&request->lxt_sa_data[0];
715
716 /* Initialize the server request. */
717 request->lxt_sa_op = LXT_SERVER_OP_OPENLOG;
718 data->lxt_ol_facility = facility;
719 data->lxt_ol_logopt = logopt;
720 (void) strlcpy(data->lxt_ol_ident, ident, sizeof (data->lxt_ol_ident));
721
722 /* Initialize door_call() arguments. */
723 bzero(&door_arg, sizeof (door_arg));
724 door_arg.data_ptr = (char *)request;
725 door_arg.data_size = request_size;
726
727 /* Call the doors server */
728 if (lxt_door_request(&door_arg) != 0) {
729 lxt_debug("lxt_openlog: door_call() failed");
730 free(request);
731 return;
732 }
733 free(request);
734
735 if (door_arg.rbuf == NULL) {
736 lxt_debug("lxt_openlog: door_call() returned NULL");
737 return;
738 }
739
740 /*LINTED*/
741 request = (lxt_server_arg_t *)door_arg.rbuf;
742
743 /* Check if the remote procedure call failed */
744 if (!request->lxt_sa_success) {
745 lxt_debug("lxt_openlog: remote function call failed");
746 }
747 (void) munmap(door_arg.rbuf, door_arg.rsize);
748 }
749
750 static void
751 lxt_vsyslog(int priority, const char *message, va_list va)
752 {
753 door_arg_t door_arg;
754 lxt_syslog_arg_t *data;
755 lxt_server_arg_t *request;
756 psinfo_t p;
757 char procfile[PRFNSZ], *buf = NULL, *estr;
758 int buf_len, buf_i, estr_len, request_size, procfd;
759 int i, key, err_count = 0, tok_count = 0;
760 int errno_backup = errno;
761
762 /*
763 * Here we're going to use vsnprintf() to expand the message
764 * string passed in before we hand it off to a Linux process.
765 * Before we can call vsnprintf() we'll need to do modify the
766 * string to deal with certain special tokens.
767 *
768 * syslog() supports a special '%m' format token that expands to
769 * the error message string associated with the current value
770 * of errno. Unfortunatly if we pass this token to vsnprintf()
771 * it will choke so we need to expand that token manually here.
772 *
773 * We also need to expand any "%%" characters into "%%%%".
774 * The reason is that we'll be calling vsnprintf() which will
775 * translate "%%%%" back to "%%", which is safe to pass to the
776 * Linux version if syslog. If we didn't do this then vsnprintf()
777 * would translate "%%" to "%" and then the Linux syslog would
778 * attempt to intrepret "%" and whatever character follows it
779 * as a printf format style token.
780 */
781 for (key = i = 0; message[i] != '\0'; i++) {
782 if (!key && message[i] == '%') {
783 key = 1;
784 continue;
785 }
786 if (key && message[i] == '%')
787 tok_count++;
788 if (key && message[i] == 'm')
789 err_count++;
790 key = 0;
791 }
792
793 /* We found some tokens that we need to expand. */
794 if (err_count || tok_count) {
795 estr = strerror(errno_backup);
796 estr_len = strlen(estr);
797 assert(estr_len >= 2);
798
799 /* Allocate a buffer to hold the expanded string. */
800 buf_len = i + 1 +
801 (tok_count * 2) + (err_count * (estr_len - 2));
802 if ((buf = calloc(1, buf_len)) == NULL) {
803 lxt_debug("lxt_vsyslog: calloc() failed");
804 return;
805 }
806
807 /* Finally, expand %% and %m. */
808 for (key = buf_i = i = 0; message[i] != '\0'; i++) {
809 assert(buf_i < buf_len);
810 if (!key && message[i] == '%') {
811 buf[buf_i++] = '%';
812 key = 1;
813 continue;
814 }
815 if (key && message[i] == 'm') {
816 (void) bcopy(estr, &buf[buf_i - 1], estr_len);
817 buf_i += estr_len - 1;
818 } else if (key && message[i] == '%') {
819 (void) bcopy("%%%%", &buf[buf_i - 1], 4);
820 buf_i += 4 - 1;
821 } else {
822 buf[buf_i++] = message[i];
823 }
824 key = 0;
825 }
826 assert(buf[buf_i] == '\0');
827 assert(buf_i == (buf_len - 1));
828
829 /* Use the expanded buffer as our format string. */
830 message = buf;
831 }
832
833 /* Allocate the request we're going to send to the server */
834 request_size = sizeof (*request) + sizeof (*data);
835 if ((request = calloc(1, request_size)) == NULL) {
836 lxt_debug("lxt_vsyslog: calloc() failed");
837 return;
838 }
839
840 /*LINTED*/
841 data = (lxt_syslog_arg_t *)&request->lxt_sa_data[0];
842
843 /* Initialize the server request. */
844 request->lxt_sa_op = LXT_SERVER_OP_SYSLOG;
845 data->lxt_sl_priority = priority;
846 data->lxt_sl_pid = getpid();
847 (void) vsnprintf(data->lxt_sl_message, sizeof (data->lxt_sl_message),
848 message, va);
849
850 /* If we did token expansion then free the intermediate buffer. */
851 if (err_count || tok_count)
852 free(buf);
853
854 /* Add the current program name into the request */
855 (void) sprintf(procfile, "/proc/%u/psinfo", (int)getpid());
856 /* (void) sprintf(procfile, "/native/proc/%u/psinfo", (int)getpid()); */
857 if ((procfd = open(procfile, O_RDONLY)) >= 0) {
858 if (read(procfd, &p, sizeof (psinfo_t)) >= 0) {
859 (void) strncpy(data->lxt_sl_progname, p.pr_fname,
860 sizeof (data->lxt_sl_progname));
861 }
862 (void) close(procfd);
863 }
864
865 /* Initialize door_call() arguments. */
866 bzero(&door_arg, sizeof (door_arg));
867 door_arg.data_ptr = (char *)request;
868 door_arg.data_size = request_size;
869
870 /* Call the doors server */
871 if (lxt_door_request(&door_arg) != 0) {
872 lxt_debug("lxt_vsyslog: door_call() failed");
873 free(request);
874 return;
875 }
876 free(request);
877
878 if (door_arg.rbuf == NULL) {
879 lxt_debug("lxt_vsyslog: door_call() returned NULL");
880 return;
881 }
882
883 /*LINTED*/
884 request = (lxt_server_arg_t *)door_arg.rbuf;
885
886 /* Check if the remote procedure call failed */
887 if (!request->lxt_sa_success) {
888 lxt_debug("lxt_vsyslog: remote function call failed");
889 }
890 (void) munmap(door_arg.rbuf, door_arg.rsize);
891 }
892
893 static void
894 lxt_closelog(void)
895 {
896 door_arg_t door_arg;
897 lxt_server_arg_t *request;
898 int request_size;
899
900 request_size = sizeof (*request);
901 if ((request = calloc(1, request_size)) == NULL) {
902 lxt_debug("lxt_closelog: calloc() failed");
903 return;
904 }
905
906 /* Initialize the server request. */
907 request->lxt_sa_op = LXT_SERVER_OP_CLOSELOG;
908
909 /* Initialize door_call() arguments. */
910 bzero(&door_arg, sizeof (door_arg));
911 door_arg.data_ptr = (char *)request;
912 door_arg.data_size = request_size;
913
914 /* Call the doors server */
915 if (lxt_door_request(&door_arg) != 0) {
916 lxt_debug("lxt_closelog: door_call() failed");
917 free(request);
918 return;
919 }
920 free(request);
921
922 if (door_arg.rbuf == NULL) {
923 lxt_debug("lxt_closelog: door_call() returned NULL");
924 return;
925 }
926
927 /*LINTED*/
928 request = (lxt_server_arg_t *)door_arg.rbuf;
929
930 /* Check if the remote procedure call failed */
931 if (!request->lxt_sa_success) {
932 lxt_debug("lxt_closelog: remote function call failed");
933 }
934 (void) munmap(door_arg.rbuf, door_arg.rsize);
935 }
936
937 static void
938 lxt_pset_keep(priv_op_t op, priv_ptype_t type, priv_set_t *pset,
939 const char *priv)
940 {
941 if (priv_ismember(pset, priv) == B_TRUE) {
942 if (op == PRIV_OFF) {
943 (void) priv_delset(pset, priv);
944 lxt_debug("lxt_pset_keep: "
945 "preventing drop of \"%s\" from \"%s\" set",
946 priv, type);
947 }
948 } else {
949 if (op == PRIV_SET) {
950 (void) priv_addset(pset, priv);
951 lxt_debug("lxt_pset_keep: "
952 "preventing drop of \"%s\" from \"%s\" set",
953 priv, type);
954 }
955 }
956 }
957
958 /*
959 * Public interfaces - used by lx_nametoaddr
960 */
961 void
962 lxt_vdebug(const char *msg, va_list va)
963 {
964 char buf[LXT_MSG_MAXLEN + 1];
965 int rv, n;
966
967 if (debug_fd == -1)
968 return;
969
970 /* Prefix the message with pid/tid. */
971 if ((n = snprintf(buf, sizeof (buf), "%u/%u: ",
972 getpid(), thr_self())) == -1)
973 return;
974
975 /* Format the message. */
976 if (vsnprintf(&buf[n], sizeof (buf) - n, msg, va) == -1)
977 return;
978
979 /* Add a carrige return if there isn't one already. */
980 if ((buf[strlen(buf) - 1] != '\n') &&
981 (strlcat(buf, "\n", sizeof (buf)) >= sizeof (buf)))
982 return;
983
984 /* We retry in case of EINTR */
985 do {
986 rv = write(debug_fd, buf, strlen(buf));
987 } while ((rv == -1) && (errno == EINTR));
988 }
989
990 void
991 lxt_debug(const char *msg, ...)
992 {
993 va_list va;
994 int errno_backup;
995
996 if (debug_fd == -1)
997 return;
998
999 errno_backup = errno;
1000 va_start(va, msg);
1001 lxt_vdebug(msg, va);
1002 va_end(va);
1003 errno = errno_backup;
1004 }
1005
1006 struct hostent *
1007 lxt_gethostbyaddr_r(const char *addr, int addr_len, int type,
1008 struct hostent *result, char *buf, int buf_len, int *h_errnop)
1009 {
1010 lxt_debug("lxt_gethostbyaddr_r: request recieved");
1011 return (lxt_gethost(LXT_SERVER_OP_ADDR2HOST,
1012 addr, addr_len, type, result, buf, buf_len, h_errnop));
1013 }
1014
1015 struct hostent *
1016 lxt_gethostbyname_r(const char *name,
1017 struct hostent *result, char *buf, int buf_len, int *h_errnop)
1018 {
1019 lxt_debug("lxt_gethostbyname_r: request recieved");
1020 return (lxt_gethost(LXT_SERVER_OP_NAME2HOST,
1021 name, strlen(name) + 1, 0, result, buf, buf_len, h_errnop));
1022 }
1023
1024 struct servent *
1025 lxt_getservbyport_r(int port, const char *proto,
1026 struct servent *result, char *buf, int buf_len)
1027 {
1028 lxt_debug("lxt_getservbyport_r: request recieved");
1029 return (lxt_getserv(LXT_SERVER_OP_PORT2SERV,
1030 (const char *)&port, sizeof (int), proto, result, buf, buf_len));
1031 }
1032
1033 struct servent *
1034 lxt_getservbyname_r(const char *name, const char *proto,
1035 struct servent *result, char *buf, int buf_len)
1036 {
1037 lxt_debug("lxt_getservbyname_r: request recieved");
1038 return (lxt_getserv(LXT_SERVER_OP_NAME2SERV,
1039 name, strlen(name) + 1, proto, result, buf, buf_len));
1040 }
1041
1042 /*
1043 * "Public" interfaces - used to override public existing interfaces
1044 */
1045 #pragma weak _close = close
1046 int
1047 close(int fd)
1048 {
1049 static fp1_t fp = NULL;
1050
1051 /*
1052 * Don't let the process close our file descriptor that points
1053 * back to the root directory.
1054 */
1055 if (fd == root_fd)
1056 return (0);
1057 if (fd == debug_fd)
1058 return (0);
1059
1060 if (fp == NULL)
1061 fp = (fp1_t)dlsym(RTLD_NEXT, "close");
1062 return (fp((uintptr_t)fd));
1063 }
1064
1065 int
1066 _setppriv(priv_op_t op, priv_ptype_t type, const priv_set_t *pset)
1067 {
1068 static fp3_t fp = NULL;
1069 priv_set_t *pset_new;
1070 int rv;
1071
1072 lxt_debug("_setppriv: request caught");
1073
1074 if (fp == NULL)
1075 fp = (fp3_t)dlsym(RTLD_NEXT, "_setppriv");
1076
1077 while ((pset_new = priv_allocset()) == NULL)
1078 (void) sleep(1);
1079
1080 priv_copyset(pset, pset_new);
1081 lxt_pset_keep(op, type, pset_new, PRIV_PROC_EXEC);
1082 lxt_pset_keep(op, type, pset_new, PRIV_PROC_FORK);
1083 lxt_pset_keep(op, type, pset_new, PRIV_PROC_CHROOT);
1084 lxt_pset_keep(op, type, pset_new, PRIV_FILE_DAC_READ);
1085 lxt_pset_keep(op, type, pset_new, PRIV_FILE_DAC_WRITE);
1086 lxt_pset_keep(op, type, pset_new, PRIV_FILE_DAC_SEARCH);
1087
1088 rv = fp(op, (uintptr_t)type, (uintptr_t)pset_new);
1089 priv_freeset(pset_new);
1090 return (rv);
1091 }
1092
1093 void
1094 openlog(const char *ident, int logopt, int facility)
1095 {
1096 lxt_debug("openlog: request caught");
1097 lxt_openlog(ident, logopt, facility);
1098 }
1099
1100 void
1101 syslog(int priority, const char *message, ...)
1102 {
1103 va_list va;
1104
1105 lxt_debug("syslog: request caught");
1106 va_start(va, message);
1107 lxt_vsyslog(priority, message, va);
1108 va_end(va);
1109 }
1110
1111 void
1112 vsyslog(int priority, const char *message, va_list va)
1113 {
1114 lxt_debug("vsyslog: request caught");
1115 lxt_vsyslog(priority, message, va);
1116 }
1117
1118 void
1119 closelog(void)
1120 {
1121 lxt_debug("closelog: request caught");
1122 lxt_closelog();
1123 }