"Fossies" - the Fresh Open Source Software Archive

Member "shellinabox-2.20/shellinabox/launcher.c" (9 Nov 2016, 61624 Bytes) of package /linux/privat/shellinabox-2.20.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "launcher.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.19_vs_2.20.

    1 // launcher.c -- Launch services from a privileged process
    2 // Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
    3 //
    4 // This program is free software; you can redistribute it and/or modify
    5 // it under the terms of the GNU General Public License version 2 as
    6 // published by the Free Software Foundation.
    7 //
    8 // This program is distributed in the hope that it will be useful,
    9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
   10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11 // GNU General Public License for more details.
   12 //
   13 // You should have received a copy of the GNU General Public License along
   14 // with this program; if not, write to the Free Software Foundation, Inc.,
   15 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   16 //
   17 // In addition to these license terms, the author grants the following
   18 // additional rights:
   19 //
   20 // If you modify this program, or any covered work, by linking or
   21 // combining it with the OpenSSL project's OpenSSL library (or a
   22 // modified version of that library), containing parts covered by the
   23 // terms of the OpenSSL or SSLeay licenses, the author
   24 // grants you additional permission to convey the resulting work.
   25 // Corresponding Source for a non-source form of such a combination
   26 // shall include the source code for the parts of OpenSSL used as well
   27 // as that of the covered work.
   28 //
   29 // You may at your option choose to remove this additional permission from
   30 // the work, or from any part of it.
   31 //
   32 // It is possible to build this program in a way that it loads OpenSSL
   33 // libraries at run-time. If doing so, the following notices are required
   34 // by the OpenSSL and SSLeay licenses:
   35 //
   36 // This product includes software developed by the OpenSSL Project
   37 // for use in the OpenSSL Toolkit. (http://www.openssl.org/)
   38 //
   39 // This product includes cryptographic software written by Eric Young
   40 // (eay@cryptsoft.com)
   41 //
   42 //
   43 // The most up-to-date version of this program is always available from
   44 // http://shellinabox.com
   45 
   46 #define _GNU_SOURCE
   47 #include "config.h"
   48 
   49 #define pthread_once    x_pthread_once
   50 #define execle          x_execle
   51 
   52 #include <dirent.h>
   53 #include <dlfcn.h>
   54 #include <fcntl.h>
   55 #include <grp.h>
   56 #include <limits.h>
   57 #include <pwd.h>
   58 #include <signal.h>
   59 #include <stdio.h>
   60 #include <stdlib.h>
   61 #include <string.h>
   62 #include <sys/ioctl.h>
   63 #include <sys/socket.h>
   64 #include <sys/stat.h>
   65 #include <sys/time.h>
   66 #include <sys/ttydefaults.h>
   67 #include <sys/types.h>
   68 #include <sys/wait.h>
   69 #include <sys/utsname.h>
   70 #include <termios.h>
   71 #include <unistd.h>
   72 
   73 #ifdef HAVE_LIBUTIL_H
   74 #include <libutil.h>
   75 #endif
   76 
   77 #ifdef HAVE_PTY_H
   78 #include <pty.h>
   79 #endif
   80 
   81 #ifdef HAVE_SYS_UIO_H
   82 #include <sys/uio.h>
   83 #endif
   84 
   85 #ifdef HAVE_UTIL_H
   86 #include <util.h>
   87 #endif
   88 
   89 #ifdef HAVE_UTMP_H
   90 #include <utmp.h>
   91 #endif
   92 
   93 #ifdef HAVE_UTMPX_H
   94 #include <utmpx.h>
   95 #endif
   96 
   97 #if defined(HAVE_SECURITY_PAM_APPL_H)
   98 #include <security/pam_appl.h>
   99 
  100 #if defined(HAVE_SECURITY_PAM_MISC_H)
  101 #include <security/pam_misc.h>
  102 #endif
  103 
  104 #ifndef PAM_DATA_SILENT
  105 #define PAM_DATA_SILENT 0
  106 #endif
  107 #else
  108 struct pam_message;
  109 struct pam_response;
  110 struct pam_conv;
  111 typedef struct pam_handle pam_handle_t;
  112 #endif
  113 
  114 #ifdef HAVE_STRLCAT
  115 #define strncat(a,b,c) ({ char *_a = (a); strlcat(_a, (b), (c)+1); _a; })
  116 #endif
  117 
  118 #include "shellinabox/launcher.h"
  119 #include "shellinabox/privileges.h"
  120 #include "shellinabox/service.h"
  121 #include "libhttp/hashmap.h"
  122 #include "logging/logging.h"
  123 
  124 #ifdef HAVE_UNUSED
  125 #defined ATTR_UNUSED __attribute__((unused))
  126 #defined UNUSED(x)   do { } while (0)
  127 #else
  128 #define ATTR_UNUSED
  129 #define UNUSED(x)    do { (void)(x); } while (0)
  130 #endif
  131 
  132 #define UNUSED_RETURN(x) do { (void)((x)+1); } while (0)
  133 
  134 #undef pthread_once
  135 #undef execle
  136 int execle(const char *, const char *, ...);
  137 
  138 #if defined(HAVE_PTHREAD_H) && defined(__linux__)
  139 #include <pthread.h>
  140 extern int pthread_once(pthread_once_t *, void (*)(void))__attribute__((weak));
  141 #endif
  142 
  143 // If PAM support is available, take advantage of it. Otherwise, silently fall
  144 // back on legacy operations for session management.
  145 #if defined(HAVE_SECURITY_PAM_APPL_H) && defined(HAVE_DLOPEN)
  146 static int (*x_pam_acct_mgmt)(pam_handle_t *, int);
  147 static int (*x_pam_authenticate)(pam_handle_t *, int);
  148 #if defined(HAVE_SECURITY_PAM_CLIENT_H)
  149 static int (**x_pam_binary_handler_fn)(void *, pamc_bp_t *);
  150 #endif
  151 static int (*x_pam_close_session)(pam_handle_t *, int);
  152 static int (*x_pam_end)(pam_handle_t *, int);
  153 static int (*x_pam_get_item)(const pam_handle_t *, int, const void **);
  154 static int (*x_pam_open_session)(pam_handle_t *, int);
  155 static int (*x_pam_set_item)(pam_handle_t *, int, const void *);
  156 static int (*x_pam_start)(const char *, const char *, const struct pam_conv *,
  157                           pam_handle_t **);
  158 static int (*x_misc_conv)(int, const struct pam_message **,
  159                           struct pam_response **, void *);
  160 
  161 #define pam_acct_mgmt         x_pam_acct_mgmt
  162 #define pam_authenticate      x_pam_authenticate
  163 #define pam_binary_handler_fn x_pam_binary_handler_fn
  164 #define pam_close_session     x_pam_close_session
  165 #define pam_end               x_pam_end
  166 #define pam_get_item          x_pam_get_item
  167 #define pam_open_session      x_pam_open_session
  168 #define pam_set_item          x_pam_set_item
  169 #define pam_start             x_pam_start
  170 #define misc_conv             x_misc_conv
  171 #endif
  172 
  173 static int   launcher = -1;
  174 static uid_t restricted;
  175 
  176 // From shellinabox/shellinaboxd.c
  177 extern int enableUtmpLogging;
  178 
  179 #if defined(HAVE_SECURITY_PAM_APPL_H)
  180 static int pamSessionSighupFlag;
  181 #endif
  182 
  183 // MacOS X has a somewhat unusual definition of getgrouplist() which can
  184 // trigger a compile warning.
  185 #if defined(HAVE_GETGROUPLIST_TAKES_INTS)
  186 static int x_getgrouplist(const char *user, gid_t group,
  187                           gid_t *groups, int *ngroups) {
  188   return getgrouplist(user, (int)group, (int *)groups, ngroups);
  189 }
  190 #define getgrouplist x_getgrouplist
  191 #endif
  192 
  193 // BSD systems have special requirements on how utmp entries have to be filled
  194 // out in order to be updated by non-privileged users. In particular, they
  195 // want the real user name in the utmp recode.
  196 // This all wouldn't be so bad, if pututxline() wouldn't print an error message
  197 // to stderr, if it fails to run. Unfortunately, it has been observed to do so.
  198 // That means, we need to jump through some hoops to intercept these messages.
  199 #ifdef HAVE_UTMPX_H
  200 struct utmpx *x_pututxline(struct utmpx *ut) {
  201   // N.B. changing global file descriptors isn't thread safe. But all call
  202   // sites are guaranteed to be single-threaded. If that ever changes, this
  203   // code will need rewriting.
  204   int oldStdin         = dup(0);
  205   int oldStdout        = dup(1);
  206   int oldStderr        = dup(2);
  207   check(oldStdin > 2 && oldStdout > 2 && oldStderr > 2);
  208   int nullFd           = open("/dev/null", O_RDWR);
  209   check(nullFd > 2);
  210   check(dup2(nullFd, 0) == 0);
  211   NOINTR(close(nullFd));
  212 
  213   // Set up a pipe so that we can read error messages that might be printed
  214   // to stderr. We assume that the kernel maintains a buffer that is
  215   // sufficiently large to receive the bytes written to it without causing
  216   // the I/O operation to block.
  217   int fds[2];
  218   check(!pipe(fds));
  219   check(dup2(fds[1], 1) == 1);
  220   check(dup2(fds[1], 2) == 2);
  221   NOINTR(close(fds[1]));
  222   struct utmpx *ret    = pututxline(ut);
  223   int err              = ret == NULL;
  224 
  225   // Close the write end of the pipe, so that we can read until EOF.
  226   check(dup2(0, 1) == 1);
  227   check(dup2(0, 2) == 2);
  228   char buf[128];
  229   while (NOINTR(read(fds[0], buf, sizeof(buf))) > 0) {
  230     err                = 1;
  231   }
  232   NOINTR(close(fds[0]));
  233 
  234   // If we either received an error from pututxline() or if we saw an error
  235   // message being written out, adjust the utmp record and retry.
  236   if (err) {
  237     uid_t uid          = getuid();
  238     if (uid) {
  239       // We only retry if the code is not running as root. Otherwise, fixing
  240       // the utmp record is unlikely to do anything for us.
  241       // If running as non-root, we set the actual user name in the utmp
  242       // record. This is not ideal, but if it allows us to update the record
  243       // then that's the best we do.
  244       const char *user = getUserName(uid);
  245       if (user) {
  246         memset(&ut->ut_user[0], 0, sizeof(ut->ut_user));
  247         strncat(&ut->ut_user[0], user, sizeof(ut->ut_user) - 1);
  248         ret            = pututxline(ut);
  249         free((char *)user);
  250       }
  251     }
  252   }
  253 
  254   // Clean up. Reset file descriptors back to their original values.
  255   check(dup2(oldStderr, 2) == 2);
  256   check(dup2(oldStdout, 1) == 1);
  257   check(dup2(oldStdin,  0) == 0);
  258   NOINTR(close(oldStdin));
  259   NOINTR(close(oldStdout));
  260   NOINTR(close(oldStderr));
  261 
  262   // It is quite likely that we won't always be in a situation to update the
  263   // system's utmp records. Return a non-fatal error to the caller.
  264 
  265   return ret;
  266 }
  267 #define pututxline x_pututxline
  268 #endif
  269 
  270 // If the PAM misc library cannot be found, we have to provide our own basic
  271 // conversation function. As we know that this code is only ever called from
  272 // ShellInABox, it can be kept significantly simpler than the more generic
  273 // code that the PAM library implements.
  274 
  275 static int read_string(int echo, const char *prompt, char **retstr) {
  276   *retstr                = NULL;
  277   struct termios term_before, term_tmp;
  278   if (tcgetattr(0, &term_before) != 0) {
  279     return -1;
  280   }
  281   memcpy(&term_tmp, &term_before, sizeof(term_tmp));
  282   if (!echo) {
  283     term_tmp.c_lflag    &= ~ECHO;
  284   }
  285   int nc;
  286   tcsetattr(0, TCSAFLUSH, &term_tmp);
  287   fprintf(stderr, "%s", prompt);
  288   char *line;
  289   const int lineLength = 512;
  290   check(line           = calloc(1, lineLength));
  291   nc                   = read(0, line, lineLength - 1);
  292   tcsetattr(0, TCSADRAIN, &term_before);
  293   if (!echo) {
  294     fprintf(stderr, "\n");
  295   }
  296   if (nc > 0) {
  297     if (line[nc-1] == '\n') {
  298       nc--;
  299     } else if (echo) {
  300       fprintf(stderr, "\n");
  301     }
  302     line[nc]           = '\000';
  303     check(*retstr      = line);
  304   } else {
  305     memset(line, 0, lineLength);
  306     free(line);
  307     if (echo) {
  308       fprintf(stderr, "\n");
  309     }
  310   }
  311   tcsetattr(0, TCSADRAIN, &term_before);
  312   return nc;
  313 }
  314 
  315 #if defined(HAVE_SECURITY_PAM_APPL_H) && defined(HAVE_DLOPEN)
  316 #if defined(HAVE_SECURITY_PAM_CLIENT_H)
  317 static pamc_bp_t *p(pamc_bp_t *p) {
  318   // GCC is too smart for its own good, and triggers a warning in
  319   // PAM_BP_RENEW, unless we pass the first argument through a function.
  320   return p;
  321 }
  322 #endif
  323 
  324 static int my_misc_conv(int num_msg, const struct pam_message **msgm,
  325                         struct pam_response **response, void *appdata_ptr) {
  326   if (num_msg <= 0) {
  327     return PAM_CONV_ERR;
  328   }
  329   struct pam_response *reply;
  330   check(reply = (struct pam_response *)calloc(num_msg,
  331                                               sizeof(struct pam_response)));
  332   for (int count = 0; count < num_msg; count++) {
  333     char *string                 = NULL;
  334     switch(msgm[count]->msg_style) {
  335       case PAM_PROMPT_ECHO_OFF:
  336         if (read_string(0, msgm[count]->msg, &string) < 0) {
  337           goto failed_conversation;
  338         }
  339         break;
  340       case PAM_PROMPT_ECHO_ON:
  341         if (read_string(1, msgm[count]->msg, &string) < 0) {
  342           goto failed_conversation;
  343         }
  344         break;
  345       case PAM_ERROR_MSG:
  346         if (fprintf(stderr, "%s\n", msgm[count]->msg) < 0) {
  347           goto failed_conversation;
  348         }
  349         break;
  350       case PAM_TEXT_INFO:
  351         if (fprintf(stdout, "%s\n", msgm[count]->msg) < 0) {
  352           goto failed_conversation;
  353         }
  354         break;
  355 #if defined(HAVE_SECURITY_PAM_CLIENT_H)
  356       case PAM_BINARY_PROMPT: {
  357         pamc_bp_t binary_prompt = NULL;
  358         if (!msgm[count]->msg || !*pam_binary_handler_fn) {
  359           goto failed_conversation;
  360         }
  361         PAM_BP_RENEW(p(&binary_prompt), PAM_BP_RCONTROL(msgm[count]->msg),
  362                      PAM_BP_LENGTH(msgm[count]->msg));
  363         PAM_BP_FILL(binary_prompt, 0, PAM_BP_LENGTH(msgm[count]->msg),
  364                     PAM_BP_RDATA(msgm[count]->msg));
  365         if ((*pam_binary_handler_fn)(appdata_ptr, &binary_prompt) !=
  366             PAM_SUCCESS || !binary_prompt) {
  367           goto failed_conversation;
  368         }
  369         string                  = (char *)binary_prompt;
  370         break; }
  371 #endif
  372       default:
  373         goto failed_conversation;
  374     }
  375     if (string) {
  376       reply[count].resp_retcode = 0;
  377       reply[count].resp         = string;
  378     }
  379   }
  380 failed_conversation:
  381   *response                     = reply;
  382   return PAM_SUCCESS;
  383 }
  384 
  385 static void *loadSymbol(const char *lib, const char *fn) {
  386   void *dl = RTLD_DEFAULT;
  387   void *rc = dlsym(dl, fn);
  388   if (!rc) {
  389 #ifdef RTLD_NOLOAD
  390     dl     = dlopen(lib, RTLD_LAZY|RTLD_GLOBAL|RTLD_NOLOAD);
  391 #else
  392     dl     = NULL;
  393 #endif
  394     if (dl == NULL) {
  395       dl   = dlopen(lib, RTLD_LAZY|RTLD_GLOBAL);
  396     }
  397     if (dl != NULL) {
  398       rc   = dlsym(dl, fn);
  399     }
  400   }
  401   return rc;
  402 }
  403 
  404 static void loadPAM(void) {
  405   check(!pam_start);
  406   check(!misc_conv);
  407   struct {
  408     union {
  409       void     *avoid_gcc_warning_about_type_punning;
  410       void     **var;
  411     };
  412     const char *lib;
  413     const char *fn;
  414   } symbols[] = {
  415     { { &pam_acct_mgmt },         "libpam.so",      "pam_acct_mgmt"     },
  416     { { &pam_authenticate },      "libpam.so",      "pam_authenticate"  },
  417 #if defined(HAVE_SECURITY_PAM_CLIENT_H)
  418     { { &pam_binary_handler_fn }, "libpam_misc.so", "pam_binary_handler_fn" },
  419 #endif
  420     { { &pam_close_session },     "libpam.so",      "pam_close_session" },
  421     { { &pam_end },               "libpam.so",      "pam_end"           },
  422     { { &pam_get_item },          "libpam.so",      "pam_get_item"      },
  423     { { &pam_open_session },      "libpam.so",      "pam_open_session"  },
  424     { { &pam_set_item },          "libpam.so",      "pam_set_item"      },
  425     { { &pam_start },             "libpam.so",      "pam_start"         },
  426     { { &misc_conv },             "libpam_misc.so", "misc_conv"         }
  427   };
  428   for (unsigned i = 0; i < sizeof(symbols)/sizeof(symbols[0]); i++) {
  429     if (!(*symbols[i].var = loadSymbol(symbols[i].lib, symbols[i].fn))) {
  430 #if defined(HAVE_SECURITY_PAM_CLIENT_H)
  431       if (!strcmp(symbols[i].fn, "pam_binary_handler_fn")) {
  432         // Binary conversation support is optional
  433         continue;
  434       } else
  435 #endif
  436       if (!strcmp(symbols[i].fn, "misc_conv")) {
  437         // PAM misc is optional
  438         *symbols[i].var = (void *)my_misc_conv;
  439         continue;
  440       }
  441       debug("[server] Failed to load PAM support. Could not find \"%s\"!",
  442             symbols[i].fn);
  443       for (unsigned j = 0; j < sizeof(symbols)/sizeof(symbols[0]); j++) {
  444         *symbols[j].var = NULL;
  445       }
  446       return;
  447     }
  448   }
  449   debug("[server] Loaded PAM suppport");
  450 }
  451 #endif
  452 
  453 int supportsPAM(void) {
  454 #if defined(HAVE_SECURITY_PAM_APPL_H) && !defined(HAVE_DLOPEN)
  455   return 1;
  456 #else
  457 #if defined(HAVE_SECURITY_PAM_APPL_H)
  458 
  459   // We want to call loadPAM() exactly once. For single-threaded applications,
  460   // this is straight-forward. For threaded applications, we need to call
  461   // pthread_once(), instead. We perform run-time checks for whether we are
  462   // single- or multi-threaded, so that the same code can be used.
  463   // This currently only works on Linux.
  464 #if defined(HAVE_PTHREAD_H) && defined(__linux__) && defined(__i386__)
  465   if (!!&pthread_once) {
  466     static pthread_once_t once = PTHREAD_ONCE_INIT;
  467     pthread_once(&once, loadPAM);
  468   } else
  469 #endif
  470   {
  471     static int initialized;
  472     if (!initialized) {
  473       initialized = 1;
  474       loadPAM();
  475     }
  476   }
  477   return misc_conv && pam_start;
  478 #else
  479   return 0;
  480 #endif
  481 #endif
  482 }
  483 
  484 #ifndef HAVE_GETPWUID_R
  485 // This is a not-thread-safe replacement for getpwuid_r()
  486 #define getpwuid_r x_getpwuid_r
  487 static int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, size_t buflen,
  488                       struct passwd **result) {
  489   if (result) {
  490     *result        = NULL;
  491   }
  492   if (!pwd) {
  493     return -1;
  494   }
  495   errno            = 0;
  496   struct passwd *p = getpwuid(uid);
  497   if (!p) {
  498     return errno ? -1 : 0;
  499   }
  500   *pwd             = *p;
  501   if (result) {
  502     *result        = pwd;
  503   }
  504   return 0;
  505 }
  506 #endif
  507 
  508 int launchChild(int service, struct Session *session, const char *url) {
  509   if (launcher < 0) {
  510     errno              = EINVAL;
  511     return -1;
  512   }
  513 
  514   char *u;
  515   check(u              = strdup(url));
  516   for (int i; u[i = strcspn(u, "\\\"'`${};() \r\n\t\v\f")]; ) {
  517     static const char hex[] = "0123456789ABCDEF";
  518     check(u            = realloc(u, strlen(u) + 4));
  519     memmove(u + i + 3, u + i + 1, strlen(u + i));
  520     u[i + 2]           = hex[ u[i]       & 0xF];
  521     u[i + 1]           = hex[(u[i] >> 4) & 0xF];
  522     u[i]               = '%';
  523   }
  524 
  525   struct LaunchRequest *request;
  526   ssize_t len          = sizeof(struct LaunchRequest) + strlen(u) + 1;
  527   check(request        = calloc(len, 1));
  528   request->service     = service;
  529   request->terminate   = -1;
  530   request->width       = session->width;
  531   request->height      = session->height;
  532   const char *peerName = httpGetPeerName(session->http);
  533   strncat(request->peerName, peerName, sizeof(request->peerName) - 1);
  534   const char *realIP   = httpGetRealIP(session->http);
  535   if (realIP && *realIP) {
  536     strncat(request->realIP, realIP, sizeof(request->realIP) - 1);
  537   }
  538   request->urlLength   = strlen(u);
  539   memcpy(&request->url, u, request->urlLength);
  540   free(u);
  541   if (NOINTR(write(launcher, request, len)) != len) {
  542     free(request);
  543     return -1;
  544   }
  545   free(request);
  546   pid_t pid;
  547   char cmsg_buf[CMSG_SPACE(sizeof(int))];
  548   struct iovec iov     = { 0 };
  549   struct msghdr msg    = { 0 };
  550   iov.iov_base         = &pid;
  551   iov.iov_len          = sizeof(pid);
  552   msg.msg_iov          = &iov;
  553   msg.msg_iovlen       = 1;
  554   msg.msg_control      = &cmsg_buf;
  555   msg.msg_controllen   = sizeof(cmsg_buf);
  556   int bytes            = NOINTR(recvmsg(launcher, &msg, 0));
  557   if (bytes < 0) {
  558     return -1;
  559   }
  560   check(bytes == sizeof(pid));
  561   check(session->pid = pid);
  562   struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
  563   check(cmsg);
  564   check(cmsg->cmsg_level == SOL_SOCKET);
  565   check(cmsg->cmsg_type  == SCM_RIGHTS);
  566   memcpy(&session->pty, CMSG_DATA(cmsg), sizeof(int));
  567   return pid;
  568 }
  569 
  570 int terminateChild(struct Session *session) {
  571   if (launcher < 0) {
  572     errno              = EINVAL;
  573     return -1;
  574   }
  575 
  576   if (session->pid < 1) {
  577     debug("[server] Child pid for termination not valid!");
  578     return -1;
  579   }
  580 
  581   // Send terminate request to launcher process
  582   struct LaunchRequest *request;
  583   ssize_t len          = sizeof(struct LaunchRequest);
  584   check(request        = calloc(len, 1));
  585   request->terminate   = session->pid;
  586   if (NOINTR(write(launcher, request, len)) != len) {
  587     debug("[server] Child %d termination request failed!", request->terminate);
  588     free(request);
  589     return -1;
  590   }
  591 
  592   free(request);
  593   session->pid         = 0;
  594   session->cleanup     = 0;
  595   return 0;
  596 }
  597 
  598 struct Utmp {
  599   const char   pid[32];
  600   int          pty;
  601   int          useLogin;
  602 #ifdef HAVE_UTMPX_H
  603   struct utmpx utmpx;
  604 #endif
  605 };
  606 
  607 static HashMap *childProcesses;
  608 
  609 void initUtmp(struct Utmp *utmp, int useLogin, const char *ptyPath,
  610               const char *peerName, const char *realIP) {
  611   memset(utmp, 0, sizeof(struct Utmp));
  612   utmp->pty                 = -1;
  613   utmp->useLogin            = useLogin;
  614 #ifdef HAVE_UTMPX_H
  615   utmp->utmpx.ut_type       = useLogin ? LOGIN_PROCESS : USER_PROCESS;
  616   dcheck(!strncmp(ptyPath, "/dev/pts", 8) ||
  617          !strncmp(ptyPath, "/dev/pty", 8) ||
  618          !strncmp(ptyPath, "/dev/tty", 8));
  619   strncat(&utmp->utmpx.ut_line[0], ptyPath + 5,   sizeof(utmp->utmpx.ut_line) - 1);
  620   strncat(&utmp->utmpx.ut_id[0],   ptyPath + 8,   sizeof(utmp->utmpx.ut_id) - 1);
  621   strncat(&utmp->utmpx.ut_user[0], "SHELLINABOX", sizeof(utmp->utmpx.ut_user) - 1);
  622   char remoteHost[256];
  623   snprintf(remoteHost, 256,
  624            (*realIP) ? "%s, %s" : "%s%s", peerName,
  625            (*realIP) ? realIP : "");
  626   strncat(&utmp->utmpx.ut_host[0], remoteHost,    sizeof(utmp->utmpx.ut_host) - 1);
  627   struct timeval tv;
  628   check(!gettimeofday(&tv, NULL));
  629   utmp->utmpx.ut_tv.tv_sec  = tv.tv_sec;
  630   utmp->utmpx.ut_tv.tv_usec = tv.tv_usec;
  631 #endif
  632 }
  633 
  634 struct Utmp *newUtmp(int useLogin, const char *ptyPath,
  635                      const char *peerName, const char *realIP) {
  636   struct Utmp *utmp;
  637   check(utmp = malloc(sizeof(struct Utmp)));
  638   initUtmp(utmp, useLogin, ptyPath, peerName, realIP);
  639   return utmp;
  640 }
  641 
  642 #if defined(HAVE_UPDWTMP) && !defined(HAVE_UPDWTMPX)
  643 #define min(a,b) ({ typeof(a) _a=(a); typeof(b) _b=(b); _a < _b ? _a : _b; })
  644 #define updwtmpx x_updwtmpx
  645 
  646 static void updwtmpx(const char *wtmpx_file, const struct utmpx *utx) {
  647   struct utmp ut   = { 0 };
  648   ut.ut_type       = utx->ut_type;
  649   ut.ut_pid        = utx->ut_pid;
  650   ut.ut_tv.tv_sec  = utx->ut_tv.tv_sec;
  651   ut.ut_tv.tv_usec = utx->ut_tv.tv_usec;
  652   memcpy(&ut.ut_line, &utx->ut_line,
  653          min(sizeof(ut.ut_line), sizeof(utx->ut_line)));
  654   memcpy(&ut.ut_id, &utx->ut_id,
  655          min(sizeof(ut.ut_id), sizeof(utx->ut_id)));
  656   memcpy(&ut.ut_user, &utx->ut_user,
  657          min(sizeof(ut.ut_user), sizeof(utx->ut_user)));
  658   memcpy(&ut.ut_host, &utx->ut_host,
  659          min(sizeof(ut.ut_host), sizeof(utx->ut_host)));
  660   updwtmp(wtmpx_file, &ut);
  661 }
  662 #endif
  663 
  664 void destroyUtmp(struct Utmp *utmp) {
  665   if (utmp) {
  666     if (utmp->pty >= 0) {
  667 #ifdef HAVE_UTMPX_H
  668       utmp->utmpx.ut_type = DEAD_PROCESS;
  669       memset(&utmp->utmpx.ut_user, 0, sizeof(utmp->utmpx.ut_user));
  670       memset(&utmp->utmpx.ut_host, 0, sizeof(utmp->utmpx.ut_host));
  671       struct timeval tv;
  672       check(!gettimeofday(&tv, NULL));
  673       utmp->utmpx.ut_tv.tv_sec  = tv.tv_sec;
  674       utmp->utmpx.ut_tv.tv_usec = tv.tv_usec;
  675 
  676       // Temporarily regain privileges to update the utmp database
  677       uid_t r_uid, e_uid, s_uid;
  678       uid_t r_gid, e_gid, s_gid;
  679       check(!getresuid(&r_uid, &e_uid, &s_uid));
  680       check(!getresgid(&r_gid, &e_gid, &s_gid));
  681       UNUSED_RETURN(setresuid(0, 0, 0));
  682       UNUSED_RETURN(setresgid(0, 0, 0));
  683 
  684       if (enableUtmpLogging) {
  685         setutxent();
  686         pututxline(&utmp->utmpx);
  687         endutxent();
  688 
  689 #if defined(HAVE_UPDWTMP) || defined(HAVE_UPDWTMPX)
  690         if (!utmp->useLogin) {
  691           updwtmpx("/var/log/wtmp", &utmp->utmpx);
  692         }
  693 #endif
  694       }
  695 
  696       // Switch back to the lower privileges
  697       check(!setresgid(r_gid, e_gid, s_gid));
  698       check(!setresuid(r_uid, e_uid, s_uid));
  699 #endif
  700 
  701       NOINTR(close(utmp->pty));
  702     }
  703   }
  704 }
  705 
  706 void deleteUtmp(struct Utmp *utmp) {
  707   destroyUtmp(utmp);
  708   free(utmp);
  709 }
  710 
  711 static void destroyUtmpHashEntry(void *arg ATTR_UNUSED, char *key ATTR_UNUSED,
  712                                  char *value) {
  713   UNUSED(arg);
  714   UNUSED(key);
  715   deleteUtmp((struct Utmp *)value);
  716 }
  717 
  718 void closeAllFds(int *exceptFds, int num) {
  719   // Close all file handles. If possible, scan through "/proc/self/fd" as
  720   // that is faster than calling close() on all possible file handles.
  721   int nullFd  = open("/dev/null", O_RDWR);
  722   check(nullFd > 2);
  723   DIR *dir    = opendir("/proc/self/fd");
  724   if (dir == 0) {
  725     for (int i = sysconf(_SC_OPEN_MAX); --i > 0; ) {
  726       if (i != nullFd) {
  727         for (int j = 0; j < num; j++) {
  728           if (i == exceptFds[j]) {
  729             goto no_close_1;
  730           }
  731         }
  732         // Closing handles 0..2 is never a good idea. Instead, redirect them
  733         // to /dev/null
  734         if (i <= 2) {
  735           NOINTR(dup2(nullFd, i));
  736         } else {
  737           NOINTR(close(i));
  738         }
  739       }
  740     no_close_1:;
  741     }
  742   } else {
  743     struct dirent de, *res;
  744     while (!readdir_r(dir, &de, &res) && res) {
  745       if (res->d_name[0] < '0')
  746         continue;
  747       int fd  = atoi(res->d_name);
  748       if (fd != nullFd && fd != dirfd(dir)) {
  749         for (int j = 0; j < num; j++) {
  750           if (fd == exceptFds[j]) {
  751             goto no_close_2;
  752           }
  753         }
  754         // Closing handles 0..2 is never a good idea. Instead, redirect them
  755         // to /dev/null
  756         if (fd <= 2) {
  757           NOINTR(dup2(nullFd, fd));
  758         } else {
  759           NOINTR(close(fd));
  760         }
  761       }
  762     no_close_2:;
  763     }
  764     check(!closedir(dir));
  765   }
  766   if (nullFd > 2) {
  767     check(!close(nullFd));
  768   }
  769 }
  770 
  771 #if !defined(HAVE_PTSNAME_R)
  772 static int ptsname_r(int fd, char *buf, size_t buflen) {
  773   // It is unfortunate that ptsname_r is not universally available.
  774   // For the time being, this is not a big problem, as ShellInABox is
  775   // single-threaded (and so is the launcher process). But if this
  776   // code gets re-used in a multi-threaded application, that could
  777   // lead to problems.
  778   if (buf == NULL) {
  779     errno = EINVAL;
  780     return -1;
  781   }
  782   char *p = ptsname(fd);
  783   if (p == NULL) {
  784     return -1;
  785   }
  786   if (buflen < strlen(p) + 1) {
  787     errno = ERANGE;
  788     return -1;
  789   }
  790   strcpy(buf, p);
  791   return 0;
  792 }
  793 #endif
  794 
  795 static int forkPty(int *pty, int useLogin, struct Utmp **utmp,
  796                    const char *peerName, const char *realIP) {
  797   int slave;
  798   #ifdef HAVE_OPENPTY
  799   char* ptyPath = NULL;
  800   if (openpty(pty, &slave, NULL, NULL, NULL) < 0) {
  801     *pty                    = -1;
  802     *utmp                   = NULL;
  803     return -1;
  804   }
  805   // Recover name of PTY in a Hurd compatible way.  PATH_MAX doesn't
  806   // exist, so we need to use ttyname_r to fetch the name of the
  807   // pseudo-tty and find the buffer length that is sufficient. After
  808   // finding an adequate buffer size for the ptyPath, we allocate it
  809   // on the stack and release the freestore copy.  In this was we know
  810   // that the memory won't leak.  Note that ptsname_r is not always
  811   // available but we're already checking for this so it will be
  812   // defined in any case.
  813   {
  814     size_t length = 32;
  815     char* path = NULL;
  816     while (path == NULL) {
  817       check(path = malloc(length));
  818       *path = 0;
  819       if (ptsname_r (*pty, path, length)) {
  820         if (errno == ERANGE) {
  821           free (path);
  822           path = NULL;
  823         }
  824         else
  825           break;          // Every other error means no name for us
  826       }
  827       length <<= 1;
  828     }
  829     length = strlen (path);
  830     ptyPath = alloca (length + 1);
  831     strcpy (ptyPath, path);
  832     free (path);
  833   }
  834   #else
  835   char ptyPath[PATH_MAX];
  836   if ((*pty                 = posix_openpt(O_RDWR|O_NOCTTY))          < 0 ||
  837       grantpt(*pty)                                                   < 0 ||
  838       unlockpt(*pty)                                                  < 0 ||
  839       ptsname_r(*pty, ptyPath, sizeof(ptyPath))                       < 0 ||
  840       (slave                = NOINTR(open(ptyPath, O_RDWR|O_NOCTTY))) < 0) {
  841     if (*pty >= 0) {
  842       NOINTR(close(*pty));
  843     }
  844 
  845     // Try old-style pty handling
  846     char fname[40]          = "/dev/ptyXX";
  847     for (const char *ptr1   = "pqrstuvwxyzabcde"; *ptr1; ptr1++) {
  848       fname[8]              = *ptr1;
  849       for (const char *ptr2 = "0123456789abcdef"; *ptr2; ptr2++) {
  850         fname[9]            = *ptr2;
  851         if ((*pty           = NOINTR(open(fname, O_RDWR, 0))) < 0) {
  852           if (errno == ENOENT) {
  853             continue;
  854           }
  855         }
  856         grantpt(*pty);
  857         unlockpt(*pty);
  858         if (ptsname_r(*pty, ptyPath, sizeof(ptyPath)) < 0) {
  859           strcpy(ptyPath, fname);
  860           ptyPath[5]        = 't';
  861         }
  862         if ((slave          = NOINTR(open(ptyPath, O_RDWR|O_NOCTTY))) >= 0) {
  863           debug("[server] Opened old-style pty: %s", ptyPath);
  864           goto success;
  865         }
  866         NOINTR(close(*pty));
  867       }
  868     }
  869     *pty                    = -1;
  870     *utmp                   = NULL;
  871     return -1;
  872   }
  873  success:
  874   #endif
  875 
  876   // Fill in utmp entry
  877   *utmp                     = newUtmp(useLogin, ptyPath, peerName, realIP);
  878 
  879   // Now, fork off the child process
  880   pid_t pid;
  881   if ((pid                  = fork()) < 0) {
  882     NOINTR(close(slave));
  883     NOINTR(close(*pty));
  884     *pty                    = -1;
  885     deleteUtmp(*utmp);
  886     *utmp                   = NULL;
  887     return -1;
  888   } else if (pid == 0) {
  889     pid                     = getpid();
  890     snprintf((char *)&(*utmp)->pid[0], sizeof((*utmp)->pid), "%d", pid);
  891 #ifdef HAVE_UTMPX_H
  892     (*utmp)->utmpx.ut_pid   = pid;
  893 #endif
  894     (*utmp)->pty            = slave;
  895 
  896     closeAllFds((int []){ slave }, 1);
  897 
  898 #ifdef HAVE_LOGIN_TTY
  899     login_tty(slave);
  900 #else
  901     // Become the session/process-group leader
  902     setsid();
  903     setpgid(0, 0);
  904 
  905     // Redirect standard I/O to the pty
  906     dup2(slave, 0);
  907     dup2(slave, 1);
  908     dup2(slave, 2);
  909     if (slave > 2) {
  910       NOINTR(close(slave));
  911     }
  912 #endif
  913     *pty                    = 0;
  914 
  915     // Force the pty to be our control terminal
  916     NOINTR(close(NOINTR(open(ptyPath, O_RDWR))));
  917 
  918     return 0;
  919   } else {
  920     snprintf((char *)&(*utmp)->pid[0], sizeof((*utmp)->pid), "%d", pid);
  921 #ifdef HAVE_UTMPX_H
  922     (*utmp)->utmpx.ut_pid   = pid;
  923 #endif
  924     (*utmp)->pty            = *pty;
  925     check(!fcntl(*pty, F_SETFL, O_NONBLOCK | O_RDWR));
  926     NOINTR(close(slave));
  927     return pid;
  928   }
  929 }
  930 
  931 static const struct passwd *getPWEnt(uid_t uid) {
  932   struct passwd pwbuf, *pw;
  933   char *buf;
  934   #ifdef _SC_GETPW_R_SIZE_MAX
  935   int len                           = sysconf(_SC_GETPW_R_SIZE_MAX);
  936   if (len <= 0) {
  937     len                             = 4096;
  938   }
  939   #else
  940   int len                           = 4096;
  941   #endif
  942   check(buf                         = malloc(len));
  943   check(!getpwuid_r(uid, &pwbuf, buf, len, &pw) && pw);
  944   if (!pw->pw_name  ) pw->pw_name   = (char *)"";
  945   if (!pw->pw_passwd) pw->pw_passwd = (char *)"";
  946   if (!pw->pw_gecos ) pw->pw_gecos  = (char *)"";
  947   if (!pw->pw_dir   ) pw->pw_dir    = (char *)"";
  948   if (!pw->pw_shell ) pw->pw_shell  = (char *)"";
  949   struct passwd *passwd;
  950   check(passwd                      = calloc(sizeof(struct passwd) +
  951                                              strlen(pw->pw_name) +
  952                                              strlen(pw->pw_passwd) +
  953                                              strlen(pw->pw_gecos) +
  954                                              strlen(pw->pw_dir) +
  955                                              strlen(pw->pw_shell) + 5, 1));
  956   passwd->pw_uid                    = pw->pw_uid;
  957   passwd->pw_gid                    = pw->pw_gid;
  958   strncat(passwd->pw_shell          = strrchr(
  959   strncat(passwd->pw_dir            = strrchr(
  960   strncat(passwd->pw_gecos          = strrchr(
  961   strncat(passwd->pw_passwd         = strrchr(
  962   strncat(passwd->pw_name           = (char *)(passwd + 1),
  963          pw->pw_name,   strlen(pw->pw_name)),   '\000') + 1,
  964          pw->pw_passwd, strlen(pw->pw_passwd)), '\000') + 1,
  965          pw->pw_gecos,  strlen(pw->pw_gecos)),  '\000') + 1,
  966          pw->pw_dir,    strlen(pw->pw_dir)),    '\000') + 1,
  967          pw->pw_shell,  strlen(pw->pw_shell));
  968   free(buf);
  969   return passwd;
  970 }
  971 
  972 static void sigAlrmHandler(int sig ATTR_UNUSED, siginfo_t *info ATTR_UNUSED,
  973                            void *unused ATTR_UNUSED) {
  974   UNUSED(sig);
  975   UNUSED(info);
  976   UNUSED(unused);
  977   puts("\nLogin timed out after 60 seconds.");
  978   _exit(1);
  979 }
  980 
  981 static pam_handle_t *internalLogin(struct Service *service, struct Utmp *utmp,
  982                                    char ***environment) {
  983   // Time out after 60 seconds
  984   struct sigaction sa;
  985   memset(&sa, 0, sizeof(sa));
  986   sa.sa_flags                  = SA_SIGINFO;
  987   sa.sa_sigaction              = sigAlrmHandler;
  988   check(!sigaction(SIGALRM, &sa, NULL));
  989   alarm(60);
  990 
  991   // Change the prompt to include the host name
  992   const char *hostname         = NULL;
  993   if (service->authUser == 2 /* SSH */) {
  994     // If connecting to a remote host, include that hostname
  995     hostname                   = strrchr(service->cmdline, '@');
  996     if (!hostname || !strcmp(++hostname, "localhost")) {
  997       hostname                 = NULL;
  998     }
  999   }
 1000   struct utsname uts;
 1001   memset(&uts, 0, sizeof(uts));
 1002   if (!hostname) {
 1003     // Find our local hostname
 1004     check(!uname(&uts));
 1005     hostname                   = uts.nodename;
 1006   }
 1007   const char *fqdn;
 1008   check(fqdn                   = strdup(hostname));
 1009   check(hostname               = strdup(hostname));
 1010   char *dot                    = strchr(hostname, '.');
 1011   if (dot) {
 1012     *dot                       = '\000';
 1013   }
 1014 
 1015   const struct passwd *pw;
 1016   pam_handle_t *pam            = NULL;
 1017   if (service->authUser == 2 /* SSH */) {
 1018     // Just ask for the user name. SSH will negotiate the password
 1019     char *user                 = NULL;
 1020     char *prompt;
 1021     check(prompt               = stringPrintf(NULL, "%s login: ", hostname));
 1022     for (;;) {
 1023       if (read_string(1, prompt, &user) <= 0) {
 1024         free(user);
 1025         free(prompt);
 1026         _exit(1);
 1027       }
 1028       if (*user) {
 1029         for (char *u = user; *u; u++) {
 1030           char ch              = *u;
 1031           if (!((ch >= '0' && ch <= '9') ||
 1032                 (ch >= 'A' && ch <= 'Z') ||
 1033                 (ch >= 'a' && ch <= 'z') ||
 1034                 ch == '-' || ch == '_' || ch == '.' || ch == '@')) {
 1035             goto invalid_user_name;
 1036           }
 1037         }
 1038         break;
 1039       }
 1040     invalid_user_name:
 1041       free(user);
 1042       user                     = NULL;
 1043     }
 1044     free(prompt);
 1045     char *cmdline              = stringPrintf(NULL, service->cmdline, user);
 1046     free(user);
 1047 
 1048     // Replace '@localhost' with the actual host name. This results in a nicer
 1049     // prompt when SSH asks for the password.
 1050     char *ptr                  = strrchr(cmdline, '@');
 1051     if (!strcmp(ptr + 1, "localhost")) {
 1052       int offset               = ptr + 1 - cmdline;
 1053       check(cmdline            = realloc(cmdline,
 1054                                          strlen(cmdline) + strlen(fqdn) -
 1055                                          strlen("localhost") + 1));
 1056       ptr                      = cmdline + offset;
 1057       *ptr                     = '\000';
 1058       strncat(ptr, fqdn, strlen(fqdn));
 1059     }
 1060 
 1061     free((void *)service->cmdline);
 1062     service->cmdline           = cmdline;
 1063 
 1064     // Run SSH as an unprivileged user
 1065     if ((service->uid          = restricted) == 0) {
 1066       if (runAsUser >= 0) {
 1067         service->uid           = runAsUser;
 1068       } else {
 1069         service->uid           = getUserId("nobody");
 1070       }
 1071       if (runAsGroup >= 0) {
 1072         service->gid           = runAsGroup;
 1073       } else {
 1074         service->gid           = getGroupId("nogroup");
 1075       }
 1076     }
 1077     pw                         = getPWEnt(service->uid);
 1078     if (restricted) {
 1079       service->gid             = pw->pw_gid;
 1080     }
 1081     service->user              = getUserName(service->uid);
 1082     service->group             = getGroupName(service->gid);
 1083   } else {
 1084     // Use PAM to negotiate user authentication and authorization
 1085 #if defined(HAVE_SECURITY_PAM_APPL_H)
 1086     struct pam_conv conv       = { .conv = misc_conv };
 1087     if (service->authUser) {
 1088       check(supportsPAM());
 1089       check(pam_start("shellinabox", NULL, &conv, &pam) == PAM_SUCCESS);
 1090 
 1091       const char *origPrompt;
 1092       check(pam_get_item(pam, PAM_USER_PROMPT, (void *)&origPrompt) ==
 1093             PAM_SUCCESS);
 1094       char *prompt;
 1095       check(prompt             = stringPrintf(NULL, "%s %s", hostname,
 1096                                          origPrompt ? origPrompt : "login: "));
 1097       check(pam_set_item(pam, PAM_USER_PROMPT, prompt) == PAM_SUCCESS);
 1098 
 1099       // Up to three attempts to enter the user id and password
 1100       for (int i = 0;;) {
 1101         check(pam_set_item(pam, PAM_USER, NULL) == PAM_SUCCESS);
 1102         int rc;
 1103         if ((rc                = pam_authenticate(pam, PAM_SILENT)) ==
 1104             PAM_SUCCESS &&
 1105             (geteuid() ||
 1106              (rc               = pam_acct_mgmt(pam, PAM_SILENT)) ==
 1107              PAM_SUCCESS)) {
 1108           break;
 1109         }
 1110         if (++i == 3) {
 1111           // Quit if login failed.
 1112           puts("\nMaximum number of tries exceeded (3)");
 1113           pam_end(pam, rc);
 1114           _exit(1);
 1115         } else {
 1116           puts("\nLogin incorrect");
 1117         }
 1118       }
 1119       check(pam_set_item(pam, PAM_USER_PROMPT, "login: ") == PAM_SUCCESS);
 1120       free(prompt);
 1121 
 1122       // Retrieve user id, and group id.
 1123       const char *name;
 1124       check(pam_get_item(pam, PAM_USER, (void *)&name) == PAM_SUCCESS);
 1125       pw                       = getPWEnt(getUserId(name));
 1126       check(service->uid < 0);
 1127       check(service->gid < 0);
 1128       check(!service->user);
 1129       check(!service->group);
 1130       service->uid             = pw->pw_uid;
 1131       service->gid             = pw->pw_gid;
 1132       check(service->user      = strdup(pw->pw_name));
 1133       service->group           = getGroupName(pw->pw_gid);
 1134     } else {
 1135       check(service->uid >= 0);
 1136       check(service->gid >= 0);
 1137       check(service->user);
 1138       check(service->group);
 1139       if (supportsPAM()) {
 1140         check(pam_start("shellinabox", service->user, &conv, &pam) ==
 1141               PAM_SUCCESS);
 1142         int rc;
 1143 
 1144         // PAM account management requires root access. Just skip it, if we
 1145         // are running with lower privileges.
 1146         if (!geteuid() &&
 1147             (rc                = pam_acct_mgmt(pam, PAM_SILENT)) !=
 1148             PAM_SUCCESS) {
 1149           pam_end(pam, rc);
 1150           _exit(1);
 1151         }
 1152       }
 1153       pw                       = getPWEnt(service->uid);
 1154     }
 1155 #else
 1156     check(!supportsPAM());
 1157     pw                         = getPWEnt(service->uid);
 1158 #endif
 1159   }
 1160   free((void *)fqdn);
 1161   free((void *)hostname);
 1162 
 1163   if (service->useDefaultShell) {
 1164     check(!service->cmdline);
 1165     service->cmdline           = strdup(*pw->pw_shell ?
 1166                                         pw->pw_shell : "/bin/sh");
 1167   }
 1168 
 1169   if (restricted &&
 1170       (service->uid != (int)restricted || service->gid != (int)pw->pw_gid)) {
 1171     puts("\nAccess denied!");
 1172 #if defined(HAVE_SECURITY_PAM_APPL_H)
 1173     if (service->authUser != 2 /* SSH */) {
 1174       pam_end(pam, PAM_SUCCESS);
 1175     }
 1176 #endif
 1177     _exit(1);
 1178   }
 1179 
 1180   if (service->authUser != 2 /* SSH */) {
 1181 #if defined(HAVE_SECURITY_PAM_APPL_H)
 1182     if (pam) {
 1183 #ifdef HAVE_UTMPX_H
 1184       check(pam_set_item(pam, PAM_TTY, (const void **)utmp->utmpx.ut_line) ==
 1185             PAM_SUCCESS);
 1186 #endif
 1187     }
 1188 #else
 1189     check(!pam);
 1190 #endif
 1191   }
 1192 
 1193   // Retrieve supplementary group ids.
 1194   int ngroups;
 1195 #if defined(__linux__)
 1196   // On Linux, we can query the number of supplementary groups. On all other
 1197   // platforms, we play it safe and just assume a fixed upper bound.
 1198   ngroups                      = 0;
 1199   getgrouplist(service->user, pw->pw_gid, NULL, &ngroups);
 1200 #else
 1201   ngroups                      = 128;
 1202 #endif
 1203   check(ngroups >= 0);
 1204   if (ngroups > 0) {
 1205     // Set supplementary group ids
 1206     gid_t *groups;
 1207     check(groups               = malloc((ngroups + 1) * sizeof(gid_t)));
 1208     check(getgrouplist(service->user, pw->pw_gid, groups, &ngroups) >= 0);
 1209 
 1210     // Make sure that any group that was requested on the command line is
 1211     // included, if it is not one of the normal groups for this user.
 1212     for (int i = 0; ; i++) {
 1213       if (i == ngroups) {
 1214         groups[ngroups++]      = service->gid;
 1215         break;
 1216       } else if ((int)groups[i] == service->gid) {
 1217         break;
 1218       }
 1219     }
 1220     setgroups(ngroups, groups);
 1221     free(groups);
 1222   }
 1223 
 1224   // Add standard environment variables
 1225   int numEnvVars               = 0;
 1226   for (char **e = *environment; *e; numEnvVars++, e++) {
 1227   }
 1228   check(*environment           = realloc(*environment,
 1229                                      (numEnvVars + 6)*sizeof(char *)));
 1230   (*environment)[numEnvVars++] = stringPrintf(NULL, "HOME=%s", pw->pw_dir);
 1231   (*environment)[numEnvVars++] = stringPrintf(NULL, "SHELL=%s", pw->pw_shell);
 1232   check(
 1233   (*environment)[numEnvVars++] = strdup(
 1234                               "PATH=/usr/local/bin:/usr/bin:/bin:/usr/games"));
 1235   (*environment)[numEnvVars++] = stringPrintf(NULL, "LOGNAME=%s",
 1236                                               service->user);
 1237   (*environment)[numEnvVars++] = stringPrintf(NULL, "USER=%s", service->user);
 1238   (*environment)[numEnvVars++] = NULL;
 1239   free((void *)pw);
 1240 
 1241   // Update utmp/wtmp entries
 1242 #ifdef HAVE_UTMPX_H
 1243   if (enableUtmpLogging && service->authUser != 2 /* SSH */) {
 1244     memset(&utmp->utmpx.ut_user, 0, sizeof(utmp->utmpx.ut_user));
 1245     strncat(&utmp->utmpx.ut_user[0], service->user,
 1246             sizeof(utmp->utmpx.ut_user) - 1);
 1247     setutxent();
 1248     pututxline(&utmp->utmpx);
 1249     endutxent();
 1250 
 1251 #if defined(HAVE_UPDWTMP) || defined(HAVE_UPDWTMPX)
 1252     updwtmpx("/var/log/wtmp", &utmp->utmpx);
 1253 #endif
 1254   }
 1255 #endif
 1256 
 1257   alarm(0);
 1258   return pam;
 1259 }
 1260 
 1261 static void destroyVariableHashEntry(void *arg ATTR_UNUSED, char *key,
 1262                                      char *value) {
 1263   UNUSED(arg);
 1264   free(key);
 1265   free(value);
 1266 }
 1267 
 1268 static void execService(int width ATTR_UNUSED, int height ATTR_UNUSED,
 1269                         struct Service *service, const char *peerName,
 1270                         const char *realIP, char **environment,
 1271                         const char *url) {
 1272   UNUSED(width);
 1273   UNUSED(height);
 1274 
 1275   // Create a hash table with all the variables that we can expand. This
 1276   // includes all environment variables being passed to the child.
 1277   HashMap *vars;
 1278   check(vars                  = newHashMap(destroyVariableHashEntry, NULL));
 1279   for (char **e = environment; *e; e++) {
 1280     char *ptr                 = strchr(*e, '=');
 1281     char *key, *value;
 1282     if (!ptr) {
 1283       check(key               = strdup(*e));
 1284       check(value             = strdup(""));
 1285     } else {
 1286       check(key               = malloc(ptr - *e + 1));
 1287       memcpy(key, *e, ptr - *e);
 1288       key[ptr - *e]           = '\000';
 1289       check(value             = strdup(ptr + 1));
 1290     }
 1291     // All of our variables are lower-case
 1292     for (ptr = key; *ptr; ptr++) {
 1293       if (*ptr >= 'A' && *ptr <= 'Z') {
 1294         *ptr += 'a' - 'A';
 1295       }
 1296     }
 1297     addToHashMap(vars, key, value);
 1298   }
 1299   char *key, *value;
 1300   check(key                   = strdup("gid"));
 1301   addToHashMap(vars, key, stringPrintf(NULL, "%d", service->gid));
 1302   check(key                   = strdup("group"));
 1303   check(value                 = strdup(service->group));
 1304   addToHashMap(vars, key, value);
 1305   check(key                   = strdup("peer"));
 1306   check(value                 = strdup(peerName));
 1307   addToHashMap(vars, key, value);
 1308   check(key                   = strdup("realip"));
 1309   check(value                 = strdup(realIP));
 1310   addToHashMap(vars, key, value);
 1311   check(key                   = strdup("uid"));
 1312   addToHashMap(vars, key, stringPrintf(NULL, "%d", service->uid));
 1313   check(key                   = strdup("url"));
 1314   addToHashMap(vars, key, strdup(url));
 1315 
 1316   enum { ENV, ARGS } state    = ENV;
 1317   enum { NONE, SINGLE, DOUBLE
 1318   } quote                     = NONE;
 1319   char *cmdline;
 1320   check(cmdline               = strdup(service->cmdline));
 1321   int argc                    = 0;
 1322   char **argv;
 1323   check(argv                  = malloc(sizeof(char *)));
 1324   key                         = NULL;
 1325   value                       = NULL;
 1326   for (char *ptr = cmdline; ; ptr++) {
 1327     if (!key && *ptr && *ptr != ' ') {
 1328       key                     = ptr;
 1329     }
 1330     switch (*ptr) {
 1331     case '\'':
 1332       if (quote == SINGLE || quote == NONE) {
 1333         memmove(ptr, ptr + 1, strlen(ptr));
 1334         ptr--;
 1335         quote                 = quote == SINGLE ? NONE : SINGLE;
 1336       } else {
 1337         dcheck(quote == DOUBLE);
 1338       }
 1339       break;
 1340     case '\"':
 1341       if (quote == DOUBLE || quote == NONE) {
 1342         memmove(ptr, ptr + 1, strlen(ptr));
 1343         ptr--;
 1344         quote                 = quote == DOUBLE ? NONE : DOUBLE;
 1345       } else {
 1346         dcheck(quote == SINGLE);
 1347       }
 1348       break;
 1349     case '$':
 1350       if ((quote == NONE || quote == DOUBLE) && ptr[1] == '{') {
 1351         // Always treat environment variables as if they were quoted. There
 1352         // is no good reason for us to try to look for spaces within
 1353         // expanded environment variables. This just leads to subtle bugs.
 1354         char *end             = ptr + 2;
 1355         while (*end && *end != '}') {
 1356           end++;
 1357         }
 1358         char ch               = *end;
 1359         *end                  = '\000';
 1360         const char *repl      = getFromHashMap(vars, ptr + 2);
 1361         int replLen           = repl ? strlen(repl) : 0;
 1362         *end                  = ch;
 1363         if (ch) {
 1364           end++;
 1365         }
 1366         int incr              = replLen - (end - ptr);
 1367         if (incr > 0) {
 1368           char *oldCmdline    = cmdline;
 1369           check(cmdline       = realloc(cmdline,
 1370                                         (end - cmdline) + strlen(end) +
 1371                                         incr + 1));
 1372           ptr                += cmdline - oldCmdline;
 1373           end                += cmdline - oldCmdline;
 1374           if (key) {
 1375             key              += cmdline - oldCmdline;
 1376           }
 1377           if (value) {
 1378             value            += cmdline - oldCmdline;
 1379           }
 1380         }
 1381         memmove(ptr + replLen, end, strlen(end) + 1);
 1382         if (repl) {
 1383           memcpy(ptr, repl, replLen);
 1384         }
 1385         ptr                  += replLen - 1;
 1386       }
 1387       break;
 1388     case '\\':
 1389       if (!ptr[1]) {
 1390         *ptr--                = '\000';
 1391       } else {
 1392         memmove(ptr, ptr + 1, strlen(ptr));
 1393       }
 1394       break;
 1395     case '=':
 1396       // This is the seperator between keys and values of any environment
 1397       // variable that we are asked to set.
 1398       if (state == ENV && quote == NONE && !value) {
 1399         *ptr                  = '\000';
 1400         value                 = ptr + 1;
 1401       }
 1402       break;
 1403     case ' ':
 1404       // If this space character is not quoted, this is the start of a new
 1405       // command line argument.
 1406       if (quote != NONE) {
 1407         break;
 1408       }
 1409       // Fall thru
 1410     case '\000':;
 1411       char ch                 = *ptr;
 1412       if (key) {
 1413         *ptr                  = '\000';
 1414         if (state == ENV && value) {
 1415           // Override an existing environment variable.
 1416           int numEnvVars = 0;
 1417           int len             = strlen(key);
 1418           for (char **e = environment; *e; e++, numEnvVars++) {
 1419             if (!strncmp(*e, key, len) && (*e)[len] == '=') {
 1420               int s_size      = len + strlen(value) + 1;
 1421               check(*e        = realloc(*e, s_size + 1));
 1422               (*e)[len + 1]   = '\000';
 1423               strncat(*e, value, s_size);
 1424               numEnvVars      = -1;
 1425               break;
 1426             }
 1427           }
 1428           // Add a new environment variable
 1429           if (numEnvVars >= 0) {
 1430             check(environment = realloc(environment,
 1431                                         (numEnvVars + 2)*sizeof(char *)));
 1432             value[-1]         = '=';
 1433             environment[numEnvVars++] = strdup(key);
 1434             environment[numEnvVars]   = NULL;
 1435           }
 1436         } else {
 1437           // Add entry to argv.
 1438           state               = ARGS;
 1439           argv[argc++]        = strdup(key);
 1440           check(argv          = realloc(argv, (argc + 1)*sizeof(char *)));
 1441         }
 1442       }
 1443       key                     = NULL;
 1444       value                   = NULL;
 1445       if (!ch) {
 1446         goto done;
 1447       }
 1448       break;
 1449     default:
 1450       break;
 1451     }
 1452   }
 1453  done:
 1454   free(cmdline);
 1455   argv[argc]                  = NULL;
 1456   deleteHashMap(vars);
 1457   check(argc);
 1458 
 1459   extern char **environ;
 1460   environ                     = environment;
 1461   char *cmd;
 1462   check(cmd                   = strdup(argv[0]));
 1463   char *slash                 = strrchr(argv[0], '/');
 1464   if (slash) {
 1465     memmove(argv[0], slash + 1, strlen(slash));
 1466   }
 1467   if (service->useDefaultShell) {
 1468     int len                   = strlen(argv[0]);
 1469     check(argv[0]             = realloc(argv[0], len + 2));
 1470     memmove(argv[0] + 1, argv[0], len);
 1471     argv[0][0]                = '-';
 1472     argv[0][len + 1]          = '\000';
 1473   }
 1474   if (execvp(cmd, argv) < 0) {
 1475     free(argv);
 1476     free(cmd);
 1477   }
 1478 }
 1479 
 1480 void setWindowSize(int pty, int width, int height) {
 1481   if (width > 0 && height > 0) {
 1482     #ifdef TIOCSSIZE
 1483     {
 1484       struct ttysize win;
 1485       ioctl(pty, TIOCGSIZE, &win);
 1486       win.ts_lines = height;
 1487       win.ts_cols  = width;
 1488       ioctl(pty, TIOCSSIZE, &win);
 1489     }
 1490     #endif
 1491     #ifdef TIOCGWINSZ
 1492     {
 1493       struct winsize win;
 1494       ioctl(pty, TIOCGWINSZ, &win);
 1495       win.ws_row   = height;
 1496       win.ws_col   = width;
 1497       ioctl(pty, TIOCSWINSZ, &win);
 1498     }
 1499     #endif
 1500   }
 1501 }
 1502 
 1503 #if defined(HAVE_SECURITY_PAM_APPL_H)
 1504 static void pamSessionSighupHandler(int sig ATTR_UNUSED,
 1505                                     siginfo_t *info ATTR_UNUSED,
 1506                                     void *unused ATTR_UNUSED) {
 1507   UNUSED(sig);
 1508   UNUSED(info);
 1509   UNUSED(unused);
 1510   pamSessionSighupFlag = 1;
 1511 }
 1512 #endif
 1513 
 1514 static void childProcess(struct Service *service, int width, int height,
 1515                          struct Utmp *utmp, const char *peerName, const char *realIP,
 1516                          const char *url) {
 1517   // Set initial window size
 1518   setWindowSize(0, width, height);
 1519 
 1520   // Set up environment variables
 1521   static const char *legalEnv[] = { "TZ", "HZ", NULL };
 1522   char **environment;
 1523   check(environment             = malloc(2*sizeof(char *)));
 1524   int numEnvVars                = 1;
 1525   check(environment[0]          = strdup("TERM=xterm"));
 1526   if (width > 0 && height > 0) {
 1527     numEnvVars                 += 2;
 1528     check(environment           = realloc(environment,
 1529                                           (numEnvVars + 1)*sizeof(char *)));
 1530     environment[numEnvVars-2]   = stringPrintf(NULL, "COLUMNS=%d", width);
 1531     environment[numEnvVars-1]   = stringPrintf(NULL, "LINES=%d", height);
 1532   }
 1533   for (int i = 0; legalEnv[i]; i++) {
 1534     char *value                 = getenv(legalEnv[i]);
 1535     if (value) {
 1536       numEnvVars++;
 1537       check(environment         = realloc(environment,
 1538                                           (numEnvVars + 1)*sizeof(char *)));
 1539       environment[numEnvVars-1] = stringPrintf(NULL, "%s=%s",
 1540                                                legalEnv[i], value);
 1541     }
 1542   }
 1543 
 1544   // Add useful environment variables that can be used in custom client scripts
 1545   // or programs.
 1546   numEnvVars                   += 3;
 1547   check(environment             = realloc(environment,
 1548                                           (numEnvVars + 1)*sizeof(char *)));
 1549   environment[numEnvVars-3]     = stringPrintf(NULL, "SHELLINABOX_URL=%s",
 1550                                                url);
 1551   environment[numEnvVars-2]     = stringPrintf(NULL, "SHELLINABOX_PEERNAME=%s",
 1552                                                peerName);
 1553   environment[numEnvVars-1]     = stringPrintf(NULL, "SHELLINABOX_REALIP=%s",
 1554                                                realIP);
 1555   environment[numEnvVars]       = NULL;
 1556 
 1557   // Set initial terminal settings
 1558   struct termios tt = { 0 };
 1559   tcgetattr(0, &tt);
 1560   tt.c_iflag                    =  TTYDEF_IFLAG & ~ISTRIP;
 1561   tt.c_oflag                    =  TTYDEF_OFLAG;
 1562   tt.c_lflag                    =  TTYDEF_LFLAG;
 1563   tt.c_cflag                    = (TTYDEF_CFLAG & ~(CS7|PARENB|HUPCL)) | CS8;
 1564   tt.c_cc[VERASE]               = '\x7F';
 1565   cfsetispeed(&tt, B38400);
 1566   cfsetospeed(&tt, B38400);
 1567   tcsetattr(0, TCSAFLUSH, &tt);
 1568 
 1569   // Assert root privileges in order to update utmp entry. We can only do that,
 1570   // if we have root permissions otherwise this fails.
 1571   UNUSED_RETURN(setresuid(0, 0, 0));
 1572   UNUSED_RETURN(setresgid(0, 0, 0));
 1573 #ifdef HAVE_UTMPX_H
 1574   if (enableUtmpLogging) {
 1575     setutxent();
 1576     struct utmpx utmpx            = utmp->utmpx;
 1577     if (service->useLogin || service->authUser) {
 1578       utmpx.ut_type               = LOGIN_PROCESS;
 1579       memset(utmpx.ut_host, 0, sizeof(utmpx.ut_host));
 1580     }
 1581     pututxline(&utmpx);
 1582     endutxent();
 1583 
 1584 #if defined(HAVE_UPDWTMP) || defined(HAVE_UPDWTMPX)
 1585     if (!utmp->useLogin) {
 1586       memset(&utmpx.ut_user, 0, sizeof(utmpx.ut_user));
 1587       strncat(&utmpx.ut_user[0], "LOGIN", sizeof(utmpx.ut_user) - 1);
 1588       updwtmpx("/var/log/wtmp", &utmpx);
 1589     }
 1590 #endif
 1591   }
 1592 #endif
 1593 
 1594   // Create session. We might have to fork another process as PAM wants us
 1595   // to close the session when the child terminates. And we must retain
 1596   // permissions, as session closure could require root permissions.
 1597   // None of this really applies if we are running as an unprivileged user.
 1598   // In that case, we do not bother about session management.
 1599   if (!service->useLogin) {
 1600     pam_handle_t *pam           = internalLogin(service, utmp, &environment);
 1601 #if defined(HAVE_SECURITY_PAM_APPL_H)
 1602     if (pam && !geteuid()) {
 1603       if (pam_open_session(pam, PAM_SILENT) != PAM_SUCCESS) {
 1604         fprintf(stderr, "[server] Unable to open PAM session!\n");
 1605         _exit(1);
 1606       }
 1607       pid_t pid                 = fork();
 1608       switch (pid) {
 1609       case -1:
 1610         _exit(1);
 1611       case 0:
 1612         break;
 1613       default:;
 1614         // This process is used for finishing all pending PAM operations.
 1615         struct sigaction sa;
 1616         memset(&sa, 0, sizeof(sa));
 1617         sa.sa_flags             = SA_NOCLDSTOP | SA_SIGINFO;
 1618         sa.sa_sigaction         = pamSessionSighupHandler;
 1619         check(!sigaction(SIGHUP, &sa, NULL));
 1620         int status, rc;
 1621         while (1) {
 1622           pamSessionSighupFlag  = 0;
 1623           int val               = waitpid(pid, &status, 0);
 1624           if (val < 0 && errno == EINTR) {
 1625             if (pamSessionSighupFlag) {
 1626               // If SIGHUP signal is received it needs to be forwarded to child
 1627               // process, which is acctual service process.
 1628               kill(pid, SIGHUP);
 1629             }
 1630           } else {
 1631             break;
 1632           }
 1633         }
 1634         rc                      = pam_close_session(pam, PAM_SILENT);
 1635         pam_end(pam, rc | PAM_DATA_SILENT);
 1636         _exit(WIFEXITED(status) ? WEXITSTATUS(status) : -WTERMSIG(status));
 1637       }
 1638     }
 1639 #else
 1640     check(!pam);
 1641 #endif
 1642   }
 1643 
 1644   // Change user and group ids
 1645   check(!setresgid(service->gid, service->gid, service->gid));
 1646   check(!setresuid(service->uid, service->uid, service->uid));
 1647 
 1648   // Change working directory
 1649   if (service->useHomeDir) {
 1650     check(!service->useLogin);
 1651     const struct passwd *pw     = getPWEnt(getuid());
 1652     check(!service->cwd);
 1653     check(service->cwd          = strdup(pw->pw_dir));
 1654     free((void *)pw);
 1655   }
 1656   check(service->cwd);
 1657   if (!*service->cwd || *service->cwd != '/' || chdir(service->cwd)) {
 1658     check(service->cwd          = realloc((char *)service->cwd, 2));
 1659     *(char *)service->cwd       = '\000';
 1660     strncat((char *)service->cwd, "/", 1);
 1661     puts("No directory, logging in with HOME=/");
 1662     check(!chdir("/"));
 1663     for (int i = 0; environment[i]; i++) {
 1664       if (!strncmp(environment[i], "HOME=", 5)) {
 1665         free(environment[i]);
 1666         check(environment[i]    = strdup("HOME=/"));
 1667         break;
 1668       }
 1669     }
 1670   }
 1671 
 1672   // Reset the sigaction for HUP to the default, so the child does not inherit the action of SIG_IGN from us.
 1673   // We need the child to be able to get HUP's because we send HUP if the session times out/closes.
 1674   signal(SIGHUP, SIG_DFL);
 1675 
 1676   // Finally, launch the child process.
 1677   if (service->useLogin == 1) {
 1678     // At login service launch, we try to pass real IP in '-h' parameter. Real
 1679     // IP is provided in HTTP header field 'X-Real-IP', if ShellInABox is used
 1680     // behind properly configured HTTP proxy.
 1681     char remoteHost[256];
 1682     snprintf(remoteHost, 256,
 1683              (*realIP) ? "%s, %s" : "%s%s", peerName,
 1684              (*realIP) ? realIP : "");
 1685     execle("/bin/login", "login", "-p", "-h", remoteHost,
 1686            (void *)0, environment);
 1687     execle("/usr/bin/login", "login", "-p", "-h", remoteHost,
 1688            (void *)0, environment);
 1689   } else {
 1690     // Launch user provied service
 1691     execService(width, height, service, peerName, realIP, environment, url);
 1692   }
 1693   _exit(1);
 1694 }
 1695 
 1696 static void sigChildHandler(int sig ATTR_UNUSED, siginfo_t *info ATTR_UNUSED,
 1697                             void *unused ATTR_UNUSED) {
 1698   UNUSED(sig);
 1699   UNUSED(info);
 1700   UNUSED(unused);
 1701 }
 1702 
 1703 static void launcherDaemon(int fd) {
 1704   struct sigaction sa;
 1705   memset(&sa, 0, sizeof(sa));
 1706   sa.sa_flags                 = SA_NOCLDSTOP | SA_SIGINFO;
 1707   sa.sa_sigaction             = sigChildHandler;
 1708   check(!sigaction(SIGCHLD, &sa, NULL));
 1709 
 1710   // pututxline() can cause spurious SIGHUP signals. Better ignore those.
 1711   signal(SIGHUP, SIG_IGN);
 1712 
 1713   struct LaunchRequest request;
 1714   for (;;) {
 1715     errno                     = 0;
 1716     int len                   = read(fd, &request, sizeof(request));
 1717     if (len != sizeof(request) && errno != EINTR) {
 1718       if (len) {
 1719         debug("[server] Failed to read launch request!");
 1720       }
 1721       break;
 1722     }
 1723 
 1724     // Check whether our read operation got interrupted, because a child
 1725     // has died.
 1726     int   status;
 1727     pid_t pid;
 1728     while (NOINTR(pid = waitpid(-1, &status, WNOHANG)) > 0) {
 1729       debug("[server] Child %d exited with exit code %d.",
 1730             pid, WEXITSTATUS(status));
 1731       if (WIFEXITED(status) || WIFSIGNALED(status)) {
 1732         char key[32];
 1733         snprintf(&key[0], sizeof(key), "%d", pid);
 1734         deleteFromHashMap(childProcesses, key);
 1735       }
 1736     }
 1737     if (len != sizeof(request)) {
 1738       continue;
 1739     }
 1740 
 1741     // Check if we received terminate request from parent process and
 1742     // try to terminate child, if child is still running
 1743     if (request.terminate > 0) {
 1744       errno = 0;
 1745       NOINTR(pid = waitpid(request.terminate, &status, WNOHANG));
 1746       if (pid == 0 && errno == 0) {
 1747         if (kill(request.terminate, SIGHUP) == 0) {
 1748           debug("[server] Terminating child %d! [HUP]", request.terminate);
 1749         } else {
 1750           debug("[server] Terminating child %d failed! [%s]", request.terminate,
 1751                 strerror(errno));
 1752         }
 1753       }
 1754       continue;
 1755     }
 1756 
 1757     char *url;
 1758     check(url                 = calloc(request.urlLength + 1, 1));
 1759   readURL:
 1760     len                       = read(fd, url, request.urlLength + 1);
 1761     if (len != request.urlLength + 1 && errno != EINTR) {
 1762       debug("[server] Failed to read URL!");
 1763       free(url);
 1764       break;
 1765     }
 1766     while (NOINTR(pid = waitpid(-1, &status, WNOHANG)) > 0) {
 1767       debug("[server] Child %d exited with exit code %d.", pid,
 1768             WEXITSTATUS(status));
 1769       if (WIFEXITED(status) || WIFSIGNALED(status)) {
 1770         char key[32];
 1771         snprintf(&key[0], sizeof(key), "%d", pid);
 1772         deleteFromHashMap(childProcesses, key);
 1773       }
 1774     }
 1775     if (len != request.urlLength + 1) {
 1776       goto readURL;
 1777     }
 1778 
 1779     check(request.service >= 0);
 1780     check(request.service < numServices);
 1781 
 1782     // Sanitize peer name and real IP, so that we do not pass any unexpected
 1783     // characters to our child process.
 1784     request.peerName[sizeof(request.peerName)-1] = '\000';
 1785     for (char *s = request.peerName; *s; s++) {
 1786       if (!((*s >= '0' && *s <= '9') ||
 1787             (*s >= 'A' && *s <= 'Z') ||
 1788             (*s >= 'a' && *s <= 'z') ||
 1789              *s == '.' || *s == '-')) {
 1790         *s                    = '-';
 1791       }
 1792     }
 1793 
 1794     request.realIP[sizeof(request.realIP)-1] = '\000';
 1795     for (char *s = request.realIP; *s; s++) {
 1796       if (!((*s >= '0' && *s <= '9') ||
 1797             (*s >= 'A' && *s <= 'Z') ||
 1798             (*s >= 'a' && *s <= 'z') ||
 1799              *s == '.' || *s == '-')) {
 1800         *s                    = '-';
 1801       }
 1802     }
 1803 
 1804     // Fork and exec the child process.
 1805     int pty;
 1806     struct Utmp *utmp;
 1807     if ((pid                  = forkPty(&pty,
 1808                                         services[request.service]->useLogin,
 1809                                         &utmp,
 1810                                         request.peerName,
 1811                                         request.realIP)) == 0) {
 1812       childProcess(services[request.service], request.width, request.height,
 1813                    utmp, request.peerName, request.realIP, url);
 1814       free(url);
 1815       _exit(1);
 1816     } else {
 1817       // Remember the utmp entry so that we can clean up when the child
 1818       // terminates.
 1819       free(url);
 1820       if (pid > 0) {
 1821         if (!childProcesses) {
 1822           childProcesses      = newHashMap(destroyUtmpHashEntry, NULL);
 1823         }
 1824         addToHashMap(childProcesses, utmp->pid, (char *)utmp);
 1825         debug("[server] Child %d launched", pid);
 1826       } else {
 1827         int fds[2];
 1828         if (!pipe(fds)) {
 1829           NOINTR(write(fds[1], "forkpty() failed\r\n", 18));
 1830           NOINTR(close(fds[1]));
 1831           pty                 = fds[0];
 1832           pid                 = 0;
 1833         }
 1834       }
 1835 
 1836       // Send file handle and process id back to parent
 1837       char cmsg_buf[CMSG_SPACE(sizeof(int))]; // = { 0 }; // Valid initializer makes OSX mad.
 1838       memset (cmsg_buf, 0, sizeof (cmsg_buf)); // Quiet complaint from valgrind
 1839       struct iovec  iov       = { 0 };
 1840       struct msghdr msg       = { 0 };
 1841       iov.iov_base            = &pid;
 1842       iov.iov_len             = sizeof(pid);
 1843       msg.msg_iov             = &iov;
 1844       msg.msg_iovlen          = 1;
 1845       msg.msg_control         = &cmsg_buf;
 1846       msg.msg_controllen      = sizeof(cmsg_buf);
 1847       struct cmsghdr *cmsg    = CMSG_FIRSTHDR(&msg);
 1848       check(cmsg);
 1849       cmsg->cmsg_level        = SOL_SOCKET;
 1850       cmsg->cmsg_type         = SCM_RIGHTS;
 1851       cmsg->cmsg_len          = CMSG_LEN(sizeof(int));
 1852       memcpy(CMSG_DATA(cmsg), &pty, sizeof(int));
 1853       if (NOINTR(sendmsg(fd, &msg, 0)) != sizeof(pid)) {
 1854         break;
 1855       }
 1856       NOINTR(close(pty));
 1857     }
 1858   }
 1859   deleteHashMap(childProcesses);
 1860   _exit(0);
 1861 }
 1862 
 1863 int forkLauncher(void) {
 1864   int pair[2];
 1865   check(!socketpair(AF_UNIX, SOCK_STREAM, 0, pair));
 1866 
 1867   switch (fork()) {
 1868   case 0:;
 1869     // If our real-uid is not "root", then we should not allow anybody to
 1870     // login unauthenticated users as anyone other than their own.
 1871     uid_t tmp;
 1872     check(!getresuid(&restricted, &tmp, &tmp));
 1873 
 1874     // Temporarily drop most permissions. We still retain the ability to
 1875     // switch back to root, which is necessary for launching "login".
 1876     lowerPrivileges();
 1877     closeAllFds((int []){ pair[1], 2 }, 2);
 1878     launcherDaemon(pair[1]);
 1879     fatal("[server] Launcher exit() failed!");
 1880   case -1:
 1881     fatal("[server] Launcher fork() failed!");
 1882   default:
 1883     NOINTR(close(pair[1]));
 1884     launcher = pair[0];
 1885     return launcher;
 1886   }
 1887 }
 1888 
 1889 void terminateLauncher(void) {
 1890   if (launcher >= 0) {
 1891     NOINTR(close(launcher));
 1892     launcher = -1;
 1893   }
 1894 }