]> andersk Git - test.git/commitdiff
Allow unprivileged users to run the daemon. This requires calling "ssh" instead of...
authorMarkus Gutschke <markus@shellinabox.com>
Wed, 29 Jul 2009 18:30:03 +0000 (18:30 +0000)
committerMarkus Gutschke <markus@shellinabox.com>
Wed, 29 Jul 2009 18:30:03 +0000 (18:30 +0000)
ChangeLog
shellinabox/launcher.c
shellinabox/service.c
shellinabox/shellinaboxd.c

index bb06a9ae1c2563dac65f1fa661c97356853979fd..e1fe74a8bf81671f1a0bbe365fb78d70f99f5526 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2009-07-29  Markus Gutschke  <markus@shellinabox.com>
+
+       * Allow unprivileged users to run the daemon. This requires
+       calling "ssh" instead of "login".
+
 2009-07-27  Markus Gutschke  <markus@shellinabox.com>
 
        * Use JavaScript redirection for attaching the missing slash to
        redirecting to a URL that includes a trailing slash.
 
        * Run-time testing for availability of libpthread functions does not
-         work reliably on some platforms. So, avoid doing so on anything
-         other than Linux/i386. For all other platforms, assume that the code
-         is not linked against libpthread. For ShellInABox, this is always
-         the correct assumption. But if the code gets embedded into other
-         projects, this might have to be changed.
+       work reliably on some platforms. So, avoid doing so on anything
+       other than Linux/i386. For all other platforms, assume that the code
+       is not linked against libpthread. For ShellInABox, this is always
+       the correct assumption. But if the code gets embedded into other
+       projects, this might have to be changed.
 
 2009-07-05  Markus Gutschke  <markus@shellinabox.com>
 
index 579086c6841aeda4267aec21cc2c9a37e544831f..1386ae51da4567bb85e245bb0bb1e9994b0e650a 100644 (file)
@@ -159,8 +159,6 @@ static int   launcher = -1;
 static uid_t restricted;
 
 
-#if defined(HAVE_SECURITY_PAM_APPL_H) && defined(HAVE_DLOPEN)
-
 // If the PAM misc library cannot be found, we have to provide our own basic
 // conversation function. As we know that this code is only ever called from
 // ShellInABox, it can be kept significantly simpler than the more generic
@@ -210,6 +208,7 @@ static int read_string(int echo, const char *prompt, char **retstr) {
   return nc;
 }
 
