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