#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "lock_maildrop.h"
#include "rw.h"
#include "process.h"
#include "signal.h"
#include "ssl.h"
#include "strlcpy.h"
#include "mysql.h"
#include <grp.h>
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <syslog.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#if HAVE_LIBSSL
# include <openssl/rsa.h>
# include <openssl/crypto.h>
# include <openssl/pem.h>
# include <openssl/ssl.h>
# include <openssl/err.h>
#endif
#define STATE_AUTH 1
#define STATE_TRANS 2
#define STATE_UPDATE 3
extern const char * ssl_certfile;
extern const char * ssl_keyfile;
extern const char * local_mbox;
extern char real_username[MAXLINE+1];
extern char real_maildrop[MAXLINE+1];
static char * mdl;
extern const char * mailspool;
int authenticate(char * username, char * password);
void show_uidl(int fd, char * line);
static void do_remove_lock(void) {
do_cleanup();
remove_lock(mdl);
}
#if ENABLE_RFC2449
static void print_capa(int fd) {
write_line(fd,"+OK Capability list follows\r\n");
write_line(fd,"TOP\r\n");
write_line(fd,"UIDL\r\n");
write_line(fd,"USER\r\n");
write_line(fd,"EXPIRE NEVER\r\n");
write_line(fd,"AUTH-RESP-CODE\r\n");
write_line(fd,".\r\n");
}
#endif
static void sig_handler(int signo) {
remove_lock(mdl);
syslog(LOG_INFO,"%s: %u", "caught signal",signo);
exit(EXIT_FAILURE);
}
void pop3_session(int fd) {
int state = STATE_AUTH;
char user[MAXLINE+1], pass[MAXLINE+1], line[MAXLINE+1];
char * userx, * passx;
char * maildrop;
int modified = 0;
int got_username = 0, got_password = 0;
struct passwd * u_inf;
struct group * g_inf;
int rc;
#if HAVE_LIBSSL
SSL_CTX * ctx = NULL;
SSL * ssl = NULL;
SSL_METHOD * meth = NULL;
if (use_ssl()) {
SSL_load_error_strings();
SSLeay_add_ssl_algorithms();
meth = SSLv23_server_method();
ctx = SSL_CTX_new(meth);
if (ctx==NULL) {
syslog(LOG_ERR,"%s","SSL: failed to create new context");
exit(EXIT_FAILURE);
}
if (SSL_CTX_use_certificate_file(ctx,ssl_certfile,SSL_FILETYPE_PEM) <= 0) {
syslog(LOG_ERR,"%s: %s","SSL: failed to use certificate file",ssl_certfile);
exit(EXIT_FAILURE);
}
if (SSL_CTX_use_PrivateKey_file(ctx,ssl_keyfile,SSL_FILETYPE_PEM) <= 0) {
syslog(LOG_ERR,"%s: %s","SSL: failed to use key file",ssl_keyfile);
exit(EXIT_FAILURE);
}
ssl = SSL_new(ctx);
if (ssl==NULL) {
syslog(LOG_ERR,"%s","SSL: failed to get new handle");
exit(EXIT_FAILURE);
}
set_ssl_handle(ssl);
SSL_set_fd(ssl,fd);
rc = SSL_accept(ssl);
if (rc==-1) {
syslog(LOG_ERR,"%s","SSL: failed to accept connection");
exit(EXIT_FAILURE);
}
}
#endif
/* I'm ready */
write_line(fd, "+OK\r\n");
do {
read_line(fd,line,MAXLINE);
if (strncasecmp(line,"QUIT",4)==0) {
write_line(fd,"+OK\r\n");
exit(EXIT_SUCCESS);
} else if (strncasecmp(line,"USER ",5)==0) {
strlcpy(user,line,sizeof(user));
if (!got_password) {
write_line(fd,"+OK\r\n");
}
got_username = 1;
} else if (strncasecmp(line,"PASS ",5)==0) {
strlcpy(pass,line,sizeof(pass));
if (!got_username) {
write_line(fd,"+OK\r\n");
}
got_password = 1;
#if ENABLE_RFC2449
} else if (strncasecmp(line,"CAPA",4)==0) {
print_capa(fd);
#endif
} else {
write_line(fd,"-ERR\r\n");
}
} while (!got_username || !got_password);
#define clean_up(str) { \
char * x; \
if ((x=memchr(str,'\r',strlen(str))) != NULL) { \
*x = '\0'; \
} else if ((x=memchr(str,'\n',strlen(str))) != NULL) { \
*x = '\0'; \
} \
}
/*
* we both have username and password, we can now
* authenticate.
*/
if ((rc=authenticate(user+5,pass+5))<=0) {
if (rc==0) {
write_line(fd,"-ERR [AUTH] authentication failed\r\n");
} else if (rc==-1) {
write_line(fd,"-ERR [SYS/TEMP] temporary resource problem\r\n");
} else {
write_line(fd,"-ERR unknown error\r\n");
}
clean_up(user);
syslog(LOG_WARNING,"%s ('%.16s') return value %d","login failed",user+5,rc);
exit(EXIT_FAILURE);
}
clean_up(user);
syslog( LOG_INFO, "Authenticated %s", user+5 );
userx = user + 5;
clean_up(pass);
passx = pass + 5;
/* we've reached transaction state */
state = STATE_TRANS;
if (real_username[0] != 0) {
userx = real_username;
}
#ifndef HAVE_LIBMYSQLCLIENT
u_inf = getpwnam(userx);
#else
u_inf = getMpwnam( userx ); /* getMpwnam() first checks getpwnam() anyway */
#endif /* HAVE_LIBMYSQLCLIENT */
if (u_inf==NULL) {
syslog(LOG_ERR,"%s: %s","UID lookup failed",strerror(errno));
write_line(fd,"-ERR [SYS/TEMP] user no longer exists\r\n");
exit(EXIT_FAILURE);
}
if (real_maildrop[0] != 0) {
maildrop = real_maildrop;
} else {
if (local_mbox) {
size_t md_len = (size_t)strlen(u_inf->pw_dir)+strlen(local_mbox)+2;
maildrop = alloca(md_len);
if (maildrop) {
snprintf(maildrop,md_len,"%s/%s",u_inf->pw_dir,local_mbox);
} else {
syslog(LOG_ERR,"%s: %s","alloca() failed",strerror(errno));
write_line(fd,"-ERR [SYS/TEMP] unable to allocate enough memory\r\n");
exit(EXIT_FAILURE);
}
} else {
size_t md_len = (size_t)strlen(mailspool)+strlen(userx)+1;
maildrop = alloca(md_len);
if (maildrop) {
snprintf(maildrop,md_len,"%s%s",mailspool,userx);
} else {
syslog(LOG_ERR,"%s: %s","alloca() failed",strerror(errno));
}
}
}
mdl = maildrop;
g_inf = getgrnam("mail");
if (g_inf==NULL) {
syslog(LOG_ERR,"%s","group 'mail' not found");
write_line(fd,"-ERR [SYS/TEMP] group 'mail' not found\r\n");
exit(EXIT_FAILURE);
}
if (setegid(g_inf->gr_gid)!=0 && real_username[0] == 0) {
syslog(LOG_ERR,"%s: %u: %s","setegid() failed",g_inf->gr_gid,strerror(errno));
write_line(fd,"-ERR [SYS/TEMP] failed to join 'mail' group (setegid)\r\n");
exit(EXIT_FAILURE);
}
if (setgid(g_inf->gr_gid)!=0 && real_username[0] == 0) {
syslog(LOG_ERR,"%s: %u: %s","setgid() failed",g_inf->gr_gid,strerror(errno));
write_line(fd,"-ERR [SYS/TEMP] failed to join 'mail' group (setgid)\r\n");
exit(EXIT_FAILURE);
}
if ((rc=lock_maildrop(maildrop,u_inf->pw_uid,g_inf->gr_gid))<=0) {
syslog(LOG_ERR,"%s: %s: %s","failed to lock maildrop",maildrop,strerror(errno));
if (rc==-1) {
write_line(fd,"-ERR [SYS/TEMP] failed to lock maildrop\r\n");
} else {
write_line(fd,"-ERR [IN-USE] failed to lock maildrop\r\n");
}
exit(EXIT_FAILURE);
}
if (setuid(u_inf->pw_uid)!=0) {
syslog(LOG_ERR,"%s: %u: %s","setuid() failed",u_inf->pw_uid,strerror(errno));
write_line(fd,"-ERR [SYS/TEMP] failed to set user identity\r\n");
do_remove_lock();
exit(EXIT_FAILURE);
}
if (seteuid(u_inf->pw_uid)!=0) {
syslog(LOG_ERR,"%s: %u: %s","seteuid() failed",u_inf->pw_uid,strerror(errno));
write_line(fd,"-ERR [SYS/TEMP] failed to set effective user identity\r\n");
do_remove_lock();
exit(EXIT_FAILURE);
}
if (atexit(do_remove_lock)!=0) {
syslog(LOG_WARNING,"%s: %s","atexit() failed; lock files may fail to expire",strerror(errno));
}
Signal(SIGPIPE,sig_handler);
if (process_mails(maildrop)==0) {
write_line(fd,"-ERR [SYS/PERM] failed to scan maildrop contents\r\n");
do_cleanup();
do_remove_lock();
exit(EXIT_FAILURE);
}
/* authenticated and maildrop locked
*/
write_line(fd,"+OK\r\n");
read_line(fd,line,MAXLINE);
while (strncasecmp(line,"QUIT",4)!=0) {
if (strncasecmp(line,"STAT",4)==0) {
long msg, size;
get_stats(&msg,&size);
snprintf(line,MAXLINE,"+OK %ld %ld\r\n",msg,size);
write_line(fd,line);
} else if (strncasecmp(line,"LIST",4)==0) {
do_list(fd,line); /* parses itself */
} else if (strncasecmp(line,"RETR ",5)==0) {
retrieve_msg(fd,line); /* parses itself */
} else if (strncasecmp(line,"DELE ",5)==0) {
delete_msg(fd,line); /* parses itself */
modified = 1;
} else if (strncasecmp(line,"NOOP",4)==0) {
write_line(fd,"+OK\r\n");
/* do nothing */
} else if (strncasecmp(line,"RSET",4)==0) {
reset_msg();
modified = 0;
write_line(fd,"+OK\r\n");
} else if (strncasecmp(line,"TOP ",4)==0) {
show_top_msg(fd,line);
} else if (strncasecmp(line,"UIDL",4)==0) {
show_uidl(fd,line+4);
#if ENABLE_RFC2449
} else if (strncasecmp(line,"CAPA",4)==0) {
print_capa(fd);
#endif
} else {
write_line(fd,"-ERR command not understood\r\n");
}
read_line(fd,line,MAXLINE);
}
write_line(fd,"+OK\r\n");
close(fd);
#if HAVE_LIBSSL
if (use_ssl()) {
SSL_free(ssl);
SSL_CTX_free(ctx);
}
#endif
if (modified) {
do_update(maildrop);
}
do_cleanup();
remove_lock(maildrop);
}
syntax highlighted by Code2HTML, v. 0.9.1