[PATCH #9] Password expiration via /bin/passwd.
Darren Tucker
dtucker at zip.com.au
Thu Nov 21 01:00:39 EST 2002
This is an attempt to simplify the AIX expiry-via-passwd stuff and make
it more generic. (There's actually a net reduction in #ifdefs).
Patch against CVS:
1) configure finds passwd.
2) sshd uses passwd during session if required.
3) sshd uses passwd for PAM change if privsep disabled.
4) sshd uses Buffers for expire and post-login messages (no longer AIX
specific).
5) password_change_required generalized (no longer PAM specific).
Tested on the following configurations:
Redhat 8 (without PAM)
AIX 4.3.3
Solaris 8 (without PAM)
HP-UX 11.0 (trusted configuration, with PAM)
I'm confused about this from auth-pam.c:
/* XXX: This would need to be done in the parent process,
* but there's currently no way to pass such request. */
no_port_forwarding_flag &= ~2;
no_agent_forwarding_flag &= ~2;
no_x11_forwarding_flag &= ~2;
if (!no_port_forwarding_flag && options.allow_tcp_forwarding)
channel_permit_all_opens();
Isn't this all in the post-auth privsep slave? Or am I overlooking
something?
--
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: acconfig.h
===================================================================
RCS file: /cvs/openssh/acconfig.h,v
retrieving revision 1.145
diff -u -r1.145 acconfig.h
--- acconfig.h 26 Sep 2002 00:38:48 -0000 1.145
+++ acconfig.h 20 Nov 2002 13:12:12 -0000
@@ -25,6 +25,9 @@
/* from environment and PATH */
#undef LOGIN_PROGRAM_FALLBACK
+/* Path to passwd program */
+#undef PASSWD_PROGRAM_PATH
+
/* Define if your password has a pw_class field */
#undef HAVE_PW_CLASS_IN_PASSWD
Index: auth-pam.c
===================================================================
RCS file: /cvs/openssh/auth-pam.c,v
retrieving revision 1.54
diff -u -r1.54 auth-pam.c
--- auth-pam.c 28 Jul 2002 20:24:08 -0000 1.54
+++ auth-pam.c 20 Nov 2002 13:12:12 -0000
@@ -60,7 +60,7 @@
/* states for do_pam_conversation() */
enum { INITIAL_LOGIN, OTHER } pamstate = INITIAL_LOGIN;
/* remember whether pam_acct_mgmt() returned PAM_NEW_AUTHTOK_REQD */
-static int password_change_required = 0;
+extern int password_change_required;
/* remember whether the last pam_authenticate() succeeded or not */
static int was_authenticated = 0;
@@ -256,7 +256,6 @@
case PAM_SUCCESS:
/* This is what we want */
break;
-#if 0
case PAM_NEW_AUTHTOK_REQD:
message_cat(&__pam_msg, use_privsep ?
NEW_AUTHTOK_MSG_PRIVSEP : NEW_AUTHTOK_MSG);
@@ -267,7 +266,6 @@
no_agent_forwarding_flag |= 2;
no_x11_forwarding_flag |= 2;
break;
-#endif
default:
log("PAM rejected by account configuration[%d]: "
"%.200s", pam_retval, PAM_STRERROR(__pamh,
@@ -352,6 +350,8 @@
if (pam_retval != PAM_SUCCESS)
fatal("PAM pam_chauthtok failed[%d]: %.200s",
pam_retval, PAM_STRERROR(__pamh, pam_retval));
+ else
+ password_change_required = 0;
#if 0
/* XXX: This would need to be done in the parent process,
* but there's currently no way to pass such request. */
Index: auth-passwd.c
===================================================================
RCS file: /cvs/openssh/auth-passwd.c,v
retrieving revision 1.48
diff -u -r1.48 auth-passwd.c
--- auth-passwd.c 25 Sep 2002 23:14:16 -0000 1.48
+++ auth-passwd.c 20 Nov 2002 13:12:13 -0000
@@ -42,6 +42,8 @@
#include "log.h"
#include "servconf.h"
#include "auth.h"
+#include "buffer.h"
+#include "misc.h"
#if !defined(USE_PAM) && !defined(HAVE_OSF_SIA)
/* Don't need any of these headers for the PAM or SIA cases */
@@ -81,8 +83,10 @@
#endif /* !USE_PAM && !HAVE_OSF_SIA */
extern ServerOptions options;
+extern Buffer login_message;
+extern int password_change_required;
#ifdef WITH_AIXAUTHENTICATE
-extern char *aixloginmsg;
+void aix_remove_embedded_newlines(char *);
#endif
/*
@@ -149,13 +153,23 @@
#endif
#ifdef WITH_AIXAUTHENTICATE
authsuccess = (authenticate(pw->pw_name,password,&reenter,&authmsg) == 0);
+ aix_remove_embedded_newlines(authmsg);
- if (authsuccess)
+ if (authsuccess) {
+ char *msg;
+
+ debug("authenticate() succeeded for user %s: %.100s", pw->pw_name, authmsg);
/* We don't have a pty yet, so just label the line as "ssh" */
if (loginsuccess(authctxt->user,
- get_canonical_hostname(options.verify_reverse_mapping),
- "ssh", &aixloginmsg) < 0)
- aixloginmsg = NULL;
+ get_canonical_hostname(options.verify_reverse_mapping),
+ "ssh", &msg) < 0)
+ msg = NULL;
+ buffer_append(&login_message, msg, strlen(msg));
+ } else {
+ debug("authenticate() failed for user %s: %.100s", pw->pw_name, authmsg);
+ }
+ if (authmsg)
+ xfree(authmsg);
return(authsuccess);
#endif
@@ -232,4 +246,43 @@
/* Authentication is accepted if the encrypted passwords are identical. */
return (strcmp(encrypted_password, pw_password) == 0);
#endif /* !USE_PAM && !HAVE_OSF_SIA */
+}
+
+/*
+ * Perform generic password change via tty
+ * Like do_pam_chauthtok(), it throws a fatal error if the password can't be changed.
+ */
+void
+do_tty_change_password(struct passwd *pw)
+{
+ pid_t pid;
+ int status;
+ mysig_t old_signal;
+
+ old_signal = mysignal(SIGCHLD, SIG_DFL);
+
+ if ((pid = fork()) == -1)
+ fatal("Couldn't fork: %s", strerror(errno));
+
+ if (pid == 0) {
+ setuid(pw->pw_uid);
+ if (geteuid() == 0)
+ execl(PASSWD_PROGRAM_PATH, "passwd", pw->pw_name,
+ (char *)NULL);
+ else
+ execl(PASSWD_PROGRAM_PATH, "passwd", (char *)NULL);
+
+ /* execl shouldn't return */
+ fatal("Couldn't exec %s", PASSWD_PROGRAM_PATH);
+ exit(1);
+ }
+
+ if (waitpid(pid, &status, 0) == -1)
+ fatal("Couldn't wait for child: %s", strerror(errno));
+
+ if (WEXITSTATUS(status)) /* Passwd exited abnormally */
+ fatal("Failed to change password for %s, passwd returned %d", pw->pw_name, status);
+
+ mysignal(SIGCHLD, old_signal);
+ password_change_required = 0;
}
Index: auth.c
===================================================================
RCS file: /cvs/openssh/auth.c,v
retrieving revision 1.61
diff -u -r1.61 auth.c
--- auth.c 9 Nov 2002 16:11:12 -0000 1.61
+++ auth.c 20 Nov 2002 13:12:13 -0000
@@ -58,6 +58,12 @@
/* Debugging messages */
Buffer auth_debug;
int auth_debug_init;
+extern int password_change_required;
+extern Buffer expire_message;
+
+#ifdef WITH_AIXAUTHENTICATE
+void aix_remove_embedded_newlines(char *);
+#endif
/*
* Check if the user is allowed to log in via ssh. If user is listed
@@ -75,21 +81,22 @@
const char *hostname = NULL, *ipaddr = NULL;
char *shell;
int i;
-#ifdef WITH_AIXAUTHENTICATE
- char *loginmsg;
-#endif /* WITH_AIXAUTHENTICATE */
#if !defined(USE_PAM) && defined(HAVE_SHADOW_H) && \
!defined(DISABLE_SHADOW) && defined(HAS_SHADOW_EXPIRE)
struct spwd *spw;
+#endif
/* Shouldn't be called if pw is NULL, but better safe than sorry... */
if (!pw || !pw->pw_name)
return 0;
+ buffer_init(&expire_message);
+#if !defined(USE_PAM) && defined(HAVE_SHADOW_H) && \
+ !defined(DISABLE_SHADOW) && defined(HAS_SHADOW_EXPIRE)
#define DAY (24L * 60 * 60) /* 1 day in seconds */
spw = getspnam(pw->pw_name);
if (spw != NULL) {
- time_t today = time(NULL) / DAY;
+ time_t expiredate, 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);
@@ -106,20 +113,28 @@
if (spw->sp_lstchg == 0) {
log("User %.100s password has expired (root forced)",
pw->pw_name);
- return 0;
+ password_change_required = 1;
+ buffer_append(&expire_message,
+ "You must change your password now.\n", 35);
}
- if (spw->sp_max != -1 &&
- today > spw->sp_lstchg + spw->sp_max) {
+ expiredate = spw->sp_lstchg + spw->sp_max;
+ if (spw->sp_max != -1 && today > expiredate) {
log("User %.100s password has expired (password aged)",
pw->pw_name);
- return 0;
+ password_change_required = 1;
+ buffer_append(&expire_message,
+ "Your password has expired, you must change it now.\n",
+ 51);
+ } else if (spw->sp_max != -1 && expiredate - today < 14) {
+ char msg[100];
+
+ snprintf(msg, 100,
+ "Your password will expire in %d days.\n",
+ (int)(expiredate - today));
+ buffer_append(&expire_message, msg, strlen(msg));
}
}
-#else
- /* Shouldn't be called if pw is NULL, but better safe than sorry... */
- if (!pw || !pw->pw_name)
- return 0;
#endif
/*
@@ -203,25 +218,46 @@
#ifdef WITH_AIXAUTHENTICATE
/*
- * Don't check loginrestrictions() for root account (use
+ * Don't check loginrestrictions or expiry for root account (use
* PermitRootLogin to control logins via ssh), or if running as
* non-root user (since loginrestrictions will always fail).
*/
- if ( (pw->pw_uid != 0) && (geteuid() == 0) &&
- loginrestrictions(pw->pw_name, S_RLOGIN, NULL, &loginmsg) != 0) {
- if (loginmsg && *loginmsg) {
- /* Remove embedded newlines (if any) */
- char *p;
- for (p = loginmsg; *p; p++) {
- if (*p == '\n')
- *p = ' ';
+ if ( (pw->pw_uid != 0) && (geteuid() == 0) ) {
+ char *msg;
+ int passexpcode;
+
+ /* check for AIX account restrictions */
+ if (loginrestrictions(pw->pw_name, S_RLOGIN, NULL, &msg) != 0) {
+ if (msg && *msg) {
+ aix_remove_embedded_newlines(msg);
+ log("Login restricted for %s: %.100s", pw->pw_name, msg);
+ xfree(msg);
}
- /* Remove trailing newline */
- *--p = '\0';
- log("Login restricted for %s: %.100s", pw->pw_name, loginmsg);
+ return 0;
}
- return 0;
- }
+
+ /* check for AIX expired account */
+ passexpcode = passwdexpired(pw->pw_name, &msg);
+ buffer_append(&expire_message, msg, strlen(msg));
+
+ switch (passexpcode) {
+ case 0: /* success, password not expired */
+ break;
+ case 1: /* expired, password change required */
+ password_change_required = 1;
+ break;
+ default: /* expired too long (2) or other error (-1) */
+ if (msg && *msg)
+ aix_remove_embedded_newlines(msg);
+ debug3("AIX passwdexpired returned %d, msg %.100s",
+ passexpcode, msg);
+ log("Password expired too long or system failure for user %s: %.100s",
+ pw->pw_name, msg);
+ if (msg)
+ xfree(msg);
+ return 0;
+ }
+ }
#endif /* WITH_AIXAUTHENTICATE */
/* We found no reason not to let this user try to log on... */
Index: configure.ac
===================================================================
RCS file: /cvs/openssh/configure.ac,v
retrieving revision 1.92
diff -u -r1.92 configure.ac
--- configure.ac 13 Nov 2002 23:55:57 -0000 1.92
+++ configure.ac 20 Nov 2002 13:12:14 -0000
@@ -40,6 +40,13 @@
fi
fi
+AC_PATH_PROG(PASSWD_PROGRAM_PATH, passwd)
+if test ! -z "$PASSWD_PROGRAM_PATH" ; then
+ AC_DEFINE_UNQUOTED(PASSWD_PROGRAM_PATH, "$PASSWD_PROGRAM_PATH")
+else
+ AC_MSG_ERROR([*** passwd command not found - check config.log ***])
+fi
+
if test -z "$LD" ; then
LD=$CC
fi
Index: session.c
===================================================================
RCS file: /cvs/openssh/session.c,v
retrieving revision 1.222
diff -u -r1.222 session.c
--- session.c 26 Sep 2002 00:38:50 -0000 1.222
+++ session.c 20 Nov 2002 13:12:16 -0000
@@ -102,10 +102,11 @@
/* data */
#define MAX_SESSIONS 10
Session sessions[MAX_SESSIONS];
+Buffer expire_message; /* "password will expire/has expired" messages */
+Buffer login_message; /* message to be displayed after login */
+int password_change_required = 0;
-#ifdef WITH_AIXAUTHENTICATE
-char *aixloginmsg;
-#endif /* WITH_AIXAUTHENTICATE */
+void do_tty_change_password(struct passwd *);
#ifdef HAVE_LOGIN_CAP
login_cap_t *lc;
@@ -456,10 +457,11 @@
#if defined(USE_PAM)
do_pam_session(s->pw->pw_name, NULL);
do_pam_setcred(1);
- if (is_pam_password_change_required())
+#endif /* USE_PAM */
+
+ if (password_change_required)
packet_disconnect("Password change required but no "
"TTY available");
-#endif /* USE_PAM */
/* Fork the child. */
if ((pid = fork()) == 0) {
@@ -723,6 +725,7 @@
socklen_t fromlen;
struct sockaddr_storage from;
struct passwd * pw = s->pw;
+ int password_changed = 0;
pid_t pid = getpid();
/*
@@ -746,16 +749,23 @@
options.verify_reverse_mapping),
(struct sockaddr *)&from, fromlen);
-#ifdef USE_PAM
/*
* If password change is needed, do it now.
* This needs to occur before the ~/.hushlogin check.
*/
+#ifdef USE_PAM
if (is_pam_password_change_required()) {
print_pam_messages();
- do_pam_chauthtok();
+ if (!use_privsep)
+ do_pam_chauthtok();
}
#endif
+ buffer_append(&expire_message, "\0", 1);
+ if (password_change_required) {
+ printf("%s", (char *)buffer_ptr(&expire_message));
+ do_tty_change_password(pw);
+ password_changed = 1;
+ }
if (check_quietlogin(s, command))
return;
@@ -764,10 +774,11 @@
if (!is_pam_password_change_required())
print_pam_messages();
#endif /* USE_PAM */
-#ifdef WITH_AIXAUTHENTICATE
- if (aixloginmsg && *aixloginmsg)
- printf("%s\n", aixloginmsg);
-#endif /* WITH_AIXAUTHENTICATE */
+ if (!password_changed)
+ printf("%s", (char *)buffer_ptr(&expire_message));
+
+ buffer_append(&login_message, "\0", 1);
+ printf("%s", (char *)buffer_ptr(&login_message));
#ifndef NO_SSH_LASTLOG
if (options.print_lastlog && s->last_login_time != 0) {
Index: openbsd-compat/port-aix.c
===================================================================
RCS file: /cvs/openssh/openbsd-compat/port-aix.c,v
retrieving revision 1.6
diff -u -r1.6 port-aix.c
--- openbsd-compat/port-aix.c 7 Jul 2002 02:17:36 -0000 1.6
+++ openbsd-compat/port-aix.c 20 Nov 2002 13:12:16 -0000
@@ -52,5 +52,25 @@
xfree(cp);
}
-#endif /* _AIX */
+#ifdef WITH_AIXAUTHENTICATE
+/*
+ * Remove embedded newlines in string (if any).
+ * Used before logging messages returned by AIX authentication functions
+ * so the message is logged on one line.
+ */
+void
+aix_remove_embedded_newlines(char *p)
+{
+ if (p == NULL)
+ return;
+
+ for (; *p; p++) {
+ if (*p == '\n')
+ *p = ' ';
+ }
+ /* Remove trailing newline */
+ *--p = '\0';
+}
+#endif /* WITH_AIXAUTHENTICATE */
+#endif /* _AIX */
More information about the openssh-unix-dev
mailing list