Andrey A. Chernov
2000-Feb-27 00:01 UTC
[PATCH] Fix login.conf, expiration, BSD compatibility in OpenSSH
This patch revive almost all login.conf and password/account expiration features, makes OpenSSH more FreeBSD login compatible and fix non-critical memory leak. Please review and commit. --- sshd.c.old Fri Feb 25 08:23:45 2000 +++ sshd.c Sun Feb 27 02:53:33 2000 @@ -37,9 +37,8 @@ #endif /* LIBWRAP */ #ifdef __FreeBSD__ -#include <libutil.h> -#include <syslog.h> #define LOGIN_CAP +#define _PATH_CHPASS "/usr/bin/passwd" #endif /* __FreeBSD__ */ #ifdef LOGIN_CAP @@ -1246,6 +1245,7 @@ return 0; } } +#ifndef __FreeBSD__ /* FreeBSD handle it later */ /* Fail if the account's expiration time has passed. */ if (pw->pw_expire != 0) { struct timeval tv; @@ -1254,6 +1254,7 @@ if (tv.tv_sec >= pw->pw_expire) return 0; } +#endif /* !__FreeBSD__ */ /* We found no reason not to let this user try to log on... */ return 1; } @@ -1268,6 +1269,12 @@ struct passwd *pw, pwcopy; int plen, ulen; char *user; +#ifdef LOGIN_CAP + login_cap_t *lc; + char *hosts; + const char *from_host, *from_ip; + int denied; +#endif /* LOGIN_CAP */ /* Get the name of the user that we wish to log in as. */ packet_read_expect(&plen, SSH_CMSG_USER); @@ -1338,6 +1345,38 @@ packet_disconnect("ROOT LOGIN REFUSED FROM %.200s", get_canonical_hostname()); } + +#ifdef LOGIN_CAP + lc = login_getpwclass(pw); + if (lc == NULL) + lc = login_getclassbyname(NULL, pw); + from_host = get_canonical_hostname(); + from_ip = get_remote_ipaddr(); + + denied = 0; + if ((hosts = login_getcapstr(lc, "host.deny", NULL, NULL)) != NULL) { + denied = match_hostname(from_host, hosts, strlen(hosts)); + if (!denied) + denied = match_hostname(from_ip, hosts, strlen(hosts)); + } + if (!denied && + (hosts = login_getcapstr(lc, "host.allow", NULL, NULL)) != NULL) { + denied = !match_hostname(from_host, hosts, strlen(hosts)); + if (denied) + denied = !match_hostname(from_ip, hosts, strlen(hosts)); + } + login_close(lc); + if (denied) { + log("Denied connection for %.200s from %.200s [%.200s].", + pw->pw_name, from_host, from_ip); + packet_disconnect("Sorry, you are not allowed to connect."); + } +#endif /* LOGIN_CAP */ + + if (pw->pw_uid == 0) + log("ROOT LOGIN as '%.100s' from %.100s", + pw->pw_name, get_canonical_hostname()); + /* The user has been authenticated and accepted. */ packet_start(SSH_SMSG_SUCCESS); packet_send(); @@ -2086,6 +2125,11 @@ login_cap_t *lc; char *fname; #endif /* LOGIN_CAP */ +#ifdef __FreeBSD__ +#define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ + struct timeval tv; + time_t warntime = DEFAULT_WARN; +#endif /* __FreeBSD__ */ /* Get remote host name. */ hostname = get_canonical_hostname(); @@ -2157,6 +2201,50 @@ quiet_login = login_getcapbool(lc, "hushlogin", quiet_login); #endif /* LOGIN_CAP */ +#ifdef __FreeBSD__ + if (pw->pw_change || pw->pw_expire) + (void)gettimeofday(&tv, NULL); +#ifdef LOGIN_CAP + warntime = login_getcaptime(lc, "warnpassword", + DEFAULT_WARN, DEFAULT_WARN); +#endif /* LOGIN_CAP */ + /* + * If the password change time is set and has passed, give the + * user a password expiry notice and chance to change it. + */ + if (pw->pw_change != 0) { + if (tv.tv_sec >= pw->pw_change) { + (void)printf( + "Sorry -- your password has expired.\n"); + log("%s Password expired - forcing change", + pw->pw_name); + command = _PATH_CHPASS; + } else if (pw->pw_change - tv.tv_sec < warntime && + !quiet_login) + (void)printf( + "Warning: your password expires on %s", + ctime(&pw->pw_change)); + } +#ifdef LOGIN_CAP + warntime = login_getcaptime(lc, "warnexpire", + DEFAULT_WARN, DEFAULT_WARN); +#endif /* LOGIN_CAP */ + if (pw->pw_expire) { + if (tv.tv_sec >= pw->pw_expire) { + (void)printf( + "Sorry -- your account has expired.\n"); + log( + "LOGIN %.200s REFUSED (EXPIRED) FROM %.200s ON TTY %.200s", + pw->pw_name, hostname, ttyname); + exit(254); + } else if (pw->pw_expire - tv.tv_sec < warntime && + !quiet_login) + (void)printf( + "Warning: your account expires on %s", + ctime(&pw->pw_expire)); + } +#endif /* __FreeBSD__ */ + /* * If the user has logged in before, display the time of last * login. However, don't display anything extra if a command @@ -2203,10 +2291,9 @@ !options.use_login) { #ifdef LOGIN_CAP fname = login_getcapstr(lc, "welcome", NULL, NULL); - login_close(lc); if (fname == NULL || (f = fopen(fname, "r")) == NULL) f = fopen("/etc/motd", "r"); -#else /* LOGIN_CAP */ +#else /* !LOGIN_CAP */ f = fopen("/etc/motd", "r"); #endif /* LOGIN_CAP */ /* Print /etc/motd if it exists. */ @@ -2216,6 +2303,9 @@ fclose(f); } } +#ifdef LOGIN_CAP + login_close(lc); +#endif /* LOGIN_CAP */ /* Do common processing for the child, such as execing the command. */ do_child(command, pw, term, display, auth_proto, auth_data, ttyname); @@ -2363,7 +2453,7 @@ char buf[256]; FILE *f; unsigned int envsize, i; - char **env; + char **env = NULL; extern char **environ; struct stat st; char *argv[10]; @@ -2373,29 +2463,24 @@ lc = login_getpwclass(pw); if (lc == NULL) lc = login_getclassbyname(NULL, pw); -#endif /* LOGIN_CAP */ - + if (pw->pw_uid != 0) + auth_checknologin(lc); +#else /* !LOGIN_CAP */ f = fopen("/etc/nologin", "r"); -#ifdef __FreeBSD__ - if (f == NULL) - f = fopen("/var/run/nologin", "r"); -#endif /* __FreeBSD__ */ if (f) { /* /etc/nologin exists. Print its contents and exit. */ -#ifdef LOGIN_CAP - /* On FreeBSD, etc., allow overriding nologin via login.conf. */ - if (!login_getcapbool(lc, "ignorenologin", 0)) { -#else /* LOGIN_CAP */ - if (1) { -#endif /* LOGIN_CAP */ - while (fgets(buf, sizeof(buf), f)) - fputs(buf, stderr); - fclose(f); - if (pw->pw_uid != 0) - exit(254); - } + while (fgets(buf, sizeof(buf), f)) + fputs(buf, stderr); + fclose(f); + if (pw->pw_uid != 0) + exit(254); } +#endif /* LOGIN_CAP */ + +#ifdef LOGIN_CAP + if (options.use_login) +#endif /* LOGIN_CAP */ /* Set login name in the kernel. */ if (setlogin(pw->pw_name) < 0) error("setlogin failed: %s", strerror(errno)); @@ -2405,12 +2490,42 @@ switch, so we let login(1) to this for us. */ if (!options.use_login) { #ifdef LOGIN_CAP - if (setclasscontext(pw->pw_class, LOGIN_SETPRIORITY | - LOGIN_SETRESOURCES | LOGIN_SETUMASK) == -1) { - perror("setclasscontext"); - exit(1); - } -#endif /* LOGIN_CAP */ + char **tmpenv; + + /* Initialize temp environment */ + envsize = 64; + env = xmalloc(envsize * sizeof(char *)); + env[0] = NULL; + + child_set_env(&env, &envsize, "PATH", + (pw->pw_uid == 0) ? + _PATH_STDPATH : _PATH_DEFPATH); + + snprintf(buf, sizeof buf, "%.200s/%.50s", + _PATH_MAILDIR, pw->pw_name); + child_set_env(&env, &envsize, "MAIL", buf); + + if (getenv("TZ")) + child_set_env(&env, &envsize, "TZ", getenv("TZ")); + + /* Save parent environment */ + tmpenv = environ; + environ = env; + + if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETALL) < 0) + fatal("setusercontext failed: %s", strerror(errno)); + + /* Restore parent environment */ + env = environ; + environ = tmpenv; + + for (envsize = 0; env[envsize] != NULL; ++envsize) + ; + envsize = (envsize < 100) ? 100 : envsize + 16; + env = xrealloc(env, envsize * sizeof(char *)); + +#else /* !LOGIN_CAP */ + if (getuid() == 0 || geteuid() == 0) { if (setgid(pw->pw_gid) < 0) { perror("setgid"); @@ -2428,18 +2543,15 @@ } if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) fatal("Failed to set uids to %d.", (int) pw->pw_uid); +#endif /* LOGIN_CAP */ } /* * Get the shell from the password data. An empty shell field is * legal, and means /bin/sh. */ + shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; #ifdef LOGIN_CAP - shell = pw->pw_shell; shell = login_getcapstr(lc, "shell", shell, shell); - if (shell[0] == '\0') - shell = _PATH_BSHELL; -#else /* LOGIN_CAP */ - shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; #endif /* LOGIN_CAP */ #ifdef AFS @@ -2455,29 +2567,31 @@ #endif /* AFS */ /* Initialize the environment. */ - envsize = 100; - env = xmalloc(envsize * sizeof(char *)); - env[0] = NULL; + if (env == NULL) { + envsize = 100; + env = xmalloc(envsize * sizeof(char *)); + env[0] = NULL; + } if (!options.use_login) { /* Set basic environment. */ child_set_env(&env, &envsize, "USER", pw->pw_name); child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); child_set_env(&env, &envsize, "HOME", pw->pw_dir); -#ifdef LOGIN_CAP - child_set_env(&env, &envsize, "PATH", - login_getpath(lc, "path", _PATH_STDPATH)); -#else /* LOGIN_CAP */ +#ifndef LOGIN_CAP child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); -#endif /* LOGIN_CAP */ snprintf(buf, sizeof buf, "%.200s/%.50s", _PATH_MAILDIR, pw->pw_name); child_set_env(&env, &envsize, "MAIL", buf); +#endif /* !LOGIN_CAP */ /* Normal systems set SHELL by default. */ child_set_env(&env, &envsize, "SHELL", shell); } +#ifdef LOGIN_CAP + if (options.use_login) +#endif /* LOGIN_CAP */ if (getenv("TZ")) child_set_env(&env, &envsize, "TZ", getenv("TZ")); @@ -2559,10 +2673,6 @@ */ endpwent(); -#ifdef LOGIN_CAP - login_close(lc); -#endif /* LOGIN_CAP */ - /* * Close any extra open file descriptors so that we don\'t have them * hanging around in clients. Note that we want to do this after @@ -2573,9 +2683,46 @@ close(i); /* Change current directory to the user\'s home directory. */ - if (chdir(pw->pw_dir) < 0) + if ( +#ifdef __FreeBSD__ + !*pw->pw_dir || +#endif /* __FreeBSD__ */ + chdir(pw->pw_dir) < 0 + ) { +#ifdef __FreeBSD__ + int quiet_login = 0; +#endif /* __FreeBSD__ */ +#ifdef LOGIN_CAP + if (login_getcapbool(lc, "requirehome", 0)) { + (void)printf("Home directory not available\n"); + log("LOGIN %.200s REFUSED (HOMEDIR) ON TTY %.200s", + pw->pw_name, ttyname); + exit(254); + } +#endif /* LOGIN_CAP */ +#ifdef __FreeBSD__ + if (chdir("/") < 0) { + (void)printf("Cannot find root directory\n"); + log("LOGIN %.200s REFUSED (ROOTDIR) ON TTY %.200s", + pw->pw_name, ttyname); + exit(254); + } +#ifdef LOGIN_CAP + quiet_login = login_getcapbool(lc, "hushlogin", 0); +#endif /* LOGIN_CAP */ + if (!quiet_login || *pw->pw_dir) + (void)printf( + "No home directory.\nLogging in with home = \"/\".\n"); + +#else /* !__FreeBSD__ */ + fprintf(stderr, "Could not chdir to home directory %s: %s\n", pw->pw_dir, strerror(errno)); +#endif /* __FreeBSD__ */ + } +#ifdef LOGIN_CAP + login_close(lc); +#endif /* LOGIN_CAP */ /* * Must take new environment into use so that .ssh/rc, /etc/sshrc and @@ -2588,26 +2735,6 @@ * in this order). */ if (!options.use_login) { -#ifdef __FreeBSD__ - /* - * If the password change time is set and has passed, give the - * user a password expiry notice and chance to change it. - */ - if (pw->pw_change != 0) { - struct timeval tv; - - (void)gettimeofday(&tv, NULL); - if (tv.tv_sec >= pw->pw_change) { - (void)printf( - "Sorry -- your password has expired.\n"); - syslog(LOG_INFO, - "%s Password expired - forcing change", - pw->pw_name); - if (system("/usr/bin/passwd") != 0) - perror("/usr/bin/passwd"); - } - } -#endif /* __FreeBSD__ */ if (stat(SSH_USER_RC, &st) >= 0) { if (debug_flag) fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC); @@ -2675,7 +2802,11 @@ mailbox = getenv("MAIL"); if (mailbox != NULL) { if (stat(mailbox, &mailstat) != 0 || mailstat.st_size == 0) +#ifdef __FreeBSD__ + ; +#else /* !__FreeBSD__ */ printf("No mail.\n"); +#endif /* __FreeBSD__ */ else if (mailstat.st_mtime < mailstat.st_atime) printf("You have mail.\n"); else -- Andrey A. Chernov <ache at nagual.pp.ru> http://nagual.pp.ru/~ache/ To Unsubscribe: send mail to majordomo at FreeBSD.org with "unsubscribe freebsd-current" in the body of the message