Test for locked account in auth.c (bug #442).

Darren Tucker dtucker at zip.com.au
Mon Jan 13 20:59:17 EST 2003


Lee Eakin wrote:
> If we simplify to the point of 'strlen(passwd) < 13'

That is precisely what I was trying to avoid as it would stop valid use
of public-key only authentication via existing no-password strings (eg
"NP" on Solaris).

Damien Miller wrote:
> Kevin Steves wrote:
> > i just wonder if we really want to attempt all these checks.  if you
> > lock a user's password but leave the authorized_keys file permitting
> > access is the account locked?  there's a split in opinion on that i
> > think.

	That's the crux of the issue.  I recently had to disable an account
(normally we just delete 'em) and I realized that sshd would probably
still allow public-key auth.  I had to check the code to be sure, hence
the bug and patch.

	Anyway, here's what a few man pages say about locking accounts:

Redhat passwd -l
	"This option is used to lock the specified account"
Solaris passwd -l
	"Locks password entry for name."
Solaris shadow passwd entry
	"a lock string to indicate that the login is not accessible" 
HP-UX passwd -l
	"Lock user account"
AIX chuser account_locked=yes
	"Indicates if the user account is locked."

	Actually, the AIX example is irrelevant for the code being discussed
since it will be enforced by loginrestrictions(), it's just included for
comparison.

	Depending on which platform and which man page you read, you might
reasonably expect that the account will be rendered unusable even via
public-key authentication.

> I am beginning to agree - the change is much less attractive now than
> when I merged it. This change also moves us away from the OpenBSD
> behaviour, which is something we shouldn't do.

	OpenBSD doesn't have a concept of a "locked account", does it?  (I
couldn't find one in the man pages, anyway.)

	I think the code can be made less ugly by moving the checks into a
separate function (see attached patch).  The question is: should it be,
or should it be backed out?

		-Daz.

-- 
Darren Tucker (dtucker at zip.com.au)
GPG key 8FF4FA69 / D9A3 86E9 7EEE AF4B B2D4  37C9 C982 80C7 8FF4 FA69
    Good judgement comes with experience. Unfortunately, the experience
usually comes from bad judgement.
-------------- next part --------------
Index: auth.c
===================================================================
RCS file: /cvs/openssh/auth.c,v
retrieving revision 1.66
diff -u -r1.66 auth.c
--- auth.c	9 Jan 2003 04:04:28 -0000	1.66
+++ auth.c	13 Jan 2003 08:44:10 -0000
@@ -60,6 +60,38 @@
 int auth_debug_init;
 
 /*
+ * check for locked account
+ */
+int
+locked_account(const char * passwd)
+{
+	/* Solaris, IRIX */
+	if (strcmp(passwd, "*LK*") == 0)
+		return 1;
+
+	/* HP-UX, Tru64 */
+	if (strcmp(passwd, "*") == 0)
+		return 1;
+
+	/* Redhat */
+	if (passwd[0] == '!' && passwd[1] == '!')
+		return 1;
+
+#ifdef __osf
+	/* Tru64 */
+	if (strstr(passwd, "Nologin"))
+		return 1;
+
+	/* we also want to allow "passwordless" accounts */
+	if (passwd[0] == '*' && strcmp(passwd, "*NP*") != 0)
+		return 1;
+#endif
+
+	/* found no reason to think account is locked */
+	return 0;
+}
+
+/*
  * 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
@@ -72,7 +104,7 @@
 allowed_user(struct passwd * pw)
 {
 	struct stat st;
-	const char *hostname = NULL, *ipaddr = NULL, *passwd;
+	const char *hostname = NULL, *ipaddr = NULL, *passwd = NULL;
 	char *shell;
 	int i;
 #ifdef WITH_AIXAUTHENTICATE
@@ -92,15 +124,14 @@
 	/* Grab the password for locked account checking */
 #if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW)
 	spw = getspnam(pw->pw_name);
-	if (!spw)
-		return 0;
-	passwd = spw->sp_pwdp;
+	if (spw)
+		passwd = spw->sp_pwdp;
 #else
 	passwd = pw->pw_passwd;
 #endif
 
 	/* check for locked account */
-	if (strcmp(passwd, "*LK*") == 0 || passwd[0] == '!') {
+	if (passwd && locked_account(passwd)) {
 		log("User %.100s not allowed because account is locked",
 		    pw->pw_name);
 		return 0;
@@ -109,31 +140,33 @@
 #if !defined(USE_PAM) && defined(HAVE_SHADOW_H) && \
     !defined(DISABLE_SHADOW) && defined(HAS_SHADOW_EXPIRE)
 #define	DAY		(24L * 60 * 60) /* 1 day in seconds */
-	today = time(NULL) / DAY;
-	debug3("allowed_user: today %d sp_expire %d sp_lstchg %d"
-	    " sp_max %d", (int)today, (int)spw->sp_expire,
-	    (int)spw->sp_lstchg, (int)spw->sp_max);
-
-	/*
-	 * We assume account and password expiration occurs the
-	 * day after the day specified.
-	 */
-	if (spw->sp_expire != -1 && today > spw->sp_expire) {
-		log("Account %.100s has expired", pw->pw_name);
-		return 0;
-	}
-
-	if (spw->sp_lstchg == 0) {
-		log("User %.100s password has expired (root forced)",
-		    pw->pw_name);
-		return 0;
-	}
-
-	if (spw->sp_max != -1 &&
-	    today > spw->sp_lstchg + spw->sp_max) {
-		log("User %.100s password has expired (password aged)",
-		    pw->pw_name);
-		return 0;
+	if (spw) {
+		today = time(NULL) / DAY;
+		debug3("allowed_user: today %d sp_expire %d sp_lstchg %d"
+		    " sp_max %d", (int)today, (int)spw->sp_expire,
+		    (int)spw->sp_lstchg, (int)spw->sp_max);
+
+		/*
+		 * We assume account and password expiration occurs the
+		 * day after the day specified.
+		 */
+		if (spw->sp_expire != -1 && today > spw->sp_expire) {
+			log("Account %.100s has expired", pw->pw_name);
+			return 0;
+		}
+
+		if (spw->sp_lstchg == 0) {
+			log("User %.100s password has expired (root forced)",
+			    pw->pw_name);
+			return 0;
+		}
+
+		if (spw->sp_max != -1 &&
+		    today > spw->sp_lstchg + spw->sp_max) {
+			log("User %.100s password has expired (password aged)",
+			    pw->pw_name);
+			return 0;
+		}
 	}
 #endif
 
Index: sshd.8
===================================================================
RCS file: /cvs/openssh/sshd.8,v
retrieving revision 1.150
diff -u -r1.150 sshd.8
--- sshd.8	25 Sep 2002 02:20:54 -0000	1.150
+++ sshd.8	13 Jan 2003 08:44:11 -0000
@@ -112,6 +112,30 @@
 authentication, RSA challenge-response authentication, or password
 based authentication.
 .Pp
+Regardless of the authentication type, the account is checked to
+ensure that it is accessible.  An account is not accessible if it is
+locked, listed in
+.Cm DenyUsers
+or its group is listed in
+.Cm DenyGroups
+\&.  An account is considered locked if the passwd entry equals
+.Ql \&*LK\&*
+or
+.Ql \&*
+, contains the string
+.Ql Nologin
+, or has a leading
+.Ql \&!
+(or, on Tru64, a
+.Ql \&*
+) character.  If there is a requirement to disable password authentication
+for the account while allowing still public-key, then the passwd field
+should be set to something other than these values (eg
+.Ql NP
+or
+.Ql \&*NP\&*
+).
+.Pp
 Rhosts authentication is normally disabled
 because it is fundamentally insecure, but can be enabled in the server
 configuration file if desired.


More information about the openssh-unix-dev mailing list