diff options
author | Adam Conrad <adconrad@0c3.net> | 2005-01-21 01:35:21 -0800 |
---|---|---|
committer | Adam Conrad <adconrad@0c3.net> | 2005-01-21 01:35:21 -0800 |
commit | 41cae64487170058599237d290a2d0f59d50a539 (patch) | |
tree | 303d1540d5dcacfb81fc2edf2c6cbaf2ffa10b91 /poppassd.c |
Import poppassd_1.8.5.orig.tar.gz
[dgit import orig poppassd_1.8.5.orig.tar.gz]
Diffstat (limited to 'poppassd.c')
-rw-r--r-- | poppassd.c | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/poppassd.c b/poppassd.c new file mode 100644 index 0000000..b9113de --- /dev/null +++ b/poppassd.c @@ -0,0 +1,273 @@ +/* + * poppassd.c + * + * A Eudora and NUPOP change password server. + * + * Pawel Krawczyk <kravietz@echelon.pl> + * + * Based on poppassd by John Norstad <j-norstad@nwu.edu>, + * Roy Smith <roy@nyu.edu> and Daniel L. Leavitt <dll.mitre.org>. + * Shadow file update code taken from shadow-960810 by John F. Haugh + * II <jfh@rpp386.cactus.org> and Marek Michalkiewicz + * <marekm@i17linuxb.ists.pwr.wroc.pl> + * + * 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: <closes connection> + * E: <closes connection> + */ + +#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 <sys/types.h> +#include <fcntl.h> +#include <syslog.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <strings.h> +#include <errno.h> +#include <stdarg.h> +#include <pwd.h> +#include <string.h> +#include <dirent.h> +#include <shadow.h> +#include <signal.h> +#include <sys/time.h> +#if !__GLIBC__ >= 2 +#include <ulimit.h> +#endif +#include <security/pam_appl.h> +#include <security/pam_misc.h> + +#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; i<num_msg; i++) { + if(msg[i]->msg_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<POP_MIN_UID || pw == NULL) { + WriteToClient("500 Old password is incorrect."); + syslog(LOG_ERR, "failed attempt to change password for %s", user); + exit(1); + } + + pop_state = POP_NEWPASS; + + WriteToClient ("200 Your new password please."); + ReadFromClient (line); + sscanf (line, "newpass %" MAX_LEN_PASS "c", newpass); + + /* new pass required */ + if (strlen (newpass) == 0) + { + WriteToClient ("500 New password required."); + exit(1); + } + + if(pam_chauthtok(pamh, 0) != PAM_SUCCESS) { + WriteToClient("500 Server error, password not changed"); + exit(1); + } else { + syslog(LOG_ERR, "changed POP3 password for %s", user); + WriteToClient("200 Password changed, thank-you."); + ReadFromClient (line); + if (strncmp(line, "quit", 4) != 0) { + WriteToClient("500 Quit required."); + exit (1); + } + } + + pam_end(pamh, 0); + WriteToClient("200 Bye."); + closelog(); + exit(0); + } + |