/[imapfilter]/imapfilter/file.c
ViewVC logotype

Annotation of /imapfilter/file.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.56 - (hide annotations)
Sun Jul 27 18:39:34 2003 UTC (20 years, 8 months ago) by lefcha
Branch: MAIN
Changes since 1.55: +2 -2 lines
File MIME type: text/plain
Remove ssl argument in the account command.

1 lefcha 1.1 #include <stdio.h>
2 lefcha 1.28 #include <unistd.h>
3 lefcha 1.1 #include <errno.h>
4 lefcha 1.5 #include <sys/types.h>
5 lefcha 1.1 #include <regex.h>
6     #include <string.h>
7     #include <stdlib.h>
8 lefcha 1.50 #include <ctype.h>
9 lefcha 1.1 #include <limits.h>
10 lefcha 1.10 #include <sys/stat.h>
11 lefcha 1.28 #include <fcntl.h>
12 lefcha 1.29 #include <time.h>
13 lefcha 1.1
14     #include "config.h"
15     #include "imapfilter.h"
16 lefcha 1.13 #include "data.h"
17 lefcha 1.1
18 lefcha 1.50 #ifdef SSL_TLS
19     #include <openssl/ssl.h>
20     #include <openssl/x509.h>
21     #include <openssl/pem.h>
22     #endif
23    
24 lefcha 1.1
25 lefcha 1.6 extern char logfile[PATH_MAX];
26 lefcha 1.11 extern unsigned int options;
27 lefcha 1.46 extern char charset[CHARSET_LEN];
28 lefcha 1.34 extern unsigned int flags;
29     extern unsigned int interval;
30 lefcha 1.28 extern long timeout;
31 lefcha 1.29 extern char *home;
32    
33     #ifdef ENCRYPTED_PASSWORDS
34 lefcha 1.52 char *passphr = NULL; /* Master password to access the passwords
35 lefcha 1.48 * file. */
36    
37 lefcha 1.28 #endif
38 lefcha 1.27
39 lefcha 1.1
40     /*
41 lefcha 1.13 * Find the path to configuration file, open it and call parse_config().
42 lefcha 1.1 */
43 lefcha 1.48 int
44     read_config(char *cfg)
45 lefcha 1.1 {
46 lefcha 1.48 int r;
47     FILE *fd;
48     char *c;
49    
50     c = NULL;
51    
52     if (cfg == NULL) {
53     cfg = c = (char *)xmalloc(PATH_MAX * sizeof(char));
54     snprintf(cfg, PATH_MAX, "%s/%s", home, ".imapfilterrc");
55     }
56 lefcha 1.1 #ifdef DEBUG
57 lefcha 1.48 fprintf(stderr, "debug: configuration file: '%s'\n", cfg);
58 lefcha 1.1 #endif
59 lefcha 1.13 #ifdef CHECK_PERMISSIONS
60 lefcha 1.48 check_file_perms(cfg, S_IRUSR | S_IWUSR);
61 lefcha 1.12 #endif
62 lefcha 1.48 fd = fopen(cfg, "r");
63     if (fd == NULL)
64     fatal(ERROR_FILE_OPEN, "opening config file %s; %s\n", cfg,
65     strerror(errno));
66    
67     if (c != NULL)
68     xfree(c);
69    
70     if ((r = parse_config(fd)))
71     fatal(ERROR_CONFIG_PARSE,
72     "parse error in config file at row %d\n", r);
73 lefcha 1.1
74 lefcha 1.48 fclose(fd);
75 lefcha 1.1
76 lefcha 1.11 #ifdef DEBUG
77 lefcha 1.48 fprintf(stderr, "debug: options: %0#10x '%s'\n", options, charset);
78 lefcha 1.11 #endif
79    
80 lefcha 1.48 return 0;
81 lefcha 1.1 }
82    
83 lefcha 1.6
84 lefcha 1.10 /*
85 lefcha 1.13 * Parse configuration file.
86 lefcha 1.1 */
87 lefcha 1.48 int
88     parse_config(FILE * fd)
89 lefcha 1.1 {
90 lefcha 1.48 int i, r;
91     unsigned int row;
92     char line[LINE_MAX];
93     regex_t creg[13];
94     regmatch_t match[11];
95     const char *reg[13] = {
96     "^([[:blank:]]*\n|#.*\n)$",
97    
98     "^[[:blank:]]*ACCOUNT[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]+"
99     "(([[:graph:]]+):([[:graph:]]*)|([[:graph:]]+))@"
100     "([[:alnum:].-]+)(:[[:digit:]]+)?[[:blank:]]*"
101 lefcha 1.56 "([[:blank:]]SSL2|[[:blank:]]SSL3|[[:blank:]]TLS1)?"
102     "[[:blank:]]*\n$",
103 lefcha 1.48
104     "^[[:blank:]]*FOLDER[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]+"
105     "([[:print:]]+)[[:blank:]]*\n$",
106    
107     "^[[:blank:]]*FILTER[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]*"
108     "([[:blank:]]OR|[[:blank:]]AND)?[[:blank:]]*\n$",
109    
110     "^[[:blank:]]*ACTION[[:blank:]]+(DELETE|"
111     "COPY[[:blank:]]+(\"[[:print:]]*\"|[[:graph:]]+)|"
112     "MOVE[[:blank:]]+(\"[[:print:]]*\"|[[:graph:]]+)|"
113     "RCOPY[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]+"
114     "(\"[[:print:]]*\"|[[:graph:]]+)|"
115     "RMOVE[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]+"
116     "(\"[[:print:]]*\"|[[:graph:]]+)|"
117     "FLAG[[:blank:]]+(REPLACE|ADD|REMOVE)[[:blank:]]+"
118     "([[:alpha:],]+)|"
119     "LIST)[[:blank:]]*([[:graph:]]*)[[:blank:]]*\n$",
120    
121     "^[[:blank:]]*(MASK[[:blank:]])?[[:blank:]]*(OR[[:blank:]]|"
122     "AND[[:blank:]])?[[:blank:]]*(NOT[[:blank:]])?[[:blank:]]*"
123     "(ANSWERED|DELETED|DRAFT|FLAGGED|NEW|OLD|RECENT|SEEN|"
124     "UNANSWERED|UNDELETED|UNDRAFT|UNFLAGGED|UNSEEN)[[:blank:]]*\n$",
125    
126     "^[[:blank:]]*(MASK[[:blank:]])?[[:blank:]]*(OR[[:blank:]]|"
127     "AND[[:blank:]])?[[:blank:]]*(NOT[[:blank:]])?[[:blank:]]*"
128     "(BCC|BODY|CC|FROM|SUBJECT|TEXT|TO)[[:blank:]]+"
129     "(\"[[:print:]]*\"|[[:graph:]]+)[[:blank:]]*\n$",
130    
131     "^[[:blank:]]*(MASK[[:blank:]])?[[:blank:]]*(OR[[:blank:]]|"
132     "AND[[:blank:]])?[[:blank:]]*(NOT[[:blank:]])?[[:blank:]]*"
133     "(HEADER)[[:blank:]]+(\"[[:print:]]*\"|[[:graph:]]+)"
134     "[[:blank:]]+(\"[[:print:]]*\"|[[:graph:]]+)[[:blank:]]*\n$",
135    
136     "^[[:blank:]]*(MASK[[:blank:]])?[[:blank:]]*(OR[[:blank:]]|"
137     "AND[[:blank:]])?[[:blank:]]*(NOT[[:blank:]])?[[:blank:]]*"
138     "(LARGER|SMALLER|OLDER|NEWER)[[:blank:]]+([[:digit:]]+)"
139     "[[:blank:]]*\n$",
140    
141     "^[[:blank:]]*JOB[[:blank:]]+([[:alnum:],_-]+)[[:blank:]]+"
142     "([[:alnum:],_-]+)[[:blank:]]*\n$",
143    
144     "^[[:blank:]]*(SET[[:blank:]])?[[:blank:]]*(LOGFILE|CHARSET)"
145     "[[:blank:]]*=[[:blank:]]*(\"[[:print:]]*\"|[[:graph:]]+)"
146     "[[:blank:]]*\n$",
147    
148     "^[[:blank:]]*(SET[[:blank:]])?[[:blank:]]*(ERRORS|EXPUNGE|"
149 lefcha 1.53 "HEADERS|NAMESPACE|SUBSCRIBE)[[:blank:]]*=[[:blank:]]*"
150 lefcha 1.48 "(YES|NO)[[:blank:]]*\n$",
151    
152     "^[[:blank:]]*(SET[[:blank:]])?[[:blank:]]*(DAEMON|TIMEOUT)"
153     "[[:blank:]]*=[[:blank:]]*([[:digit:]]+)\n$"
154     };
155    
156     r = row = 0;
157    
158     for (i = 0; i < 13; i++)
159     regcomp(&creg[i], reg[i], REG_EXTENDED | REG_ICASE);
160    
161     /* First process all the variables. This is done because some
162     * variables' arguments are used before the other commands are
163     * processed. */
164     while (fgets(line, LINE_MAX - 1, fd))
165     if (!regexec(&creg[10], line, 4, match, 0) ||
166     !regexec(&creg[11], line, 4, match, 0) ||
167     !regexec(&creg[12], line, 4, match, 0))
168     set_options(line, match);
169 lefcha 1.3
170 lefcha 1.48 /* Then rewind and process everything else. */
171     fseek(fd, 0L, SEEK_SET);
172     while (fgets(line, LINE_MAX - 1, fd)) {
173     row++;
174     if (!regexec(&creg[0], line, 0, match, 0))
175     continue;
176     else if (!regexec(&creg[1], line, 9, match, 0))
177     set_account(line, match);
178     else if (!regexec(&creg[2], line, 3, match, 0))
179     r = set_mboxgrp(line, match);
180     else if (!regexec(&creg[3], line, 3, match, 0))
181     r = set_filter(line, match);
182     else if (!regexec(&creg[4], line, 11, match, 0))
183     r = set_action(line, match);
184     else if (!regexec(&creg[5], line, 5, match, 0))
185     r = set_mask(line, match, MASK_MATCH_1);
186     else if (!regexec(&creg[6], line, 6, match, 0))
187     r = set_mask(line, match, MASK_MATCH_2);
188     else if (!regexec(&creg[7], line, 7, match, 0))
189     r = set_mask(line, match, MASK_MATCH_3);
190     else if (!regexec(&creg[8], line, 6, match, 0))
191     r = set_mask(line, match, MASK_MATCH_4);
192     else if (!regexec(&creg[9], line, 3, match, 0))
193     r = set_job(line, match);
194     /* Skip variable processing. */
195     else if (!regexec(&creg[10], line, 4, match, 0) ||
196     !regexec(&creg[11], line, 4, match, 0) ||
197     !regexec(&creg[12], line, 4, match, 0));
198     else
199     return row;
200    
201     if (r == ERROR_CONFIG_PARSE)
202     return row;
203     }
204 lefcha 1.18
205 lefcha 1.48 for (i = 0; i < 13; i++)
206     regfree(&creg[i]);
207 lefcha 1.6
208 lefcha 1.52 destroy_unneeded();
209 lefcha 1.48
210     return 0;
211 lefcha 1.1 }
212    
213    
214     /*
215 lefcha 1.52 * Signal SIGHUP received, destroy all data structures and reread the
216     * configuration file.
217     */
218     void
219     reread_config(char *cfg)
220     {
221     destroy_all();
222     read_config(cfg);
223     read_passwords();
224    
225     flags &= ~(FLAG_SIGHUP_RECEIVED);
226     }
227    
228    
229     /*
230 lefcha 1.13 * Set other options found in config file.
231 lefcha 1.11 */
232 lefcha 1.48 void
233     set_options(char *line, regmatch_t * m)
234 lefcha 1.11 {
235 lefcha 1.48 if (!strncasecmp(line + m[2].rm_so, "logfile", 7)) {
236     if (*logfile == '\0') {
237     if (*(line + m[3].rm_so) == '"' &&
238     *(line + m[3].rm_eo - 1) == '"')
239     strncat(logfile, line + m[3].rm_so + 1,
240     min((m[3].rm_eo - m[3].rm_so - 2),
241     PATH_MAX - 1));
242     else
243     strncat(logfile, line + m[3].rm_so,
244     min((m[3].rm_eo - m[3].rm_so),
245     PATH_MAX - 1));
246     }
247     } else if (!strncasecmp(line + m[2].rm_so, "charset", 7)) {
248     if (*(line + m[3].rm_so) == '"' &&
249     *(line + m[3].rm_eo - 1) == '"')
250     strncat(charset, line + m[3].rm_so + 1,
251     min((m[3].rm_eo - m[3].rm_so - 2),
252     CHARSET_LEN - 1));
253     else
254     strncat(charset, line + m[3].rm_so,
255     min((m[3].rm_eo - m[3].rm_so), CHARSET_LEN - 1));
256     } else if (!strncasecmp(line + m[2].rm_so, "errors", 6)) {
257     if (!strncasecmp(line + m[3].rm_so, "yes", 3))
258     options |= OPTION_ERRORS;
259     else
260     options &= ~(OPTION_ERRORS);
261     } else if (!strncasecmp(line + m[2].rm_so, "expunge", 7)) {
262     if (!strncasecmp(line + m[3].rm_so, "yes", 3))
263     options |= OPTION_EXPUNGE;
264     else
265     options &= ~(OPTION_EXPUNGE);
266     } else if (!strncasecmp(line + m[2].rm_so, "header", 6)) {
267     if (!strncasecmp(line + m[3].rm_so, "yes", 3))
268     options |= OPTION_HEADERS;
269     else
270     options &= ~(OPTION_HEADERS);
271     } else if (!strncasecmp(line + m[2].rm_so, "namespace", 9)) {
272     if (!strncasecmp(line + m[3].rm_so, "yes", 3))
273     options |= OPTION_NAMESPACE;
274     else
275     options &= ~(OPTION_NAMESPACE);
276     } else if (!strncasecmp(line + m[2].rm_so, "subscribe", 9)) {
277     if (!strncasecmp(line + m[3].rm_so, "yes", 3))
278     options |= OPTION_SUBSCRIBE;
279     else
280     options &= ~(OPTION_SUBSCRIBE);
281     } else if (!strncasecmp(line + m[2].rm_so, "timeout", 7)) {
282     errno = 0;
283     timeout = strtol(line + m[3].rm_so, NULL, 10);
284     if (errno)
285     timeout = 0;
286     } else if (!strncasecmp(line + m[2].rm_so, "daemon", 6) &&
287     !(options & OPTION_DAEMON_MODE)) {
288     options |= OPTION_DAEMON_MODE;
289     errno = 0;
290     interval = strtoul(line + m[3].rm_so, NULL, 10);
291     if (errno)
292     interval = 0;
293     }
294 lefcha 1.1 }
295 lefcha 1.29
296    
297     #ifdef ENCRYPTED_PASSWORDS
298     /*
299     * Open password file and call parse_passwords().
300     */
301 lefcha 1.48 int
302     read_passwords(void)
303 lefcha 1.29 {
304 lefcha 1.48 FILE *fd;
305     char pwfile[PATH_MAX];
306 lefcha 1.38
307 lefcha 1.48 if (!(flags & FLAG_BLANK_PASSWORD))
308     return ERROR_CONFIG_PARSE;
309 lefcha 1.38
310 lefcha 1.54 if (!passphr) {
311     passphr = (char *)smalloc(PASSPHRASE_LEN);
312     *passphr = '\0';
313     }
314 lefcha 1.48 snprintf(pwfile, PATH_MAX, "%s/%s", home, ".imapfilter/passwords");
315 lefcha 1.29 #ifdef DEBUG
316 lefcha 1.48 fprintf(stderr, "debug: passwords file: '%s'\n", pwfile);
317 lefcha 1.29 #endif
318 lefcha 1.38
319 lefcha 1.48 if (!exists_file(pwfile))
320     return ERROR_FILE_OPEN;
321 lefcha 1.38
322 lefcha 1.29 #ifdef CHECK_PERMISSIONS
323 lefcha 1.48 check_file_perms(pwfile, S_IRUSR | S_IWUSR);
324 lefcha 1.29 #endif
325    
326 lefcha 1.48 fd = fopen(pwfile, "r");
327     if (fd == NULL)
328     fatal(ERROR_FILE_OPEN, "opening passwords file %s; %s\n",
329     pwfile, strerror(errno));
330 lefcha 1.29
331 lefcha 1.48 parse_passwords(fd);
332 lefcha 1.29
333 lefcha 1.48 fclose(fd);
334 lefcha 1.29
335 lefcha 1.48 return 0;
336 lefcha 1.29 }
337    
338    
339     /*
340     * Parse unencrypted password file.
341     */
342 lefcha 1.48 int
343     parse_passwords(FILE * fd)
344 lefcha 1.29 {
345 lefcha 1.48 int t;
346     char *pe;
347     char user[USERNAME_LEN], serv[SERVER_LEN];
348     unsigned char *buf;
349     char *c, *cp, *line;
350     regex_t creg;
351     regmatch_t match[4];
352     const char *reg;
353    
354     t = 3;
355     pe = NULL;
356     reg = "([[:alnum:].-]+) ([[:graph:]]+) ([[:graph:]]+)";
357    
358 lefcha 1.54 if (!(flags & FLAG_DAEMON_MODE)) {
359 lefcha 1.52 do {
360     fseek(fd, 0L, SEEK_SET);
361     printf("Enter master passphrase: ");
362     get_password(passphr, PASSPHRASE_LEN);
363 lefcha 1.54 } while (decrypt_passwords(&buf, fd) && --t != 0);
364 lefcha 1.52
365     if (!t)
366     return ERROR_PASSPHRASE;
367 lefcha 1.54 } else if (decrypt_passwords(&buf, fd))
368     fatal(ERROR_DECRYPT,
369     "failed decrypting passwords in daemon mode\n");
370 lefcha 1.48
371     c = cp = sstrdup(buf);
372    
373     regcomp(&creg, reg, REG_EXTENDED | REG_ICASE);
374    
375     line = strtok_r(c, "\n", &c);
376     while (line != NULL && !regexec(&creg, line, 4, match, 0)) {
377     user[0] = serv[0] = '\0';
378    
379     strncat(serv, line + match[1].rm_so,
380     min(match[1].rm_eo - match[1].rm_so, SERVER_LEN - 1));
381     strncat(user, line + match[2].rm_so,
382     min(match[2].rm_eo - match[2].rm_so, USERNAME_LEN - 1));
383    
384     if ((pe = (char *)find_password(user, serv)))
385     strncat(pe, line + match[3].rm_so,
386     min(match[3].rm_eo - match[3].rm_so,
387     PASSWORD_LEN - 1));
388 lefcha 1.29
389 lefcha 1.48 line = strtok_r(NULL, "\n", &c);
390     }
391 lefcha 1.38
392 lefcha 1.48 regfree(&creg);
393     sfree(cp);
394     sfree(buf);
395 lefcha 1.29
396 lefcha 1.48 return 0;
397 lefcha 1.29 }
398    
399    
400     /*
401     * Store encrypted passwords to file.
402     */
403 lefcha 1.48 int
404 lefcha 1.54 store_passwords(account_t ** accts)
405 lefcha 1.29 {
406 lefcha 1.48 char pwfile[PATH_MAX];
407     FILE *fd;
408 lefcha 1.29
409 lefcha 1.48 snprintf(pwfile, PATH_MAX, "%s/%s", home, ".imapfilter/passwords");
410 lefcha 1.38
411 lefcha 1.48 create_file(pwfile, S_IRUSR | S_IWUSR);
412 lefcha 1.29
413 lefcha 1.48 fd = fopen(pwfile, "w");
414 lefcha 1.29
415 lefcha 1.48 if (fd == NULL)
416     fatal(ERROR_FILE_OPEN, "opening passwords file %s; %s\n",
417     pwfile, strerror(errno));
418 lefcha 1.38
419 lefcha 1.48 encrypt_passwords(fd, accts);
420 lefcha 1.38
421 lefcha 1.48 fclose(fd);
422 lefcha 1.29
423 lefcha 1.48 return 0;
424 lefcha 1.29 }
425 lefcha 1.48
426 lefcha 1.47 #endif /* ENCRYPTED_PASSWORDS */
427 lefcha 1.31
428    
429     /*
430     * Create $HOME/.imapfilter directory.
431     */
432 lefcha 1.48 int
433     create_homedir(void)
434 lefcha 1.31 {
435 lefcha 1.48 char *hdn;
436 lefcha 1.38
437 lefcha 1.48 hdn = ".imapfilter";
438 lefcha 1.31
439 lefcha 1.48 if (home != NULL)
440     if (chdir(home))
441     error("could not change directory; %s\n",
442     strerror(errno));
443    
444 lefcha 1.49 if (!exists_dir(hdn)) {
445 lefcha 1.48 if (mkdir(hdn, S_IRUSR | S_IWUSR | S_IXUSR))
446     error("could not create directory %s; %s\n", hdn,
447     strerror(errno));
448 lefcha 1.49 }
449     #ifdef CHECK_PERMISSIONS
450     else {
451     check_dir_perms(hdn, S_IRUSR | S_IWUSR | S_IXUSR);
452     }
453     #endif
454 lefcha 1.36
455 lefcha 1.48 return 0;
456 lefcha 1.31 }
457    
458    
459     /*
460     * Check if a file exists.
461     */
462 lefcha 1.48 int
463     exists_file(char *fname)
464 lefcha 1.31 {
465 lefcha 1.48 struct stat fs;
466 lefcha 1.38
467 lefcha 1.48 if (access(fname, F_OK))
468     return 0;
469 lefcha 1.38
470 lefcha 1.48 stat(fname, &fs);
471     if (!S_ISREG(fs.st_mode)) {
472     error("file %s not a regular file\n", fname);
473     return ERROR_FILE_OPEN;
474     }
475     return 1;
476 lefcha 1.31 }
477    
478    
479 lefcha 1.38 /*
480 lefcha 1.31 * Check if a directory exists.
481     */
482 lefcha 1.48 int
483     exists_dir(char *dname)
484 lefcha 1.31 {
485 lefcha 1.48 struct stat ds;
486 lefcha 1.38
487 lefcha 1.48 if (access(dname, F_OK))
488     return 0;
489 lefcha 1.38
490 lefcha 1.48 stat(dname, &ds);
491     if (!S_ISDIR(ds.st_mode)) {
492     error("file %s not a directory\n", dname);
493     return ERROR_FILE_OPEN;
494     }
495     return 1;
496 lefcha 1.31 }
497    
498    
499     /*
500 lefcha 1.36 * Create a file with the specified permissions.
501 lefcha 1.31 */
502 lefcha 1.48 int
503     create_file(char *fname, mode_t mode)
504 lefcha 1.31 {
505 lefcha 1.48 int fd;
506    
507     fd = 0;
508 lefcha 1.38
509 lefcha 1.48 if (!exists_file(fname)) {
510     fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, mode);
511     if (fd == -1) {
512     error("could not create file %s; %s\n", fname,
513     strerror(errno));
514     return ERROR_FILE_OPEN;
515     }
516     close(fd);
517     }
518     return 0;
519 lefcha 1.31 }
520    
521    
522     #ifdef CHECK_PERMISSIONS
523     /*
524     * Check the permissions of a file.
525     */
526 lefcha 1.48 int
527     check_file_perms(char *fname, mode_t mode)
528 lefcha 1.31 {
529 lefcha 1.48 struct stat fs;
530 lefcha 1.38
531 lefcha 1.48 if (stat(fname, &fs)) {
532     error("getting file %s status; %s\n", fname,
533     strerror(errno));
534     return ERROR_TRIVIAL;
535     }
536     if (!S_ISREG(fs.st_mode)) {
537     error("file %s not a regular file\n", fname);
538     return ERROR_TRIVIAL;
539     }
540     if ((fs.st_mode & 00777) != mode) {
541     error("warning: improper file %s permissions\n", fname);
542     error("warning: file's mode should be %o not %o\n", mode,
543     fs.st_mode & 00777);
544     return ERROR_TRIVIAL;
545     }
546     return 0;
547 lefcha 1.31 }
548    
549    
550     /*
551     * Check the permissions of a directory.
552     */
553 lefcha 1.48 int
554     check_dir_perms(char *dname, mode_t mode)
555 lefcha 1.31 {
556 lefcha 1.48 struct stat ds;
557 lefcha 1.31
558 lefcha 1.48 if (stat(dname, &ds)) {
559     error("getting file %s status; %s\n", dname,
560     strerror(errno));
561     return ERROR_TRIVIAL;
562     }
563     if (!S_ISDIR(ds.st_mode)) {
564     error("file %s not a directory\n", dname);
565     return ERROR_TRIVIAL;
566     }
567     if ((ds.st_mode & 00777) != mode) {
568     error("warning: improper dir %s permissions\n", dname);
569 lefcha 1.50 error("warning: dir's mode should be %o not %o\n", mode,
570 lefcha 1.48 ds.st_mode & 00777);
571     return ERROR_TRIVIAL;
572     }
573     return 0;
574 lefcha 1.31 }
575 lefcha 1.38
576 lefcha 1.47 #endif /* CHECK_PERMISSIONS */
577 lefcha 1.50
578    
579     #ifdef SSL_TLS
580     /*
581     * Get SSL/TLS certificate check it, maybe ask user about it and act
582     * accordingly.
583     */
584     int
585 lefcha 1.55 imf_ssl_cert(conn_t * conn)
586 lefcha 1.50 {
587     X509 *cert;
588     unsigned char md[EVP_MAX_MD_SIZE];
589     unsigned int mdlen;
590    
591     mdlen = 0;
592    
593 lefcha 1.55 if (!(cert = SSL_get_peer_certificate(conn->ssl)))
594 lefcha 1.50 return ERROR_SSL;
595    
596     if (!(X509_digest(cert, EVP_md5(), md, &mdlen)))
597     return ERROR_SSL;
598    
599     switch (imf_ssl_check_cert(cert, md, &mdlen)) {
600     case SSL_CERT_NONEXISTENT:
601     imf_ssl_print_cert(cert, md, &mdlen);
602 lefcha 1.51 if (flags & FLAG_DAEMON_MODE ||
603     imf_ssl_new_cert(cert) == SSL_CERT_ACTION_REJECT)
604 lefcha 1.50 goto abort;
605     break;
606     case SSL_CERT_MISMATCH:
607     imf_ssl_print_cert(cert, md, &mdlen);
608 lefcha 1.51 if (flags & FLAG_DAEMON_MODE ||
609     imf_ssl_cert_mismatch() == SSL_CERT_ACTION_ABORT)
610 lefcha 1.50 goto abort;
611     break;
612     case SSL_CERT_OK:
613     if (options & OPTION_DETAILS_VERBOSE)
614     imf_ssl_print_cert(cert, md, &mdlen);
615     }
616    
617     X509_free(cert);
618     return 0;
619    
620     abort:
621     X509_free(cert);
622     return ERROR_SSL;
623     }
624    
625    
626     /*
627     * Check if the SSL/TLS certificate exists in the certificates file.
628     */
629     int
630     imf_ssl_check_cert(X509 * pcert, unsigned char *pmd, unsigned int *pmdlen)
631     {
632     int r;
633     FILE *fd;
634     X509 *cert;
635     unsigned char md[EVP_MAX_MD_SIZE];
636     unsigned int mdlen;
637     char *cfn;
638    
639     r = SSL_CERT_NONEXISTENT;
640     cert = NULL;
641     cfn = ".imapfilter/certificates";
642    
643     if (!exists_file(cfn))
644     return SSL_CERT_NONEXISTENT;
645    
646     fd = fopen(cfn, "r");
647     if (fd == NULL)
648     return ERROR_FILE_OPEN;
649    
650     while ((cert = PEM_read_X509(fd, &cert, NULL, NULL)) != NULL) {
651     if (X509_subject_name_cmp(cert, pcert) != 0 ||
652     X509_issuer_name_cmp(cert, pcert) != 0)
653     continue;
654    
655     if (!X509_digest(cert, EVP_md5(), md, &mdlen) ||
656     *pmdlen != mdlen)
657     continue;
658    
659     if (memcmp(pmd, md, mdlen) != 0) {
660     r = SSL_CERT_MISMATCH;
661     break;
662     }
663     r = SSL_CERT_OK;
664     break;
665     }
666    
667     fclose(fd);
668     X509_free(cert);
669    
670     return r;
671     }
672    
673    
674     /*
675     * Print information about the SSL/TLS certificate.
676     */
677     void
678     imf_ssl_print_cert(X509 * cert, unsigned char *md, unsigned int *mdlen)
679     {
680     unsigned int i;
681     char *c;
682    
683     c = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
684     printf("Server certificate subject: %s\n", c);
685     xfree(c);
686    
687     c = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0);
688     printf("Server certificate issuer: %s\n", c);
689     xfree(c);
690    
691     printf("Server key fingerprint: ");
692     for (i = 0; i < *mdlen; i++)
693     printf(i != *mdlen - 1 ? "%02X:" : "%02X\n", md[i]);
694     }
695    
696    
697     /*
698     * Ask the user to reject/accept the SSL/TLS certificate.
699     */
700     int
701     imf_ssl_new_cert(X509 * cert)
702     {
703     FILE *fd;
704     char c, *cfn, buf[LINE_MAX];
705    
706     do {
707     printf("(R)eject, accept (t)emporarily or "
708     "accept (p)ermanently? ");
709     fgets(buf, LINE_MAX, stdin);
710     c = tolower(*buf);
711     } while (c != 'r' && c != 't' && c != 'p');
712    
713     if (c == 'r')
714     return SSL_CERT_ACTION_REJECT;
715     else if (c == 't')
716     return SSL_CERT_ACTION_ACCEPT;
717    
718     cfn = ".imapfilter/certificates";
719    
720     create_file(cfn, S_IRUSR | S_IWUSR);
721    
722     fd = fopen(cfn, "a");
723     if (fd == NULL)
724     return SSL_CERT_ACTION_REJECT;
725    
726     PEM_write_X509(fd, cert);
727    
728     fclose(fd);
729    
730     return SSL_CERT_ACTION_ACCEPT;
731     }
732    
733    
734     /*
735     * Ask user to proceed, while a fingerprint mismatch in the SSL/TLS
736     * certificate was found.
737     */
738     int
739     imf_ssl_cert_mismatch(void)
740     {
741     char c, buf[LINE_MAX];
742    
743     do {
744     printf("WARNING: SSL/TLS certificate fingerprint mismatch.\n"
745     "Proceed with the connection (y/n)? ");
746     fgets(buf, LINE_MAX, stdin);
747     c = tolower(*buf);
748     } while (c != 'y' && c != 'n');
749    
750     if (c == 'y')
751     return SSL_CERT_ACTION_CONTINUE;
752     else
753     return SSL_CERT_ACTION_ABORT;
754     }
755    
756     #endif /* SSL_TLS */

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26