$NetBSD: patch-ac,v 1.8 2005/12/05 14:12:29 taca Exp $ --- password/poppassd.c.orig Mon Dec 5 10:45:21 2005 +++ password/poppassd.c @@ -171,8 +171,13 @@ /* LANMAN allows up to 14 char passwords (truncates if longer), but tacacs only seems to allow 11. */ +#ifndef PASSWD_BINARY #define PASSWD_BINARY "/usr/bin/passwd" /* TBD: config.h */ +#endif + +#ifndef SMBPASSWD_BINARY #define SMBPASSWD_BINARY "/usr/bin/smbpasswd" /* TBD: config.h */ +#endif #include "config.h" @@ -180,6 +185,8 @@ #include #include +#include + #if HAVE_SYS_NETINET_IN_H # include #endif @@ -257,7 +264,7 @@ int dochild (int master, char *slave int findpty (char **slave); void writestring (int fd, char *s); int talktochild (int master, char *user, char *oldpass, char *newpass, - char *emess, int asroot); + char *emess); int match (char *str, char *pat); int expect (int master, char **expected, char *buf); void getemess (int master, char **expected, char *buf); @@ -274,7 +281,7 @@ void get_client_info ( POP *p, BOOL n char *sock_ntop ( struct sockaddr *p, int salen ); int sock_port ( struct sockaddr *p, int salen ); char *debug_str ( char *p, int inLen, int order ); - +void reapchild (int); /* * External prototypes @@ -295,6 +302,8 @@ pop_result auth_user ( POP *p, char *pas static char *P1[] = { + "changing local password for *\nold password: ", /* BSD */ + "old password: ", /* NetBSD>=3.0 */ "changing password for *\nold password: ", /* shadow */ "enter login password: ", /* Solaris */ "old smb password: ", /* smb */ @@ -324,6 +333,7 @@ static char *P4[] = { "password changed. ", /* shadow */ "password changed ", /* smb */ + "password changed for user *\n", /* smb */ "" }; @@ -338,6 +348,8 @@ char msg_buf [ 2048 ] = "" char *pwd_binary = PASSWD_BINARY; char *smb_binary = SMBPASSWD_BINARY; +int child_pid; +int child_status; /* * Be careful using TRACE in an 'if' statement! @@ -347,18 +359,19 @@ char *smb_binary = SM #define RUN_PASSWD 1 #define RUN_SMBPASSWD 2 - int main ( int argc, char *argv[] ) { char line [BUFSIZE] = ""; char oldpass [BUFSIZE] = ""; char newpass [BUFSIZE] = ""; int nopt = -1; - static char options [] = "dl:p:Rs:t:vy:?"; + static char options [] = "dhl:oPp:RSs:t:vy:"; int mode = 0; char *ptr = NULL; POP p; BOOL no_rev_lookup = FALSE; + int compat_mode = 0; + BOOL bad_user = FALSE; #ifdef HAS_SHADOW struct spwd *spwd; @@ -376,8 +389,6 @@ int main ( int argc, char *argv[] ) pname = ptr + 1; } - openlog ( pname, POP_LOGOPTS, LOG_LOCAL2 ); - /* * Set up some stuff in -p- so we can call Qpopper routines */ @@ -385,6 +396,17 @@ int main ( int argc, char *argv[] ) p.AuthType = noauth; p.myname = pname; +#ifndef POP_FACILITY +# if defined(OSF1) || defined(LINUX) +# define POP_FACILITY LOG_MAIL +# else +# define POP_FACILITY LOG_LOCAL0 +# endif /* OSF1 or Linux */ +#endif /* POP_FACILITY not defined */ + + p.log_facility = (log_facility_type) POP_FACILITY; + openlog ( pname, POP_LOGOPTS, p.log_facility ); + /* * Handle command-line options */ @@ -393,9 +415,9 @@ int main ( int argc, char *argv[] ) { switch (nopt) { - case '?': - fprintf ( stderr, "%s [-?] [-d] [-l 0|1|2] [-p [passd-path]] " - "[-R] [-s [smbpasswd-path]]\n\t" + case 'h': + fprintf ( stderr, "%s [-h] [-d] [-l 0|1|2] [-p [passd-path]] " + "[-P] [-R] [-S] [-s [smbpasswd-path]]\n\t" "[-t trace-file] [-v] [-y log-facility]\n", pname ); exit (1); @@ -412,20 +434,34 @@ int main ( int argc, char *argv[] ) verbose = TRUE; break; + case 'S': + mode |= RUN_SMBPASSWD; + TRACE ( trace_file, POP_DEBUG, HERE, + "Changing SMB password enabled" ); + break; + + case 's': mode |= RUN_SMBPASSWD; if ( optarg != NULL && *optarg != '\0' ) smb_binary = optarg; TRACE ( trace_file, POP_DEBUG, HERE, - "Changing SMB passwords using %s", smb_binary ); + "Changing SMB password using %s", smb_binary ); + break; + + case 'P': + mode |= RUN_PASSWD; + TRACE ( trace_file, POP_DEBUG, HERE, + "Changing standard password enabled" ); break; + case 'p': mode |= RUN_PASSWD; if ( optarg != NULL && *optarg != '\0' ) pwd_binary = optarg; TRACE ( trace_file, POP_DEBUG, HERE, - "Changing standard passwords using %s", pwd_binary ); + "Changing standard password using %s", pwd_binary ); break; case 't': @@ -476,6 +512,10 @@ int main ( int argc, char *argv[] ) "Avoiding reverse lookups (-R)" ); break; + case 'o': /* compatibility mode */ + compat_mode = 1; + break; + case 'y': /* log facility */ if ( optarg == NULL || *optarg == '\0' ) { err_msg ( HERE, "-y value expected" ); @@ -563,44 +603,50 @@ int main ( int argc, char *argv[] ) return 1; } - WriteToClient ( "200 your new password please." ); - ReadFromClient ( line ); - sscanf ( line, "newpass %s", newpass ); - - /* new pass required */ - if ( strlen (newpass) == 0 ) - { - WriteToClient ("500 New password required."); - return 1; - } - pw = getpwnam ( userid ); if ( pw == NULL ) { - WriteToClient ( "500 Invalid user or password" ); - return 1; - } + bad_user = TRUE; + } else { #ifdef HAS_SHADOW - if ((spwd = getspnam(userid)) == NULL) - pw->pw_passwd = ""; - else - pw->pw_passwd = spwd->sp_pwdp; + if ((spwd = getspnam(userid)) == NULL) + pw->pw_passwd = ""; + else + pw->pw_passwd = spwd->sp_pwdp; #endif + if ( chkPass ( userid, oldpass, pw, &p ) == FAILURE ) + { + syslog ( LOG_ERR, "password failure for %s", userid ); + bad_user = TRUE; + } + + if ( pw->pw_uid <= BLOCK_UID ) + { + syslog( LOG_ERR, "someone tried to change %s's password", userid ); + bad_user = TRUE; + } + } + if (compat_mode && bad_user) { + sleep(1); /* XXX */ + WriteToClient ( "500 Invalid user or password" ); + return 1; + } - if ( chkPass ( userid, oldpass, pw, &p ) == FAILURE ) - { - syslog ( LOG_ERR, "password failure for %s", userid ); - WriteToClient ( "500 Invalid user or password" ); - return 1; + WriteToClient ( "200 your new password please." ); + ReadFromClient ( line ); + sscanf ( line, "newpass %s", newpass ); + + if (bad_user) { + WriteToClient ( "500 Invalid user or password" ); + return 1; } - if ( pw->pw_uid <= BLOCK_UID ) - + /* new pass required */ + if ( strlen (newpass) == 0 ) { - syslog ( LOG_ERR, "someone tried to change %s's password", userid ); - WriteToClient ( "500 Not a user account." ); + WriteToClient ("500 New password required."); return 1; } @@ -633,6 +679,20 @@ int main ( int argc, char *argv[] ) return 0; } +/* catch child */ +void +reapchild(sig) + int sig; +{ + int status; + int pid; + + while ((pid = wait3(&status, WNOHANG, NULL)) > 0) { + child_pid = pid; + child_status = status; + } +} + /* Run a child process to do the password change */ @@ -653,6 +713,10 @@ void runchild ( char *userid, char *oldp exit ( 1 ); } + signal(SIGCHLD, reapchild); + child_pid = 0; + child_status = -1; + /* fork child process to talk to password program */ pid = fork(); @@ -665,7 +729,7 @@ void runchild ( char *userid, char *oldp if ( pid > 0 ) /* Parent */ { - if (talktochild (master, userid, oldpass, newpass, emess, smb) == FAILURE) + if (talktochild (master, userid, oldpass, newpass, emess) == FAILURE) { logit ( trace_file, LOG_ERR, HERE, "%s failed for %s", smb ? "smbpasswd" : "passwd", userid ); @@ -677,10 +741,16 @@ void runchild ( char *userid, char *oldp wpid = waitpid ( pid, &wstat, 0 ); if ( wpid < 0 ) { - logit ( trace_file, LOG_ERR, HERE, "wait for child failed" ); - WriteToClient ("500 Server error (wait failed), get help!"); - exit(1); + if (child_pid > 0) { + wpid = child_pid; + wstat = child_status; + } else { + logit ( trace_file, LOG_ERR, HERE, "wait for child failed" ); + WriteToClient ("500 Server error (wait failed), get help!"); + exit(1); + } } + signal(SIGCHLD, SIG_DFL); if ( pid != wpid ) { @@ -702,7 +772,6 @@ void runchild ( char *userid, char *oldp WriteToClient ("500 Server error (abnormal exit), get help!"); exit(1); } - close ( master ); /* done with the pty */ } else /* Child */ @@ -784,6 +853,13 @@ int dochild (int master, char *slavedev, chdir ("/"); umask (0); +#ifdef HAVE_SETLOGIN + if (setlogin(userid) < 0) { + err_msg ( HERE, "setlogin failed: %m" ); + return(0); + } +#endif + /* * Become the user and run passwd. Linux shadowed passwd doesn't need * to be run as root with the username passed on the command line. @@ -883,20 +959,19 @@ void writestring (int fd, char *s) * that the password wasn't changed). */ int talktochild (int master, char *userid, char *oldpass, char *newpass, - char *emess, int asroot) + char *emess) { + int n; char buf[BUFSIZE]; char pswd[BUFSIZE+1]; *emess = 0; TRACE ( trace_file, POP_DEBUG, HERE, - "talktochild; master=%d; userid=%s; asroot=%d", - master, userid, asroot ); + "talktochild; master=%d; userid=%s", master, userid); /* only get current password if not root */ - if (!asroot) - { + if (geteuid() == 0) { /* wait for current password prompt */ if (!expect(master, P1, buf)) return FAILURE; @@ -926,6 +1001,10 @@ int talktochild (int master, char *useri if ( !expect(master, P4, buf) ) TRACE ( trace_file, POP_DEBUG, HERE, "no response -- assuming OK" ); + while ((n = read(master, buf, sizeof buf)) > 0) { + TRACE ( trace_file, POP_DEBUG, HERE, "reading remained output" ); + ; + } return SUCCESS; } @@ -955,8 +1034,12 @@ int match (char *str, char *pat) strlen(pat), debug_str(pat, strlen(pat), 1) ); while (*str && *pat) { - if (*pat == '*') - break; + if (*pat == '*') { + pat++; + while (*str != '\0' && *str != '\n') + *str++; + continue; + } /* ignore multiple space sequences */ if (*pat == ' ' && isspace (*str)) { @@ -1055,7 +1138,11 @@ int expect (int master, char **expected, if ( m < 0 ) { err_msg ( HERE, "read error from child" ); return FAILURE; - } + } else if (m == 0) { + TRACE ( trace_file, POP_DEBUG, HERE, "no data from child"); + return FAILURE; + } + buf [ n + m ] = '\0'; TRACE ( trace_file, POP_DEBUG, HERE, "...read: (%d) '%.128s'",