1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4 #include "lock_maildrop.h"
5 #include "rw.h"
6 #include "process.h"
7 #include "signal.h"
8 #include "ssl.h"
9 #include "strlcpy.h"
10 #include "mysql.h"
11 
12 #include <grp.h>
13 #include <pwd.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <syslog.h>
19 #include <errno.h>
20 #include <stdio.h>
21 #include <signal.h>
22 #include <string.h>
23 
24 #if HAVE_LIBSSL
25 # include <openssl/rsa.h>
26 # include <openssl/crypto.h>
27 # include <openssl/pem.h>
28 # include <openssl/ssl.h>
29 # include <openssl/err.h>
30 #endif
31 
32 #define STATE_AUTH	1
33 #define STATE_TRANS	2
34 #define STATE_UPDATE	3
35 
36 extern const char * ssl_certfile;
37 extern const char * ssl_keyfile;
38 extern const char * local_mbox;
39 extern char real_username[MAXLINE+1];
40 extern char real_maildrop[MAXLINE+1];
41 static char * mdl;
42 
43 extern const char * mailspool;
44 
45 int authenticate(char * username, char * password);
46 void show_uidl(int fd, char * line);
47 
do_remove_lock(void)48 static void do_remove_lock(void) {
49   do_cleanup();
50   remove_lock(mdl);
51 }
52 
53 #if ENABLE_RFC2449
print_capa(int fd)54 static void print_capa(int fd) {
55   write_line(fd,"+OK Capability list follows\r\n");
56   write_line(fd,"TOP\r\n");
57   write_line(fd,"UIDL\r\n");
58   write_line(fd,"USER\r\n");
59   write_line(fd,"EXPIRE NEVER\r\n");
60   write_line(fd,"AUTH-RESP-CODE\r\n");
61   write_line(fd,".\r\n");
62 }
63 #endif
64 
65 
sig_handler(int signo)66 static void sig_handler(int signo) {
67   remove_lock(mdl);
68   syslog(LOG_INFO,"%s: %u", "caught signal",signo);
69   exit(EXIT_FAILURE);
70 }
71 
pop3_session(int fd)72 void pop3_session(int fd) {
73   int state = STATE_AUTH;
74   char user[MAXLINE+1], pass[MAXLINE+1], line[MAXLINE+1];
75   char * userx, * passx;
76   char * maildrop;
77   int modified = 0;
78   int got_username = 0, got_password = 0;
79   struct passwd * u_inf;
80   struct group * g_inf;
81   int rc;
82 #if HAVE_LIBSSL
83   SSL_CTX * ctx = NULL;
84   SSL * ssl = NULL;
85   SSL_METHOD * meth = NULL;
86 
87   if (use_ssl()) {
88     SSL_load_error_strings();
89     SSLeay_add_ssl_algorithms();
90     meth = SSLv23_server_method();
91     ctx = SSL_CTX_new(meth);
92     if (ctx==NULL) {
93       syslog(LOG_ERR,"%s","SSL: failed to create new context");
94       exit(EXIT_FAILURE);
95     }
96 
97     if (SSL_CTX_use_certificate_file(ctx,ssl_certfile,SSL_FILETYPE_PEM) <= 0) {
98       syslog(LOG_ERR,"%s: %s","SSL: failed to use certificate file",ssl_certfile);
99       exit(EXIT_FAILURE);
100     }
101 
102     if (SSL_CTX_use_PrivateKey_file(ctx,ssl_keyfile,SSL_FILETYPE_PEM) <= 0) {
103       syslog(LOG_ERR,"%s: %s","SSL: failed to use key file",ssl_keyfile);
104       exit(EXIT_FAILURE);
105     }
106 
107     ssl = SSL_new(ctx);
108     if (ssl==NULL) {
109       syslog(LOG_ERR,"%s","SSL: failed to get new handle");
110       exit(EXIT_FAILURE);
111     }
112     set_ssl_handle(ssl);
113 
114     SSL_set_fd(ssl,fd);
115     rc = SSL_accept(ssl);
116     if (rc==-1) {
117       syslog(LOG_ERR,"%s","SSL: failed to accept connection");
118       exit(EXIT_FAILURE);
119     }
120   }
121 #endif
122 
123   /* I'm ready */
124   write_line(fd, "+OK\r\n");
125 
126   do {
127     read_line(fd,line,MAXLINE);
128     if (strncasecmp(line,"QUIT",4)==0) {
129       write_line(fd,"+OK\r\n");
130       exit(EXIT_SUCCESS);
131     } else if (strncasecmp(line,"USER ",5)==0) {
132       strlcpy(user,line,sizeof(user));
133       if (!got_password) {
134         write_line(fd,"+OK\r\n");
135       }
136       got_username = 1;
137     } else if (strncasecmp(line,"PASS ",5)==0) {
138       strlcpy(pass,line,sizeof(pass));
139       if (!got_username) {
140         write_line(fd,"+OK\r\n");
141       }
142       got_password = 1;
143 #if ENABLE_RFC2449
144     } else if (strncasecmp(line,"CAPA",4)==0) {
145       print_capa(fd);
146 #endif
147     } else {
148       write_line(fd,"-ERR\r\n");
149     }
150   } while (!got_username || !got_password);
151 
152 #define clean_up(str) { \
153     char * x; \
154     if ((x=memchr(str,'\r',strlen(str))) != NULL) { \
155       *x = '\0'; \
156     } else if ((x=memchr(str,'\n',strlen(str))) != NULL) { \
157       *x = '\0'; \
158     } \
159   }
160 
161   /*
162    * we both have username and password, we can now
163    * authenticate.
164    */
165   if ((rc=authenticate(user+5,pass+5))<=0) {
166     if (rc==0) {
167       write_line(fd,"-ERR [AUTH] authentication failed\r\n");
168     } else if (rc==-1) {
169       write_line(fd,"-ERR [SYS/TEMP] temporary resource problem\r\n");
170     } else {
171       write_line(fd,"-ERR unknown error\r\n");
172     }
173     clean_up(user);
174     syslog(LOG_WARNING,"%s ('%.16s') return value %d","login failed",user+5,rc);
175     exit(EXIT_FAILURE);
176 
177   }
178 
179   clean_up(user);
180   syslog( LOG_INFO, "Authenticated %s", user+5 );
181   userx = user + 5;
182   clean_up(pass);
183   passx = pass + 5;
184 
185   /* we've reached transaction state */
186   state = STATE_TRANS;
187 
188   if (real_username[0] != 0) {
189     userx = real_username;
190   }
191 
192 #ifndef HAVE_LIBMYSQLCLIENT
193   u_inf = getpwnam(userx);
194 #else
195   u_inf = getMpwnam( userx ); /* getMpwnam() first checks getpwnam() anyway */
196 #endif /* HAVE_LIBMYSQLCLIENT */
197 
198   if (u_inf==NULL) {
199     syslog(LOG_ERR,"%s: %s","UID lookup failed",strerror(errno));
200     write_line(fd,"-ERR [SYS/TEMP] user no longer exists\r\n");
201     exit(EXIT_FAILURE);
202   }
203 
204   if (real_maildrop[0] != 0) {
205     maildrop = real_maildrop;
206   } else {
207     if (local_mbox) {
208       size_t md_len = (size_t)strlen(u_inf->pw_dir)+strlen(local_mbox)+2;
209       maildrop = alloca(md_len);
210       if (maildrop) {
211         snprintf(maildrop,md_len,"%s/%s",u_inf->pw_dir,local_mbox);
212       } else {
213         syslog(LOG_ERR,"%s: %s","alloca() failed",strerror(errno));
214         write_line(fd,"-ERR [SYS/TEMP] unable to allocate enough memory\r\n");
215         exit(EXIT_FAILURE);
216       }
217     } else {
218       size_t md_len = (size_t)strlen(mailspool)+strlen(userx)+1;
219       maildrop = alloca(md_len);
220       if (maildrop) {
221         snprintf(maildrop,md_len,"%s%s",mailspool,userx);
222       } else {
223         syslog(LOG_ERR,"%s: %s","alloca() failed",strerror(errno));
224       }
225     }
226   }
227 
228   mdl = maildrop;
229 
230   g_inf = getgrnam("mail");
231   if (g_inf==NULL) {
232     syslog(LOG_ERR,"%s","group 'mail' not found");
233     write_line(fd,"-ERR [SYS/TEMP] group 'mail' not found\r\n");
234     exit(EXIT_FAILURE);
235   }
236   if (setegid(g_inf->gr_gid)!=0 && real_username[0] == 0) {
237     syslog(LOG_ERR,"%s: %u: %s","setegid() failed",g_inf->gr_gid,strerror(errno));
238     write_line(fd,"-ERR [SYS/TEMP] failed to join 'mail' group (setegid)\r\n");
239     exit(EXIT_FAILURE);
240   }
241 
242   if (setgid(g_inf->gr_gid)!=0 && real_username[0] == 0) {
243     syslog(LOG_ERR,"%s: %u: %s","setgid() failed",g_inf->gr_gid,strerror(errno));
244     write_line(fd,"-ERR [SYS/TEMP] failed to join 'mail' group (setgid)\r\n");
245     exit(EXIT_FAILURE);
246   }
247 
248   if ((rc=lock_maildrop(maildrop,u_inf->pw_uid,g_inf->gr_gid))<=0) {
249     syslog(LOG_ERR,"%s: %s: %s","failed to lock maildrop",maildrop,strerror(errno));
250     if (rc==-1) {
251       write_line(fd,"-ERR [SYS/TEMP] failed to lock maildrop\r\n");
252     } else {
253       write_line(fd,"-ERR [IN-USE] failed to lock maildrop\r\n");
254     }
255     exit(EXIT_FAILURE);
256   }
257 
258   if (setuid(u_inf->pw_uid)!=0) {
259     syslog(LOG_ERR,"%s: %u: %s","setuid() failed",u_inf->pw_uid,strerror(errno));
260     write_line(fd,"-ERR [SYS/TEMP] failed to set user identity\r\n");
261     do_remove_lock();
262     exit(EXIT_FAILURE);
263   }
264 
265   if (seteuid(u_inf->pw_uid)!=0) {
266     syslog(LOG_ERR,"%s: %u: %s","seteuid() failed",u_inf->pw_uid,strerror(errno));
267     write_line(fd,"-ERR [SYS/TEMP] failed to set effective user identity\r\n");
268     do_remove_lock();
269     exit(EXIT_FAILURE);
270   }
271 
272   if (atexit(do_remove_lock)!=0) {
273     syslog(LOG_WARNING,"%s: %s","atexit() failed; lock files may fail to expire",strerror(errno));
274   }
275 
276   Signal(SIGPIPE,sig_handler);
277 
278   if (process_mails(maildrop)==0) {
279     write_line(fd,"-ERR [SYS/PERM] failed to scan maildrop contents\r\n");
280     do_cleanup();
281     do_remove_lock();
282     exit(EXIT_FAILURE);
283   }
284 
285   /* authenticated and maildrop locked
286    */
287   write_line(fd,"+OK\r\n");
288 
289   read_line(fd,line,MAXLINE);
290   while (strncasecmp(line,"QUIT",4)!=0) {
291     if (strncasecmp(line,"STAT",4)==0) {
292       long msg, size;
293       get_stats(&msg,&size);
294       snprintf(line,MAXLINE,"+OK %ld %ld\r\n",msg,size);
295       write_line(fd,line);
296     } else if (strncasecmp(line,"LIST",4)==0) {
297       do_list(fd,line); /* parses itself */
298     } else if (strncasecmp(line,"RETR ",5)==0) {
299       retrieve_msg(fd,line); /* parses itself */
300     } else if (strncasecmp(line,"DELE ",5)==0) {
301       delete_msg(fd,line); /* parses itself */
302       modified = 1;
303     } else if (strncasecmp(line,"NOOP",4)==0) {
304       write_line(fd,"+OK\r\n");
305       /* do nothing */
306     } else if (strncasecmp(line,"RSET",4)==0) {
307       reset_msg();
308       modified = 0;
309       write_line(fd,"+OK\r\n");
310     } else if (strncasecmp(line,"TOP ",4)==0) {
311       show_top_msg(fd,line);
312     } else if (strncasecmp(line,"UIDL",4)==0) {
313       show_uidl(fd,line+4);
314 #if ENABLE_RFC2449
315     } else if (strncasecmp(line,"CAPA",4)==0) {
316       print_capa(fd);
317 #endif
318     } else {
319       write_line(fd,"-ERR command not understood\r\n");
320     }
321     read_line(fd,line,MAXLINE);
322   }
323 
324   write_line(fd,"+OK\r\n");
325   close(fd);
326 #if HAVE_LIBSSL
327   if (use_ssl()) {
328     SSL_free(ssl);
329     SSL_CTX_free(ctx);
330   }
331 #endif
332 
333   if (modified) {
334     do_update(maildrop);
335   }
336   do_cleanup();
337   remove_lock(maildrop);
338 }
339