+#if defined(HAVE_SECURITY_PAM_APPL_H) && defined(HAVE_DLOPEN)
 #if defined(HAVE_SECURITY_PAM_CLIENT_H)
 static pamc_bp_t *p(pamc_bp_t *p) {
   // GCC is too smart for its own good, and triggers a warning in
@@ -757,106 +756,166 @@ static pam_handle_t *internalLogin(struct Service *service, struct Utmp *utmp,
   check(!sigaction(SIGALRM, &sa, NULL));
   alarm(60);
 
-  // Use PAM to negotiate user authentication and authorization
+  // Change the prompt to include the host name
+  const char *hostname         = NULL;
+  if (service->authUser == 2 /* SSH */) {
+    // If connecting to a remote host, include that hostname
+    hostname                   = strrchr(service->cmdline, '@');
+    if (!hostname || !strcmp(++hostname, "localhost")) {
+      hostname                 = NULL;
+    }
+  }
+  struct utsname uts;
+  memset(&uts, 0, sizeof(uts));
+  if (!hostname) {
+    // Find our local hostname
+    check(!uname(&uts));
+    hostname                   = uts.nodename;
+  }
+
   const struct passwd *pw;
   pam_handle_t *pam            = NULL;
-#if defined(HAVE_SECURITY_PAM_APPL_H)
-  struct pam_conv conv         = { .conv = misc_conv };
-  if (service->authUser) {
-    check(supportsPAM());
-    check(pam_start("shellinabox", NULL, &conv, &pam) == PAM_SUCCESS);
-
-    // Change the prompt to include the host name
-    struct utsname uts;
-    check(!uname(&uts));
-    const char *origPrompt;
-    check(pam_get_item(pam, PAM_USER_PROMPT, (void *)&origPrompt) ==
-          PAM_SUCCESS);
+  if (service->authUser == 2 /* SSH */) {
+    // Just ask for the user name. SSH will negotiate the password
+    char *user                 = NULL;
     char *prompt;
-    check(prompt               = stringPrintf(NULL, "%s %s", uts.nodename,
-                                         origPrompt ? origPrompt : "login: "));
-    check(pam_set_item(pam, PAM_USER_PROMPT, prompt) == PAM_SUCCESS);
-
-    // Up to three attempts to enter the user id and password
-    for (int i = 0;;) {
-      check(pam_set_item(pam, PAM_USER, NULL) == PAM_SUCCESS);
-      int rc;
-      if ((rc                  = pam_authenticate(pam, PAM_SILENT)) ==
-          PAM_SUCCESS &&
-          (geteuid() ||
-          (rc                  = pam_acct_mgmt(pam, PAM_SILENT)) ==
-           PAM_SUCCESS)) {
-        break;
+    check(prompt               = stringPrintf(NULL, "%s login: ", hostname));
+    if (read_string(1, prompt, &user) <= 0) {
+      free(user);
+      free(prompt);
+      _exit(1);
+    }
+    free(prompt);
+    char *localhost            = strstr(service->cmdline, "@localhost");
+    if (localhost) {
+      memcpy(localhost+1, "%s", 3);
+    }
+    char *cmdline              = stringPrintf(NULL, service->cmdline, user,
+                                              hostname);
+    free(user);
+    free((void *)service->cmdline);
+    service->cmdline           = cmdline;
+
+    // Run SSH as an unprivileged user
+    if ((service->uid          = restricted) == 0) {
+      if (runAsUser >= 0) {
+        service->uid           = runAsUser;
+      } else {
+        service->uid           = getUserId("nobody");
       }
-      if (++i == 3) {
-        // Quit if login failed.
-        puts("\nMaximum number of tries exceeded (3)");
-        pam_end(pam, rc);
-        _exit(1);
+      if (runAsGroup >= 0) {
+        service->gid           = runAsGroup;
       } else {
-        puts("\nLogin incorrect");
+        service->gid           = getGroupId("nogroup");
       }
     }
-    check(pam_set_item(pam, PAM_USER_PROMPT, "login: ") == PAM_SUCCESS);
-    free(prompt);
-
-    // Retrieve user id, and group id.
-    const char *name;
-    check(pam_get_item(pam, PAM_USER, (void *)&name) == PAM_SUCCESS);
-    pw                         = getPWEnt(getUserId(name));
-    check(service->uid < 0);
-    check(service->gid < 0);
-    check(!service->user);
-    check(!service->group);
-    service->uid               = pw->pw_uid;
-    service->gid               = pw->pw_gid;
-    check(service->user        = strdup(pw->pw_name));
-    service->group             = getGroupName(pw->pw_gid);
+    pw                         = getPWEnt(service->uid);
+    if (restricted) {
+      service->gid             = pw->pw_gid;
+    }
+    service->user              = getUserName(service->uid);
+    service->group             = getGroupName(service->gid);
   } else {
-    check(service->uid >= 0);
-    check(service->gid >= 0);
-    check(service->user);
-    check(service->group);
-    if (supportsPAM()) {
-      check(pam_start("shellinabox", service->user, &conv, &pam) ==
+    // Use PAM to negotiate user authentication and authorization
+#if defined(HAVE_SECURITY_PAM_APPL_H)
+    struct pam_conv conv       = { .conv = misc_conv };
+    if (service->authUser) {
+      check(supportsPAM());
+      check(pam_start("shellinabox", NULL, &conv, &pam) == PAM_SUCCESS);
+
+      const char *origPrompt;
+      check(pam_get_item(pam, PAM_USER_PROMPT, (void *)&origPrompt) ==
             PAM_SUCCESS);
-      int rc;
-
-      // PAM account management requires root access. Just skip it, if we
-      // are running with lower privileges.
-      if (!geteuid() &&
-          (rc                  = pam_acct_mgmt(pam, PAM_SILENT)) !=
-          PAM_SUCCESS) {
-        pam_end(pam, rc);
-        _exit(1);
+      char *prompt;
+      check(prompt             = stringPrintf(NULL, "%s %s", hostname,
+                                         origPrompt ? origPrompt : "login: "));
+      check(pam_set_item(pam, PAM_USER_PROMPT, prompt) == PAM_SUCCESS);
+
+      // Up to three attempts to enter the user id and password
+      for (int i = 0;;) {
+        check(pam_set_item(pam, PAM_USER, NULL) == PAM_SUCCESS);
+        int rc;
+        if ((rc                = pam_authenticate(pam, PAM_SILENT)) ==
+            PAM_SUCCESS &&
+            (geteuid() ||
+             (rc               = pam_acct_mgmt(pam, PAM_SILENT)) ==
+             PAM_SUCCESS)) {
+          break;
+        }
+        if (++i == 3) {
+          // Quit if login failed.
+          puts("\nMaximum number of tries exceeded (3)");
+          pam_end(pam, rc);
+          _exit(1);
+        } else {
+          puts("\nLogin incorrect");
+        }
       }
+      check(pam_set_item(pam, PAM_USER_PROMPT, "login: ") == PAM_SUCCESS);
+      free(prompt);
+
+      // Retrieve user id, and group id.
+      const char *name;
+      check(pam_get_item(pam, PAM_USER, (void *)&name) == PAM_SUCCESS);
+      pw                       = getPWEnt(getUserId(name));
+      check(service->uid < 0);
+      check(service->gid < 0);
+      check(!service->user);
+      check(!service->group);
+      service->uid             = pw->pw_uid;
+      service->gid             = pw->pw_gid;
+      check(service->user      = strdup(pw->pw_name));
+      service->group           = getGroupName(pw->pw_gid);
+    } else {
+      check(service->uid >= 0);
+      check(service->gid >= 0);
+      check(service->user);
+      check(service->group);
+      if (supportsPAM()) {
+        check(pam_start("shellinabox", service->user, &conv, &pam) ==
+              PAM_SUCCESS);
+        int rc;
+
+        // PAM account management requires root access. Just skip it, if we
+        // are running with lower privileges.
+        if (!geteuid() &&
+            (rc                = pam_acct_mgmt(pam, PAM_SILENT)) !=
+            PAM_SUCCESS) {
+          pam_end(pam, rc);
+          _exit(1);
+        }
+      }
+      pw                       = getPWEnt(service->uid);
     }
-    pw                         = getPWEnt(service->uid);
-  }
 #else
-  check(!supportsPAM());
-  pw                           = getPWEnt(service->uid);
+    check(!supportsPAM());
+    pw                         = getPWEnt(service->uid);
 #endif
+  }
 
   if (restricted &&
       (service->uid != restricted || service->gid != pw->pw_gid)) {
     puts("\nAccess denied!");
 #if defined(HAVE_SECURITY_PAM_APPL_H)
-    pam_end(pam, PAM_SUCCESS);
+    if (service->authUser != 2 /* SSH */) {
+      pam_end(pam, PAM_SUCCESS);
+    }
 #endif
     _exit(1);
   }
 
+  if (service->authUser != 2 /* SSH */) {
 #if defined(HAVE_SECURITY_PAM_APPL_H)
-  if (pam) {
+    if (pam) {
 #ifdef HAVE_UTMPX_H
-    check(pam_set_item(pam, PAM_TTY, (const void **)utmp->utmpx.ut_line) ==
-          PAM_SUCCESS);
+      check(pam_set_item(pam, PAM_TTY, (const void **)utmp->utmpx.ut_line) ==
+            PAM_SUCCESS);
 #endif
-  }
+    }
 #else
-  check(!pam);
+    check(!pam);
 #endif
+  }
 
   // Retrieve supplementary group ids.
   int ngroups;
@@ -908,12 +967,15 @@ static pam_handle_t *internalLogin(struct Service *service, struct Utmp *utmp,
 
   // Update utmp/wtmp entries
 #ifdef HAVE_UTMPX_H
-  memset(&utmp->utmpx.ut_user, 0, sizeof(utmp->utmpx.ut_user));
-  strncat(&utmp->utmpx.ut_user[0], service->user, sizeof(utmp->utmpx.ut_user));
-  setutxent();
-  pututxline(&utmp->utmpx);
-  endutxent();
-  updwtmpx("/var/log/wtmp", &utmp->utmpx);
+  if (service->authUser != 2 /* SSH */) {
+    memset(&utmp->utmpx.ut_user, 0, sizeof(utmp->utmpx.ut_user));
+    strncat(&utmp->utmpx.ut_user[0], service->user,
+            sizeof(utmp->utmpx.ut_user));
+    setutxent();
+    pututxline(&utmp->utmpx);
+    endutxent();
+    updwtmpx("/var/log/wtmp", &utmp->utmpx);
+  }
 #endif
 
   alarm(0);
@@ -1228,7 +1290,7 @@ static void childProcess(struct Service *service, int width, int height,
   }
 
   // Finally, launch the child process.
-  if (service->useLogin) {
+  if (service->useLogin == 1) {
     execle("/bin/login", "login", "-p", "-h", peerName,
            (void *)0, environment);
     execle("/usr/bin/login", "login", "-p", "-h", peerName,
index ae6b9ff1cf904823af1bc35c7cd1bd0ac8b698bd..d99ca5c732c6fecd570eed5875374ec4a13ee338 100644 (file)
@@ -100,6 +100,42 @@ void initService(struct Service *service, const char *arg) {
     check(service->cwd                      = strdup("/"));
     check(service->cmdline                  = strdup(
                                                   "/bin/login -p -h ${peer}"));
+  } else if (!strcmp(arg, "SSH") || !strncmp(arg, "SSH:", 4)) {
+    service->useLogin                       = 0;
+    service->useHomeDir                     = 0;
+    service->authUser                       = 2;
+    service->uid                            = -1;
+    service->gid                            = -1;
+    service->user                           = NULL;
+    service->group                          = NULL;
+    check(service->cwd                      = strdup("/"));
+    char *host;
+    check(host                              = strdup("localhost"));
+    if ((ptr                                = strchr(arg, ':')) != NULL) {
+      check(ptr                             = strdup(ptr + 1));
+      char *end;
+      if ((end                              = strchr(ptr, ':')) != NULL) {
+        *end                                = '\000';
+      }
+      if (*ptr) {
+        free(host);
+        host                                = ptr;
+      } else {
+        free(ptr);
+      }
+    }
+    service->cmdline                        = stringPrintf(NULL,
+      "ssh -a -e none -i /dev/null -x -oChallengeResponseAuthentication=no "
+          "-oCheckHostIP=no -oClearAllForwardings=yes -oCompression=no "
+          "-oControlMaster=no -oGSSAPIAuthentication=no "
+          "-oHostbasedAuthentication=no -oIdentitiesOnly=yes "
+          "-oKbdInteractiveAuthentication=yes -oPasswordAuthentication=yes "
+          "-oPreferredAuthentications=keyboard-interactive,password "
+          "-oPubkeyAuthentication=no -oRhostsRSAAuthentication=no "
+          "-oRSAAuthentication=no -oStrictHostKeyChecking=no -oTunnel=no "
+          "-oUserKnownHostsFile=/dev/null -oVerifyHostKeyDNS=no "
+          "-oVisualHostKey=no -oLogLevel=QUIET %%s@%s", host);
+    free(host);
   } else {
     service->useLogin                       = 0;
 
index 29bde7187e964bc4e43d65fa668de7aff3776d0f..abb26693842b66f1d515d1a6d8dd5b46eda9de04 100644 (file)
@@ -612,7 +612,8 @@ static void usage(void) {
           "be made available \n"
           "through the web interface:\n"
           "  SERVICE := <url-path> ':' APP\n"
-          "  APP     := 'LOGIN' | USER ':' CWD ':' <cmdline>\n"
+          "  APP     := 'LOGIN' | 'SSH' [ : <host> ] | "
+                        "USER ':' CWD ':' <cmdline>\n"
           "  USER    := %s<username> ':' <groupname>\n"
           "  CWD     := 'HOME' | <dir>\n"
           "\n"
@@ -880,7 +881,8 @@ static void parseArgs(int argc, char * const argv[]) {
 
   // If the user did not register any services, provide the default service
   if (!getHashmapSize(serviceTable)) {
-    addToHashMap(serviceTable, "/", (char *)newService(":LOGIN"));
+    addToHashMap(serviceTable, "/", (char *)newService(geteuid() ? ":SSH" :
+                                                                   ":LOGIN"));
   }
   enumerateServices(serviceTable);
   deleteHashMap(serviceTable);
This page took 0.051066 seconds and 5 git commands to generate.