/* $Cambridge: hermes/src/prayer/accountd/authenicate.c,v 1.2 2003/04/16 09:02:41 dpc22 Exp $ */ /************************************************ * Prayer - a Webmail Interface * ************************************************/ /* Copyright (c) University of Cambridge 2000 - 2002 */ /* See the file NOTICE for conditions of use and distribution. */ #include "accountd.h" #include #include /* Code for authenicating user across iostream connection to client. * User interaction involving a root process: important that this code is * protected and properly verified! */ /* Definies a whole series of different authenication methods, including * PAM if PAM support configured in ../Config */ /* No prototype for crypt, at least on Linux */ extern char *crypt(char *password, char *salt); /* ====================================================================== */ #ifdef ACCOUNTD_PAM_ENABLE #include #include /* checkpassword() code checks password using PAM library. Probably need * simple crypt and compare version as well */ static int chkpwconv(int num_msg, struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { char *password = (char *) appdata_ptr; const struct pam_message *m; struct pam_response *r; if (num_msg <= 0) return PAM_CONV_ERR; if (!(*resp = calloc(num_msg, sizeof(struct pam_response)))) return PAM_BUF_ERR; m = *msg; r = *resp; if (m->msg_style == PAM_PROMPT_ECHO_OFF) { if (!(r->resp = strdup(password))) { free(*resp); *resp = 0; return PAM_BUF_ERR; } } return PAM_SUCCESS; } static int checkpassword_pam(char *username, char *password) { pam_handle_t *pamh; struct pam_conv conv; int status; conv.conv = (int (*)()) chkpwconv; /* Prevents hdr mismatch */ conv.appdata_ptr = password; status = pam_start("accountd", username, &conv, &pamh); if (status == PAM_SUCCESS) status = pam_authenticate(pamh, 0); pam_end(pamh, status); return ((status == PAM_SUCCESS) ? T : NIL); } #else static int checkpassword_pam(char *username, char *password) { log_panic("PAM authenication selected but support not compiled in"); return (NIL); } #endif /* ====================================================================== */ /* Naive implementation: no support for expired passwords yet * (we don't use them) */ static int checkpassword_spnam(char *username, char *password, int md5) { struct spwd *spwd; char *crypted; int rc; if ((spwd = getspnam(username)) == NIL) return (NIL); if (md5) { char tmp[64]; sprintf(tmp, "%s", spwd->sp_pwdp); tmp[11] = '\0'; if ((crypted = crypt(password, tmp)) != NIL) rc = !strcmp(spwd->sp_pwdp, crypted) ? T : NIL; else rc = NIL; } else { if ((crypted = crypt(password, spwd->sp_pwdp)) != NIL) rc = !strcmp(spwd->sp_pwdp, crypted); else rc = NIL; } endspent(); return (rc); } static int checkpassword_pwnam(char *username, char *password, int md5) { struct passwd *pwd; char *crypted; int rc; if ((pwd = getpwnam(username)) == NIL) return (NIL); if (md5) { char tmp[64]; sprintf(tmp, "$1$%s", pwd->pw_passwd); tmp[8] = '\0'; if ((crypted = crypt(password, tmp)) != NIL) rc = !strcmp(pwd->pw_passwd, crypted) ? T : NIL; else rc = NIL; } else { if ((crypted = crypt(password, pwd->pw_passwd)) != NIL) rc = !strcmp(pwd->pw_passwd, crypted); else rc = NIL; } endspent(); return (rc); } /* ====================================================================== */ static void ucase(char *s) { while (*s) { *s = Utoupper(*s); s++; } } /* authenicate() ********************************************************* * * Authenicate login session and drop priveledges. * config: Global configuration * stream: iostream connection to client * usernamep: Used to return username of logged in user * * Returns: T => successful login. NIL otherwise ************************************************************************/ BOOL authenicate(struct config *config, struct iostream *stream, char **usernamep) { char buffer[MAXLENGTH]; char *username = NIL; char *password = NIL; struct passwd *pwd; char *cmd, *line; int rc = NIL; *usernamep = NIL; if (getuid() != 0) log_fatal("authenicate(): called without root priveledges"); ioputs(stream, "* "); if (config->filter_restricted) ioputs(stream, "[FILTER_RESTRICTED] "); ioprintf(stream, "Prayer ACCOUNTD server %s" CRLF, VERSION); ioflush(stream); while (1) { if (!iostream_getline(stream, buffer, MAXLENGTH)) return (NIL); if (buffer[0] == '\0') /* Ignore empty lines */ continue; line = buffer; if (!(cmd = string_get_token(&line))) { ioputs(stream, "BAD Invalid command" CRLF); ioflush(stream); continue; } ucase(cmd); if (!strcmp(cmd, "LOGOUT")) { ioputs(stream, "OK Logout" CRLF); ioflush(stream); return (NIL); } if (strcmp(cmd, "LOGIN") != NIL) { ioputs(stream, "BAD Invalid command" CRLF); ioflush(stream); continue; } if (!((username = string_get_token(&line)) && (username[0]))) { ioputs(stream, "BAD No username" CRLF); ioflush(stream); continue; } string_canon_decode(username); if (!((password = string_get_token(&line)) && (password[0]))) { ioputs(stream, "BAD No password" CRLF); ioflush(stream); continue; } string_canon_decode(password); /* Empty username/password already reported, just need to loop */ if (!(username && username[0] && password && password[0])) continue; /* Only need to authenicate if root */ switch (config->authtype) { case AUTHTYPE_PAM: rc = checkpassword_pam(username, password); break; case AUTHTYPE_PWD: rc = checkpassword_pwnam(username, password, 0); break; case AUTHTYPE_PWD_MD5: rc = checkpassword_pwnam(username, password, 1); break; case AUTHTYPE_SHADOW: rc = checkpassword_spnam(username, password, 0); break; case AUTHTYPE_SHADOW_MD5: rc = checkpassword_spnam(username, password, 1); break; default: log_fatal("Unsupported authenication type"); } if (!rc) { ioputs(stream, "NO Incorrect authenication" CRLF); ioflush(stream); log_misc("Incorrect authenication for: %s", username); continue; } /* getpwnam() shouldn't fail if PAM authenication working properly */ if ((pwd = getpwnam(username)) == NIL) { ioputs(stream, "NO Incorrect authenication" CRLF); ioflush(stream); log_misc("Incorrect authenication for: %s", username); continue; } if (pwd->pw_uid == 0) { ioputs(stream, "NO Root login disabled" CRLF); ioflush(stream); continue; } break; } setgid(pwd->pw_gid); /* Change GID permanently for session */ setuid(pwd->pw_uid); /* Change UID permanently for session */ if (chdir(pwd->pw_dir)) { ioputs(stream, "NO Can't select user home directory" CRLF); ioflush(stream); return (NIL); } ioputs(stream, "OK Session authenicated" CRLF); ioflush(stream); if (getuid() == 0) log_fatal("authenicate(): Ended up as root user!"); *usernamep = strdup(username); return (T); } /* ====================================================================== */ /* authenicate_preauth() ************************************************* * * Authenicate login session and drop priveledges. * config: Global configuration * stream: iostream connection to client * usernamep: Used to return username of logged in user * * Returns: T => successful login. NIL otherwise ************************************************************************/ BOOL authenicate_preauth(struct config * config, struct iostream * stream, char **usernamep) { struct passwd *pwd; if (getuid() == 0) log_fatal("authenicate_preauth(): called as root"); if ((pwd = getpwuid(getuid())) == NIL) log_fatal("session(): getpwuid() failed"); *usernamep = strdup(pwd->pw_name); ioputs(stream, "* "); if (config->filter_restricted) ioputs(stream, "[FILTER_RESTRICTED] "); ioprintf(stream, "PREAUTH Prayer ACCOUNTD server %s [user=%s]" CRLF, VERSION, *usernamep); ioflush(stream); if (chdir(pwd->pw_dir)) { ioputs(stream, "* Can't switch to PREAUTH user's home directory" CRLF); ioflush(stream); return (NIL); } return (T); }