#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 #include #include #include #include #include #include #include #include #include #include #if HAVE_LIBSSL # include # include # include # include # include #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); }