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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 
  26 
  27 /*
  28  * main() of idmapd(1M)
  29  */
  30 
  31 #include "idmapd.h"
  32 #include <atomic.h>
  33 #include <signal.h>
  34 #include <rpc/pmap_clnt.h> /* for pmap_unset */
  35 #include <string.h> /* strcmp */
  36 #include <unistd.h> /* setsid */
  37 #include <sys/types.h>
  38 #include <memory.h>
  39 #include <stropts.h>
  40 #include <netconfig.h>
  41 #include <sys/resource.h> /* rlimit */
  42 #include <rpcsvc/daemon_utils.h> /* DAEMON_UID and DAEMON_GID */
  43 #include <priv_utils.h> /* privileges */
  44 #include <locale.h>
  45 #include <sys/systeminfo.h>
  46 #include <errno.h>
  47 #include <sys/wait.h>
  48 #include <sys/time.h>
  49 #include <zone.h>
  50 #include <door.h>
  51 #include <port.h>
  52 #include <tsol/label.h>
  53 #include <sys/resource.h>
  54 #include <sys/sid.h>
  55 #include <sys/idmap.h>
  56 #include <pthread.h>
  57 #include <stdarg.h>
  58 #include <assert.h>
  59 #include <note.h>
  60 
  61 #define CBUFSIZ 26      /* ctime(3c) */
  62 
  63 static void     term_handler(int);
  64 static void     init_idmapd();
  65 static void     fini_idmapd();
  66 
  67 /* The DC Locator lives inside idmap (for now). */
  68 extern void     init_dc_locator(void);
  69 extern void     fini_dc_locator(void);
  70 
  71 idmapd_state_t  _idmapdstate;
  72 
  73 SVCXPRT *xprt = NULL;
  74 
  75 static int dfd = -1;            /* our door server fildes, for unregistration */
  76 static boolean_t degraded = B_FALSE;
  77 
  78 
  79 static uint32_t         num_threads = 0;
  80 static pthread_key_t    create_threads_key;
  81 static uint32_t         max_threads = 40;
  82 
  83 /*
  84  * Server door thread start routine.
  85  *
  86  * Set a TSD value to the door thread. This enables the destructor to
  87  * be called when this thread exits.
  88  */
  89 /*ARGSUSED*/
  90 static void *
  91 idmapd_door_thread_start(void *arg)
  92 {
  93         static void *value = 0;
  94 
  95         /*
  96          * Disable cancellation to avoid memory leaks from not running
  97          * the thread cleanup code.
  98          */
  99         (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
 100         (void) pthread_setspecific(create_threads_key, value);
 101         (void) door_return(NULL, 0, NULL, 0);
 102 
 103         /* make lint happy */
 104         return (NULL);
 105 }
 106 
 107 /*
 108  * Server door threads creation
 109  */
 110 /*ARGSUSED*/
 111 static void
 112 idmapd_door_thread_create(door_info_t *dip)
 113 {
 114         int             num;
 115         pthread_t       thread_id;
 116 
 117         if ((num = atomic_inc_32_nv(&num_threads)) > max_threads) {
 118                 atomic_dec_32(&num_threads);
 119                 idmapdlog(LOG_DEBUG,
 120                     "thread creation refused - %d threads currently active",
 121                     num - 1);
 122                 return;
 123         }
 124         (void) pthread_create(&thread_id, NULL, idmapd_door_thread_start, NULL);
 125         idmapdlog(LOG_DEBUG,
 126             "created thread ID %d - %d threads currently active",
 127             thread_id, num);
 128 }
 129 
 130 /*
 131  * Server door thread cleanup
 132  */
 133 /*ARGSUSED*/
 134 static void
 135 idmapd_door_thread_cleanup(void *arg)
 136 {
 137         int num;
 138 
 139         num = atomic_dec_32_nv(&num_threads);
 140         idmapdlog(LOG_DEBUG,
 141             "exiting thread ID %d - %d threads currently active",
 142             pthread_self(), num);
 143 }
 144 
 145 /*
 146  * This is needed for mech_krb5 -- we run as daemon, yes, but we want
 147  * mech_krb5 to think we're root so it can get host/nodename.fqdn
 148  * tickets for us so we can authenticate to AD as the machine account
 149  * that we are.  For more details look at the entry point in mech_krb5
 150  * corresponding to gss_init_sec_context().
 151  *
 152  * As a side effect of faking our effective UID to mech_krb5 we will use
 153  * root's default ccache (/tmp/krb5cc_0).  But if that's created by
 154  * another process then we won't have access to it: we run as daemon and
 155  * keep PRIV_FILE_DAC_READ, which is insufficient to share the ccache
 156  * with others.  We putenv("KRB5CCNAME=/var/run/idmap/ccache") in main()
 157  * to avoid this issue; see main().
 158  *
 159  * Someday we'll have gss/mech_krb5 extensions for acquiring initiator
 160  * creds with keytabs/raw keys, and someday we'll have extensions to
 161  * libsasl to specify creds/name to use on the initiator side, and
 162  * someday we'll have extensions to libldap to pass those through to
 163  * libsasl.  Until then this interposer will have to do.
 164  *
 165  * Also, we have to tell lint to shut up: it thinks app_krb5_user_uid()
 166  * is defined but not used.
 167  */
 168 /*LINTLIBRARY*/
 169 uid_t
 170 app_krb5_user_uid(void)
 171 {
 172         return (0);
 173 }
 174 
 175 /*ARGSUSED*/
 176 static void
 177 term_handler(int sig)
 178 {
 179         idmapdlog(LOG_INFO, "Terminating.");
 180         fini_dc_locator();
 181         fini_idmapd();
 182         _exit(0);
 183 }
 184 
 185 /*ARGSUSED*/
 186 static void
 187 usr1_handler(int sig)
 188 {
 189         NOTE(ARGUNUSED(sig))
 190         print_idmapdstate();
 191 }
 192 
 193 static int pipe_fd = -1;
 194 
 195 static void
 196 daemonize_ready(void)
 197 {
 198         char data = '\0';
 199         /*
 200          * wake the parent
 201          */
 202         (void) write(pipe_fd, &data, 1);
 203         (void) close(pipe_fd);
 204 }
 205 
 206 static int
 207 daemonize_start(void)
 208 {
 209         char    data;
 210         int     status;
 211         int     devnull;
 212         int     filedes[2];
 213         pid_t   pid;
 214 
 215         (void) sigset(SIGPIPE, SIG_IGN);
 216         devnull = open("/dev/null", O_RDONLY);
 217         if (devnull < 0)
 218                 return (-1);
 219         (void) dup2(devnull, 0);
 220         (void) dup2(2, 1);      /* stderr only */
 221         if (pipe(filedes) < 0)
 222                 return (-1);
 223         if ((pid = fork1()) < 0)
 224                 return (-1);
 225         if (pid != 0) {
 226                 /*
 227                  * parent
 228                  */
 229                 (void) close(filedes[1]);
 230                 if (read(filedes[0], &data, 1) == 1) {
 231                         /* presume success */
 232                         _exit(0);
 233                 }
 234                 status = -1;
 235                 (void) wait4(pid, &status, 0, NULL);
 236                 if (WIFEXITED(status))
 237                         _exit(WEXITSTATUS(status));
 238                 else
 239                         _exit(-1);
 240         }
 241 
 242         /*
 243          * child
 244          */
 245         pipe_fd = filedes[1];
 246         (void) close(filedes[0]);
 247         (void) setsid();
 248         (void) umask(0077);
 249         openlog("idmap", LOG_PID, LOG_DAEMON);
 250 
 251         return (0);
 252 }
 253 
 254 
 255 int
 256 main(int argc, char **argv)
 257 {
 258         int c;
 259         struct rlimit rl;
 260 
 261         if (rwlock_init(&_idmapdstate.rwlk_cfg, USYNC_THREAD, NULL) != 0)
 262                 return (-1);
 263         if (mutex_init(&_idmapdstate.addisc_lk, USYNC_THREAD, NULL) != 0)
 264                 return (-1);
 265         if (cond_init(&_idmapdstate.addisc_cv, USYNC_THREAD, NULL) != 0)
 266                 return (-1);
 267 
 268         _idmapdstate.daemon_mode = TRUE;
 269         while ((c = getopt(argc, argv, "d")) != -1) {
 270                 switch (c) {
 271                         case 'd':
 272                                 _idmapdstate.daemon_mode = FALSE;
 273                                 break;
 274                         default:
 275                                 (void) fprintf(stderr,
 276                                     "Usage: /usr/lib/idmapd [-d]\n");
 277                                 return (SMF_EXIT_ERR_CONFIG);
 278                 }
 279         }
 280 
 281         /* set locale and domain for internationalization */
 282         (void) setlocale(LC_ALL, "");
 283         (void) textdomain(TEXT_DOMAIN);
 284 
 285         idmap_set_logger(idmapdlog);
 286         adutils_set_logger(idmapdlog);
 287 
 288         if (is_system_labeled() && getzoneid() != GLOBAL_ZONEID) {
 289                 idmapdlog(LOG_ERR,
 290                     "with Trusted Extensions idmapd runs only in the "
 291                     "global zone");
 292                 exit(1);
 293         }
 294 
 295         /*
 296          * Raise the fd limit to max
 297          */
 298         if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {
 299                 idmapdlog(LOG_ERR, "getrlimit failed");
 300         } else if (rl.rlim_cur < rl.rlim_max) {
 301                 rl.rlim_cur = rl.rlim_max;
 302                 if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
 303                         idmapdlog(LOG_ERR,
 304                             "Unable to raise RLIMIT_NOFILE to %d",
 305                             rl.rlim_cur);
 306         }
 307 
 308         (void) mutex_init(&_svcstate_lock, USYNC_THREAD, NULL);
 309 
 310         if (_idmapdstate.daemon_mode == TRUE) {
 311                 if (daemonize_start() < 0) {
 312                         idmapdlog(LOG_ERR, "unable to daemonize");
 313                         exit(-1);
 314                 }
 315         } else
 316                 (void) umask(0077);
 317 
 318         idmap_init_tsd_key();
 319 
 320         init_idmapd();
 321         init_dc_locator();
 322 
 323         /* signal handlers that should run only after we're initialized */
 324         (void) sigset(SIGTERM, term_handler);
 325         (void) sigset(SIGUSR1, usr1_handler);
 326         (void) sigset(SIGHUP, idmap_cfg_hup_handler);
 327 
 328         if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET,
 329             DAEMON_UID, DAEMON_GID,
 330             PRIV_PROC_AUDIT, PRIV_FILE_DAC_READ,
 331             (char *)NULL) == -1) {
 332                 idmapdlog(LOG_ERR, "unable to drop privileges");
 333                 exit(1);
 334         }
 335 
 336         __fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION,
 337             PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL);
 338 
 339         if (_idmapdstate.daemon_mode == TRUE)
 340                 daemonize_ready();
 341 
 342         /* With doors RPC this just wastes this thread, oh well */
 343         svc_run();
 344         return (0);
 345 }
 346 
 347 static void
 348 init_idmapd()
 349 {
 350         int     error;
 351         int     connmaxrec = IDMAP_MAX_DOOR_RPC;
 352 
 353 
 354         /* create directories as root and chown to daemon uid */
 355         if (create_directory(IDMAP_DBDIR, DAEMON_UID, DAEMON_GID) < 0)
 356                 exit(1);
 357         if (create_directory(IDMAP_CACHEDIR, DAEMON_UID, DAEMON_GID) < 0)
 358                 exit(1);
 359 
 360         /*
 361          * Set KRB5CCNAME in the environment.  See app_krb5_user_uid()
 362          * for more details.  We blow away the existing one, if there is
 363          * one.
 364          */
 365         (void) unlink(IDMAP_CACHEDIR "/ccache");
 366         (void) putenv("KRB5CCNAME=" IDMAP_CACHEDIR "/ccache");
 367         (void) putenv("MS_INTEROP=1");
 368 
 369         if (sysinfo(SI_HOSTNAME, _idmapdstate.hostname,
 370             sizeof (_idmapdstate.hostname)) == -1) {
 371                 error = errno;
 372                 idmapdlog(LOG_ERR, "unable to determine hostname, error: %d",
 373                     error);
 374                 exit(1);
 375         }
 376 
 377         if ((error = init_mapping_system()) < 0) {
 378                 idmapdlog(LOG_ERR, "unable to initialize mapping system");
 379                 exit(error < -2 ? SMF_EXIT_ERR_CONFIG : 1);
 380         }
 381 
 382         (void) door_server_create(idmapd_door_thread_create);
 383         if ((error = pthread_key_create(&create_threads_key,
 384             idmapd_door_thread_cleanup)) != 0) {
 385                 idmapdlog(LOG_ERR, "unable to create threads key (%s)",
 386                     strerror(error));
 387                 goto errout;
 388         }
 389 
 390         xprt = svc_door_create(idmap_prog_1, IDMAP_PROG, IDMAP_V1, connmaxrec);
 391         if (xprt == NULL) {
 392                 idmapdlog(LOG_ERR, "unable to create door RPC service");
 393                 goto errout;
 394         }
 395 
 396         if (!svc_control(xprt, SVCSET_CONNMAXREC, &connmaxrec)) {
 397                 idmapdlog(LOG_ERR, "unable to limit RPC request size");
 398                 goto errout;
 399         }
 400 
 401         dfd = xprt->xp_fd;
 402 
 403         if (dfd == -1) {
 404                 idmapdlog(LOG_ERR, "unable to register door");
 405                 goto errout;
 406         }
 407         if ((error = __idmap_reg(dfd)) != 0) {
 408                 idmapdlog(LOG_ERR, "unable to register door (%s)",
 409                     strerror(errno));
 410                 goto errout;
 411         }
 412 
 413         if ((error = allocids(_idmapdstate.new_eph_db,
 414             8192, &_idmapdstate.next_uid,
 415             8192, &_idmapdstate.next_gid)) != 0) {
 416                 idmapdlog(LOG_ERR, "unable to allocate ephemeral IDs (%s)",
 417                     strerror(errno));
 418                 _idmapdstate.next_uid = IDMAP_SENTINEL_PID;
 419                 _idmapdstate.limit_uid = IDMAP_SENTINEL_PID;
 420                 _idmapdstate.next_gid = IDMAP_SENTINEL_PID;
 421                 _idmapdstate.limit_gid = IDMAP_SENTINEL_PID;
 422         } else {
 423                 _idmapdstate.limit_uid = _idmapdstate.next_uid + 8192;
 424                 _idmapdstate.limit_gid = _idmapdstate.next_gid + 8192;
 425         }
 426 
 427         if (DBG(CONFIG, 1))
 428                 print_idmapdstate();
 429 
 430         return;
 431 
 432 errout:
 433         fini_idmapd();
 434         exit(1);
 435 }
 436 
 437 static void
 438 fini_idmapd()
 439 {
 440         (void) __idmap_unreg(dfd);
 441         fini_mapping_system();
 442         if (xprt != NULL)
 443                 svc_destroy(xprt);
 444 }
 445 
 446 static
 447 const char *
 448 get_fmri(void)
 449 {
 450         static char *fmri = NULL;
 451         static char buf[60];
 452         char *s;
 453 
 454         membar_consumer();
 455         s = fmri;
 456         if (s != NULL && *s == '\0')
 457                 return (NULL);
 458         else if (s != NULL)
 459                 return (s);
 460 
 461         if ((s = getenv("SMF_FMRI")) == NULL || strlen(s) >= sizeof (buf))
 462                 buf[0] = '\0';
 463         else
 464                 (void) strlcpy(buf, s, sizeof (buf));
 465 
 466         membar_producer();
 467         fmri = buf;
 468 
 469         return (get_fmri());
 470 }
 471 
 472 /*
 473  * Wrappers for smf_degrade/restore_instance()
 474  *
 475  * smf_restore_instance() is too heavy duty to be calling every time we
 476  * have a successful AD name<->SID lookup.
 477  */
 478 void
 479 degrade_svc(int poke_discovery, const char *reason)
 480 {
 481         const char *fmri;
 482 
 483         membar_consumer();
 484         if (degraded)
 485                 return;
 486 
 487         idmapdlog(LOG_ERR, "Degraded operation (%s).", reason);
 488 
 489         membar_producer();
 490         degraded = B_TRUE;
 491 
 492         if ((fmri = get_fmri()) != NULL)
 493                 (void) smf_degrade_instance(fmri, 0);
 494 
 495         /*
 496          * If the config update thread is in a state where auto-discovery could
 497          * be re-tried, then this will make it try it -- a sort of auto-refresh.
 498          */
 499         if (poke_discovery)
 500                 idmap_cfg_poke_updates();
 501 }
 502 
 503 void
 504 restore_svc(void)
 505 {
 506         const char *fmri;
 507 
 508         membar_consumer();
 509         if (!degraded)
 510                 return;
 511 
 512         if ((fmri = get_fmri()) == NULL)
 513                 (void) smf_restore_instance(fmri);
 514 
 515         membar_producer();
 516         degraded = B_FALSE;
 517 
 518         idmapdlog(LOG_NOTICE, "Normal operation restored");
 519 }
 520 
 521 
 522 /* printflike */
 523 void
 524 idmapdlog(int pri, const char *format, ...) {
 525         static time_t prev_ts;
 526         va_list args;
 527         char cbuf[CBUFSIZ];
 528         time_t ts;
 529 
 530         ts = time(NULL);
 531         if (prev_ts != ts) {
 532                 prev_ts = ts;
 533                 /* NB: cbuf has \n */
 534                 (void) fprintf(stderr, "@ %s",
 535                     ctime_r(&ts, cbuf, sizeof (cbuf)));
 536         }
 537 
 538         va_start(args, format);
 539         (void) vfprintf(stderr, format, args);
 540         (void) fprintf(stderr, "\n");
 541         va_end(args);
 542 
 543         /*
 544          * We don't want to fill up the logs with useless messages when
 545          * we're degraded, but we still want to log.
 546          */
 547         if (degraded)
 548                 pri = LOG_DEBUG;
 549 
 550         va_start(args, format);
 551         vsyslog(pri, format, args);
 552         va_end(args);
 553 }
 554 
 555 static void
 556 trace_str(nvlist_t *entry, char *n1, char *n2, char *str)
 557 {
 558         char name[IDMAP_TRACE_NAME_MAX+1];      /* Max used is only about 11 */
 559 
 560         (void) strlcpy(name, n1, sizeof (name));
 561         if (n2 != NULL)
 562                 (void) strlcat(name, n2, sizeof (name));
 563 
 564         (void) nvlist_add_string(entry, name, str);
 565 }
 566 
 567 static void
 568 trace_int(nvlist_t *entry, char *n1, char *n2, int64_t i)
 569 {
 570         char name[IDMAP_TRACE_NAME_MAX+1];      /* Max used is only about 11 */
 571 
 572         (void) strlcpy(name, n1, sizeof (name));
 573         if (n2 != NULL)
 574                 (void) strlcat(name, n2, sizeof (name));
 575 
 576         (void) nvlist_add_int64(entry, name, i);
 577 }
 578 
 579 static void
 580 trace_sid(nvlist_t *entry, char *n1, char *n2, idmap_sid *sid)
 581 {
 582         char *str;
 583 
 584         (void) asprintf(&str, "%s-%u", sid->prefix, sid->rid);
 585         if (str == NULL)
 586                 return;
 587 
 588         trace_str(entry, n1, n2, str);
 589         free(str);
 590 }
 591 
 592 static void
 593 trace_id(nvlist_t *entry, char *fromto, idmap_id *id, char *name, char *domain)
 594 {
 595         trace_int(entry, fromto, IDMAP_TRACE_TYPE, (int64_t)id->idtype);
 596         if (IS_ID_SID(*id)) {
 597                 if (name != NULL) {
 598                         char *str;
 599 
 600                         (void) asprintf(&str, "%s%s%s", name,
 601                             domain == NULL ? "" : "@",
 602                             domain == NULL ? "" : domain);
 603                         if (str != NULL) {
 604                                 trace_str(entry, fromto, IDMAP_TRACE_NAME, str);
 605                                 free(str);
 606                         }
 607                 }
 608                 if (id->idmap_id_u.sid.prefix != NULL) {
 609                         trace_sid(entry, fromto, IDMAP_TRACE_SID,
 610                             &id->idmap_id_u.sid);
 611                 }
 612         } else if (IS_ID_POSIX(*id)) {
 613                 if (name != NULL)
 614                         trace_str(entry, fromto, IDMAP_TRACE_NAME, name);
 615                 if (id->idmap_id_u.uid != IDMAP_SENTINEL_PID) {
 616                         trace_int(entry, fromto, IDMAP_TRACE_UNIXID,
 617                             (int64_t)id->idmap_id_u.uid);
 618                 }
 619         }
 620 }
 621 
 622 /*
 623  * Record a trace event.  TRACE() has already decided whether or not
 624  * tracing is required; what we do here is collect the data and send it
 625  * to its destination - to the trace log in the response, if
 626  * IDMAP_REQ_FLG_TRACE is set, and to the SMF service log, if debug/mapping
 627  * is greater than zero.
 628  */
 629 int
 630 trace(idmap_mapping *req, idmap_id_res *res, char *fmt, ...)
 631 {
 632         va_list va;
 633         char *buf;
 634         int err;
 635         nvlist_t *entry;
 636 
 637         assert(req != NULL);
 638         assert(res != NULL);
 639 
 640         err = nvlist_alloc(&entry, NV_UNIQUE_NAME, 0);
 641         if (err != 0) {
 642                 (void) fprintf(stderr, "trace nvlist_alloc(entry):  %s\n",
 643                     strerror(err));
 644                 return (0);
 645         }
 646 
 647         trace_id(entry, "from", &req->id1, req->id1name, req->id1domain);
 648         trace_id(entry, "to", &res->id, req->id2name, req->id2domain);
 649 
 650         if (IDMAP_ERROR(res->retcode)) {
 651                 trace_int(entry, IDMAP_TRACE_ERROR, NULL,
 652                     (int64_t)res->retcode);
 653         }
 654 
 655         va_start(va, fmt);
 656         (void) vasprintf(&buf, fmt, va);
 657         va_end(va);
 658         if (buf != NULL) {
 659                 trace_str(entry, IDMAP_TRACE_MESSAGE, NULL, buf);
 660                 free(buf);
 661         }
 662 
 663         if (DBG(MAPPING, 1))
 664                 idmap_trace_print_1(stderr, "", entry);
 665 
 666         if (req->flag & IDMAP_REQ_FLG_TRACE) {
 667                 /* Lazily allocate the trace list */
 668                 if (res->info.trace == NULL) {
 669                         err = nvlist_alloc(&res->info.trace, 0, 0);
 670                         if (err != 0) {
 671                                 res->info.trace = NULL; /* just in case */
 672                                 (void) fprintf(stderr,
 673                                     "trace nvlist_alloc(trace):  %s\n",
 674                                     strerror(err));
 675                                 nvlist_free(entry);
 676                                 return (0);
 677                         }
 678                 }
 679                 (void) nvlist_add_nvlist(res->info.trace, "", entry);
 680                 /* Note that entry is copied, so we must still free our copy */
 681         }
 682 
 683         nvlist_free(entry);
 684 
 685         return (0);
 686 }