[PATCH] Fix login.conf, expiration, BSD compatibility in OpenSSH

Andrey A. Chernov ache at nagual.pp.ru
Sun Feb 27 11:01:49 EST 2000


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





More information about the openssh-unix-dev mailing list