Allow unprivileged users to run the daemon. This requires calling "ssh" instead of "login".
git-svn-id: https://shellinabox.googlecode.com/svn/trunk@154 0da03de8-d603-11dd-86c2-0f8696b7b6f9
This commit is contained in:
parent
218d901131
commit
0a834e6488
4 changed files with 197 additions and 92 deletions
|
@ -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
|
||||
|
|
|
@ -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,23 +756,78 @@ 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 (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 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 (runAsGroup >= 0) {
|
||||
service->gid = runAsGroup;
|
||||
} else {
|
||||
service->gid = getGroupId("nogroup");
|
||||
}
|
||||
}
|
||||
pw = getPWEnt(service->uid);
|
||||
if (restricted) {
|
||||
service->gid = pw->pw_gid;
|
||||
}
|
||||
service->user = getUserName(service->uid);
|
||||
service->group = getGroupName(service->gid);
|
||||
} else {
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
char *prompt;
|
||||
check(prompt = stringPrintf(NULL, "%s %s", uts.nodename,
|
||||
check(prompt = stringPrintf(NULL, "%s %s", hostname,
|
||||
origPrompt ? origPrompt : "login: "));
|
||||
check(pam_set_item(pam, PAM_USER_PROMPT, prompt) == PAM_SUCCESS);
|
||||
|
||||
|
@ -837,16 +891,20 @@ static pam_handle_t *internalLogin(struct Service *service, struct Utmp *utmp,
|
|||
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)
|
||||
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) {
|
||||
#ifdef HAVE_UTMPX_H
|
||||
|
@ -857,6 +915,7 @@ static pam_handle_t *internalLogin(struct Service *service, struct Utmp *utmp,
|
|||
#else
|
||||
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
|
||||
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));
|
||||
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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue