Mailing List Archive

[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@nagual.pp.ru>
http://nagual.pp.ru/~ache/


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-current" in the body of the message