/* $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 <pwd.h>
#include <shadow.h>
/* 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 <libgen.h>
#include <security/pam_appl.h>
/* 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);
}
syntax highlighted by Code2HTML, v. 0.9.1