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 }