#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "md5.h"
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef HAVE_SHADOW_H
#include <shadow.h>
#endif
#include <unistd.h>
#include <fcntl.h>
#include <syslog.h>
#include <errno.h>
#include <ctype.h>
#ifdef HAVE_CRYPT_H
#include <crypt.h>
#endif
#include "strlcpy.h"
#include "mysql.h"
extern const char * authfile;
char real_username[MAXLINE+1];
char real_maildrop[MAXLINE+1];
extern int use_pop3_allow_deny;
/* user_in_file() and is_user_allowed() by Lukasz Luzar
* modified by Andreas Krennmair
*/
static int user_in_file(char * user, char * path) {
int fd;
size_t userlen;
size_t len;
char * buffer, * ptr;
struct stat sb;
/* sanity check */
if (user==NULL || path==NULL) {
return -1;
}
userlen = strlen(user);
fd = open(path,O_RDONLY);
if (fd < 0) {
return -1;
}
if (0!=fstat(fd,&sb)) {
close(fd);
return -1;
}
len = (size_t)sb.st_size;
buffer = alloca(len+1);
/* sanity check */
if (buffer==NULL) {
return -1;
}
if (read(fd,buffer,(size_t)len)!=(ssize_t)len) {
close(fd);
return -1;
}
if (0!=close(fd)) {
return -1;
}
buffer[len] = '\0';
ptr = buffer;
while (*ptr != '\0') {
/* omit whitespaces */
while (*ptr == '\t' || *ptr == ' ') {
ptr++;
}
if (*ptr == '\0') {
break;
}
if ((*ptr!='#') && (strncmp(ptr,user,userlen)==0) &&
(ptr[userlen] == ' ' || ptr[userlen] == '\t' || ptr[userlen] == '\n')) {
return 1;
}
while (*ptr != '\0' && *ptr != '\n') {
ptr++;
}
if (*ptr == '\n') {
ptr++;
}
}
return 0;
}
static int is_user_allowed(char * user) {
int allow, deny;
switch (user_in_file(user,"/etc/pop3.allow")>0) {
case 0:
allow = 0;
break;
case 1:
allow = 1;
break;
default: /* user_in_file() must not return anything else! */
return 0;
}
switch (user_in_file(user,"/etc/pop3.deny")) {
case 0:
deny = 0;
break;
case 1:
deny = 1;
break;
default: /* dito */
return 0;
}
if ((allow == 0 && deny == 0) || (allow == 1 && deny == 0)) {
return 1;
}
return 0;
}
/* returns a static buffer containing the string representation of an
* MD5'ified password hash. the string starts with "MD5-". the rest
* of the string is the lower case ASCII representation of an MD5 hash
* (32 bytes). the hash is calculated from the password followed by a
* line feed (ASCII 10), followed by the user name, another line feed,
* and the "magic" string "akpop3d". */
static char * calc_password_hash(char * username, char * password) {
/* Sverre H. Huseby, 2003-05-09 */
/* this program is not multi threaded, and we know we won't mix two
* calls to the function, so let's make it simple using a static
* return variable. */
static char ret[4 + 32 + 1];
static char * magic = "akpop3d";
static char * hex_digits = "0123456789abcdef";
char buf[1024];
unsigned char md5[16];
int q;
char * p;
unsigned char * up;
if (strlen(username) + strlen(password) + strlen(magic) + 3 > sizeof(buf)) {
syslog(LOG_ERR, "overly long username or password");
return NULL;
}
snprintf(buf,sizeof(buf),"%s\n%s\n%s",password,username,magic);
md5_buffer(buf, strlen(buf), md5);
strlcpy(ret, "MD5-", sizeof(ret));
p = ret + 4;
up = md5;
for (q = 0; q < 16; q++) {
*p++ = hex_digits[*up >> 4];
*p++ = hex_digits[*up & 0xf];
++up;
}
*p = '\0';
return ret;
}
static int is_password_match(char * username, char * stored_password,
char * given_password) {
/* Sverre H. Huseby, 2003-05-09 */
char * hash;
char * p1;
char * p2;
int q;
if (strlen(stored_password) == (4 + 32)
&& strncmp(stored_password, "MD5-", 3) == 0) {
/* the password is stored as and MD5 hash */
hash = calc_password_hash(username, given_password);
p1 = hash + 4;
p2 = stored_password + 4;
/* compare MD5 hashes represented as ASCII (32 bytes) , including
* the terminating NUL byte (an additional byte). could have used
* strcasecmp, but as this function is not supported by all
* systems, we do it the hard way. i guess i should have learned
* to make those fancy autoconf rules, but i haven't. */
for (q = 0; q < 32 + 1; q++) {
/* tolower may be a macro I guess, so we can't increase the
* p1/p2 pointers in the following if statement. */
if (tolower(*p1) != tolower(*p2))
return 0;
++p1;
++p2;
}
/* the hashes are equal. */
return 1;
} else {
/* the password is stored in the clear. yikes! */
return (strcmp(given_password, stored_password) == 0);
}
}
/* returns 0 if authentication failed, !0 if it succeeded. */
static int authenticate_by_file(char * username, char * password) {
char * ptr;
char linebuf[MAXLINE+1];
FILE * fptr;
if (authfile == NULL)
return 0;
fptr = fopen(authfile, "r");
if (fptr == NULL) {
syslog(LOG_ERR, "%s: %s: %s",
"failed to read auth file", authfile, strerror(errno));
return 0;
}
while (!ferror(fptr) && !feof(fptr)) {
linebuf[0] = '\0';
if (fgets(linebuf, sizeof(linebuf), fptr) == NULL) {
fclose(fptr);
return 0;
}
ptr = strtok(linebuf, ":");
if (ptr == NULL)
continue;
if (strcmp(username, ptr) != 0)
continue;
ptr = strtok(NULL, ":");
if (ptr == NULL) {
fclose(fptr);
return 0;
}
if (!is_password_match(username, ptr, password)) {
fclose(fptr);
return 0;
}
/*
* At this point we've authenticated, but we now need to find out
* what Unix username to use and what maildrop file to read
*/
ptr = strtok(NULL, ":");
if (ptr == NULL) {
fclose(fptr);
return 0;
}
strlcpy(real_username, ptr, sizeof(real_username));
ptr = strtok(NULL, ":\n");
if (ptr == NULL) {
real_username[0] = '\0';
fclose(fptr);
return 0;
}
fclose(fptr);
strlcpy(real_maildrop, ptr, sizeof(real_maildrop));
return 1;
}
fclose(fptr);
return 0;
}
int authenticate(char * username, char * password) {
char user[MAXLINE+1], pass[MAXLINE+1];
char * sys_pw;
char * crp;
struct passwd * u;
#ifdef HAVE_SHADOW_H
struct spwd * s;
#endif
int len;
/* sanity checks */
if (username==NULL || password==NULL) {
return -1;
}
real_username[0] = '\0';
real_maildrop[0] = '\0';
/* extract username */
crp = memchr(username,'\r',strlen(username));
if (crp==NULL) {
crp = memchr(username,'\n',strlen(username));
if (crp==NULL) {
crp = username + strlen(username);
}
}
len = crp - username;
if (len+1>sizeof(user)) {
len = sizeof(user)-1;
}
memset(user,0,sizeof(user));
strlcpy(user,username,len+1);
/* extract password */
crp = memchr(password,'\r',strlen(password));
if (crp==NULL) {
crp = memchr(password,'\n',strlen(password));
if (crp==NULL) {
crp = password + strlen(password);
}
}
len = crp - password;
if (len+1>sizeof(pass)) {
len = sizeof(pass)-1;
}
memset(pass,0,sizeof(pass));
strlcpy(pass,password,len+1);
/* check /etc/pop3.{allow,deny} */
if (0!=use_pop3_allow_deny && 0==is_user_allowed(user)) {
return 0;
}
/*
* Text-file authentication
*/
if (authfile != NULL) {
return authenticate_by_file(user, pass);
}
#ifndef HAVE_LIBMYSQLCLIENT
u = getpwnam(user);
#else
u = getMpwnam( user ); /* getMpwnam first checks getpwnam() */
#endif /* HAVE_LIBMYSQLCLIENT */
if (u == NULL || u->pw_passwd == NULL) {
return -1;
}
/* handle shadowed passwd files */
#if HAVE_SHADOW_H
if (strcmp(u->pw_passwd,"x")==0) {
#ifndef HAVE_LIBMYSQLCLIENT
s = getspnam(user);
#else
s = getMspnam(user); /* getMspanm first checks getspnam() */
#endif
if (s==NULL) {
return -1;
}
sys_pw = s->sp_pwdp;
} else {
sys_pw = u->pw_passwd;
}
#else
sys_pw = u->pw_passwd;
#endif
if (strcmp(sys_pw,crypt(pass,sys_pw))==0) {
return 1;
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1