/* * Select the system's default password hashing scheme and generate * a stable fake salt under it for use by a non-existent account. * Prevents jpake method being used to infer the validity of accounts. */ static void fake_salt_and_scheme(Authctxt *authctxt, char **salt, char **scheme) { char *rounds_s, *style; long long rounds; login_cap_t *lc; if ((lc = login_getclass(authctxt->pw->pw_class)) == NULL && (lc = login_getclass(NULL)) == NULL) fatal("%s: login_getclass failed", __func__); style = login_getcapstr(lc, "localcipher", NULL, NULL); if (style == NULL) style = xstrdup("blowfish,6"); login_close(lc); if ((rounds_s = strchr(style, ',')) != NULL) *rounds_s++ = '\0'; rounds = strtonum(rounds_s, 1, 1<<31, NULL); if (strcmp(style, "md5") == 0) { xasprintf(salt, "$1$%s$", makesalt(8, authctxt->user)); *scheme = xstrdup("md5"); } else if (strcmp(style, "old") == 0) { *salt = xstrdup(makesalt(2, authctxt->user)); *scheme = xstrdup("crypt"); } else if (strcmp(style, "newsalt") == 0) { rounds = MAX(rounds, 7250); rounds = MIN(rounds, (1<<24) - 1); xasprintf(salt, "_%c%c%c%c%s", pw_encode64(rounds), pw_encode64(rounds >> 6), pw_encode64(rounds >> 12), pw_encode64(rounds >> 18), makesalt(4, authctxt->user)); *scheme = xstrdup("crypt-extended"); } else {
void print_passwd(char *string, int operation, void *extra) { char msalt[3], *salt, *cryptstr; login_cap_t *lc; int pwd_gensalt(char *, int, login_cap_t *, char); void to64(char *, u_int32_t, int n); switch(operation) { case DO_MAKEKEY: /* * makekey mode: parse string into separate DES key and salt. */ if (strlen(string) != 10) { /* To be compatible... */ errx(1, "%s", strerror(EFTYPE)); } strlcpy(msalt, &string[8], sizeof msalt); salt = msalt; break; case DO_MD5: strlcpy(buffer, "$1$", sizeof buffer); to64(&buffer[3], arc4random(), 4); to64(&buffer[7], arc4random(), 4); strlcpy(buffer + 11, "$", sizeof buffer - 11); salt = buffer; break; case DO_BLF: strlcpy(buffer, bcrypt_gensalt(*(int *)extra), _PASSWORD_LEN); salt = buffer; break; case DO_DES: salt = extra; break; default: if ((lc = login_getclass(extra)) == NULL) errx(1, "unable to get login class `%s'", extra ? (char *)extra : "default"); if (!pwd_gensalt(buffer, _PASSWORD_LEN, lc, 'l')) errx(1, "can't generate salt"); salt = buffer; break; } if ((cryptstr = crypt(string, salt)) == NULL) errx(1, "crypt failed"); fputs(cryptstr, stdout); }
struct passwd * getpwnamallow(const char *user) { #ifdef HAVE_LOGIN_CAP extern login_cap_t *lc; #ifdef BSD_AUTH auth_session_t *as; #endif #endif struct passwd *pw; pw = getpwnam(user); if (pw == NULL) { logit("Illegal user %.100s from %.100s", user, get_remote_ipaddr()); #ifdef CONFIG_AMAZON access__attempted(1, user); #endif #ifdef CUSTOM_FAILED_LOGIN record_failed_login(user, "ssh"); #endif return (NULL); } if (!allowed_user(pw)) { #ifdef CONFIG_AMAZON access__attempted(1, user); #endif return (NULL); } #ifdef HAVE_LOGIN_CAP if ((lc = login_getclass(pw->pw_class)) == NULL) { debug("unable to get login class: %s", user); #ifdef CONFIG_AMAZON access__attempted(1, user); #endif return (NULL); } #ifdef BSD_AUTH if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 || auth_approval(as, lc, pw->pw_name, "ssh") <= 0) { debug("Approval failure for %s", user); pw = NULL; } if (as != NULL) auth_close(as); #endif #endif if (pw != NULL) return (pwcopy(pw)); return (NULL); }
struct passwd * getpwnamallow(const char *user) { #ifdef HAVE_LOGIN_CAP extern login_cap_t *lc; #ifdef BSD_AUTH auth_session_t *as; #endif #endif struct passwd *pw; parse_server_match_config(&options, user, get_canonical_hostname(options.use_dns), get_remote_ipaddr()); pw = getpwnam(user); if (pw == NULL) { logit("Invalid user %.100s from %.100s", user, get_remote_ipaddr()); #ifdef CUSTOM_FAILED_LOGIN record_failed_login(user, get_canonical_hostname(options.use_dns), "ssh"); #endif #ifdef SSH_AUDIT_EVENTS audit_event(SSH_INVALID_USER); #endif /* SSH_AUDIT_EVENTS */ return (NULL); } if (!allowed_user(pw)) return (NULL); #ifdef HAVE_LOGIN_CAP if ((lc = login_getclass(pw->pw_class)) == NULL) { debug("unable to get login class: %s", user); return (NULL); } #ifdef BSD_AUTH if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 || auth_approval(as, lc, pw->pw_name, "ssh") <= 0) { debug("Approval failure for %s", user); pw = NULL; } if (as != NULL) auth_close(as); #endif #endif if (pw != NULL) return (pwcopy(pw)); return (NULL); }
struct passwd * getpwnamallow(const char *user) { #ifdef HAVE_LOGIN_CAP extern login_cap_t *lc; #ifdef BSD_AUTH auth_session_t *as; #endif #endif struct passwd *pw; pw = getpwnam(user); if (pw == NULL) { debug("Unknown user %.100s from %.100s", user, get_remote_ipaddr()); /* * Note, was ifdef CUSTOM_FAILED_LOGIN, but this is not an error for * us, as it may just be a mapped user name. This is also why the * above message is a debug() instead of a logit(). */ #if 0 && defined(CUSTOM_FAILED_LOGIN) record_failed_login(user, get_canonical_hostname(options.use_dns), "ssh"); #endif return (NULL); } if (!allowed_user(pw)) return (NULL); #ifdef HAVE_LOGIN_CAP if ((lc = login_getclass(pw->pw_class)) == NULL) { debug("unable to get login class: %s", user); return (NULL); } #ifdef BSD_AUTH if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 || auth_approval(as, lc, pw->pw_name, "ssh") <= 0) { debug("Approval failure for %s", user); pw = NULL; } if (as != NULL) auth_close(as); #endif #endif if (pw != NULL) return (pwcopy(pw)); return (NULL); }
struct passwd * getpwnamallow(const char *user) { #ifdef HAVE_LOGIN_CAP extern login_cap_t *lc; #ifdef BSD_AUTH auth_session_t *as; #endif #endif struct ssh *ssh = active_state; /* XXX */ struct passwd *pw; struct connection_info *ci = get_connection_info(1, options.use_dns); ci->user = user; parse_server_match_config(&options, ci); log_change_level(options.log_level); process_permitopen(ssh, &options); pw = getpwnam(user); if (pw == NULL) { pfilter_notify(1); logit("Invalid user %.100s from %.100s port %d", user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); return (NULL); } if (!allowed_user(pw)) return (NULL); #ifdef HAVE_LOGIN_CAP if ((lc = login_getclass(pw->pw_class)) == NULL) { debug("unable to get login class: %s", user); return (NULL); } #ifdef BSD_AUTH if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 || auth_approval(as, lc, pw->pw_name, "ssh") <= 0) { debug("Approval failure for %s", user); pw = NULL; } if (as != NULL) auth_close(as); #endif #endif if (pw != NULL) return (pwcopy(pw)); return (NULL); }
struct passwd * getpwnamallow(const char *user) { #ifdef HAVE_LOGIN_CAP extern login_cap_t *lc; #ifdef BSD_AUTH auth_session_t *as; #endif #endif struct passwd *pw; parse_server_match_config(&options, user, get_canonical_hostname(options.use_dns), get_remote_ipaddr()); #if defined(_AIX) && defined(HAVE_SETAUTHDB) aix_setauthdb(user); #endif pw = getpwnam(user); #if defined(_AIX) && defined(HAVE_SETAUTHDB) aix_restoreauthdb(); #endif #ifdef HAVE_CYGWIN /* * Windows usernames are case-insensitive. To avoid later problems * when trying to match the username, the user is only allowed to * login if the username is given in the same case as stored in the * user database. */ if (pw != NULL && strcmp(user, pw->pw_name) != 0) { logit("Login name %.100s does not match stored username %.100s", user, pw->pw_name); pw = NULL; } #endif if (pw == NULL) { logit("Invalid user %.100s from %.100s", user, get_remote_ipaddr()); #ifdef CUSTOM_FAILED_LOGIN record_failed_login(user, get_canonical_hostname(options.use_dns), "ssh"); #endif #ifdef SSH_AUDIT_EVENTS audit_event(SSH_INVALID_USER); #endif /* SSH_AUDIT_EVENTS */ return (NULL); } if (!allowed_user(pw)) return (NULL); #ifdef HAVE_LOGIN_CAP if ((lc = login_getclass(pw->pw_class)) == NULL) { debug("unable to get login class: %s", user); return (NULL); } #ifdef BSD_AUTH if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 || auth_approval(as, lc, pw->pw_name, "ssh") <= 0) { debug("Approval failure for %s", user); pw = NULL; } if (as != NULL) auth_close(as); #endif #endif if (pw != NULL) return (pwcopy(pw)); return (NULL); }
static void child_process(entry *e) { int stdin_pipe[2], stdout_pipe[2]; char * volatile input_data; char *homedir, *usernm, * volatile mailto; int children = 0; Debug(DPROC, ("[%ld] child_process('%s')\n", (long)getpid(), e->cmd)); setproctitle("running job"); /* discover some useful and important environment settings */ usernm = e->pwd->pw_name; mailto = env_get("MAILTO", e->envp); /* our parent is watching for our death by catching SIGCHLD. we * do not care to watch for our children's deaths this way -- we * use wait() explicitly. so we have to reset the signal (which * was inherited from the parent). */ (void) signal(SIGCHLD, SIG_DFL); /* create some pipes to talk to our future child */ if (pipe(stdin_pipe) == -1) /* child's stdin */ log_it("CRON", getpid(), "error", "create child stdin pipe"); if (pipe(stdout_pipe) == -1) /* child's stdout */ log_it("CRON", getpid(), "error", "create child stdout pipe"); /* since we are a forked process, we can diddle the command string * we were passed -- nobody else is going to use it again, right? * * if a % is present in the command, previous characters are the * command, and subsequent characters are the additional input to * the command. An escaped % will have the escape character stripped * from it. Subsequent %'s will be transformed into newlines, * but that happens later. */ /*local*/{ int escaped = FALSE; int ch; char *p; /* translation: * \% -> % * % -> end of command, following is command input. * \x -> \x for all x != % */ input_data = p = e->cmd; while ((ch = *input_data++) != '\0') { if (escaped) { if (ch != '%') *p++ = '\\'; } else { if (ch == '%') { break; } } if (!(escaped = (ch == '\\'))) { *p++ = ch; } } if (ch == '\0') { /* move pointer back, so that code below * won't think we encountered % sequence */ input_data--; } if (escaped) *p++ = '\\'; *p = '\0'; } /* fork again, this time so we can exec the user's command. */ switch (vfork()) { case -1: log_it("CRON", getpid(), "error", "can't vfork"); exit(ERROR_EXIT); /*NOTREACHED*/ case 0: Debug(DPROC, ("[%ld] grandchild process vfork()'ed\n", (long)getpid())); /* write a log message. we've waited this long to do it * because it was not until now that we knew the PID that * the actual user command shell was going to get and the * PID is part of the log message. */ if ((e->flags & DONT_LOG) == 0) { char *x = mkprints(e->cmd, strlen(e->cmd)); log_it(usernm, getpid(), "CMD START", x); free(x); } /* that's the last thing we'll log. close the log files. */ log_close(); /* get new pgrp, void tty, etc. */ if (setsid() == -1) syslog(LOG_ERR, "setsid() failure: %m"); /* close the pipe ends that we won't use. this doesn't affect * the parent, who has to read and write them; it keeps the * kernel from recording us as a potential client TWICE -- * which would keep it from sending SIGPIPE in otherwise * appropriate circumstances. */ (void)close(stdin_pipe[WRITE_PIPE]); (void)close(stdout_pipe[READ_PIPE]); /* grandchild process. make std{in,out} be the ends of * pipes opened by our daddy; make stderr go to stdout. */ if (stdin_pipe[READ_PIPE] != STDIN) { (void)dup2(stdin_pipe[READ_PIPE], STDIN); (void)close(stdin_pipe[READ_PIPE]); } if (stdout_pipe[WRITE_PIPE] != STDOUT) { (void)dup2(stdout_pipe[WRITE_PIPE], STDOUT); (void)close(stdout_pipe[WRITE_PIPE]); } (void)dup2(STDOUT, STDERR); /* set our directory, uid and gid. Set gid first, since once * we set uid, we've lost root privledges. */ #ifdef LOGIN_CAP { #ifdef BSD_AUTH auth_session_t *as; #endif login_cap_t *lc; char *p; if ((lc = login_getclass(e->pwd->pw_class)) == NULL) { warnx("unable to get login class for `%s'", e->pwd->pw_name); _exit(ERROR_EXIT); } if (setusercontext(lc, e->pwd, e->pwd->pw_uid, LOGIN_SETALL) < 0) { warnx("setusercontext failed for `%s'", e->pwd->pw_name); _exit(ERROR_EXIT); } #ifdef BSD_AUTH as = auth_open(); if (as == NULL || auth_setpwd(as, e->pwd) != 0) { warn("can't malloc"); _exit(ERROR_EXIT); } if (auth_approval(as, lc, usernm, "cron") <= 0) { warnx("approval failed for `%s'", e->pwd->pw_name); _exit(ERROR_EXIT); } auth_close(as); #endif /* BSD_AUTH */ login_close(lc); /* If no PATH specified in crontab file but * we just added one via login.conf, add it to * the crontab environment. */ if (env_get("PATH", e->envp) == NULL) { if ((p = getenv("PATH")) != NULL) e->envp = env_set(e->envp, p); } } #else if (setgid(e->pwd->pw_gid) != 0) { syslog(LOG_ERR, "setgid(%d) failed for %s: %m", e->pwd->pw_gid, e->pwd->pw_name); _exit(ERROR_EXIT); } if (initgroups(usernm, e->pwd->pw_gid) != 0) { syslog(LOG_ERR, "initgroups(%s, %d) failed for %s: %m", usernm, e->pwd->pw_gid, e->pwd->pw_name); _exit(ERROR_EXIT); } #if (defined(BSD)) && (BSD >= 199103) if (setlogin(usernm) < 0) { syslog(LOG_ERR, "setlogin(%s) failure for %s: %m", usernm, e->pwd->pw_name); _exit(ERROR_EXIT); } #endif /* BSD */ if (setuid(e->pwd->pw_uid) != 0) { syslog(LOG_ERR, "setuid(%d) failed for %s: %m", e->pwd->pw_uid, e->pwd->pw_name); _exit(ERROR_EXIT); } /* we aren't root after this... */ #endif /* LOGIN_CAP */ homedir = env_get("HOME", e->envp); if (chdir(homedir) != 0) { syslog(LOG_ERR, "chdir(%s) $HOME failed for %s: %m", homedir, e->pwd->pw_name); _exit(ERROR_EXIT); } #ifdef USE_SIGCHLD /* our grandparent is watching for our death by catching * SIGCHLD. the parent is ignoring SIGCHLD's; we want * to restore default behaviour. */ (void) signal(SIGCHLD, SIG_DFL); #endif (void) signal(SIGHUP, SIG_DFL); /* * Exec the command. */ { char *shell = env_get("SHELL", e->envp); # if DEBUGGING if (DebugFlags & DTEST) { (void)fprintf(stderr, "debug DTEST is on, not exec'ing command.\n"); (void)fprintf(stderr, "\tcmd='%s' shell='%s'\n", e->cmd, shell); _exit(OK_EXIT); } # endif /*DEBUGGING*/ (void)execle(shell, shell, "-c", e->cmd, NULL, e->envp); warn("execl: couldn't exec `%s'", shell); _exit(ERROR_EXIT); } break; default: /* parent process */ break; } children++; /* middle process, child of original cron, parent of process running * the user's command. */ Debug(DPROC, ("[%ld] child continues, closing pipes\n",(long)getpid())); /* close the ends of the pipe that will only be referenced in the * grandchild process... */ (void)close(stdin_pipe[READ_PIPE]); (void)close(stdout_pipe[WRITE_PIPE]); /* * write, to the pipe connected to child's stdin, any input specified * after a % in the crontab entry. while we copy, convert any * additional %'s to newlines. when done, if some characters were * written and the last one wasn't a newline, write a newline. * * Note that if the input data won't fit into one pipe buffer (2K * or 4K on most BSD systems), and the child doesn't read its stdin, * we would block here. thus we must fork again. */ if (*input_data && fork() == 0) { FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w"); int need_newline = FALSE; int escaped = FALSE; int ch; Debug(DPROC, ("[%ld] child2 sending data to grandchild\n", (long)getpid())); /* close the pipe we don't use, since we inherited it and * are part of its reference count now. */ (void)close(stdout_pipe[READ_PIPE]); /* translation: * \% -> % * % -> \n * \x -> \x for all x != % */ while ((ch = *input_data++) != '\0') { if (escaped) { if (ch != '%') (void)putc('\\', out); } else { if (ch == '%') ch = '\n'; } if (!(escaped = (ch == '\\'))) { (void)putc(ch, out); need_newline = (ch != '\n'); } } if (escaped) (void)putc('\\', out); if (need_newline) (void)putc('\n', out); /* close the pipe, causing an EOF condition. fclose causes * stdin_pipe[WRITE_PIPE] to be closed, too. */ (void)fclose(out); Debug(DPROC, ("[%ld] child2 done sending to grandchild\n", (long)getpid())); exit(0); } /* close the pipe to the grandkiddie's stdin, since its wicked uncle * ernie back there has it open and will close it when he's done. */ (void)close(stdin_pipe[WRITE_PIPE]); children++; /* * read output from the grandchild. it's stderr has been redirected to * it's stdout, which has been redirected to our pipe. if there is any * output, we'll be mailing it to the user whose crontab this is... * when the grandchild exits, we'll get EOF. */ Debug(DPROC, ("[%ld] child reading output from grandchild\n", (long)getpid())); /*local*/{ FILE *in = fdopen(stdout_pipe[READ_PIPE], "r"); int ch = getc(in); if (ch != EOF) { FILE *mail = NULL; int bytes = 1; int status = 0; Debug(DPROC|DEXT, ("[%ld] got data (%x:%c) from grandchild\n", (long)getpid(), ch, ch)); /* get name of recipient. this is MAILTO if set to a * valid local username; USER otherwise. */ if (mailto) { /* MAILTO was present in the environment */ if (!*mailto) { /* ... but it's empty. set to NULL */ mailto = NULL; } } else { /* MAILTO not present, set to USER. */ mailto = usernm; } /* if we are supposed to be mailing, MAILTO will * be non-NULL. only in this case should we set * up the mail command and subjects and stuff... */ if (mailto && safe_p(usernm, mailto)) { char **env; char mailcmd[MAX_COMMAND]; char hostname[MAXHOSTNAMELEN + 1]; (void)gethostname(hostname, MAXHOSTNAMELEN); if (strlens(MAILFMT, MAILARG, NULL) + 1 >= sizeof mailcmd) { warnx("mailcmd too long"); (void) _exit(ERROR_EXIT); } (void)snprintf(mailcmd, sizeof(mailcmd), MAILFMT, MAILARG); if (!(mail = cron_popen(mailcmd, "w", e->pwd))) { warn("cannot run `%s'", mailcmd); (void) _exit(ERROR_EXIT); } (void)fprintf(mail, "From: root (Cron Daemon)\n"); (void)fprintf(mail, "To: %s\n", mailto); (void)fprintf(mail, "Subject: Cron <%s@%s> %s\n", usernm, first_word(hostname, "."), e->cmd); (void)fprintf(mail, "Auto-Submitted: auto-generated\n"); #ifdef MAIL_DATE (void)fprintf(mail, "Date: %s\n", arpadate(&StartTime)); #endif /*MAIL_DATE*/ for (env = e->envp; *env; env++) (void)fprintf(mail, "X-Cron-Env: <%s>\n", *env); (void)fprintf(mail, "\n"); /* this was the first char from the pipe */ (void)putc(ch, mail); } /* we have to read the input pipe no matter whether * we mail or not, but obviously we only write to * mail pipe if we ARE mailing. */ while (EOF != (ch = getc(in))) { bytes++; if (mailto) (void)putc(ch, mail); } /* only close pipe if we opened it -- i.e., we're * mailing... */ if (mailto) { Debug(DPROC, ("[%ld] closing pipe to mail\n", (long)getpid())); /* Note: the pclose will probably see * the termination of the grandchild * in addition to the mail process, since * it (the grandchild) is likely to exit * after closing its stdout. */ status = cron_pclose(mail); } /* if there was output and we could not mail it, * log the facts so the poor user can figure out * what's going on. */ if (mailto && status) { char buf[MAX_TEMPSTR]; (void)snprintf(buf, sizeof(buf), "mailed %d byte%s of output but got status 0x%04x\n", bytes, (bytes==1)?"":"s", status); log_it(usernm, getpid(), "MAIL", buf); } } /*if data from grandchild*/ Debug(DPROC, ("[%ld] got EOF from grandchild\n", (long)getpid())); (void)fclose(in); /* also closes stdout_pipe[READ_PIPE] */ } /* wait for children to die. */ for (; children > 0; children--) { WAIT_T waiter; PID_T pid; Debug(DPROC, ("[%ld] waiting for grandchild #%d to finish\n", (long)getpid(), children)); while ((pid = wait(&waiter)) < OK && errno == EINTR) ; if (pid < OK) { Debug(DPROC, ("[%ld] no more grandchildren--mail written?\n", (long)getpid())); break; } Debug(DPROC, ("[%ld] grandchild #%ld finished, status=%04x", (long)getpid(), (long)pid, WEXITSTATUS(waiter))); if (WIFSIGNALED(waiter) && WCOREDUMP(waiter)) Debug(DPROC, (", dumped core")); Debug(DPROC, ("\n")); } /* Log the time when we finished deadling with the job */ /*local*/{ char *x = mkprints(e->cmd, strlen(e->cmd)); log_it(usernm, getpid(), "CMD FINISH", x); free(x); } }
/* * User shell for authenticating gateways. Sole purpose is to allow * a user to ssh to a gateway, and have the gateway modify packet * filters to allow access, then remove access when the user finishes * up. Meant to be used only from ssh(1) connections. */ int main(void) { int lockcnt = 0, n; FILE *config; struct in6_addr ina; struct passwd *pw; char *cp; gid_t gid; uid_t uid; const char *shell; login_cap_t *lc; if (strcmp(__progname, "-authpf-noip") == 0) user_ip = 0; config = fopen(PATH_CONFFILE, "r"); if (config == NULL) { syslog(LOG_ERR, "cannot open %s (%m)", PATH_CONFFILE); exit(1); } if ((cp = getenv("SSH_TTY")) == NULL) { syslog(LOG_ERR, "non-interactive session connection for authpf"); exit(1); } if ((cp = getenv("SSH_CLIENT")) == NULL) { syslog(LOG_ERR, "cannot determine connection source"); exit(1); } if (strlcpy(ipsrc, cp, sizeof(ipsrc)) >= sizeof(ipsrc)) { syslog(LOG_ERR, "SSH_CLIENT variable too long"); exit(1); } cp = strchr(ipsrc, ' '); if (!cp) { syslog(LOG_ERR, "corrupt SSH_CLIENT variable %s", ipsrc); exit(1); } *cp = '\0'; if (inet_pton(AF_INET, ipsrc, &ina) != 1 && inet_pton(AF_INET6, ipsrc, &ina) != 1) { syslog(LOG_ERR, "cannot determine IP from SSH_CLIENT %s", ipsrc); exit(1); } /* open the pf device */ dev = open(PATH_DEVFILE, O_RDWR); if (dev == -1) { syslog(LOG_ERR, "cannot open packet filter device (%m)"); goto die; } uid = getuid(); pw = getpwuid(uid); if (pw == NULL) { syslog(LOG_ERR, "cannot find user for uid %u", uid); goto die; } if ((lc = login_getclass(pw->pw_class)) != NULL) shell = login_getcapstr(lc, "shell", pw->pw_shell, pw->pw_shell); else shell = pw->pw_shell; #ifndef __FreeBSD__ login_close(lc); #endif if (strcmp(shell, PATH_AUTHPF_SHELL) && strcmp(shell, PATH_AUTHPF_SHELL_NOIP)) { syslog(LOG_ERR, "wrong shell for user %s, uid %u", pw->pw_name, pw->pw_uid); #ifdef __FreeBSD__ login_close(lc); #else if (shell != pw->pw_shell) free(shell); #endif goto die; } #ifdef __FreeBSD__ login_close(lc); #else if (shell != pw->pw_shell) free(shell); #endif /* * Paranoia, but this data _does_ come from outside authpf, and * truncation would be bad. */ if (strlcpy(luser, pw->pw_name, sizeof(luser)) >= sizeof(luser)) { syslog(LOG_ERR, "username too long: %s", pw->pw_name); goto die; } if ((n = snprintf(rulesetname, sizeof(rulesetname), "%s(%ld)", luser, (long)getpid())) < 0 || (u_int)n >= sizeof(rulesetname)) { syslog(LOG_INFO, "%s(%ld) too large, ruleset name will be %ld", luser, (long)getpid(), (long)getpid()); if ((n = snprintf(rulesetname, sizeof(rulesetname), "%ld", (long)getpid())) < 0 || (u_int)n >= sizeof(rulesetname)) { syslog(LOG_ERR, "pid too large for ruleset name"); goto die; } } /* Make our entry in /var/authpf as ipaddr or username */ n = snprintf(pidfile, sizeof(pidfile), "%s/%s", PATH_PIDFILE, user_ip ? ipsrc : luser); if (n < 0 || (u_int)n >= sizeof(pidfile)) { syslog(LOG_ERR, "path to pidfile too long"); goto die; } signal(SIGTERM, need_death); signal(SIGINT, need_death); signal(SIGALRM, need_death); signal(SIGPIPE, need_death); signal(SIGHUP, need_death); signal(SIGQUIT, need_death); signal(SIGTSTP, need_death); /* * If someone else is already using this ip, then this person * wants to switch users - so kill the old process and exit * as well. * * Note, we could print a message and tell them to log out, but the * usual case of this is that someone has left themselves logged in, * with the authenticated connection iconized and someone else walks * up to use and automatically logs in before using. If this just * gets rid of the old one silently, the new user never knows they * could have used someone else's old authentication. If we * tell them to log out before switching users it is an invitation * for abuse. */ do { int save_errno, otherpid = -1; char otherluser[MAXLOGNAME]; if ((pidfd = open(pidfile, O_RDWR|O_CREAT, 0644)) == -1 || (pidfp = fdopen(pidfd, "r+")) == NULL) { if (pidfd != -1) close(pidfd); syslog(LOG_ERR, "cannot open or create %s: %s", pidfile, strerror(errno)); goto die; } if (flock(fileno(pidfp), LOCK_EX|LOCK_NB) == 0) break; save_errno = errno; /* Mark our pid, and username to our file. */ rewind(pidfp); /* 31 == MAXLOGNAME - 1 */ if (fscanf(pidfp, "%d\n%31s\n", &otherpid, otherluser) != 2) otherpid = -1; syslog(LOG_DEBUG, "tried to lock %s, in use by pid %d: %s", pidfile, otherpid, strerror(save_errno)); if (otherpid > 0) { syslog(LOG_INFO, "killing prior auth (pid %d) of %s by user %s", otherpid, ipsrc, otherluser); if (kill((pid_t) otherpid, SIGTERM) == -1) { syslog(LOG_INFO, "could not kill process %d: (%m)", otherpid); } } /* * We try to kill the previous process and acquire the lock * for 10 seconds, trying once a second. if we can't after * 10 attempts we log an error and give up. */ if (want_death || ++lockcnt > 10) { if (!want_death) syslog(LOG_ERR, "cannot kill previous authpf (pid %d)", otherpid); fclose(pidfp); pidfp = NULL; pidfd = -1; goto dogdeath; } sleep(1); /* re-open, and try again. The previous authpf process * we killed above should unlink the file and release * it's lock, giving us a chance to get it now */ fclose(pidfp); pidfp = NULL; pidfd = -1; } while (1); /* whack the group list */ gid = getegid(); if (setgroups(1, &gid) == -1) { syslog(LOG_INFO, "setgroups: %s", strerror(errno)); do_death(0); } /* revoke privs */ uid = getuid(); if (setresuid(uid, uid, uid) == -1) { syslog(LOG_INFO, "setresuid: %s", strerror(errno)); do_death(0); } openlog("authpf", LOG_PID | LOG_NDELAY, LOG_DAEMON); if (!check_luser(PATH_BAN_DIR, luser) || !allowed_luser(pw)) { syslog(LOG_INFO, "user %s prohibited", luser); do_death(0); } if (read_config(config)) { syslog(LOG_ERR, "invalid config file %s", PATH_CONFFILE); do_death(0); } if (remove_stale_rulesets()) { syslog(LOG_INFO, "error removing stale rulesets"); do_death(0); } /* We appear to be making headway, so actually mark our pid */ rewind(pidfp); fprintf(pidfp, "%ld\n%s\n", (long)getpid(), luser); fflush(pidfp); (void) ftruncate(fileno(pidfp), ftello(pidfp)); if (change_filter(1, luser, ipsrc) == -1) { printf("Unable to modify filters\r\n"); do_death(0); } if (user_ip && change_table(1, ipsrc) == -1) { printf("Unable to modify table\r\n"); change_filter(0, luser, ipsrc); do_death(0); } while (1) { printf("\r\nHello %s. ", luser); printf("You are authenticated from host \"%s\"\r\n", ipsrc); setproctitle("%s@%s", luser, ipsrc); print_message(PATH_MESSAGE); while (1) { sleep(10); if (want_death) do_death(1); } } /* NOTREACHED */ dogdeath: printf("\r\n\r\nSorry, this service is currently unavailable due to "); printf("technical difficulties\r\n\r\n"); print_message(PATH_PROBLEM); printf("\r\nYour authentication process (pid %ld) was unable to run\n", (long)getpid()); sleep(180); /* them lusers read reaaaaal slow */ die: do_death(0); }
int main(int argc, char *argv[]) { struct group *gr; struct stat st; int ask, ch, cnt, fflag, hflag, pflag, sflag, quietlog, rootlogin, rval; uid_t uid, saved_uid; gid_t saved_gid, saved_gids[NGROUPS_MAX]; int nsaved_gids; #ifdef notdef char *domain; #endif char *p, *ttyn; const char *pwprompt; char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10]; char localhost[MAXHOSTNAMELEN + 1]; int need_chpass, require_chpass; int login_retries = DEFAULT_RETRIES, login_backoff = DEFAULT_BACKOFF; time_t pw_warntime = _PASSWORD_WARNDAYS * SECSPERDAY; char *loginname = NULL; #ifdef KERBEROS5 int Fflag; krb5_error_code kerror; #endif #if defined(KERBEROS5) int got_tickets = 0; #endif #ifdef LOGIN_CAP char *shell = NULL; login_cap_t *lc = NULL; #endif tbuf[0] = '\0'; rval = 0; pwprompt = NULL; nested = NULL; need_chpass = require_chpass = 0; (void)signal(SIGALRM, timedout); (void)alarm(timeout); (void)signal(SIGQUIT, SIG_IGN); (void)signal(SIGINT, SIG_IGN); (void)setpriority(PRIO_PROCESS, 0, 0); openlog("login", 0, LOG_AUTH); /* * -p is used by getty to tell login not to destroy the environment * -f is used to skip a second login authentication * -h is used by other servers to pass the name of the remote host to * login so that it may be placed in utmp/utmpx and wtmp/wtmpx * -a in addition to -h, a server may supply -a to pass the actual * server address. * -s is used to force use of S/Key or equivalent. */ if (gethostname(localhost, sizeof(localhost)) < 0) { syslog(LOG_ERR, "couldn't get local hostname: %m"); strcpy(hostname, "amnesiac"); } #ifdef notdef domain = strchr(localhost, '.'); #endif localhost[sizeof(localhost) - 1] = '\0'; fflag = hflag = pflag = sflag = 0; have_ss = 0; #ifdef KERBEROS5 Fflag = 0; have_forward = 0; #endif uid = getuid(); while ((ch = getopt(argc, argv, "a:Ffh:ps")) != -1) switch (ch) { case 'a': if (uid) errx(EXIT_FAILURE, "-a option: %s", strerror(EPERM)); decode_ss(optarg); #ifdef notdef (void)sockaddr_snprintf(optarg, sizeof(struct sockaddr_storage), "%a", (void *)&ss); #endif break; case 'F': #ifdef KERBEROS5 Fflag = 1; #endif /* FALLTHROUGH */ case 'f': fflag = 1; break; case 'h': if (uid) errx(EXIT_FAILURE, "-h option: %s", strerror(EPERM)); hflag = 1; #ifdef notdef if (domain && (p = strchr(optarg, '.')) != NULL && strcasecmp(p, domain) == 0) *p = '\0'; #endif hostname = optarg; break; case 'p': pflag = 1; break; case 's': sflag = 1; break; default: case '?': usage(); break; } setproctitle(NULL); argc -= optind; argv += optind; if (*argv) { username = loginname = *argv; ask = 0; } else ask = 1; #ifdef F_CLOSEM (void)fcntl(3, F_CLOSEM, 0); #else for (cnt = getdtablesize(); cnt > 2; cnt--) (void)close(cnt); #endif ttyn = ttyname(STDIN_FILENO); if (ttyn == NULL || *ttyn == '\0') { (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY); ttyn = tname; } if ((tty = strstr(ttyn, "/pts/")) != NULL) ++tty; else if ((tty = strrchr(ttyn, '/')) != NULL) ++tty; else tty = ttyn; if (issetugid()) { nested = strdup(user_from_uid(getuid(), 0)); if (nested == NULL) { syslog(LOG_ERR, "strdup: %m"); sleepexit(EXIT_FAILURE); } } #ifdef LOGIN_CAP /* Get "login-retries" and "login-backoff" from default class */ if ((lc = login_getclass(NULL)) != NULL) { login_retries = (int)login_getcapnum(lc, "login-retries", DEFAULT_RETRIES, DEFAULT_RETRIES); login_backoff = (int)login_getcapnum(lc, "login-backoff", DEFAULT_BACKOFF, DEFAULT_BACKOFF); login_close(lc); lc = NULL; } #endif #ifdef KERBEROS5 kerror = krb5_init_context(&kcontext); if (kerror) { /* * If Kerberos is not configured, that is, we are * not using Kerberos, do not log the error message. * However, if Kerberos is configured, and the * context init fails for some other reason, we need * to issue a no tickets warning to the user when the * login succeeds. */ if (kerror != ENXIO) { /* XXX NetBSD-local Heimdal hack */ syslog(LOG_NOTICE, "%s when initializing Kerberos context", error_message(kerror)); krb5_configured = 1; } login_krb5_get_tickets = 0; } #endif /* KERBEROS5 */ for (cnt = 0;; ask = 1) { #if defined(KERBEROS5) if (login_krb5_get_tickets) k5destroy(); #endif if (ask) { fflag = 0; loginname = getloginname(); } rootlogin = 0; #ifdef KERBEROS5 if ((instance = strchr(loginname, '/')) != NULL) *instance++ = '\0'; else instance = __UNCONST(""); #endif username = trimloginname(loginname); /* * Note if trying multiple user names; log failures for * previous user name, but don't bother logging one failure * for nonexistent name (mistyped username). */ if (failures && strcmp(tbuf, username)) { if (failures > (pwd ? 0 : 1)) badlogin(tbuf); failures = 0; } (void)strlcpy(tbuf, username, sizeof(tbuf)); pwd = getpwnam(username); #ifdef LOGIN_CAP /* * Establish the class now, before we might goto * within the next block. pwd can be NULL since it * falls back to the "default" class if it is. */ lc = login_getclass(pwd ? pwd->pw_class : NULL); #endif /* * if we have a valid account name, and it doesn't have a * password, or the -f option was specified and the caller * is root or the caller isn't changing their uid, don't * authenticate. */ if (pwd) { if (pwd->pw_uid == 0) rootlogin = 1; if (fflag && (uid == 0 || uid == pwd->pw_uid)) { /* already authenticated */ #ifdef KERBEROS5 if (login_krb5_get_tickets && Fflag) k5_read_creds(username); #endif break; } else if (pwd->pw_passwd[0] == '\0') { /* pretend password okay */ rval = 0; goto ttycheck; } } fflag = 0; (void)setpriority(PRIO_PROCESS, 0, -4); #ifdef SKEY if (skey_haskey(username) == 0) { static char skprompt[80]; const char *skinfo = skey_keyinfo(username); (void)snprintf(skprompt, sizeof(skprompt), "Password [ %s ]:", skinfo ? skinfo : "error getting challenge"); pwprompt = skprompt; } else #endif pwprompt = "Password:"******"Login incorrect or refused on this " "terminal.\n"); if (hostname) syslog(LOG_NOTICE, "LOGIN %s REFUSED FROM %s ON TTY %s", pwd->pw_name, hostname, tty); else syslog(LOG_NOTICE, "LOGIN %s REFUSED ON TTY %s", pwd->pw_name, tty); continue; } if (pwd && !rval) break; (void)printf("Login incorrect or refused on this " "terminal.\n"); failures++; cnt++; /* * We allow login_retries tries, but after login_backoff * we start backing off. These default to 10 and 3 * respectively. */ if (cnt > login_backoff) { if (cnt >= login_retries) { badlogin(username); sleepexit(EXIT_FAILURE); } sleep((u_int)((cnt - login_backoff) * 5)); } } /* committed to login -- turn off timeout */ (void)alarm((u_int)0); endpwent(); /* if user not super-user, check for disabled logins */ #ifdef LOGIN_CAP if (!login_getcapbool(lc, "ignorenologin", rootlogin)) checknologin(login_getcapstr(lc, "nologin", NULL, NULL)); #else if (!rootlogin) checknologin(NULL); #endif #ifdef LOGIN_CAP quietlog = login_getcapbool(lc, "hushlogin", 0); #else quietlog = 0; #endif /* Temporarily give up special privileges so we can change */ /* into NFS-mounted homes that are exported for non-root */ /* access and have mode 7x0 */ saved_uid = geteuid(); saved_gid = getegid(); nsaved_gids = getgroups(NGROUPS_MAX, saved_gids); (void)setegid(pwd->pw_gid); initgroups(username, pwd->pw_gid); (void)seteuid(pwd->pw_uid); if (chdir(pwd->pw_dir) < 0) { #ifdef LOGIN_CAP if (login_getcapbool(lc, "requirehome", 0)) { (void)printf("Home directory %s required\n", pwd->pw_dir); sleepexit(EXIT_FAILURE); } #endif (void)printf("No home directory %s!\n", pwd->pw_dir); if (chdir("/") == -1) exit(EXIT_FAILURE); pwd->pw_dir = __UNCONST("/"); (void)printf("Logging in with home = \"/\".\n"); } if (!quietlog) quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0; /* regain special privileges */ (void)seteuid(saved_uid); setgroups(nsaved_gids, saved_gids); (void)setegid(saved_gid); #ifdef LOGIN_CAP pw_warntime = login_getcaptime(lc, "password-warn", _PASSWORD_WARNDAYS * SECSPERDAY, _PASSWORD_WARNDAYS * SECSPERDAY); #endif (void)gettimeofday(&now, NULL); if (pwd->pw_expire) { if (now.tv_sec >= pwd->pw_expire) { (void)printf("Sorry -- your account has expired.\n"); sleepexit(EXIT_FAILURE); } else if (pwd->pw_expire - now.tv_sec < pw_warntime && !quietlog) (void)printf("Warning: your account expires on %s", ctime(&pwd->pw_expire)); } if (pwd->pw_change) { if (pwd->pw_change == _PASSWORD_CHGNOW) need_chpass = 1; else if (now.tv_sec >= pwd->pw_change) { (void)printf("Sorry -- your password has expired.\n"); sleepexit(EXIT_FAILURE); } else if (pwd->pw_change - now.tv_sec < pw_warntime && !quietlog) (void)printf("Warning: your password expires on %s", ctime(&pwd->pw_change)); } /* Nothing else left to fail -- really log in. */ update_db(quietlog, rootlogin, fflag); (void)chown(ttyn, pwd->pw_uid, (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); if (ttyaction(ttyn, "login", pwd->pw_name)) (void)printf("Warning: ttyaction failed.\n"); #if defined(KERBEROS5) /* Fork so that we can call kdestroy */ if (! login_krb5_retain_ccache && has_ccache) dofork(); #endif /* Destroy environment unless user has requested its preservation. */ if (!pflag) environ = envinit; #ifdef LOGIN_CAP if (nested == NULL && setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETLOGIN) != 0) { syslog(LOG_ERR, "setusercontext failed"); exit(EXIT_FAILURE); } if (setusercontext(lc, pwd, pwd->pw_uid, (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETLOGIN))) != 0) { syslog(LOG_ERR, "setusercontext failed"); exit(EXIT_FAILURE); } #else (void)setgid(pwd->pw_gid); initgroups(username, pwd->pw_gid); if (nested == NULL && setlogin(pwd->pw_name) < 0) syslog(LOG_ERR, "setlogin() failure: %m"); /* Discard permissions last so can't get killed and drop core. */ if (rootlogin) (void)setuid(0); else (void)setuid(pwd->pw_uid); #endif if (*pwd->pw_shell == '\0') pwd->pw_shell = __UNCONST(_PATH_BSHELL); #ifdef LOGIN_CAP if ((shell = login_getcapstr(lc, "shell", NULL, NULL)) != NULL) { if ((shell = strdup(shell)) == NULL) { syslog(LOG_ERR, "Cannot alloc mem"); sleepexit(EXIT_FAILURE); } pwd->pw_shell = shell; } #endif (void)setenv("HOME", pwd->pw_dir, 1); (void)setenv("SHELL", pwd->pw_shell, 1); if (term[0] == '\0') { const char *tt = stypeof(tty); #ifdef LOGIN_CAP if (tt == NULL) tt = login_getcapstr(lc, "term", NULL, NULL); #endif /* unknown term -> "su" */ (void)strlcpy(term, tt != NULL ? tt : "su", sizeof(term)); } (void)setenv("TERM", term, 0); (void)setenv("LOGNAME", pwd->pw_name, 1); (void)setenv("USER", pwd->pw_name, 1); #ifdef LOGIN_CAP setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH); #else (void)setenv("PATH", _PATH_DEFPATH, 0); #endif #ifdef KERBEROS5 if (krb5tkfile_env) (void)setenv("KRB5CCNAME", krb5tkfile_env, 1); #endif /* If fflag is on, assume caller/authenticator has logged root login. */ if (rootlogin && fflag == 0) { if (hostname) syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s", username, tty, hostname); else syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty); } #if defined(KERBEROS5) if (KERBEROS_CONFIGURED && !quietlog && notickets == 1) (void)printf("Warning: no Kerberos tickets issued.\n"); #endif if (!quietlog) { const char *fname; #ifdef LOGIN_CAP fname = login_getcapstr(lc, "copyright", NULL, NULL); if (fname != NULL && access(fname, F_OK) == 0) motd(fname); else #endif (void)printf("%s", copyrightstr); #ifdef LOGIN_CAP fname = login_getcapstr(lc, "welcome", NULL, NULL); if (fname == NULL || access(fname, F_OK) != 0) #endif fname = _PATH_MOTDFILE; motd(fname); (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name); if (stat(tbuf, &st) == 0 && st.st_size != 0) (void)printf("You have %smail.\n", (st.st_mtime > st.st_atime) ? "new " : ""); } #ifdef LOGIN_CAP login_close(lc); #endif (void)signal(SIGALRM, SIG_DFL); (void)signal(SIGQUIT, SIG_DFL); (void)signal(SIGINT, SIG_DFL); (void)signal(SIGTSTP, SIG_IGN); tbuf[0] = '-'; (void)strlcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ? p + 1 : pwd->pw_shell, sizeof(tbuf) - 1); /* Wait to change password until we're unprivileged */ if (need_chpass) { if (!require_chpass) (void)printf( "Warning: your password has expired. Please change it as soon as possible.\n"); else { int status; (void)printf( "Your password has expired. Please choose a new one.\n"); switch (fork()) { case -1: warn("fork"); sleepexit(EXIT_FAILURE); case 0: execl(_PATH_BINPASSWD, "passwd", NULL); _exit(EXIT_FAILURE); default: if (wait(&status) == -1 || WEXITSTATUS(status)) sleepexit(EXIT_FAILURE); } } } #ifdef KERBEROS5 if (login_krb5_get_tickets) k5_write_creds(); #endif execlp(pwd->pw_shell, tbuf, NULL); err(EXIT_FAILURE, "%s", pwd->pw_shell); }
_X_INTERNAL int Verify (struct display *d, struct greet_info *greet, struct verify_info *verify) { struct passwd *p; login_cap_t *lc; auth_session_t *as; char *style, *shell, *home, *s, **argv; char path[MAXPATHLEN]; int authok; size_t passwd_len; /* User may have specified an authentication style. */ if ((style = strchr(greet->name, ':')) != NULL) *style++ = '\0'; Debug ("Verify %s, style %s ...\n", greet->name, style ? style : "default"); p = getpwnam (greet->name); if (!p || strlen (greet->name) == 0) { Debug("getpwnam() failed.\n"); explicit_bzero(greet->password, strlen(greet->password)); return 0; } if ((lc = login_getclass(p->pw_class)) == NULL) { Debug("login_getclass() failed.\n"); explicit_bzero(greet->password, strlen(greet->password)); return 0; } if ((style = login_getstyle(lc, style, "xdm")) == NULL) { Debug("login_getstyle() failed.\n"); explicit_bzero(greet->password, strlen(greet->password)); return 0; } if ((as = auth_open()) == NULL) { Debug("auth_open() failed.\n"); login_close(lc); explicit_bzero(greet->password, strlen(greet->password)); return 0; } if (auth_setoption(as, "login", "yes") == -1) { Debug("auth_setoption() failed.\n"); login_close(lc); explicit_bzero(greet->password, strlen(greet->password)); return 0; } passwd_len = strlen(greet->password); /* Set up state for no challenge, just check a response. */ auth_setstate(as, 0); auth_setdata(as, "", 1); auth_setdata(as, greet->password, passwd_len + 1); /* wipe password now, otherwise it'll be copied fork() in auth_call */ explicit_bzero(greet->password, passwd_len); /* Build path of the auth script and call it */ snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style); auth_call(as, path, style, "-s", "response", greet->name, lc->lc_class, (void *)NULL); authok = auth_getstate(as); if ((authok & AUTH_ALLOW) == 0) { Debug("password verify failed\n"); auth_close(as); login_close(lc); return 0; } /* Run the approval script */ if (!auth_approval(as, lc, greet->name, "auth-xdm")) { Debug("login not approved\n"); auth_close(as); login_close(lc); return 0; } auth_close(as); login_close(lc); /* Check empty passwords against allowNullPasswd */ if (!greet->allow_null_passwd && passwd_len == 0) { Debug("empty password not allowed\n"); return 0; } /* Only accept root logins if allowRootLogin resource is set */ if (p->pw_uid == 0 && !greet->allow_root_login) { Debug("root logins not allowed\n"); return 0; } /* * Shell must be in /etc/shells */ for (;;) { s = getusershell(); if (s == NULL) { /* did not found the shell in /etc/shells -> failure */ Debug("shell not in /etc/shells\n"); endusershell(); return 0; } if (strcmp(s, p->pw_shell) == 0) { /* found the shell in /etc/shells */ endusershell(); break; } } Debug ("verify succeeded\n"); verify->uid = p->pw_uid; verify->gid = p->pw_gid; home = p->pw_dir; shell = p->pw_shell; argv = NULL; if (d->session) argv = parseArgs (argv, d->session); if (greet->string) argv = parseArgs (argv, greet->string); if (!argv) argv = parseArgs (argv, "xsession"); verify->argv = argv; verify->userEnviron = userEnv (d, p->pw_uid == 0, greet->name, home, shell); Debug ("user environment:\n"); printEnv (verify->userEnviron); verify->systemEnviron = systemEnv (d, greet->name, home); Debug ("system environment:\n"); printEnv (verify->systemEnviron); Debug ("end of environments\n"); return 1; }
int main(int argc, char **argv) { struct group *gr; struct stat st; int retries, backoff; int ask, ch, cnt, quietlog, rootlogin, rval; uid_t uid, euid; gid_t egid; char *term; char *p, *ttyn; char tname[sizeof(_PATH_TTY) + 10]; char *arg0; const char *tp; const char *shell = NULL; login_cap_t *lc = NULL; login_cap_t *lc_user = NULL; pid_t pid; #ifdef USE_BSM_AUDIT char auditsuccess = 1; #endif signal(SIGQUIT, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); if (setjmp(timeout_buf)) { if (failures) badlogin(username); fprintf(stderr, "Login timed out after %d seconds\n", timeout); bail(NO_SLEEP_EXIT, 0); } signal(SIGALRM, timedout); alarm(timeout); setpriority(PRIO_PROCESS, 0, 0); openlog("login", LOG_ODELAY, LOG_AUTH); uid = getuid(); euid = geteuid(); egid = getegid(); while ((ch = getopt(argc, argv, "fh:p")) != -1) switch (ch) { case 'f': fflag = 1; break; case 'h': if (uid != 0) errx(1, "-h option: %s", strerror(EPERM)); if (strlen(optarg) >= MAXHOSTNAMELEN) errx(1, "-h option: %s: exceeds maximum " "hostname size", optarg); hflag = 1; hostname = optarg; break; case 'p': pflag = 1; break; case '?': default: if (uid == 0) syslog(LOG_ERR, "invalid flag %c", ch); usage(); } argc -= optind; argv += optind; if (argc > 0) { username = strdup(*argv); if (username == NULL) err(1, "strdup()"); ask = 0; } else { ask = 1; } setproctitle("-%s", getprogname()); for (cnt = getdtablesize(); cnt > 2; cnt--) close(cnt); /* * Get current TTY */ ttyn = ttyname(STDIN_FILENO); if (ttyn == NULL || *ttyn == '\0') { snprintf(tname, sizeof(tname), "%s??", _PATH_TTY); ttyn = tname; } if (strncmp(ttyn, _PATH_DEV, sizeof(_PATH_DEV) -1) == 0) tty = ttyn + sizeof(_PATH_DEV) -1; else tty = ttyn; /* * Get "login-retries" & "login-backoff" from default class */ lc = login_getclass(NULL); prompt = login_getcapstr(lc, "login_prompt", default_prompt, default_prompt); passwd_prompt = login_getcapstr(lc, "passwd_prompt", default_passwd_prompt, default_passwd_prompt); retries = login_getcapnum(lc, "login-retries", DEFAULT_RETRIES, DEFAULT_RETRIES); backoff = login_getcapnum(lc, "login-backoff", DEFAULT_BACKOFF, DEFAULT_BACKOFF); login_close(lc); lc = NULL; /* * Try to authenticate the user until we succeed or time out. */ for (cnt = 0;; ask = 1) { if (ask) { fflag = 0; if (olduser != NULL) free(olduser); olduser = username; username = getloginname(); } rootlogin = 0; /* * Note if trying multiple user names; log failures for * previous user name, but don't bother logging one failure * for nonexistent name (mistyped username). */ if (failures && strcmp(olduser, username) != 0) { if (failures > (pwd ? 0 : 1)) badlogin(olduser); } /* * Load the PAM policy and set some variables */ pam_err = pam_start("login", username, &pamc, &pamh); if (pam_err != PAM_SUCCESS) { pam_syslog("pam_start()"); #ifdef USE_BSM_AUDIT au_login_fail("PAM Error", 1); #endif bail(NO_SLEEP_EXIT, 1); } pam_err = pam_set_item(pamh, PAM_TTY, tty); if (pam_err != PAM_SUCCESS) { pam_syslog("pam_set_item(PAM_TTY)"); #ifdef USE_BSM_AUDIT au_login_fail("PAM Error", 1); #endif bail(NO_SLEEP_EXIT, 1); } pam_err = pam_set_item(pamh, PAM_RHOST, hostname); if (pam_err != PAM_SUCCESS) { pam_syslog("pam_set_item(PAM_RHOST)"); #ifdef USE_BSM_AUDIT au_login_fail("PAM Error", 1); #endif bail(NO_SLEEP_EXIT, 1); } pwd = getpwnam(username); if (pwd != NULL && pwd->pw_uid == 0) rootlogin = 1; /* * If the -f option was specified and the caller is * root or the caller isn't changing their uid, don't * authenticate. */ if (pwd != NULL && fflag && (uid == (uid_t)0 || uid == (uid_t)pwd->pw_uid)) { /* already authenticated */ rval = 0; #ifdef USE_BSM_AUDIT auditsuccess = 0; /* opened a terminal window only */ #endif } else { fflag = 0; setpriority(PRIO_PROCESS, 0, -4); rval = auth_pam(); setpriority(PRIO_PROCESS, 0, 0); } if (pwd && rval == 0) break; pam_cleanup(); /* * We are not exiting here, but this corresponds to a failed * login event, so set exitstatus to 1. */ #ifdef USE_BSM_AUDIT au_login_fail("Login incorrect", 1); #endif printf("Login incorrect\n"); failures++; pwd = NULL; /* * Allow up to 'retry' (10) attempts, but start * backing off after 'backoff' (3) attempts. */ if (++cnt > backoff) { if (cnt >= retries) { badlogin(username); bail(SLEEP_EXIT, 1); } sleep((u_int)((cnt - backoff) * 5)); } } /* committed to login -- turn off timeout */ alarm((u_int)0); signal(SIGHUP, SIG_DFL); endpwent(); #ifdef USE_BSM_AUDIT /* Audit successful login. */ if (auditsuccess) au_login_success(); #endif /* * Establish the login class. */ lc = login_getpwclass(pwd); lc_user = login_getuserclass(pwd); if (!(quietlog = login_getcapbool(lc_user, "hushlogin", 0))) quietlog = login_getcapbool(lc, "hushlogin", 0); /* * Switching needed for NFS with root access disabled. * * XXX: This change fails to modify the additional groups for the * process, and as such, may restrict rights normally granted * through those groups. */ setegid(pwd->pw_gid); seteuid(rootlogin ? 0 : pwd->pw_uid); if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) { if (login_getcapbool(lc, "requirehome", 0)) refused("Home directory not available", "HOMEDIR", 1); if (chdir("/") < 0) refused("Cannot find root directory", "ROOTDIR", 1); if (!quietlog || *pwd->pw_dir) printf("No home directory.\nLogging in with home = \"/\".\n"); pwd->pw_dir = strdup("/"); if (pwd->pw_dir == NULL) { syslog(LOG_NOTICE, "strdup(): %m"); bail(SLEEP_EXIT, 1); } } seteuid(euid); setegid(egid); if (!quietlog) { quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0; if (!quietlog) pam_silent = 0; } shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell); if (*pwd->pw_shell == '\0') pwd->pw_shell = strdup(_PATH_BSHELL); if (pwd->pw_shell == NULL) { syslog(LOG_NOTICE, "strdup(): %m"); bail(SLEEP_EXIT, 1); } if (*shell == '\0') /* Not overridden */ shell = pwd->pw_shell; if ((shell = strdup(shell)) == NULL) { syslog(LOG_NOTICE, "strdup(): %m"); bail(SLEEP_EXIT, 1); } /* * Set device protections, depending on what terminal the * user is logged in. This feature is used on Suns to give * console users better privacy. */ login_fbtab(tty, pwd->pw_uid, pwd->pw_gid); /* * Clear flags of the tty. None should be set, and when the * user sets them otherwise, this can cause the chown to fail. * Since it isn't clear that flags are useful on character * devices, we just clear them. * * We don't log in the case of EOPNOTSUPP because dev might be * on NFS, which doesn't support chflags. * * We don't log in the EROFS because that means that /dev is on * a read only file system and we assume that the permissions there * are sane. */ if (ttyn != tname && chflags(ttyn, 0)) if (errno != EOPNOTSUPP && errno != EROFS) syslog(LOG_ERR, "chflags(%s): %m", ttyn); if (ttyn != tname && chown(ttyn, pwd->pw_uid, (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid)) if (errno != EROFS) syslog(LOG_ERR, "chown(%s): %m", ttyn); /* * Exclude cons/vt/ptys only, assume dialup otherwise * TODO: Make dialup tty determination a library call * for consistency (finger etc.) */ if (hflag && isdialuptty(tty)) syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); #ifdef LOGALL /* * Syslog each successful login, so we don't have to watch * hundreds of wtmp or lastlogin files. */ if (hflag) syslog(LOG_INFO, "login from %s on %s as %s", hostname, tty, pwd->pw_name); else syslog(LOG_INFO, "login on %s as %s", tty, pwd->pw_name); #endif /* * If fflag is on, assume caller/authenticator has logged root * login. */ if (rootlogin && fflag == 0) { if (hflag) syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s", username, tty, hostname); else syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty); } /* * Destroy environment unless user has requested its * preservation - but preserve TERM in all cases */ term = getenv("TERM"); if (!pflag) environ = envinit; if (term != NULL) { if (setenv("TERM", term, 0) == -1) err(1, "setenv: cannot set TERM=%s", term); } /* * PAM modules might add supplementary groups during pam_setcred(). */ if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) { syslog(LOG_ERR, "setusercontext() failed - exiting"); bail(NO_SLEEP_EXIT, 1); } pam_err = pam_setcred(pamh, pam_silent|PAM_ESTABLISH_CRED); if (pam_err != PAM_SUCCESS) { pam_syslog("pam_setcred()"); bail(NO_SLEEP_EXIT, 1); } pam_cred_established = 1; pam_err = pam_open_session(pamh, pam_silent); if (pam_err != PAM_SUCCESS) { pam_syslog("pam_open_session()"); bail(NO_SLEEP_EXIT, 1); } pam_session_established = 1; /* * We must fork() before setuid() because we need to call * pam_close_session() as root. */ pid = fork(); if (pid < 0) { err(1, "fork"); } else if (pid != 0) { /* * Parent: wait for child to finish, then clean up * session. */ int status; setproctitle("-%s [pam]", getprogname()); waitpid(pid, &status, 0); bail(NO_SLEEP_EXIT, 0); } /* * NOTICE: We are now in the child process! */ /* * Add any environment variables the PAM modules may have set. */ export_pam_environment(); /* * We're done with PAM now; our parent will deal with the rest. */ pam_end(pamh, 0); pamh = NULL; /* * We don't need to be root anymore, so set the login name and * the UID. */ if (setlogin(username) != 0) { syslog(LOG_ERR, "setlogin(%s): %m - exiting", username); bail(NO_SLEEP_EXIT, 1); } if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) { syslog(LOG_ERR, "setusercontext() failed - exiting"); exit(1); } if (setenv("SHELL", pwd->pw_shell, 1) == -1) err(1, "setenv: cannot set SHELL=%s", pwd->pw_shell); if (setenv("HOME", pwd->pw_dir, 1) == -1) err(1, "setenv: cannot set HOME=%s", pwd->pw_dir); /* Overwrite "term" from login.conf(5) for any known TERM */ if (term == NULL && (tp = stypeof(tty)) != NULL) { if (setenv("TERM", tp, 1) == -1) err(1, "setenv: cannot set TERM=%s", tp); } else { if (setenv("TERM", TERM_UNKNOWN, 0) == -1) err(1, "setenv: cannot set TERM=%s", TERM_UNKNOWN); } if (setenv("LOGNAME", username, 1) == -1) err(1, "setenv: cannot set LOGNAME=%s", username); if (setenv("USER", username, 1) == -1) err(1, "setenv: cannot set USER=%s", username); if (setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0) == -1) { err(1, "setenv: cannot set PATH=%s", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH); } if (!quietlog) { const char *cw; cw = login_getcapstr(lc, "copyright", NULL, NULL); if (cw == NULL || motd(cw) == -1) printf("%s", copyright); printf("\n"); cw = login_getcapstr(lc, "welcome", NULL, NULL); if (cw != NULL && access(cw, F_OK) == 0) motd(cw); else motd(_PATH_MOTDFILE); if (login_getcapbool(lc_user, "nocheckmail", 0) == 0 && login_getcapbool(lc, "nocheckmail", 0) == 0) { char *cx; /* $MAIL may have been set by class. */ cx = getenv("MAIL"); if (cx == NULL) { asprintf(&cx, "%s/%s", _PATH_MAILDIR, pwd->pw_name); } if (cx && stat(cx, &st) == 0 && st.st_size != 0) printf("You have %smail.\n", (st.st_mtime > st.st_atime) ? "new " : ""); if (getenv("MAIL") == NULL) free(cx); } } login_close(lc_user); login_close(lc); signal(SIGALRM, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGINT, SIG_DFL); signal(SIGTSTP, SIG_IGN); /* * Login shells have a leading '-' in front of argv[0] */ p = strrchr(pwd->pw_shell, '/'); if (asprintf(&arg0, "-%s", p ? p + 1 : pwd->pw_shell) >= MAXPATHLEN) { syslog(LOG_ERR, "user: %s: shell exceeds maximum pathname size", username); errx(1, "shell exceeds maximum pathname size"); } else if (arg0 == NULL) { err(1, "asprintf()"); } execlp(shell, arg0, NULL); err(1, "%s", shell); /* * That's it, folks! */ }
void pwlocal_process(const char *username, int argc, char **argv) { struct passwd *pw; struct passwd old_pw; time_t old_change; int pfd, tfd; int min_pw_len = 0; int pw_expiry = 0; int ch; #ifdef LOGIN_CAP login_cap_t *lc; #endif while ((ch = getopt(argc, argv, "l")) != -1) { switch (ch) { case 'l': /* * Aborb the -l that may have gotten us here. */ break; default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; switch (argc) { case 0: /* username already provided */ break; case 1: username = argv[0]; break; default: usage(); /* NOTREACHED */ } if (!(pw = getpwnam(username))) errx(1, "unknown user %s", username); uid = getuid(); if (uid && uid != pw->pw_uid) errx(1, "%s", strerror(EACCES)); /* Save the old pw information for comparing on pw_copy(). */ old_pw = *pw; /* * Get class restrictions for this user, then get the new password. */ #ifdef LOGIN_CAP if((lc = login_getclass(pw->pw_class)) != NULL) { min_pw_len = (int) login_getcapnum(lc, "minpasswordlen", 0, 0); pw_expiry = (int) login_getcaptime(lc, "passwordtime", 0, 0); login_close(lc); } #endif #if 0 printf("AAA: pw_expiry = %x\n", pw_expiry); #endif pw->pw_passwd = getnewpasswd(pw, min_pw_len); old_change = pw->pw_change; pw->pw_change = pw_expiry ? pw_expiry + time(NULL) : 0; /* * Now that the user has given us a new password, let us * change the database. */ pw_init(); tfd = pw_lock(0); if (tfd < 0) { warnx ("The passwd file is busy, waiting..."); tfd = pw_lock(10); if (tfd < 0) errx(1, "The passwd file is still busy, " "try again later."); } pfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0); if (pfd < 0) pw_error(_PATH_MASTERPASSWD, 1, 1); pw_copy(pfd, tfd, pw, &old_pw); if (pw_mkdb(username, old_change == pw->pw_change) < 0) pw_error((char *)NULL, 0, 1); syslog(LOG_AUTH | LOG_INFO, "user %s (UID %lu) successfully changed " "the local password of user %s", uid ? username : "******", (unsigned long)uid, username); }
int yp_passwd(char *username) { struct yppasswd yppwd; int r, rpcport, status, secure=0; struct passwd *pw; struct timeval tv; login_cap_t *lc; CLIENT *client; char *master; uid_t uid; /* * Get local domain */ if ((r = yp_get_default_domain(&domain)) != 0) { warnx("can't get local YP domain. Reason: %s", yperr_string(r)); return (1); } /* * Find the host for the passwd map; it should be running * the daemon. */ if ((r = yp_master(domain, "master.passwd.byname", &master)) == 0) { secure=1; } else if ((r = yp_master(domain, "passwd.byname", &master)) != 0) { warnx("can't find the master YP server. Reason: %s", yperr_string(r)); return (1); } /* * Ask the portmapper for the port of the daemon. */ if ((rpcport = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP)) == 0) { warnx("master YP server not running yppasswd daemon."); warnx("Can't change password."); return (1); } /* * Be sure the port is privileged */ if (rpcport >= IPPORT_RESERVED) { warnx("yppasswd daemon is on an invalid port."); return (1); } /* Get user's login identity */ if (!(pw = ypgetpwnam(username, secure))) { warnx("unknown user %s.", username); return (1); } if ((lc = login_getclass(pw->pw_class)) == NULL) { warnx("unable to get login class for user %s.", username); return (1); } uid = getuid(); if (uid && uid != pw->pw_uid) { warnx("you may only change your own password: %s", strerror(EACCES)); return (1); } /* prompt for new password */ yppwd.newpw.pw_passwd = ypgetnewpasswd(pw, lc, &yppwd.oldpass); /* tell rpc.yppasswdd */ yppwd.newpw.pw_name = pw->pw_name; yppwd.newpw.pw_uid = pw->pw_uid; yppwd.newpw.pw_gid = pw->pw_gid; yppwd.newpw.pw_gecos = pw->pw_gecos; yppwd.newpw.pw_dir = pw->pw_dir; yppwd.newpw.pw_shell = pw->pw_shell; client = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp"); if (client==NULL) { warnx("cannot contact yppasswdd on %s: Reason: %s", master, yperr_string(YPERR_YPBIND)); free(yppwd.newpw.pw_passwd); return (YPERR_YPBIND); } client->cl_auth = authunix_create_default(); tv.tv_sec = 2; tv.tv_usec = 0; r = clnt_call(client, YPPASSWDPROC_UPDATE, xdr_yppasswd, &yppwd, xdr_int, &status, tv); if (r) { printf("rpc to yppasswdd failed.\n"); free(yppwd.newpw.pw_passwd); return (1); } else if (status) { printf("Couldn't change YP password.\n"); free(yppwd.newpw.pw_passwd); return (1); } else { printf("The YP password has been changed on %s, " "the master YP passwd server.\n", master); free(yppwd.newpw.pw_passwd); return (0); } }
int main(int argc, char *argv[]) { struct rlimit rl; login_cap_t *lc; FILE *back; char passbuf[1], salt[_PASSWORD_LEN + 1]; int mode = 0, c; rl.rlim_cur = 0; rl.rlim_max = 0; (void)setrlimit(RLIMIT_CORE, &rl); (void)setpriority(PRIO_PROCESS, 0, 0); openlog("login", LOG_ODELAY, LOG_AUTH); while ((c = getopt(argc, argv, "v:s:")) != -1) switch (c) { case 'v': break; case 's': /* service */ if (strcmp(optarg, "login") == 0) mode = 0; else if (strcmp(optarg, "challenge") == 0) mode = 1; else if (strcmp(optarg, "response") == 0) mode = 2; else { syslog(LOG_ERR, "%s: invalid service", optarg); exit(1); } break; default: syslog(LOG_ERR, "usage error"); exit(1); } switch (argc - optind) { case 2: case 1: break; default: syslog(LOG_ERR, "usage error"); exit(1); } if (!(back = fdopen(3, "r+"))) { syslog(LOG_ERR, "reopening back channel: %m"); exit(1); } if (mode == 1) { fprintf(back, BI_SILENT "\n"); exit(0); } if (mode == 2) { mode = 0; c = -1; while (read(3, passbuf, (size_t)1) == 1) { if (passbuf[0] == '\0' && ++mode == 2) break; } if (mode < 2) { syslog(LOG_ERR, "protocol error on back channel"); exit(1); } } else getpass("Password:"******"xx", sizeof(salt)); crypt("password", salt); fprintf(back, BI_REJECT "\n"); exit(1); }
/* * Check if the user is allowed to log in via ssh. If user is listed * in DenyUsers or one of user's groups is listed in DenyGroups, false * will be returned. If AllowUsers isn't empty and user isn't listed * there, or if AllowGroups isn't empty and one of user's groups isn't * listed there, false will be returned. * If the user's shell is not executable, false will be returned. * Otherwise true is returned. */ int allowed_user(struct passwd * pw) { #ifdef HAVE_LOGIN_CAP extern login_cap_t *lc; int match_name, match_ip; char *cap_hlist, *hp; #endif struct ssh *ssh = active_state; /* XXX */ struct stat st; const char *hostname = NULL, *ipaddr = NULL; int r; u_int i; /* Shouldn't be called if pw is NULL, but better safe than sorry... */ if (!pw || !pw->pw_name) return 0; #ifdef HAVE_LOGIN_CAP hostname = auth_get_canonical_hostname(ssh, options.use_dns); ipaddr = ssh_remote_ipaddr(ssh); lc = login_getclass(pw->pw_class); /* * Check the deny list. */ cap_hlist = login_getcapstr(lc, "host.deny", NULL, NULL); if (cap_hlist != NULL) { hp = strtok(cap_hlist, ","); while (hp != NULL) { match_name = match_hostname(hostname, hp); match_ip = match_hostname(ipaddr, hp); /* * Only a positive match here causes a "deny". */ if (match_name > 0 || match_ip > 0) { free(cap_hlist); login_close(lc); return 0; } hp = strtok(NULL, ","); } free(cap_hlist); } /* * Check the allow list. If the allow list exists, and the * remote host is not in it, the user is implicitly denied. */ cap_hlist = login_getcapstr(lc, "host.allow", NULL, NULL); if (cap_hlist != NULL) { hp = strtok(cap_hlist, ","); if (hp == NULL) { /* Just in case there's an empty string... */ free(cap_hlist); login_close(lc); return 0; } while (hp != NULL) { match_name = match_hostname(hostname, hp); match_ip = match_hostname(ipaddr, hp); /* * Negative match causes an immediate "deny". * Positive match causes us to break out * of the loop (allowing a fallthrough). */ if (match_name < 0 || match_ip < 0) { free(cap_hlist); login_close(lc); return 0; } if (match_name > 0 || match_ip > 0) break; hp = strtok(NULL, ","); } free(cap_hlist); if (hp == NULL) { login_close(lc); return 0; } } login_close(lc); #endif #ifdef USE_PAM if (!options.use_pam) { #endif /* * password/account expiration. */ if (pw->pw_change || pw->pw_expire) { struct timeval tv; (void)gettimeofday(&tv, (struct timezone *)NULL); if (pw->pw_expire) { if (tv.tv_sec >= pw->pw_expire) { logit("User %.100s not allowed because account has expired", pw->pw_name); return 0; /* expired */ } } #ifdef _PASSWORD_CHGNOW if (pw->pw_change == _PASSWORD_CHGNOW) { logit("User %.100s not allowed because password needs to be changed", pw->pw_name); return 0; /* can't force password change (yet) */ } #endif if (pw->pw_change) { if (tv.tv_sec >= pw->pw_change) { logit("User %.100s not allowed because password has expired", pw->pw_name); return 0; /* expired */ } } } #ifdef USE_PAM } #endif /* * Deny if shell does not exist or is not executable unless we * are chrooting. */ /* * XXX Should check to see if it is executable by the * XXX requesting user. --thorpej */ if (options.chroot_directory == NULL || strcasecmp(options.chroot_directory, "none") == 0) { char *shell = xstrdup((pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell); /* empty = /bin/sh */ if (stat(shell, &st) != 0) { logit("User %.100s not allowed because shell %.100s " "does not exist", pw->pw_name, shell); free(shell); return 0; } if (S_ISREG(st.st_mode) == 0 || (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) { logit("User %.100s not allowed because shell %.100s " "is not executable", pw->pw_name, shell); free(shell); return 0; } free(shell); } /* * XXX Consider nuking {Allow,Deny}{Users,Groups}. We have the * XXX login_cap(3) mechanism which covers all other types of * XXX logins, too. */ if (options.num_deny_users > 0 || options.num_allow_users > 0 || options.num_deny_groups > 0 || options.num_allow_groups > 0) { hostname = auth_get_canonical_hostname(ssh, options.use_dns); ipaddr = ssh_remote_ipaddr(ssh); } /* Return false if user is listed in DenyUsers */ if (options.num_deny_users > 0) { for (i = 0; i < options.num_deny_users; i++) { r = match_user(pw->pw_name, hostname, ipaddr, options.deny_users[i]); if (r < 0) { fatal("Invalid DenyUsers pattern \"%.100s\"", options.deny_users[i]); } else if (r != 0) { logit("User %.100s from %.100s not allowed " "because listed in DenyUsers", pw->pw_name, hostname); return 0; } } } /* Return false if AllowUsers isn't empty and user isn't listed there */ if (options.num_allow_users > 0) { for (i = 0; i < options.num_allow_users; i++) { r = match_user(pw->pw_name, hostname, ipaddr, options.allow_users[i]); if (r < 0) { fatal("Invalid AllowUsers pattern \"%.100s\"", options.allow_users[i]); } else if (r == 1) break; } /* i < options.num_allow_users iff we break for loop */ if (i >= options.num_allow_users) { logit("User %.100s from %.100s not allowed because " "not listed in AllowUsers", pw->pw_name, hostname); return 0; } } if (options.num_deny_groups > 0 || options.num_allow_groups > 0) { /* Get the user's group access list (primary and supplementary) */ if (ga_init(pw->pw_name, pw->pw_gid) == 0) { logit("User %.100s from %.100s not allowed because " "not in any group", pw->pw_name, hostname); return 0; } /* Return false if one of user's groups is listed in DenyGroups */ if (options.num_deny_groups > 0) if (ga_match(options.deny_groups, options.num_deny_groups)) { ga_free(); logit("User %.100s from %.100s not allowed " "because a group is listed in DenyGroups", pw->pw_name, hostname); return 0; } /* * Return false if AllowGroups isn't empty and one of user's groups * isn't listed there */ if (options.num_allow_groups > 0) if (!ga_match(options.allow_groups, options.num_allow_groups)) { ga_free(); logit("User %.100s from %.100s not allowed " "because none of user's groups are listed " "in AllowGroups", pw->pw_name, hostname); return 0; } ga_free(); } /* We found no reason not to let this user try to log on... */ return 1; }
static int #if defined(USE_PAM) || defined(_AIX) isNoPassAllowed( const char *un ) { struct passwd *pw = 0; # ifdef HAVE_GETSPNAM /* (sic!) - not USESHADOW */ struct spwd *spw; # endif #else isNoPassAllowed( const char *un, struct passwd *pw ) { #endif struct group *gr; char **fp; int hg; if (!*un) return 0; if (cursource != PWSRC_MANUAL) return 1; for (hg = 0, fp = td->noPassUsers; *fp; fp++) if (**fp == '@') hg = 1; else if (!strcmp( un, *fp )) return 1; else if (!strcmp( "*", *fp )) { #if defined(USE_PAM) || defined(_AIX) if (!(pw = getpwnam( un ))) return 0; if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*') continue; # ifdef HAVE_GETSPNAM /* (sic!) - not USESHADOW */ if ((spw = getspnam( un )) && (spw->sp_pwdp[0] == '!' || spw->sp_pwdp[0] == '*')) continue; # endif #endif if (pw->pw_uid) return 1; } #if defined(USE_PAM) || defined(_AIX) if (hg && (pw || (pw = getpwnam( un )))) { #else if (hg) { #endif for (setgrent(); (gr = getgrent()); ) for (fp = td->noPassUsers; *fp; fp++) if (**fp == '@' && !strcmp( gr->gr_name, *fp + 1 )) { if (pw->pw_gid == gr->gr_gid) { endgrent(); return 1; } for (; *gr->gr_mem; gr->gr_mem++) if (!strcmp( un, *gr->gr_mem )) { endgrent(); return 1; } } endgrent(); } return 0; } #if !defined(USE_PAM) && !defined(_AIX) && defined(HAVE_SETUSERCONTEXT) # define LC_RET0 do { login_close(lc); return 0; } while(0) #else # define LC_RET0 return 0 #endif int verify( GConvFunc gconv, int rootok ) { #ifdef USE_PAM const char *psrv; struct pam_data pdata; int pretc, pnopass; char psrvb[64]; #elif defined(_AIX) char *msg, *curret; int i, reenter; #else struct stat st; const char *nolg; char *buf; int fd; # ifdef HAVE_GETUSERSHELL char *s; # endif # if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW) int tim, expir, warntime, quietlog; # endif #endif debug( "verify ...\n" ); #ifdef USE_PAM pnopass = FALSE; if (!strcmp( curtype, "classic" )) { if (!gconv( GCONV_USER, 0 )) return 0; if (isNoPassAllowed( curuser )) { gconv( GCONV_PASS_ND, 0 ); if (!*curpass) { pnopass = TRUE; sprintf( psrvb, "%.31s-np", PAMService ); psrv = psrvb; } else psrv = PAMService; } else psrv = PAMService; pdata.usecur = TRUE; } else { sprintf( psrvb, "%.31s-%.31s", PAMService, curtype ); psrv = psrvb; pdata.usecur = FALSE; } pdata.gconv = gconv; if (!doPAMAuth( psrv, &pdata )) return 0; #elif defined(_AIX) if ((td->displayType & d_location) == dForeign) { char *tmpch; strncpy( hostname, td->name, sizeof(hostname) - 1 ); hostname[sizeof(hostname)-1] = '\0'; if ((tmpch = strchr( hostname, ':' ))) *tmpch = '\0'; } else hostname[0] = '\0'; /* tty names should only be 15 characters long */ # if 0 for (i = 0; i < 15 && td->name[i]; i++) { if (td->name[i] == ':' || td->name[i] == '.') tty[i] = '_'; else tty[i] = td->name[i]; } tty[i] = '\0'; # else memcpy( tty, "/dev/xdm/", 9 ); for (i = 0; i < 6 && td->name[i]; i++) { if (td->name[i] == ':' || td->name[i] == '.') tty[9 + i] = '_'; else tty[9 + i] = td->name[i]; } tty[9 + i] = '\0'; # endif if (!strcmp( curtype, "classic" )) { if (!gconv( GCONV_USER, 0 )) return 0; if (isNoPassAllowed( curuser )) { gconv( GCONV_PASS_ND, 0 ); if (!*curpass) { debug( "accepting despite empty password\n" ); goto done; } } else if (!gconv( GCONV_PASS, 0 )) return 0; enduserdb(); msg = NULL; if ((i = authenticate( curuser, curpass, &reenter, &msg ))) { debug( "authenticate() failed: %s\n", msg ); if (msg) free( msg ); loginfailed( curuser, hostname, tty ); if (i == ENOENT || i == ESAD) V_RET_AUTH; else V_RET_FAIL( 0 ); } if (reenter) { logError( "authenticate() requests more data: %s\n", msg ); free( msg ); V_RET_FAIL( 0 ); } } else if (!strcmp( curtype, "generic" )) { if (!gconv( GCONV_USER, 0 )) return 0; for (curret = 0;;) { msg = NULL; if ((i = authenticate( curuser, curret, &reenter, &msg ))) { debug( "authenticate() failed: %s\n", msg ); if (msg) free( msg ); loginfailed( curuser, hostname, tty ); if (i == ENOENT || i == ESAD) V_RET_AUTH; else V_RET_FAIL( 0 ); } if (curret) free( curret ); if (!reenter) break; if (!(curret = gconv( GCONV_HIDDEN, msg ))) return 0; free( msg ); } } else { logError( "Unsupported authentication type %\"s requested\n", curtype ); V_RET_FAIL( 0 ); } if (msg) { displayStr( V_MSG_INFO, msg ); free( msg ); } done: #else if (strcmp( curtype, "classic" )) { logError( "Unsupported authentication type %\"s requested\n", curtype ); V_RET_FAIL( 0 ); } if (!gconv( GCONV_USER, 0 )) return 0; if (!(p = getpwnam( curuser ))) { debug( "getpwnam() failed.\n" ); gconv( GCONV_PASS, 0 ); V_RET_AUTH; } if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') { debug( "account is locked\n" ); gconv( GCONV_PASS, 0 ); V_RET_AUTH; } # ifdef USESHADOW if ((sp = getspnam( curuser ))) { p->pw_passwd = sp->sp_pwdp; if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') { debug( "account is locked\n" ); gconv( GCONV_PASS, 0 ); V_RET_AUTH; } } else debug( "getspnam() failed: %m. Are you root?\n" ); # endif if (!*p->pw_passwd) { if (!td->allowNullPasswd) { debug( "denying user with empty password\n" ); gconv( GCONV_PASS, 0 ); V_RET_AUTH; } goto nplogin; } if (isNoPassAllowed( curuser, p )) { nplogin: gconv( GCONV_PASS_ND, 0 ); if (!*curpass) { debug( "accepting password-less login\n" ); goto done; } } else if (!gconv( GCONV_PASS, 0 )) return 0; # ifdef KERBEROS if (p->pw_uid) { int ret; char realm[REALM_SZ]; if (krb_get_lrealm( realm, 1 )) { logError( "Cannot get KerberosIV realm.\n" ); V_RET_FAIL( 0 ); } sprintf( krbtkfile, "%s.%.*s", TKT_ROOT, MAXPATHLEN - strlen( TKT_ROOT ) - 2, td->name ); krb_set_tkt_string( krbtkfile ); unlink( krbtkfile ); ret = krb_verify_user( curuser, "", realm, curpass, 1, "rcmd" ); if (ret == KSUCCESS) { chown( krbtkfile, p->pw_uid, p->pw_gid ); debug( "KerberosIV verify succeeded\n" ); goto done; } else if (ret != KDC_PR_UNKNOWN && ret != SKDC_CANT) { logError( "KerberosIV verification failure %\"s for %s\n", krb_get_err_text( ret ), curuser ); krbtkfile[0] = '\0'; V_RET_FAIL( 0 ); } debug( "KerberosIV verify failed: %s\n", krb_get_err_text( ret ) ); } krbtkfile[0] = '\0'; # endif /* KERBEROS */ # if defined(ultrix) || defined(__ultrix__) if (authenticate_user( p, curpass, NULL ) < 0) # elif defined(HAVE_PW_ENCRYPT) if (strcmp( pw_encrypt( curpass, p->pw_passwd ), p->pw_passwd )) # elif defined(HAVE_CRYPT) if (strcmp( crypt( curpass, p->pw_passwd ), p->pw_passwd )) # else if (strcmp( curpass, p->pw_passwd )) # endif { debug( "password verify failed\n" ); V_RET_AUTH; } done: #endif /* !defined(USE_PAM) && !defined(_AIX) */ debug( "restrict %s ...\n", curuser ); #if defined(USE_PAM) || defined(_AIX) if (!(p = getpwnam( curuser ))) { logError( "getpwnam(%s) failed.\n", curuser ); V_RET_FAIL( 0 ); } #endif if (!p->pw_uid) { if (!rootok && !td->allowRootLogin) V_RET_FAIL( "Root logins are not allowed" ); return 1; /* don't deny root to log in */ } #ifdef USE_PAM debug( " pam_acct_mgmt() ...\n" ); pretc = pam_acct_mgmt( pamh, 0 ); reInitErrorLog(); debug( " pam_acct_mgmt() returned: %s\n", pam_strerror( pamh, pretc ) ); if (pretc == PAM_NEW_AUTHTOK_REQD) { pdata.usecur = FALSE; pdata.gconv = conv_interact; /* pam will have output a message already, so no prepareErrorGreet() */ if (gconv != conv_interact || pnopass) { pam_end( pamh, PAM_SUCCESS ); pamh = 0; gSendInt( V_CHTOK_AUTH ); /* this cannot auth the wrong user, as only classic auths get here */ while (!doPAMAuth( PAMService, &pdata )) if (pdata.abort) return 0; gSendInt( V_PRE_OK ); } else gSendInt( V_CHTOK ); for (;;) { debug( " pam_chauthtok() ...\n" ); pretc = pam_chauthtok( pamh, PAM_CHANGE_EXPIRED_AUTHTOK ); reInitErrorLog(); debug( " pam_chauthtok() returned: %s\n", pam_strerror( pamh, pretc ) ); if (pdata.abort) { pam_end( pamh, PAM_SUCCESS ); pamh = 0; return 0; } if (pretc == PAM_SUCCESS) break; /* effectively there is only PAM_AUTHTOK_ERR */ gSendInt( V_FAIL ); } if (curpass) free( curpass ); curpass = newpass; newpass = 0; } else if (pretc != PAM_SUCCESS) { pam_end( pamh, pretc ); pamh = 0; V_RET_AUTH; } #elif defined(_AIX) /* USE_PAM */ msg = NULL; if (loginrestrictions( curuser, ((td->displayType & d_location) == dForeign) ? S_RLOGIN : S_LOGIN, tty, &msg ) == -1) { debug( "loginrestrictions() - %s\n", msg ? msg : "error" ); loginfailed( curuser, hostname, tty ); prepareErrorGreet(); if (msg) { displayStr( V_MSG_ERR, msg ); free( msg ); } gSendInt( V_AUTH ); return 0; } if (msg) free( (void *)msg ); #endif /* USE_PAM || _AIX */ #ifndef _AIX # ifdef HAVE_SETUSERCONTEXT # ifdef HAVE_LOGIN_GETCLASS lc = login_getclass( p->pw_class ); # else lc = login_getpwclass( p ); # endif if (!lc) V_RET_FAIL( 0 ); p->pw_shell = login_getcapstr( lc, "shell", p->pw_shell, p->pw_shell ); # endif # ifndef USE_PAM /* restrict_expired */ # if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW) # if !defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || (!defined(HAVE_SETUSERCONTEXT) && defined(USESHADOW)) if (sp) # endif { # define DEFAULT_WARN (2L * 7L) /* Two weeks */ tim = time( NULL ) / 86400L; # ifdef HAVE_SETUSERCONTEXT quietlog = login_getcapbool( lc, "hushlogin", 0 ); warntime = login_getcaptime( lc, "warnexpire", DEFAULT_WARN * 86400L, DEFAULT_WARN * 86400L ) / 86400L; # else quietlog = 0; # ifdef USESHADOW warntime = sp->sp_warn != -1 ? sp->sp_warn : DEFAULT_WARN; # else warntime = DEFAULT_WARN; # endif # endif # ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE if (p->pw_expire) { expir = p->pw_expire / 86400L; # else if (sp->sp_expire != -1) { expir = sp->sp_expire; # endif if (tim > expir) { displayStr( V_MSG_ERR, "Your account has expired;" " please contact your system administrator" ); gSendInt( V_FAIL ); LC_RET0; } else if (tim > (expir - warntime) && !quietlog) { displayMsg( V_MSG_INFO, "Warning: your account will expire in %d day(s)", expir - tim ); } } # ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE if (p->pw_change) { expir = p->pw_change / 86400L; # else if (!sp->sp_lstchg) { displayStr( V_MSG_ERR, "You are required to change your password immediately" " (root enforced)" ); /* XXX todo password change */ gSendInt( V_FAIL ); LC_RET0; } else if (sp->sp_max != -1) { expir = sp->sp_lstchg + sp->sp_max; if (sp->sp_inact != -1 && tim > expir + sp->sp_inact) { displayStr( V_MSG_ERR, "Your account has expired;" " please contact your system administrator" ); gSendInt( V_FAIL ); LC_RET0; } # endif if (tim > expir) { displayStr( V_MSG_ERR, "You are required to change your password immediately" " (password aged)" ); /* XXX todo password change */ gSendInt( V_FAIL ); LC_RET0; } else if (tim > (expir - warntime) && !quietlog) { displayMsg( V_MSG_INFO, "Warning: your password will expire in %d day(s)", expir - tim ); } } } # endif /* HAVE_STRUCT_PASSWD_PW_EXPIRE || USESHADOW */ /* restrict_nologin */ # ifndef _PATH_NOLOGIN # define _PATH_NOLOGIN "/etc/nologin" # endif if (( # ifdef HAVE_SETUSERCONTEXT /* Do we ignore a nologin file? */ !login_getcapbool( lc, "ignorenologin", 0 )) && (!stat( (nolg = login_getcapstr( lc, "nologin", "", NULL )), &st ) || # endif !stat( (nolg = _PATH_NOLOGIN), &st ))) { if (st.st_size && (fd = open( nolg, O_RDONLY )) >= 0) { if ((buf = Malloc( st.st_size + 1 ))) { if (read( fd, buf, st.st_size ) == st.st_size) { close( fd ); buf[st.st_size] = 0; displayStr( V_MSG_ERR, buf ); free( buf ); gSendInt( V_FAIL ); LC_RET0; } free( buf ); } close( fd ); } displayStr( V_MSG_ERR, "Logins are not allowed at the moment.\nTry again later" ); gSendInt( V_FAIL ); LC_RET0; } /* restrict_time */ # if defined(HAVE_SETUSERCONTEXT) && defined(HAVE_AUTH_TIMEOK) if (!auth_timeok( lc, time( NULL ) )) { displayStr( V_MSG_ERR, "You are not allowed to login at the moment" ); gSendInt( V_FAIL ); LC_RET0; } # endif # ifdef HAVE_GETUSERSHELL for (;;) { if (!(s = getusershell())) { debug( "shell not in /etc/shells\n" ); endusershell(); V_RET_FAIL( "Your login shell is not listed in /etc/shells" ); } if (!strcmp( s, p->pw_shell )) { endusershell(); break; } } # endif # endif /* !USE_PAM */ /* restrict_nohome */ # ifdef HAVE_SETUSERCONTEXT if (login_getcapbool( lc, "requirehome", 0 )) { struct stat st; if (!*p->pw_dir || stat( p->pw_dir, &st ) || st.st_uid != p->pw_uid) { displayStr( V_MSG_ERR, "Home folder not available" ); gSendInt( V_FAIL ); LC_RET0; } } # endif #endif /* !_AIX */ return 1; } static const char *envvars[] = { "TZ", /* SYSV and SVR4, but never hurts */ #ifdef _AIX "AUTHSTATE", /* for kerberos */ #endif NULL }; #if defined(USE_PAM) && defined(HAVE_INITGROUPS) static int num_saved_gids; static gid_t *saved_gids; static int saveGids( void ) { num_saved_gids = getgroups( 0, 0 ); if (!(saved_gids = Malloc( sizeof(gid_t) * num_saved_gids ))) return 0; if (getgroups( num_saved_gids, saved_gids ) < 0) { logError( "saving groups failed: %m\n" ); return 0; } return 1; } static int restoreGids( void ) { if (setgroups( num_saved_gids, saved_gids ) < 0) { logError( "restoring groups failed: %m\n" ); return 0; } if (setgid( p->pw_gid ) < 0) { logError( "restoring gid failed: %m\n" ); return 0; } return 1; } #endif /* USE_PAM && HAVE_INITGROUPS */ static int resetGids( void ) { #ifdef HAVE_INITGROUPS if (setgroups( 0, &p->pw_gid /* anything */ ) < 0) { logError( "restoring groups failed: %m\n" ); return 0; } #endif if (setgid( 0 ) < 0) { logError( "restoring gid failed: %m\n" ); return 0; } return 1; } static int setGid( const char *name, int gid ) { if (setgid( gid ) < 0) { logError( "setgid(%d) (user %s) failed: %m\n", gid, name ); return 0; } #ifdef HAVE_INITGROUPS if (initgroups( name, gid ) < 0) { logError( "initgroups for %s failed: %m\n", name ); setgid( 0 ); return 0; } #endif /* QNX4 doesn't support multi-groups, no initgroups() */ return 1; } static int setUid( const char *name, int uid ) { if (setuid( uid ) < 0) { logError( "setuid(%d) (user %s) failed: %m\n", uid, name ); return 0; } return 1; } static int setUser( const char *name, int uid, int gid ) { if (setGid( name, gid )) { if (setUid( name, uid )) return 1; resetGids(); } return 0; } #if defined(SECURE_RPC) || defined(K5AUTH) static void nukeAuth( int len, const char *name ) { int i; for (i = 0; i < td->authNum; i++) if (td->authorizations[i]->name_length == len && !memcmp( td->authorizations[i]->name, name, len )) { memcpy( &td->authorizations[i], &td->authorizations[i+1], sizeof(td->authorizations[i]) * (--td->authNum - i) ); break; } } #endif static void mergeSessionArgs( int cansave ) { char *mfname; const char *fname; int i, needsave; mfname = 0; fname = ".dmrc"; if ((!curdmrc || newdmrc) && *dmrcDir) if (strApp( &mfname, dmrcDir, "/", curuser, fname, (char *)0 )) fname = mfname; needsave = 0; if (!curdmrc) { curdmrc = iniLoad( fname ); if (!curdmrc) { strDup( &curdmrc, "[Desktop]\nSession=default\n" ); needsave = 1; } } if (newdmrc) { curdmrc = iniMerge( curdmrc, newdmrc ); needsave = 1; } if (needsave && cansave) if (!iniSave( curdmrc, fname ) && errno == ENOENT && mfname) { for (i = 0; mfname[i]; i++) if (mfname[i] == '/') { mfname[i] = 0; mkdir( mfname, 0755 ); mfname[i] = '/'; } iniSave( curdmrc, mfname ); } if (mfname) free( mfname ); } static int createClientLog( const char *log ) { char randstr[32], *randstrp = 0, *lname; int lfd; for (;;) { struct expando macros[] = { { 'd', 0, td->name }, { 'u', 0, curuser }, { 'r', 0, randstrp }, { 0, 0, 0 } }; if (!(lname = expandMacros( log, macros ))) exit( 1 ); unlink( lname ); if ((lfd = open( lname, O_WRONLY|O_CREAT|O_EXCL, 0600 )) >= 0) { dup2( lfd, 1 ); dup2( lfd, 2 ); close( lfd ); free( lname ); return TRUE; } if (errno != EEXIST || !macros[2].uses) { free( lname ); return FALSE; } logInfo( "Session log file %s not usable, trying another one.\n", lname ); free( lname ); sprintf( randstr, "%d", secureRandom() ); randstrp = randstr; } }
int server_unlock(const char *s) { struct client *c; #ifdef HAVE_LOGIN_CAP login_cap_t *lc; #endif u_int i; char *out; u_int failures, tries, backoff; if (!server_locked || server_locked_pw == NULL) return (0); server_activity = time(NULL); if (server_activity < password_backoff) return (-2); if (server_password != NULL) { if (s == NULL) return (-1); out = crypt(s, server_password); if (strcmp(out, server_password) != 0) goto wrong; } for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL) continue; status_prompt_clear(c); server_redraw_client(c); } server_locked = 0; password_failures = 0; password_backoff = 0; return (0); wrong: password_failures++; password_backoff = 0; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->prompt_buffer == NULL) continue; *c->prompt_buffer = '\0'; c->prompt_index = 0; server_redraw_client(c); } /* * Start slowing down after "login-backoff" attempts and reset every * "login-tries" attempts. */ #ifdef HAVE_LOGIN_CAP lc = login_getclass(server_locked_pw->pw_class); if (lc != NULL) { tries = login_getcapnum(lc, (char *) "login-tries", 10, 10); backoff = login_getcapnum(lc, (char *) "login-backoff", 3, 3); } else { tries = 10; backoff = 3; } #else tries = 10; backoff = 3; #endif failures = password_failures % tries; if (failures > backoff) { password_backoff = server_activity + ((failures - backoff) * tries / 2); return (-2); } return (-1); }
int main(int argc, char *argv[]) { char *domain, *p, *ttyn, *shell, *fullname, *instance; char *lipaddr, *script, *ripaddr, *style, *type, *fqdn; char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10]; char localhost[MAXHOSTNAMELEN], *copyright; char mail[sizeof(_PATH_MAILDIR) + 1 + NAME_MAX]; int ask, ch, cnt, fflag, pflag, quietlog, rootlogin, lastchance; int error, homeless, needto, authok, tries, backoff; struct addrinfo *ai, hints; struct rlimit cds, scds; quad_t expire, warning; struct utmp utmp; struct group *gr; struct stat st; uid_t uid; openlog("login", LOG_ODELAY, LOG_AUTH); fqdn = lipaddr = ripaddr = fullname = type = NULL; authok = 0; tries = 10; backoff = 3; domain = NULL; if (gethostname(localhost, sizeof(localhost)) < 0) { syslog(LOG_ERR, "couldn't get local hostname: %m"); strlcpy(localhost, "localhost", sizeof(localhost)); } else if ((domain = strchr(localhost, '.'))) { domain++; if (*domain && strchr(domain, '.') == NULL) domain = localhost; } if ((as = auth_open()) == NULL) { syslog(LOG_ERR, "auth_open: %m"); err(1, "unable to initialize BSD authentication"); } auth_setoption(as, "login", "yes"); /* * -p is used by getty to tell login not to destroy the environment * -f is used to skip a second login authentication * -h is used by other servers to pass the name of the remote * host to login so that it may be placed in utmp and wtmp */ fflag = pflag = 0; uid = getuid(); while ((ch = getopt(argc, argv, "fh:pu:L:R:")) != -1) switch (ch) { case 'f': fflag = 1; break; case 'h': if (uid) { warnc(EPERM, "-h option"); quickexit(1); } free(fqdn); if ((fqdn = strdup(optarg)) == NULL) { warn(NULL); quickexit(1); } auth_setoption(as, "fqdn", fqdn); if (domain && (p = strchr(optarg, '.')) && strcasecmp(p+1, domain) == 0) *p = 0; hostname = optarg; auth_setoption(as, "hostname", hostname); break; case 'L': if (uid) { warnc(EPERM, "-L option"); quickexit(1); } if (lipaddr) { warnx("duplicate -L option"); quickexit(1); } lipaddr = optarg; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_CANONNAME; error = getaddrinfo(lipaddr, NULL, &hints, &ai); if (!error) { strlcpy(localhost, ai->ai_canonname, sizeof(localhost)); freeaddrinfo(ai); } else strlcpy(localhost, lipaddr, sizeof(localhost)); auth_setoption(as, "local_addr", lipaddr); break; case 'p': pflag = 1; break; case 'R': if (uid) { warnc(EPERM, "-R option"); quickexit(1); } if (ripaddr) { warnx("duplicate -R option"); quickexit(1); } ripaddr = optarg; auth_setoption(as, "remote_addr", ripaddr); break; case 'u': if (uid) { warnc(EPERM, "-u option"); quickexit(1); } rusername = optarg; break; default: if (!uid) syslog(LOG_ERR, "invalid flag %c", ch); (void)fprintf(stderr, "usage: login [-fp] [-h hostname] [-L local-addr] " "[-R remote-addr] [-u username]\n\t[user]\n"); quickexit(1); } argc -= optind; argv += optind; if (*argv) { username = *argv; ask = 0; } else ask = 1; /* * If effective user is not root, just run su(1) to emulate login(1). */ if (geteuid() != 0) { char *av[5], **ap; auth_close(as); closelog(); closefrom(STDERR_FILENO + 1); ap = av; *ap++ = _PATH_SU; *ap++ = "-L"; if (!pflag) *ap++ = "-l"; if (!ask) *ap++ = username; *ap = NULL; execv(_PATH_SU, av); warn("unable to exec %s", _PATH_SU); _exit(1); } ttyn = ttyname(STDIN_FILENO); if (ttyn == NULL || *ttyn == '\0') { (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY); ttyn = tname; } if ((tty = strrchr(ttyn, '/'))) ++tty; else tty = ttyn; /* * Since login deals with sensitive information, turn off coredumps. */ if (getrlimit(RLIMIT_CORE, &scds) < 0) { syslog(LOG_ERR, "couldn't get core dump size: %m"); scds.rlim_cur = scds.rlim_max = QUAD_MIN; } cds.rlim_cur = cds.rlim_max = 0; if (setrlimit(RLIMIT_CORE, &cds) < 0) { syslog(LOG_ERR, "couldn't set core dump size to 0: %m"); scds.rlim_cur = scds.rlim_max = QUAD_MIN; } (void)signal(SIGALRM, timedout); if (argc > 1) { needto = 0; (void)alarm(timeout); } else needto = 1; (void)signal(SIGQUIT, SIG_IGN); (void)signal(SIGINT, SIG_IGN); (void)signal(SIGHUP, SIG_IGN); (void)setpriority(PRIO_PROCESS, 0, 0); #ifdef notyet /* XXX - we don't (yet) support per-tty auth stuff */ /* BSDi uses a ttys.conf file but we could just overload /etc/ttys */ /* * Classify the attempt. * By default we use the value in the ttys file. * If there is a classify script we run that as * * classify [-f] [username] */ if (type = getttyauth(tty)) auth_setoption(as, "auth_type", type); #endif /* get the default login class */ if ((lc = login_getclass(0)) == NULL) { /* get the default class */ warnx("Failure to retrieve default class"); quickexit(1); } timeout = (u_int)login_getcapnum(lc, "login-timeout", 300, 300); if ((script = login_getcapstr(lc, "classify", NULL, NULL)) != NULL) { unsetenv("AUTH_TYPE"); unsetenv("REMOTE_NAME"); if (script[0] != '/') { syslog(LOG_ERR, "Invalid classify script: %s", script); warnx("Classification failure"); quickexit(1); } shell = strrchr(script, '/') + 1; auth_setstate(as, AUTH_OKAY); auth_call(as, script, shell, fflag ? "-f" : username, fflag ? username : 0, (char *)0); if (!(auth_getstate(as) & AUTH_ALLOW)) quickexit(1); auth_setenv(as); if ((p = getenv("AUTH_TYPE")) != NULL && strncmp(p, "auth-", 5) == 0) type = p; if ((p = getenv("REMOTE_NAME")) != NULL) hostname = p; /* * we may have changed some values, reset them */ auth_clroptions(as); if (type) auth_setoption(as, "auth_type", type); if (fqdn) auth_setoption(as, "fqdn", fqdn); if (hostname) auth_setoption(as, "hostname", hostname); if (lipaddr) auth_setoption(as, "local_addr", lipaddr); if (ripaddr) auth_setoption(as, "remote_addr", ripaddr); } /* * Request the things like the approval script print things * to stdout (in particular, the nologins files) */ auth_setitem(as, AUTHV_INTERACTIVE, "True"); for (cnt = 0;; ask = 1) { /* * Clean up our current authentication session. * Options are not cleared so we need to clear any * we might set below. */ auth_clean(as); auth_clroption(as, "style"); auth_clroption(as, "lastchance"); lastchance = 0; if (ask) { fflag = 0; getloginname(); } if (needto) { needto = 0; alarm(timeout); } if ((style = strchr(username, ':')) != NULL) *style++ = '\0'; if (fullname) free(fullname); if (auth_setitem(as, AUTHV_NAME, username) < 0 || (fullname = strdup(username)) == NULL) { syslog(LOG_ERR, "%m"); warn(NULL); quickexit(1); } rootlogin = 0; if ((instance = strchr(username, '/')) != NULL) { if (strncmp(instance + 1, "root", 4) == 0) rootlogin = 1; *instance++ = '\0'; } else instance = ""; if (strlen(username) > UT_NAMESIZE) username[UT_NAMESIZE] = '\0'; /* * Note if trying multiple user names; log failures for * previous user name, but don't bother logging one failure * for nonexistent name (mistyped username). */ if (failures && strcmp(tbuf, username)) { if (failures > (pwd ? 0 : 1)) badlogin(tbuf); failures = 0; } (void)strlcpy(tbuf, username, sizeof(tbuf)); if ((pwd = getpwnam(username)) != NULL && auth_setpwd(as, pwd) < 0) { syslog(LOG_ERR, "%m"); warn(NULL); quickexit(1); } lc = login_getclass(pwd ? pwd->pw_class : NULL); if (!lc) goto failed; style = login_getstyle(lc, style, type); if (!style) goto failed; /* * We allow "login-tries" attempts to login but start * slowing down after "login-backoff" attempts. */ tries = (int)login_getcapnum(lc, "login-tries", 10, 10); backoff = (int)login_getcapnum(lc, "login-backoff", 3, 3); /* * Turn off the fflag if we have an invalid user * or we are not root and we are trying to change uids. */ if (!pwd || (uid && uid != pwd->pw_uid)) fflag = 0; if (pwd && pwd->pw_uid == 0) rootlogin = 1; /* * If we do not have the force flag authenticate the user */ if (!fflag) { lastchance = login_getcaptime(lc, "password-dead", 0, 0) != 0; if (lastchance) auth_setoption(as, "lastchance", "yes"); /* * Once we start asking for a password * we want to log a failure on a hup. */ signal(SIGHUP, sighup); auth_verify(as, style, NULL, lc->lc_class, NULL); authok = auth_getstate(as); /* * If their password expired and it has not been * too long since then, give the user one last * chance to change their password */ if ((authok & AUTH_PWEXPIRED) && lastchance) { authok = AUTH_OKAY; } else lastchance = 0; if ((authok & AUTH_ALLOW) == 0) goto failed; if (auth_setoption(as, "style", style) < 0) { syslog(LOG_ERR, "%m"); warn(NULL); quickexit(1); } } /* * explicitly reject users without password file entries */ if (pwd == NULL) goto failed; /* * If trying to log in as root on an insecure terminal, * refuse the login attempt unless the authentication * style explicitly says a root login is okay. */ if (pwd && rootlogin && !rootterm(tty)) goto failed; if (fflag) { type = 0; style = "forced"; } break; failed: if (authok & AUTH_SILENT) quickexit(0); if (rootlogin && !rootterm(tty)) { warnx("%s login refused on this terminal.", fullname); if (hostname) syslog(LOG_NOTICE, "LOGIN %s REFUSED FROM %s%s%s ON TTY %s", fullname, rusername ? rusername : "", rusername ? "@" : "", hostname, tty); else syslog(LOG_NOTICE, "LOGIN %s REFUSED ON TTY %s", fullname, tty); } else { if (!as || (p = auth_getvalue(as, "errormsg")) == NULL) p = "Login incorrect"; (void)printf("%s\n", p); } failures++; if (pwd) log_failedlogin(pwd->pw_uid, hostname, rusername, tty); /* * By default, we allow 10 tries, but after 3 we start * backing off to slow down password guessers. */ if (++cnt > backoff) { if (cnt >= tries) { badlogin(username); sleepexit(1); } sleep((u_int)((cnt - backoff) * tries / 2)); } } /* committed to login -- turn off timeout */ (void)alarm(0); endpwent(); shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell); if (*shell == '\0') shell = _PATH_BSHELL; else if (strlen(shell) >= MAXPATHLEN) { syslog(LOG_ERR, "shell path too long: %s", shell); warnx("invalid shell"); quickexit(1); } /* Destroy environment unless user has requested its preservation. */ if (!pflag) { if ((environ = calloc(1, sizeof (char *))) == NULL) err(1, "calloc"); } else { char **cpp, **cpp2; for (cpp2 = cpp = environ; *cpp; cpp++) { if (strncmp(*cpp, "LD_", 3) && strncmp(*cpp, "ENV=", 4) && strncmp(*cpp, "BASH_ENV=", 9) && strncmp(*cpp, "IFS=", 4)) *cpp2++ = *cpp; } *cpp2 = 0; } /* Note: setusercontext(3) will set PATH */ if (setenv("HOME", pwd->pw_dir, 1) == -1 || setenv("SHELL", pwd->pw_shell, 1) == -1) { warn("unable to setenv()"); quickexit(1); } if (term[0] == '\0') (void)strlcpy(term, stypeof(tty), sizeof(term)); (void)snprintf(mail, sizeof(mail), "%s/%s", _PATH_MAILDIR, pwd->pw_name); if (setenv("TERM", term, 0) == -1 || setenv("LOGNAME", pwd->pw_name, 1) == -1 || setenv("USER", pwd->pw_name, 1) == -1 || setenv("MAIL", mail, 1) == -1) { warn("unable to setenv()"); quickexit(1); } if (hostname) { if (setenv("REMOTEHOST", hostname, 1) == -1) { warn("unable to setenv()"); quickexit(1); } } if (rusername) { if (setenv("REMOTEUSER", rusername, 1) == -1) { warn("unable to setenv()"); quickexit(1); } } if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH)) { warn("unable to set user context"); quickexit(1); } auth_setenv(as); /* if user not super-user, check for disabled logins */ if (!rootlogin) auth_checknologin(lc); setegid(pwd->pw_gid); seteuid(pwd->pw_uid); homeless = chdir(pwd->pw_dir); if (homeless) { if (login_getcapbool(lc, "requirehome", 0)) { (void)printf("No home directory %s!\n", pwd->pw_dir); quickexit(1); } if (chdir("/")) quickexit(0); } quietlog = ((strcmp(pwd->pw_shell, "/sbin/nologin") == 0) || login_getcapbool(lc, "hushlogin", 0) || (access(_PATH_HUSHLOGIN, F_OK) == 0)); seteuid(0); setegid(0); /* XXX use a saved gid instead? */ if ((p = auth_getvalue(as, "warnmsg")) != NULL) (void)printf("WARNING: %s\n\n", p); expire = auth_check_expire(as); if (expire < 0) { (void)printf("Sorry -- your account has expired.\n"); quickexit(1); } else if (expire > 0 && !quietlog) { warning = login_getcaptime(lc, "expire-warn", 2 * DAYSPERWEEK * SECSPERDAY, 2 * DAYSPERWEEK * SECSPERDAY); if (expire < warning) (void)printf("Warning: your account expires on %s", ctime(&pwd->pw_expire)); } /* Nothing else left to fail -- really log in. */ (void)signal(SIGHUP, SIG_DFL); memset(&utmp, 0, sizeof(utmp)); (void)time(&utmp.ut_time); (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); if (hostname) (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); login(&utmp); if (!quietlog) (void)check_failedlogin(pwd->pw_uid); dolastlog(quietlog); login_fbtab(tty, pwd->pw_uid, pwd->pw_gid); (void)chown(ttyn, pwd->pw_uid, (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); /* If fflag is on, assume caller/authenticator has logged root login. */ if (rootlogin && fflag == 0) { if (hostname) syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s%s%s", username, tty, rusername ? rusername : "", rusername ? "@" : "", hostname); else syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty); } if (!quietlog) { if ((copyright = login_getcapstr(lc, "copyright", NULL, NULL)) != NULL) auth_cat(copyright); motd(); if (stat(mail, &st) == 0 && st.st_size != 0) (void)printf("You have %smail.\n", (st.st_mtime > st.st_atime) ? "new " : ""); } (void)signal(SIGALRM, SIG_DFL); (void)signal(SIGQUIT, SIG_DFL); (void)signal(SIGHUP, SIG_DFL); (void)signal(SIGINT, SIG_DFL); (void)signal(SIGTSTP, SIG_IGN); tbuf[0] = '-'; (void)strlcpy(tbuf + 1, (p = strrchr(shell, '/')) ? p + 1 : shell, sizeof(tbuf) - 1); if ((scds.rlim_cur != QUAD_MIN || scds.rlim_max != QUAD_MIN) && setrlimit(RLIMIT_CORE, &scds) < 0) syslog(LOG_ERR, "couldn't reset core dump size: %m"); if (lastchance) (void)printf("WARNING: Your password has expired." " You must change your password, now!\n"); if (setusercontext(lc, pwd, rootlogin ? 0 : pwd->pw_uid, LOGIN_SETALL & ~LOGIN_SETPATH) < 0) { warn("unable to set user context"); quickexit(1); } if (homeless) { (void)printf("No home directory %s!\n", pwd->pw_dir); (void)printf("Logging in with home = \"/\".\n"); (void)setenv("HOME", "/", 1); } if (auth_approval(as, lc, NULL, "login") == 0) { if (auth_getstate(as) & AUTH_EXPIRED) (void)printf("Sorry -- your account has expired.\n"); else (void)printf("approval failure\n"); quickexit(1); } /* * The last thing we do is discard all of the open file descriptors. * Last because the C library may have some open. */ closefrom(STDERR_FILENO + 1); /* * Close the authentication session, make sure it is marked * as okay so no files are removed. */ auth_setstate(as, AUTH_OKAY); auth_close(as); execlp(shell, tbuf, (char *)NULL); err(1, "%s", shell); }
void yp_chpass(char *username) { char *master; int r, rpcport, status; struct yppasswd yppasswd; struct passwd *pw; struct timeval tv; CLIENT *client; extern char *domain; (void)signal(SIGINT, kbintr); (void)signal(SIGQUIT, kbintr); if ((r = yp_get_default_domain(&domain)) != 0) { warnx("can't get local YP domain. Reason: %s", yperr_string(r)); exit(1); } /* * Find the host for the passwd map; it should be running * the daemon. */ if ((r = yp_master(domain, "passwd.byname", &master)) != 0) { warnx("can't find the master YP server. Reason: %s", yperr_string(r)); exit(1); } /* Ask the portmapper for the port of the daemon. */ if ((rpcport = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP)) == 0) { warnx("master YP server not running yppasswd daemon."); warnx("Can't change password."); exit(1); } if (rpcport >= IPPORT_RESERVED) { warnx("yppasswd daemon is on an invalid port."); exit(1); } /* If user doesn't exist, just prompt for old password and exit. */ pw = ypgetpwnam(username); if (pw) { if (pw->pw_uid == 0) { syslog(LOG_ERR, "attempted root password change"); pw = NULL; } else if (*pw->pw_passwd == '\0') { syslog(LOG_ERR, "%s attempting to add password", username); pw = NULL; } } if (pw == NULL) { char *p, salt[_PASSWORD_LEN + 1]; login_cap_t *lc; /* no such user, get appropriate salt to thwart timing attack */ if ((p = getpass("Old password:"******"xx", sizeof(salt)); crypt(p, salt); memset(p, 0, strlen(p)); } warnx("YP passwd database unchanged."); exit(1); } /* prompt for new password */ yppasswd.newpw.pw_passwd = ypgetnewpasswd(pw, &yppasswd.oldpass); /* tell rpc.yppasswdd */ yppasswd.newpw.pw_name = pw->pw_name; yppasswd.newpw.pw_uid = pw->pw_uid; yppasswd.newpw.pw_gid = pw->pw_gid; yppasswd.newpw.pw_gecos = pw->pw_gecos; yppasswd.newpw.pw_dir = pw->pw_dir; yppasswd.newpw.pw_shell = pw->pw_shell; client = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp"); if (client == NULL) { warnx("cannot contact yppasswdd on %s: Reason: %s", master, yperr_string(YPERR_YPBIND)); free(yppasswd.newpw.pw_passwd); exit(1); } client->cl_auth = authunix_create_default(); tv.tv_sec = 2; tv.tv_usec = 0; r = clnt_call(client, YPPASSWDPROC_UPDATE, xdr_yppasswd, &yppasswd, xdr_int, &status, tv); if (r) warnx("rpc to yppasswdd failed."); else if (status) { printf("Couldn't change YP password.\n"); free(yppasswd.newpw.pw_passwd); exit(1); } printf("The YP password has been changed on %s, the master YP passwd server.\n", master); free(yppasswd.newpw.pw_passwd); (void)writev(BACK_CHANNEL, iov, 2); exit(0); }
void doit(struct sockaddr *fromp) { extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */ struct addrinfo hints, *res, *res0; int gaierror; struct passwd *pwd; u_short port; in_port_t *portp; struct pollfd pfd[4]; int cc, nfd, pv[2], s = 0, one = 1; pid_t pid; char *hostname, *errorstr, *errorhost = (char *) NULL; char *cp, sig, buf[BUFSIZ]; char cmdbuf[NCARGS+1], locuser[_PW_NAME_LEN+1], remuser[_PW_NAME_LEN+1]; char remotehost[2 * MAXHOSTNAMELEN + 1]; char hostnamebuf[2 * MAXHOSTNAMELEN + 1]; char naddr[NI_MAXHOST]; char saddr[NI_MAXHOST]; char raddr[NI_MAXHOST]; char pbuf[NI_MAXSERV]; auth_session_t *as; const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; #ifdef KERBEROS AUTH_DAT *kdata = (AUTH_DAT *) NULL; KTEXT ticket = (KTEXT) NULL; char instance[INST_SZ], version[VERSION_SIZE]; struct sockaddr_storage fromaddr; int rc; long authopts; #ifdef CRYPT int pv1[2], pv2[2]; #endif if (sizeof(fromaddr) < fromp->sa_len) { syslog(LOG_ERR, "malformed \"from\" address (af %d)", fromp->sa_family); exit(1); } memcpy(&fromaddr, fromp, fromp->sa_len); #endif (void) signal(SIGINT, SIG_DFL); (void) signal(SIGQUIT, SIG_DFL); (void) signal(SIGTERM, SIG_DFL); #ifdef DEBUG { int t = open(_PATH_TTY, 2); if (t >= 0) { ioctl(t, TIOCNOTTY, (char *)0); (void) close(t); } } #endif switch (fromp->sa_family) { case AF_INET: portp = &((struct sockaddr_in *)fromp)->sin_port; break; case AF_INET6: portp = &((struct sockaddr_in6 *)fromp)->sin6_port; break; default: syslog(LOG_ERR, "malformed \"from\" address (af %d)", fromp->sa_family); exit(1); } if (getnameinfo(fromp, fromp->sa_len, naddr, sizeof(naddr), pbuf, sizeof(pbuf), niflags) != 0) { syslog(LOG_ERR, "malformed \"from\" address (af %d)", fromp->sa_family); exit(1); } #ifdef IP_OPTIONS if (fromp->sa_family == AF_INET) { struct ipoption opts; socklen_t optsize = sizeof(opts); int ipproto, i; struct protoent *ip; if ((ip = getprotobyname("ip")) != NULL) ipproto = ip->p_proto; else ipproto = IPPROTO_IP; if (!getsockopt(STDIN_FILENO, ipproto, IP_OPTIONS, (char *)&opts, &optsize) && optsize != 0) { for (i = 0; (void *)&opts.ipopt_list[i] - (void *)&opts < optsize; ) { u_char c = (u_char)opts.ipopt_list[i]; if (c == IPOPT_LSRR || c == IPOPT_SSRR) exit(1); if (c == IPOPT_EOL) break; i += (c == IPOPT_NOP) ? 1 : (u_char)opts.ipopt_list[i+1]; } } } #endif #ifdef KERBEROS if (!use_kerberos) #endif if (ntohs(*portp) >= IPPORT_RESERVED || ntohs(*portp) < IPPORT_RESERVED/2) { syslog(LOG_NOTICE|LOG_AUTH, "Connection from %s on illegal port %u", naddr, ntohs(*portp)); exit(1); } (void) alarm(60); port = 0; for (;;) { char c; if ((cc = read(STDIN_FILENO, &c, 1)) != 1) { if (cc < 0) syslog(LOG_NOTICE, "read: %m"); shutdown(STDIN_FILENO, SHUT_RDWR); exit(1); } if (c == 0) break; port = port * 10 + c - '0'; } (void) alarm(0); if (port != 0) { int lport; #ifdef KERBEROS if (!use_kerberos) #endif if (port >= IPPORT_RESERVED || port < IPPORT_RESERVED/2) { syslog(LOG_ERR, "2nd port not reserved"); exit(1); } *portp = htons(port); lport = IPPORT_RESERVED - 1; s = rresvport_af(&lport, fromp->sa_family); if (s < 0) { syslog(LOG_ERR, "can't get stderr port: %m"); exit(1); } if (connect(s, (struct sockaddr *)fromp, fromp->sa_len) < 0) { syslog(LOG_INFO, "connect second port %d: %m", port); exit(1); } } #ifdef KERBEROS if (vacuous) { error("rshd: remote host requires Kerberos authentication\n"); exit(1); } #endif #ifdef notdef /* from inetd, socket is already on 0, 1, 2 */ dup2(f, 0); dup2(f, 1); dup2(f, 2); #endif errorstr = NULL; if (getnameinfo(fromp, fromp->sa_len, saddr, sizeof(saddr), NULL, 0, NI_NAMEREQD)== 0) { /* * If name returned by getnameinfo is in our domain, * attempt to verify that we haven't been fooled by someone * in a remote net; look up the name and check that this * address corresponds to the name. */ hostname = saddr; res0 = NULL; #ifdef KERBEROS if (!use_kerberos) #endif if (check_all || local_domain(saddr)) { strlcpy(remotehost, saddr, sizeof(remotehost)); errorhost = remotehost; memset(&hints, 0, sizeof(hints)); hints.ai_family = fromp->sa_family; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_CANONNAME; gaierror = getaddrinfo(remotehost, pbuf, &hints, &res0); if (gaierror) { syslog(LOG_INFO, "Couldn't look up address for %s: %s", remotehost, gai_strerror(gaierror)); errorstr = "Couldn't look up address for your host (%s)\n"; hostname = naddr; } else { for (res = res0; res; res = res->ai_next) { if (res->ai_family != fromp->sa_family) continue; if (res->ai_addrlen != fromp->sa_len) continue; if (getnameinfo(res->ai_addr, res->ai_addrlen, raddr, sizeof(raddr), NULL, 0, niflags) == 0 && strcmp(naddr, raddr) == 0) { hostname = res->ai_canonname ? res->ai_canonname : saddr; break; } } if (res == NULL) { syslog(LOG_NOTICE, "Host addr %s not listed for host %s", naddr, res0->ai_canonname ? res0->ai_canonname : saddr); errorstr = "Host address mismatch for %s\n"; hostname = naddr; } } } strlcpy(hostnamebuf, hostname, sizeof(hostnamebuf)); hostname = hostnamebuf; if (res0) freeaddrinfo(res0); } else strlcpy(hostnamebuf, naddr, sizeof(hostnamebuf)); errorhost = hostname = hostnamebuf; #ifdef KERBEROS if (use_kerberos) { kdata = (AUTH_DAT *) authbuf; ticket = (KTEXT) tickbuf; authopts = 0L; strlcpy(instance, "*", sizeof instance); version[VERSION_SIZE - 1] = '\0'; #ifdef CRYPT if (doencrypt) { struct sockaddr_in local_addr; rc = sizeof(local_addr); if (getsockname(STDIN_FILENO, (struct sockaddr *)&local_addr, &rc) < 0) { syslog(LOG_ERR, "getsockname: %m"); error("rshd: getsockname: %m"); exit(1); } authopts = KOPT_DO_MUTUAL; rc = krb_recvauth(authopts, 0, ticket, "rcmd", instance, (struct sockaddr_in *)&fromaddr, &local_addr, kdata, "", schedule, version); desrw_set_key(&kdata->session, &schedule); } else #endif rc = krb_recvauth(authopts, 0, ticket, "rcmd", instance, (struct sockaddr_in *)&fromaddr, NULL, kdata, "", NULL, version); if (rc != KSUCCESS) { error("Kerberos authentication failure: %s\n", krb_get_err_text(rc)); exit(1); } } else #endif getstr(remuser, sizeof(remuser), "remuser"); getstr(locuser, sizeof(locuser), "locuser"); getstr(cmdbuf, sizeof(cmdbuf), "command"); pwd = getpwnam(locuser); if (pwd == NULL) { syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: unknown login. cmd='%.80s'", remuser, hostname, locuser, cmdbuf); if (errorstr == NULL) errorstr = "Permission denied.\n"; goto fail; } lc = login_getclass(pwd->pw_class); if (lc == NULL) { syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: unknown class. cmd='%.80s'", remuser, hostname, locuser, cmdbuf); if (errorstr == NULL) errorstr = "Login incorrect.\n"; goto fail; } as = auth_open(); if (as == NULL || auth_setpwd(as, pwd) != 0) { syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: unable to allocate memory. cmd='%.80s'", remuser, hostname, locuser, cmdbuf); if (errorstr == NULL) errorstr = "Cannot allocate memory.\n"; goto fail; } setegid(pwd->pw_gid); seteuid(pwd->pw_uid); if (chdir(pwd->pw_dir) < 0) { (void) chdir("/"); #ifdef notdef syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: no home directory. cmd='%.80s'", remuser, hostname, locuser, cmdbuf); error("No remote directory.\n"); exit(1); #endif } seteuid(0); setegid(0); /* XXX use a saved gid instead? */ #ifdef KERBEROS if (use_kerberos) { if (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0') { if (kuserok(kdata, locuser) != 0) { syslog(LOG_INFO|LOG_AUTH, "Kerberos rsh denied to %s.%s@%s", kdata->pname, kdata->pinst, kdata->prealm); error("Permission denied.\n"); exit(1); } } } else #endif if (errorstr || (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' && iruserok_sa(fromp, fromp->sa_len, pwd->pw_uid == 0, remuser, locuser) < 0)) { if (__rcmd_errstr) syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied (%s). cmd='%.80s'", remuser, hostname, locuser, __rcmd_errstr, cmdbuf); else syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied. cmd='%.80s'", remuser, hostname, locuser, cmdbuf); fail: if (errorstr == NULL) errorstr = "Permission denied.\n"; error(errorstr, errorhost); exit(1); } if (pwd->pw_uid) auth_checknologin(lc); (void) write(STDERR_FILENO, "\0", 1); sent_null = 1; if (port) { if (pipe(pv) < 0) { error("Can't make pipe.\n"); exit(1); } #ifdef CRYPT #ifdef KERBEROS if (doencrypt) { if (pipe(pv1) < 0) { error("Can't make 2nd pipe.\n"); exit(1); } if (pipe(pv2) < 0) { error("Can't make 3rd pipe.\n"); exit(1); } } #endif #endif pid = fork(); if (pid == -1) { error("Can't fork; try again.\n"); exit(1); } if (pid) { #ifdef CRYPT #ifdef KERBEROS if (doencrypt) { static char msg[] = SECURE_MESSAGE; (void) close(pv1[1]); (void) close(pv2[1]); des_write(s, msg, sizeof(msg) - 1); } else #endif #endif { (void) close(STDIN_FILENO); (void) close(STDOUT_FILENO); } (void) close(STDERR_FILENO); (void) close(pv[1]); pfd[P_SOCKREAD].fd = s; pfd[P_SOCKREAD].events = POLLIN; pfd[P_PIPEREAD].fd = pv[0]; pfd[P_PIPEREAD].events = POLLIN; nfd = 2; #ifdef CRYPT #ifdef KERBEROS if (doencrypt) { pfd[P_CRYPTREAD].fd = pv1[0]; pfd[P_CRYPTREAD].events = POLLIN; pfd[P_CRYPTWRITE].fd = pv2[0]; pfd[P_CRYPTWRITE].events = POLLOUT; nfd += 2; } else #endif #endif ioctl(pv[0], FIONBIO, (char *)&one); /* should set s nbio! */ do { if (poll(pfd, nfd, INFTIM) < 0) break; if (pfd[P_SOCKREAD].revents & POLLIN) { int ret; #ifdef CRYPT #ifdef KERBEROS if (doencrypt) ret = des_read(s, &sig, 1); else #endif #endif ret = read(s, &sig, 1); if (ret <= 0) pfd[P_SOCKREAD].revents = 0; else killpg(pid, sig); } if (pfd[P_PIPEREAD].revents & POLLIN) { errno = 0; cc = read(pv[0], buf, sizeof(buf)); if (cc <= 0) { shutdown(s, SHUT_RDWR); pfd[P_PIPEREAD].revents = 0; } else { #ifdef CRYPT #ifdef KERBEROS if (doencrypt) (void) des_write(s, buf, cc); else #endif #endif (void) write(s, buf, cc); } } #ifdef CRYPT #ifdef KERBEROS if (doencrypt && (pfd[P_CRYPTREAD].revents & POLLIN)) { errno = 0; cc = read(pv1[0], buf, sizeof(buf)); if (cc <= 0) { shutdown(pv1[0], SHUT_RDWR); pfd[P_CRYPTREAD].revents = 0; } else (void) des_write(STDOUT_FILENO, buf, cc); } if (doencrypt && (pfd[P_CRYPTWRITE].revents & POLLIN)) { errno = 0; cc = des_read(STDIN_FILENO, buf, sizeof(buf)); if (cc <= 0) { shutdown(pv2[0], SHUT_RDWR); pfd[P_CRYPTWRITE].revents = 0; } else (void) write(pv2[0], buf, cc); } #endif #endif } while ((pfd[P_SOCKREAD].revents & POLLIN) || #ifdef CRYPT #ifdef KERBEROS (doencrypt && (pfd[P_CRYPTREAD].revents & POLLIN)) || #endif #endif (pfd[P_PIPEREAD].revents & POLLIN)); exit(0); } setsid(); (void) close(s); (void) close(pv[0]); #ifdef CRYPT #ifdef KERBEROS if (doencrypt) { close(pv1[0]); close(pv2[0]); dup2(pv1[1], 1); dup2(pv2[1], 0); close(pv1[1]); close(pv2[1]); } #endif #endif dup2(pv[1], 2); close(pv[1]); } else setsid(); if (*pwd->pw_shell == '\0') pwd->pw_shell = _PATH_BSHELL; environ = envinit; if (setenv("HOME", pwd->pw_dir, 1) == -1 || setenv("SHELL", pwd->pw_shell, 1) == -1 || setenv("USER", pwd->pw_name, 1) == -1 || setenv("LOGNAME", pwd->pw_name, 1) == -1) errx(1, "cannot setup environment"); if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL)) errx(1, "cannot set user context"); if (auth_approval(as, lc, pwd->pw_name, "rsh") <= 0) errx(1, "approval failure"); auth_close(as); login_close(lc); cp = strrchr(pwd->pw_shell, '/'); if (cp) cp++; else cp = pwd->pw_shell; endpwent(); if (log_success || pwd->pw_uid == 0) { #ifdef KERBEROS if (use_kerberos) syslog(LOG_INFO|LOG_AUTH, "Kerberos shell from %s.%s@%s on %s as %s, cmd='%.80s'", kdata->pname, kdata->pinst, kdata->prealm, hostname, locuser, cmdbuf); else #endif syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'", remuser, hostname, locuser, cmdbuf); } execl(pwd->pw_shell, cp, "-c", cmdbuf, (char *)NULL); perror(pwd->pw_shell); exit(1); }
/*ARGSUSED*/ int main(int argc, char *argv[]) { char hostname[MAXHOSTNAMELEN], s[BUFSIZ], s1[BUFSIZ], date[256]; char *p, *style, *nstyle, *ttynam; struct itimerval ntimer, otimer; int ch, sectimeout, usemine; const char *errstr; struct passwd *pw; struct tm *timp; time_t curtime; login_cap_t *lc; sectimeout = TIMEOUT; style = NULL; usemine = 0; no_timeout = 0; if (!(pw = getpwuid(getuid()))) errx(1, "unknown uid %u.", getuid()); lc = login_getclass(pw->pw_class); while ((ch = getopt(argc, argv, "a:npt:")) != -1) switch (ch) { case 'a': if (lc) { style = login_getstyle(lc, optarg, "auth-lock"); if (style == NULL) errx(1, "invalid authentication style: %s", optarg); } usemine = 1; break; case 't': sectimeout = (int)strtonum(optarg, 1, INT_MAX, &errstr); if (errstr) errx(1, "timeout %s: %s", errstr, optarg); break; case 'p': usemine = 1; break; case 'n': no_timeout = 1; break; default: (void)fprintf(stderr, "usage: %s [-np] [-a style] [-t timeout]\n", __progname); exit(1); } timeout.tv_sec = sectimeout * 60; gethostname(hostname, sizeof(hostname)); if (!(ttynam = ttyname(STDIN_FILENO))) errx(1, "not a terminal?"); curtime = time(NULL); nexttime = curtime + (sectimeout * 60); timp = localtime(&curtime); strftime(date, sizeof(date), "%c", timp); if (!usemine) { /* get key and check again */ if (!readpassphrase("Key: ", s, sizeof(s), RPP_ECHO_OFF) || *s == '\0') exit(0); /* * Don't need EOF test here, if we get EOF, then s1 != s * and the right things will happen. */ (void)readpassphrase("Again: ", s1, sizeof(s1), RPP_ECHO_OFF); if (strcmp(s1, s)) { warnx("\apasswords didn't match."); exit(1); } s[0] = '\0'; } /* set signal handlers */ (void)signal(SIGINT, hi); (void)signal(SIGQUIT, hi); (void)signal(SIGTSTP, hi); (void)signal(SIGALRM, bye); ntimer.it_interval = zerotime; ntimer.it_value = timeout; if (!no_timeout) setitimer(ITIMER_REAL, &ntimer, &otimer); /* header info */ if (no_timeout) { (void)fprintf(stderr, "%s: %s on %s. no timeout\ntime now is %s\n", __progname, ttynam, hostname, date); } else { (void)fprintf(stderr, "%s: %s on %s. timeout in %d minutes\ntime now is %s\n", __progname, ttynam, hostname, sectimeout, date); } for (;;) { if (!readpassphrase("Key: ", s, sizeof(s), RPP_ECHO_OFF) || *s == '\0') { hi(0); continue; } if (usemine) { /* * If user entered 's/key' or the style specified via * the '-a' argument, auth_userokay() will prompt * for a new password. Otherwise, use what we have. */ if ((strcmp(s, "s/key") == 0 && (nstyle = login_getstyle(lc, "skey", "auth-lock"))) || ((nstyle = style) && strcmp(s, nstyle) == 0)) p = NULL; else p = s; if (auth_userokay(pw->pw_name, nstyle, "auth-lock", p)) break; } else if (strcmp(s, s1) == 0) break; (void)putc('\a', stderr); } exit(0); }
int Verify (struct display *d, struct greet_info *greet, struct verify_info *verify) { struct passwd *p; login_cap_t *lc; auth_session_t *as; char *style, *shell, *home, *s, **argv; char path[MAXPATHLEN]; int authok; /* User may have specified an authentication style. */ if ((style = strchr(greet->name, ':')) != NULL) *style++ = '\0'; Debug ("Verify %s, style %s ...\n", greet->name, style ? style : "default"); p = getpwnam (greet->name); endpwent(); if (!p || strlen (greet->name) == 0) { Debug("getpwnam() failed.\n"); bzero(greet->password, strlen(greet->password)); return 0; } if ((lc = login_getclass(p->pw_class)) == NULL) { Debug("login_getclass() failed.\n"); bzero(greet->password, strlen(greet->password)); return 0; } if ((style = login_getstyle(lc, style, "xdm")) == NULL) { Debug("login_getstyle() failed.\n"); bzero(greet->password, strlen(greet->password)); return 0; } if ((as = auth_open()) == NULL) { Debug("auth_open() failed.\n"); login_close(lc); bzero(greet->password, strlen(greet->password)); return 0; } if (auth_setoption(as, "login", "yes") == -1) { Debug("auth_setoption() failed.\n"); login_close(lc); bzero(greet->password, strlen(greet->password)); return 0; } /* Set up state for no challenge, just check a response. */ auth_setstate(as, 0); auth_setdata(as, "", 1); auth_setdata(as, greet->password, strlen(greet->password) + 1); /* Build path of the auth script and call it */ snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style); auth_call(as, path, style, "-s", "response", greet->name, lc->lc_class, (void *)NULL); authok = auth_getstate(as); if ((authok & AUTH_ALLOW) == 0) { Debug("password verify failed\n"); bzero(greet->password, strlen(greet->password)); auth_close(as); login_close(lc); return 0; } /* Run the approval script */ if (!auth_approval(as, lc, greet->name, "auth-xdm")) { Debug("login not approved\n"); bzero(greet->password, strlen(greet->password)); auth_close(as); login_close(lc); return 0; } auth_close(as); login_close(lc); /* Check empty passwords against allowNullPasswd */ if (!greet->allow_null_passwd && strlen(greet->password) == 0) { Debug("empty password not allowed\n"); return 0; } /* Only accept root logins if allowRootLogin resource is set */ if (p->pw_uid == 0 && !greet->allow_root_login) { Debug("root logins not allowed\n"); bzero(greet->password, strlen(greet->password)); return 0; } /* * Shell must be in /etc/shells */ for (;;) { s = getusershell(); if (s == NULL) { /* did not found the shell in /etc/shells -> failure */ Debug("shell not in /etc/shells\n"); bzero(greet->password, strlen(greet->password)); endusershell(); return 0; } if (strcmp(s, p->pw_shell) == 0) { /* found the shell in /etc/shells */ endusershell(); break; } } #else /* !USE_BSDAUTH */ int Verify (struct display *d, struct greet_info *greet, struct verify_info *verify) { struct passwd *p; #ifdef USE_PAM pam_handle_t **pamhp = thepamhp(); #else #ifdef USESHADOW struct spwd *sp; #endif char *user_pass = NULL; #endif #ifdef __OpenBSD__ char *s; struct timeval tp; #endif char *shell, *home; char **argv; Debug ("Verify %s ...\n", greet->name); #if defined(sun) && defined(SVR4) /* Solaris: If CONSOLE is set to /dev/console in /etc/default/login, then root can only login on system console */ # define SOLARIS_LOGIN_DEFAULTS "/etc/default/login" if (strcmp(greet->name, "root") == 0) { char *console = NULL, *tmp = NULL; FILE *fs; if ((fs= fopen(SOLARIS_LOGIN_DEFAULTS, "r")) != NULL) { char str[120]; while (!feof(fs)) { fgets(str, 120, fs); if(str[0] == '#' || strlen(str) < 8) continue; if((tmp = strstr(str, "CONSOLE=")) != NULL) console = strdup((tmp+8)); } fclose(fs); if ( console != NULL && (strncmp(console, "/dev/console", 12) == 0) && (strncmp(d->name,":0",2) != 0) ) { Debug("Not on system console\n"); bzero(greet->password, strlen(greet->password)); XFree(console); return 0; } XFree(console); } else { Debug("Could not open %s\n", SOLARIS_LOGIN_DEFAULTS); } } #endif #ifndef USE_PAM p = getpwnam (greet->name); endpwent(); if (!p || strlen (greet->name) == 0) { Debug ("getpwnam() failed.\n"); bzero(greet->password, strlen(greet->password)); return 0; } else { #ifdef linux if (!strcmp(p->pw_passwd, "!") || !strcmp(p->pw_passwd, "*")) { Debug ("The account is locked, no login allowed.\n"); bzero(greet->password, strlen(greet->password)); return 0; } #endif user_pass = p->pw_passwd; } #endif #ifdef KERBEROS if(strcmp(greet->name, "root") != 0){ char name[ANAME_SZ]; char realm[REALM_SZ]; char *q; int ret; if(krb_get_lrealm(realm, 1)){ Debug ("Can't get Kerberos realm.\n"); } else { sprintf(krbtkfile, "%s.%s", TKT_ROOT, d->name); krb_set_tkt_string(krbtkfile); unlink(krbtkfile); ret = krb_verify_user(greet->name, "", realm, greet->password, 1, "rcmd"); if(ret == KSUCCESS){ chown(krbtkfile, p->pw_uid, p->pw_gid); Debug("kerberos verify succeeded\n"); if (k_hasafs()) { if (k_setpag() == -1) LogError ("setpag() failed for %s\n", greet->name); if((ret = k_afsklog(NULL, NULL)) != KSUCCESS) LogError("Warning %s\n", krb_get_err_text(ret)); } goto done; } else if(ret != KDC_PR_UNKNOWN && ret != SKDC_CANT){ /* failure */ Debug("kerberos verify failure %d\n", ret); krbtkfile[0] = '\0'; } } } #endif #ifndef USE_PAM #ifdef USESHADOW errno = 0; sp = getspnam(greet->name); if (sp == NULL) { Debug ("getspnam() failed, errno=%d. Are you root?\n", errno); } else { user_pass = sp->sp_pwdp; } #ifndef QNX4 endspent(); #endif /* QNX4 doesn't need endspent() to end shadow passwd ops */ #endif #if defined(ultrix) || defined(__ultrix__) if (authenticate_user(p, greet->password, NULL) < 0) #else if (strcmp (crypt (greet->password, user_pass), user_pass)) #endif { if(!greet->allow_null_passwd || strlen(p->pw_passwd) > 0) { Debug ("password verify failed\n"); bzero(greet->password, strlen(greet->password)); return 0; } /* else: null passwd okay */ } #ifdef KERBEROS done: #endif #ifdef __OpenBSD__ /* * Only accept root logins if allowRootLogin resource is set */ if ((p->pw_uid == 0) && !greet->allow_root_login) { Debug("root logins not allowed\n"); bzero(greet->password, strlen(greet->password)); return 0; } /* * Shell must be in /etc/shells */ for (;;) { s = getusershell(); if (s == NULL) { /* did not found the shell in /etc/shells -> failure */ Debug("shell not in /etc/shells\n"); bzero(greet->password, strlen(greet->password)); endusershell(); return 0; } if (strcmp(s, p->pw_shell) == 0) { /* found the shell in /etc/shells */ endusershell(); break; } } /* * Test for expired password */ if (p->pw_change || p->pw_expire) (void)gettimeofday(&tp, (struct timezone *)NULL); if (p->pw_change) { if (tp.tv_sec >= p->pw_change) { Debug("Password has expired.\n"); bzero(greet->password, strlen(greet->password)); return 0; } } if (p->pw_expire) { if (tp.tv_sec >= p->pw_expire) { Debug("account has expired.\n"); bzero(greet->password, strlen(greet->password)); return 0; } } #endif /* __OpenBSD__ */ bzero(user_pass, strlen(user_pass)); /* in case shadow password */ #else /* USE_PAM */ #define PAM_BAIL \ if (pam_error != PAM_SUCCESS) goto pam_failed; PAM_password = greet->password; pam_error = pam_start("xdm", greet->name, &PAM_conversation, pamhp); PAM_BAIL; pam_error = pam_set_item(*pamhp, PAM_TTY, d->name); PAM_BAIL; pam_error = pam_set_item(*pamhp, PAM_RHOST, ""); PAM_BAIL; pam_error = pam_authenticate(*pamhp, 0); PAM_BAIL; pam_error = pam_acct_mgmt(*pamhp, 0); /* really should do password changing, but it doesn't fit well */ PAM_BAIL; pam_error = pam_setcred(*pamhp, 0); PAM_BAIL; p = getpwnam (greet->name); endpwent(); if (!p || strlen (greet->name) == 0) { Debug ("getpwnam() failed.\n"); bzero(greet->password, strlen(greet->password)); return 0; } if (pam_error != PAM_SUCCESS) { pam_failed: pam_end(*pamhp, PAM_SUCCESS); *pamhp = NULL; return 0; } #undef PAM_BAIL #endif /* USE_PAM */ #endif /* USE_BSDAUTH */ Debug ("verify succeeded\n"); /* The password is passed to StartClient() for use by user-based authorization schemes. It is zeroed there. */ verify->uid = p->pw_uid; verify->gid = p->pw_gid; home = p->pw_dir; shell = p->pw_shell; argv = 0; if (d->session) argv = parseArgs (argv, d->session); if (greet->string) argv = parseArgs (argv, greet->string); if (!argv) argv = parseArgs (argv, "xsession"); verify->argv = argv; verify->userEnviron = userEnv (d, p->pw_uid == 0, greet->name, home, shell); Debug ("user environment:\n"); printEnv (verify->userEnviron); verify->systemEnviron = systemEnv (d, greet->name, home); Debug ("system environment:\n"); printEnv (verify->systemEnviron); Debug ("end of environments\n"); return 1; }