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  * Reparsed daemon
  28  */
  29 
  30 #include <stdio.h>
  31 #include <stdio_ext.h>
  32 #include <stdlib.h>
  33 #include <unistd.h>
  34 #include <signal.h>
  35 #include <sys/types.h>
  36 #include <sys/stat.h>
  37 #include <fcntl.h>
  38 #include <memory.h>
  39 #include <alloca.h>
  40 #include <ucontext.h>
  41 #include <errno.h>
  42 #include <syslog.h>
  43 #include <string.h>
  44 #include <strings.h>
  45 #include <door.h>
  46 #include <wait.h>
  47 #include <libintl.h>
  48 #include <locale.h>
  49 #include <sys/param.h>
  50 #include <sys/systeminfo.h>
  51 #include <sys/thread.h>
  52 #include <rpc/xdr.h>
  53 #include <priv.h>
  54 #include <sys/fs_reparse.h>
  55 #include <priv_utils.h>
  56 #include <rpcsvc/daemon_utils.h>
  57 
  58 #define REPARSED_CMD_OPTS       "v"
  59 #define DOOR_RESULT_BUFSZ       (MAXPATHLEN + sizeof (reparsed_door_res_t))
  60 #define SAFETY_BUFFER           8*1024
  61 
  62 static char *MyName;
  63 static int verbose = 0;
  64 
  65 static int start_reparsed_svcs();
  66 static void daemonize(void);
  67 static void reparsed_door_call_error(int error, int buflen);
  68 static void reparsed_doorfunc(void *cookie, char *argp, size_t arg_size,
  69                         door_desc_t *dp, uint_t n_desc);
  70 
  71 static void
  72 usage()
  73 {
  74         syslog(LOG_ERR, "Usage: %s", MyName);
  75         syslog(LOG_ERR, "\t[-v]\t\tverbose error messages)");
  76         exit(1);
  77 }
  78 
  79 static void
  80 warn_hup(int i)
  81 {
  82         syslog(LOG_ERR, "SIGHUP received: ignored");
  83         (void) signal(SIGHUP, warn_hup);
  84 }
  85 
  86 /*
  87  * Processing for daemonization
  88  */
  89 static void
  90 daemonize(void)
  91 {
  92         switch (fork()) {
  93         case -1:
  94                 syslog(LOG_ERR, "reparsed: can't fork - errno %d", errno);
  95                 exit(2);
  96                 /* NOTREACHED */
  97         case 0:         /* child */
  98                 break;
  99 
 100         default:        /* parent */
 101                 _exit(0);
 102         }
 103         (void) chdir("/");
 104 
 105         /*
 106          * Close stdin, stdout, and stderr.
 107          * Open again to redirect input+output
 108          */
 109         (void) close(0);
 110         (void) close(1);
 111         (void) close(2);
 112         (void) open("/dev/null", O_RDONLY);
 113         (void) open("/dev/null", O_WRONLY);
 114         (void) dup(1);
 115         (void) setsid();
 116 }
 117 
 118 int
 119 main(int argc, char *argv[])
 120 {
 121         pid_t pid;
 122         int c, error;
 123         struct rlimit rlset;
 124         char *defval;
 125 
 126         /*
 127          * There is no check for non-global zone and Trusted Extensions.
 128          * Reparsed works in both of these environments as long as the
 129          * services that use reparsed are supported.
 130          */
 131 
 132         MyName = argv[0];
 133         if (geteuid() != 0) {
 134                 syslog(LOG_ERR, "%s must be run as root", MyName);
 135                 exit(1);
 136         }
 137 
 138         while ((c = getopt(argc, argv, REPARSED_CMD_OPTS)) != EOF) {
 139                 switch (c) {
 140                 case 'v':
 141                         verbose++;
 142                         break;
 143                 default:
 144                         usage();
 145                 }
 146         }
 147 
 148         daemonize();
 149         openlog(MyName, LOG_PID | LOG_NDELAY, LOG_DAEMON);
 150 
 151         (void) _create_daemon_lock(REPARSED, DAEMON_UID, DAEMON_GID);
 152         (void) enable_extended_FILE_stdio(-1, -1);
 153         switch (_enter_daemon_lock(REPARSED)) {
 154         case 0:
 155                 break;
 156         case -1:
 157                 syslog(LOG_ERR, "Error locking for %s", REPARSED);
 158                 exit(2);
 159         default:
 160                 /* daemon was already running */
 161                 exit(0);
 162         }
 163 
 164         (void) signal(SIGHUP, warn_hup);
 165 
 166         /*
 167          * Make the process a privilege aware daemon.
 168          * Only "basic" privileges are required.
 169          *
 170          */
 171         if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, 0, 0,
 172             (char *)NULL) == -1) {
 173                 syslog(LOG_ERR, "should be run with sufficient privileges");
 174                 exit(3);
 175         }
 176 
 177         /*
 178          * Clear basic privileges not required by reparsed.
 179          */
 180         __fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION,
 181             PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL);
 182 
 183         return (start_reparsed_svcs());
 184 }
 185 
 186 static void
 187 reparsed_door_call_error(int error, int buflen)
 188 {
 189         reparsed_door_res_t rpd_res;
 190 
 191         memset(&rpd_res, 0, sizeof (reparsed_door_res_t));
 192         rpd_res.res_status = error;
 193         rpd_res.res_len = buflen;
 194         door_return((char *)&rpd_res, sizeof (reparsed_door_res_t), NULL, 0);
 195 
 196         (void) door_return(NULL, 0, NULL, 0);
 197         /* NOTREACHED */
 198 }
 199 
 200 /*
 201  *  reparsed_doorfunc
 202  *
 203  *  argp:  "service_type:service_data" string
 204  *  dp & n_desc: not used.
 205  */
 206 static void
 207 reparsed_doorfunc(void *cookie, char *argp, size_t arg_size,
 208     door_desc_t *dp, uint_t n_desc)
 209 {
 210         int err;
 211         size_t bufsz;
 212         char *svc_type, *svc_data;
 213         char *cp, *buf, *sbuf, res_buf[DOOR_RESULT_BUFSZ];
 214         reparsed_door_res_t *resp;
 215 
 216         if ((argp == NULL) || (arg_size == 0)) {
 217                 reparsed_door_call_error(EINVAL, 0);
 218                 /* NOTREACHED */
 219         }
 220 
 221         if (verbose)
 222                 syslog(LOG_NOTICE, "reparsed_door: [%s, %d]", argp, arg_size);
 223 
 224         if ((svc_type = strdup(argp)) == NULL) {
 225                 reparsed_door_call_error(ENOMEM, 0);
 226                 /* NOTREACHED */
 227         }
 228 
 229         /*
 230          * Door argument string comes in "service_type:service_data" format.
 231          * Need to break it into separate "service_type" and "service_data"
 232          * string before passing them to reparse_deref() to process them.
 233          */
 234         if ((cp = strchr(svc_type, ':')) == NULL) {
 235                 free(svc_type);
 236                 reparsed_door_call_error(EINVAL, 0);
 237                 /* NOTREACHED */
 238         }
 239         *cp++ = '\0';
 240         svc_data = cp;
 241 
 242         /*
 243          * Setup buffer for reparse_deref(). 'bufsz' is the actual
 244          * buffer size to hold the result returned by reparse_deref().
 245          */
 246         resp = (reparsed_door_res_t *)res_buf;
 247         buf = resp->res_data;
 248         bufsz = sizeof (res_buf) - sizeof (reparsed_door_res_t);
 249 
 250         /*
 251          * reparse_deref() calls the service type plugin library to process
 252          * the service data. The plugin library function should understand
 253          * the context of the service data and should be the one to XDR the
 254          * results before returning it to the caller.
 255          */
 256         err = reparse_deref(svc_type, svc_data, buf, &bufsz);
 257 
 258         if (verbose)
 259                 syslog(LOG_NOTICE,
 260                     "reparsed_deref(svc_type: %s, data: %s, size: %d) -> %d",
 261                     svc_type, svc_data, bufsz, err);
 262 
 263         switch (err) {
 264         case 0:
 265                 break;
 266 
 267         case EOVERFLOW:
 268                 /*
 269                  * bufsz was returned with size needed by reparse_deref().
 270                  *
 271                  * We cannot use malloc() here because door_return() never
 272                  * returns, and memory allocated by malloc() would get leaked.
 273                  */
 274                 sbuf = alloca(bufsz + sizeof (reparsed_door_res_t));
 275                 if (sbuf == NULL || stack_inbounds(buf) == 0 ||
 276                     stack_inbounds(buf + sizeof (reparsed_door_res_t) +
 277                     SAFETY_BUFFER - 1) == 0) {
 278                         free(svc_type);
 279                         reparsed_door_call_error(ENOMEM, 0);
 280                         /* NOTREACHED */
 281                 }
 282 
 283                 resp = (reparsed_door_res_t *)sbuf;
 284                 if ((err = reparse_deref(svc_type, svc_data, resp->res_data,
 285                     &bufsz)) == 0)
 286                         break;
 287 
 288                 /* fall through */
 289 
 290         default:
 291                 free(svc_type);
 292                 reparsed_door_call_error(err, 0);
 293                 /* NOTREACHED */
 294         }
 295 
 296         free(svc_type);
 297 
 298         if (verbose)
 299                 syslog(LOG_NOTICE, "reparsed_door_return <buf=%s> size=%d",
 300                     buf, bufsz);
 301 
 302         resp->res_status = 0;
 303         resp->res_len = bufsz;
 304         (void) door_return((char *)resp, bufsz + sizeof (reparsed_door_res_t),
 305             NULL, 0);
 306 
 307         (void) door_return(NULL, 0, NULL, 0);
 308         /* NOTREACHED */
 309 }
 310 
 311 static int
 312 start_reparsed_svcs()
 313 {
 314         int doorfd;
 315         int dfd;
 316 
 317         if ((doorfd = door_create(reparsed_doorfunc, NULL,
 318             DOOR_REFUSE_DESC|DOOR_NO_CANCEL)) == -1) {
 319                 syslog(LOG_ERR, "Unable to create door");
 320                 return (1);
 321         }
 322 
 323         /*
 324          * Create a file system path for the door
 325          */
 326         if ((dfd = open(REPARSED_DOOR, O_RDWR|O_CREAT|O_TRUNC,
 327             S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
 328                 syslog(LOG_ERR, "unable to open %s", REPARSED_DOOR);
 329                 (void) close(doorfd);
 330                 return (1);
 331         }
 332 
 333         /*
 334          * Clean up any stale associations
 335          */
 336         (void) fdetach(REPARSED_DOOR);
 337 
 338         /*
 339          * Register in the kernel namespace for door_ki_open().
 340          */
 341         if (fattach(doorfd, REPARSED_DOOR) == -1) {
 342                 syslog(LOG_ERR, "Unable to fattach door %s", REPARSED_DOOR);
 343                 (void) close(doorfd);
 344                 (void) close(dfd);
 345                 return (1);
 346         }
 347         (void) close(dfd);
 348 
 349         /*
 350          * Wait for incoming calls
 351          */
 352         /*CONSTCOND*/
 353         while (1)
 354                 (void) pause();
 355 
 356         syslog(LOG_ERR, "Door server exited");
 357         return (10);
 358 }