/* * poppassd.c * * A Eudora and NUPOP change password server. * * Pawel Krawczyk * * Based on poppassd by John Norstad , * Roy Smith and Daniel L. Leavitt . * Shadow file update code taken from shadow-960810 by John F. Haugh * II and Marek Michalkiewicz * * * See README for more information. */ /* Steve Dorner's description of the simple protocol: * * The server's responses should be like an FTP server's responses; * 1xx for in progress, 2xx for success, 3xx for more information * needed, 4xx for temporary failure, and 5xx for permanent failure. * Putting it all together, here's a sample conversation: * * S: 200 hello\r\n * E: user yourloginname\r\n * S: 300 please send your password now\r\n * E: pass yourcurrentpassword\r\n * S: 200 My, that was tasty\r\n * E: newpass yournewpassword\r\n * S: 200 Happy to oblige\r\n * E: quit\r\n * S: 200 Bye-bye\r\n * S: * E: */ #define VERSION "1.8.5" #define BAD_PASS_DELAY 3 /* delay in seconds after bad 'Old password' */ #define POP_MIN_UID 100 /* minimum UID which is allowed to change password via poppassd */ /* These need to be quoted because they are only used as * parts of format strings for sscanf; actual lengths are smaller * by 5 because the buffer needs to fit "user " and "pass " strings * as well */ #define MAX_LEN_USERNAME "64" /* maximum length of username buffer */ #define MAX_LEN_PASS "128" /* maximum length of password buffer */ #define SUCCESS 1 #define FAILURE 0 #define BUFSIZE 1000 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !__GLIBC__ >= 2 #include #endif #include #include #define LOCK_TRIES 30 #ifndef UL_SETFSIZE #ifdef UL_SFILLIM #define UL_SETFSIZE UL_SFILLIM #else #define UL_SETFSIZE 2 #endif #endif /* need to be global for poppassd_conv */ char oldpass[BUFSIZE]; char newpass[BUFSIZE]; #define POP_OLDPASS 0 #define POP_NEWPASS 1 #define POP_SKIPASS 2 short int pop_state = POP_OLDPASS; void WriteToClient (char *fmt, ...) { va_list ap; va_start (ap, fmt); vfprintf (stdout, fmt, ap); fputs ("\r\n", stdout ); fflush (stdout); va_end (ap); } void ReadFromClient (char *line) { char *sp; int i; bzero(line, BUFSIZE); fgets (line, BUFSIZE-1, stdin); if ((sp = strchr(line, '\n')) != NULL) *sp = '\0'; if ((sp = strchr(line, '\r')) != NULL) *sp = '\0'; /* convert initial keyword on line to lower case. */ for (sp = line; isalpha(*sp); sp++) *sp = tolower(*sp); line[BUFSIZE-1] = '\0'; } int poppassd_conv(num_msg, msg, resp, appdata_ptr) int num_msg; const struct pam_message **msg; struct pam_response **resp; void *appdata_ptr; { int i; struct pam_response *r = malloc(sizeof(struct pam_response) * num_msg); if(num_msg <= 0) return(PAM_CONV_ERR); if(r == NULL) return(PAM_CONV_ERR); for(i=0; imsg_style == PAM_ERROR_MSG) { WriteToClient ("500 PAM error: %s", msg[i]->msg); syslog(LOG_ERR, "PAM error: %s", msg[i]->msg); /* * If there is an error, we don't want to be sending in * anything more, so set pop_state to invalid */ pop_state = POP_SKIPASS; } r[i].resp_retcode = 0; if(msg[i]->msg_style == PAM_PROMPT_ECHO_OFF || msg[i]->msg_style == PAM_PROMPT_ECHO_ON) { switch(pop_state) { case POP_OLDPASS: r[i].resp = strdup(oldpass); break; case POP_NEWPASS: r[i].resp = strdup(newpass); break; case POP_SKIPASS: r[i].resp = NULL; break; default: syslog(LOG_ERR, "PAM error: too many switches (state=%d)", pop_state); } } else { r[i].resp = strdup(""); } } *resp = r; return PAM_SUCCESS; } const struct pam_conv pam_conv = { poppassd_conv, NULL }; int main (int argc, char *argv[]) { char line[BUFSIZE]; char user[BUFSIZE]; char emess[BUFSIZE]; char *slavedev; struct passwd *pw, *getpwnam(); struct spwd *sp; int c, master; pid_t pid, wpid; int wstat; int ret; pam_handle_t *pamh=NULL; char *item=oldpass; *user = *oldpass = *newpass = 0; openlog("poppassd", LOG_PID, LOG_LOCAL4); WriteToClient ("200 poppassd v%s hello, who are you?", VERSION); ReadFromClient (line); if( strlen(line) > atoi(MAX_LEN_USERNAME) ) { WriteToClient ("500 Username too long (max %d).", atoi(MAX_LEN_USERNAME)); exit(1); } sscanf (line, "user %" MAX_LEN_USERNAME "s", user) ; if (strlen (user) == 0 ) { WriteToClient ("500 Username required."); exit(1); } if(pam_start("poppassd", user, &pam_conv, &pamh) != PAM_SUCCESS) { WriteToClient("500 Invalid username."); exit(1); } WriteToClient ("200 Your password please."); ReadFromClient (line); if( strlen(line) > atoi(MAX_LEN_PASS) ) { WriteToClient("500 Password length exceeded (max %d).", atoi(MAX_LEN_PASS) ); exit(1); } sscanf (line, "pass %" MAX_LEN_PASS "c", oldpass); if(strlen(oldpass) == 0) { WriteToClient("500 Password required."); exit(1); } if(pam_authenticate(pamh, 0) != PAM_SUCCESS) { WriteToClient ("500 Old password is incorrect."); syslog(LOG_ERR, "old password is incorrect for user %s", user); /* pause to make brute force attacks harder */ sleep(BAD_PASS_DELAY); exit(1); } pw=getpwnam(user); if(pw->pw_uid