Diffstat (limited to 'libetpan/src/engine/mailprivacy_smime.c') (more/less context) (ignore whitespace changes)
-rw-r--r-- | libetpan/src/engine/mailprivacy_smime.c | 1755 |
1 files changed, 1755 insertions, 0 deletions
diff --git a/libetpan/src/engine/mailprivacy_smime.c b/libetpan/src/engine/mailprivacy_smime.c new file mode 100644 index 0000000..43eb69f --- a/dev/null +++ b/libetpan/src/engine/mailprivacy_smime.c | |||
@@ -0,0 +1,1755 @@ | |||
1 | /* | ||
2 | * libEtPan! -- a mail library | ||
3 | * | ||
4 | * Copyright (C) 2001, 2005 - DINH Viet Hoa | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * Redistribution and use in source and binary forms, with or without | ||
8 | * modification, are permitted provided that the following conditions | ||
9 | * are met: | ||
10 | * 1. Redistributions of source code must retain the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer. | ||
12 | * 2. Redistributions in binary form must reproduce the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer in the | ||
14 | * documentation and/or other materials provided with the distribution. | ||
15 | * 3. Neither the name of the libEtPan! project nor the names of its | ||
16 | * contributors may be used to endorse or promote products derived | ||
17 | * from this software without specific prior written permission. | ||
18 | * | ||
19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND | ||
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE | ||
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
29 | * SUCH DAMAGE. | ||
30 | */ | ||
31 | |||
32 | /* | ||
33 | * $Id$ | ||
34 | */ | ||
35 | |||
36 | #include "mailprivacy_smime.h" | ||
37 | #include <string.h> | ||
38 | #include <sys/wait.h> | ||
39 | #include <sys/types.h> | ||
40 | #include <sys/stat.h> | ||
41 | #include <fcntl.h> | ||
42 | #include <unistd.h> | ||
43 | #include <sys/mman.h> | ||
44 | #include "mailprivacy_tools.h" | ||
45 | #include "mailprivacy.h" | ||
46 | #include <stdlib.h> | ||
47 | #include <dirent.h> | ||
48 | #include <stdio.h> | ||
49 | #include <ctype.h> | ||
50 | #include <libetpan/libetpan-config.h> | ||
51 | |||
52 | /* | ||
53 | global variable | ||
54 | |||
55 | TODO : instance of privacy drivers | ||
56 | */ | ||
57 | |||
58 | static char cert_dir[PATH_MAX] = ""; | ||
59 | static chash * certificates = NULL; | ||
60 | static chash * private_keys = NULL; | ||
61 | static char CAcert_dir[PATH_MAX] = ""; | ||
62 | static char * CAfile = NULL; | ||
63 | static int CA_check = 1; | ||
64 | static int store_cert = 0; | ||
65 | static char private_keys_dir[PATH_MAX] = ""; | ||
66 | |||
67 | static char * get_cert_file(char * email); | ||
68 | |||
69 | static char * get_private_key_file(char * email); | ||
70 | |||
71 | |||
72 | static int smime_is_signed(struct mailmime * mime) | ||
73 | { | ||
74 | if (mime->mm_content_type != NULL) { | ||
75 | clistiter * cur; | ||
76 | |||
77 | for(cur = clist_begin(mime->mm_content_type->ct_parameters) ; cur != NULL ; | ||
78 | cur = clist_next(cur)) { | ||
79 | struct mailmime_parameter * param; | ||
80 | |||
81 | param = cur->data; | ||
82 | |||
83 | if ((strcasecmp(param->pa_name, "protocol") == 0) && | ||
84 | ((strcasecmp(param->pa_value, "application/x-pkcs7-signature") == 0) || | ||
85 | (strcasecmp(param->pa_value, "application/pkcs7-signature") == 0))) | ||
86 | return 1; | ||
87 | } | ||
88 | } | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static int smime_is_encrypted(struct mailmime * mime) | ||
94 | { | ||
95 | if (mime->mm_content_type != NULL) { | ||
96 | if ((strcasecmp(mime->mm_content_type->ct_subtype, "x-pkcs7-mime") == 0) || | ||
97 | (strcasecmp(mime->mm_content_type->ct_subtype, "pkcs7-mime") == 0)) | ||
98 | return 1; | ||
99 | } | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | #define BUF_SIZE 1024 | ||
105 | |||
106 | enum { | ||
107 | NO_ERROR_SMIME = 0, | ||
108 | ERROR_SMIME_CHECK, | ||
109 | ERROR_SMIME_COMMAND, | ||
110 | ERROR_SMIME_FILE, | ||
111 | }; | ||
112 | |||
113 | /* write output to a file */ | ||
114 | |||
115 | static int get_smime_output(FILE * dest_f, char * command) | ||
116 | { | ||
117 | FILE * p; | ||
118 | char buf[BUF_SIZE]; | ||
119 | size_t size; | ||
120 | int res; | ||
121 | int status; | ||
122 | char command_redirected[PATH_MAX]; | ||
123 | |||
124 | snprintf(command_redirected, sizeof(command_redirected), "%s 2>&1", command); | ||
125 | |||
126 | /* | ||
127 | flush buffer so that it is not flushed more than once when forking | ||
128 | */ | ||
129 | fflush(dest_f); | ||
130 | |||
131 | p = popen(command_redirected, "r"); | ||
132 | if (p == NULL) { | ||
133 | res = ERROR_SMIME_COMMAND; | ||
134 | goto err; | ||
135 | } | ||
136 | |||
137 | while ((size = fread(buf, 1, sizeof(buf), p)) != 0) { | ||
138 | size_t written; | ||
139 | |||
140 | written = fwrite(buf, 1, size, dest_f); | ||
141 | if (written != size) { | ||
142 | res = ERROR_SMIME_FILE; | ||
143 | goto close; | ||
144 | } | ||
145 | } | ||
146 | status = pclose(p); | ||
147 | |||
148 | if (WEXITSTATUS(status) != 0) | ||
149 | return ERROR_SMIME_CHECK; | ||
150 | else | ||
151 | return NO_ERROR_SMIME; | ||
152 | |||
153 | close: | ||
154 | pclose(p); | ||
155 | err: | ||
156 | return res; | ||
157 | } | ||
158 | |||
159 | static char * get_first_from_addr(struct mailmime * mime) | ||
160 | { | ||
161 | clistiter * cur; | ||
162 | struct mailimf_single_fields single_fields; | ||
163 | struct mailimf_fields * fields; | ||
164 | struct mailimf_mailbox * mb; | ||
165 | |||
166 | while (mime->mm_parent != NULL) | ||
167 | mime = mime->mm_parent; | ||
168 | |||
169 | if (mime->mm_type != MAILMIME_MESSAGE) | ||
170 | return NULL; | ||
171 | |||
172 | fields = mime->mm_data.mm_message.mm_fields; | ||
173 | if (fields == NULL) | ||
174 | return NULL; | ||
175 | |||
176 | mailimf_single_fields_init(&single_fields, fields); | ||
177 | |||
178 | if (single_fields.fld_from == NULL) | ||
179 | return NULL; | ||
180 | |||
181 | cur = clist_begin(single_fields.fld_from->frm_mb_list->mb_list); | ||
182 | if (cur == NULL) | ||
183 | return NULL; | ||
184 | |||
185 | mb = clist_content(cur); | ||
186 | |||
187 | return mb->mb_addr_spec; | ||
188 | } | ||
189 | |||
190 | #define SMIME_DECRYPT_DESCRIPTION "S/MIME encrypted part\r\n" | ||
191 | #define SMIME_DECRYPT_FAILED "S/MIME decryption FAILED\r\n" | ||
192 | #define SMIME_DECRYPT_SUCCESS "S/MIME decryption success\r\n" | ||
193 | |||
194 | static int smime_decrypt(struct mailprivacy * privacy, | ||
195 | mailmessage * msg, | ||
196 | struct mailmime * mime, struct mailmime ** result) | ||
197 | { | ||
198 | char smime_filename[PATH_MAX]; | ||
199 | char quoted_smime_filename[PATH_MAX]; | ||
200 | char description_filename[PATH_MAX]; | ||
201 | FILE * description_f; | ||
202 | char decrypted_filename[PATH_MAX]; | ||
203 | FILE * decrypted_f; | ||
204 | char command[PATH_MAX]; | ||
205 | struct mailmime * description_mime; | ||
206 | struct mailmime * decrypted_mime; | ||
207 | int r; | ||
208 | int res; | ||
209 | int sign_ok; | ||
210 | char quoted_decrypted_filename[PATH_MAX]; | ||
211 | struct mailmime * multipart; | ||
212 | char * smime_cert; | ||
213 | char * smime_key; | ||
214 | char quoted_smime_cert[PATH_MAX]; | ||
215 | char quoted_smime_key[PATH_MAX]; | ||
216 | char * email; | ||
217 | chashiter * iter; | ||
218 | |||
219 | /* fetch the whole multipart and write it to a file */ | ||
220 | |||
221 | r = mailprivacy_fetch_mime_body_to_file(privacy, | ||
222 | smime_filename, sizeof(smime_filename), | ||
223 | msg, mime); | ||
224 | if (r != MAIL_NO_ERROR) { | ||
225 | res = r; | ||
226 | goto err; | ||
227 | } | ||
228 | |||
229 | /* we are in a safe directory */ | ||
230 | |||
231 | decrypted_f = mailprivacy_get_tmp_file(privacy, | ||
232 | decrypted_filename, | ||
233 | sizeof(decrypted_filename)); | ||
234 | if (decrypted_f == NULL) { | ||
235 | res = MAIL_ERROR_FILE; | ||
236 | goto unlink_smime; | ||
237 | } | ||
238 | fclose(decrypted_f); | ||
239 | |||
240 | sign_ok = 0; | ||
241 | for(iter = chash_begin(private_keys) ; iter != NULL ; | ||
242 | iter = chash_next(private_keys, iter)) { | ||
243 | chashdatum key; | ||
244 | char email_buf[BUF_SIZE]; | ||
245 | |||
246 | chash_key(iter, &key); | ||
247 | |||
248 | if (key.len >= sizeof(email_buf)) | ||
249 | continue; | ||
250 | |||
251 | strncpy(email_buf, key.data, key.len); | ||
252 | email_buf[key.len] = '\0'; | ||
253 | email = email_buf; | ||
254 | |||
255 | /* description */ | ||
256 | |||
257 | description_f = mailprivacy_get_tmp_file(privacy, | ||
258 | description_filename, | ||
259 | sizeof(description_filename)); | ||
260 | if (description_f == NULL) { | ||
261 | res = MAIL_ERROR_FILE; | ||
262 | goto unlink_decrypted; | ||
263 | } | ||
264 | |||
265 | fprintf(description_f, SMIME_DECRYPT_DESCRIPTION); | ||
266 | |||
267 | /* get encryption key */ | ||
268 | |||
269 | #if 0 | ||
270 | email = get_first_from_addr(mime); | ||
271 | if (email == NULL) { | ||
272 | fclose(description_f); | ||
273 | res = MAIL_ERROR_INVAL; | ||
274 | goto unlink_description; | ||
275 | } | ||
276 | #endif | ||
277 | |||
278 | smime_key = get_private_key_file(email); | ||
279 | smime_cert = get_cert_file(email); | ||
280 | if ((smime_cert == NULL) || (smime_key == NULL)) { | ||
281 | fclose(description_f); | ||
282 | res = MAIL_ERROR_INVAL; | ||
283 | goto unlink_description; | ||
284 | } | ||
285 | |||
286 | r = mail_quote_filename(quoted_smime_cert, sizeof(quoted_smime_cert), | ||
287 | smime_cert); | ||
288 | if (r < 0) { | ||
289 | fclose(description_f); | ||
290 | res = MAIL_ERROR_MEMORY; | ||
291 | goto unlink_description; | ||
292 | } | ||
293 | |||
294 | r = mail_quote_filename(quoted_smime_key, sizeof(quoted_smime_key), | ||
295 | smime_key); | ||
296 | if (r < 0) { | ||
297 | fclose(description_f); | ||
298 | res = MAIL_ERROR_MEMORY; | ||
299 | goto unlink_description; | ||
300 | } | ||
301 | |||
302 | /* run the command */ | ||
303 | |||
304 | r = mail_quote_filename(quoted_smime_filename, | ||
305 | sizeof(quoted_smime_filename), smime_filename); | ||
306 | if (r < 0) { | ||
307 | fclose(description_f); | ||
308 | res = MAIL_ERROR_MEMORY; | ||
309 | goto unlink_description; | ||
310 | } | ||
311 | |||
312 | r = mail_quote_filename(quoted_decrypted_filename, | ||
313 | sizeof(quoted_decrypted_filename), decrypted_filename); | ||
314 | if (r < 0) { | ||
315 | fclose(description_f); | ||
316 | res = MAIL_ERROR_MEMORY; | ||
317 | goto unlink_description; | ||
318 | } | ||
319 | |||
320 | sign_ok = 0; | ||
321 | snprintf(command, PATH_MAX, | ||
322 | "openssl smime -decrypt -in %s -out %s -inkey %s -recip %s", | ||
323 | quoted_smime_filename, quoted_decrypted_filename, | ||
324 | quoted_smime_key, quoted_smime_cert); | ||
325 | |||
326 | r = get_smime_output(description_f, command); | ||
327 | switch (r) { | ||
328 | case NO_ERROR_SMIME: | ||
329 | sign_ok = 1; | ||
330 | break; | ||
331 | case ERROR_SMIME_CHECK: | ||
332 | sign_ok = 0; | ||
333 | break; | ||
334 | case ERROR_SMIME_COMMAND: | ||
335 | fclose(description_f); | ||
336 | res = MAIL_ERROR_COMMAND; | ||
337 | goto unlink_description; | ||
338 | case ERROR_SMIME_FILE: | ||
339 | fclose(description_f); | ||
340 | res = MAIL_ERROR_FILE; | ||
341 | goto unlink_description; | ||
342 | } | ||
343 | |||
344 | if (sign_ok) { | ||
345 | fprintf(description_f, SMIME_DECRYPT_SUCCESS); | ||
346 | fclose(description_f); | ||
347 | break; | ||
348 | } | ||
349 | else { | ||
350 | fclose(description_f); | ||
351 | unlink(description_filename); | ||
352 | } | ||
353 | } | ||
354 | |||
355 | if (!sign_ok) { | ||
356 | description_f = mailprivacy_get_tmp_file(privacy, | ||
357 | description_filename, | ||
358 | sizeof(description_filename)); | ||
359 | if (description_f == NULL) { | ||
360 | res = MAIL_ERROR_FILE; | ||
361 | goto unlink_decrypted; | ||
362 | } | ||
363 | |||
364 | fprintf(description_f, SMIME_DECRYPT_DESCRIPTION); | ||
365 | fprintf(description_f, SMIME_DECRYPT_FAILED); | ||
366 | fclose(description_f); | ||
367 | } | ||
368 | |||
369 | /* building multipart */ | ||
370 | |||
371 | r = mailmime_new_with_content("multipart/x-decrypted", NULL, &multipart); | ||
372 | if (r != MAILIMF_NO_ERROR) { | ||
373 | res = MAIL_ERROR_MEMORY; | ||
374 | goto unlink_description; | ||
375 | } | ||
376 | |||
377 | /* building the description part */ | ||
378 | |||
379 | description_mime = mailprivacy_new_file_part(privacy, | ||
380 | description_filename, | ||
381 | "text/plain", MAILMIME_MECHANISM_8BIT); | ||
382 | if (description_mime == NULL) { | ||
383 | mailprivacy_mime_clear(multipart); | ||
384 | mailmime_free(multipart); | ||
385 | res = MAIL_ERROR_MEMORY; | ||
386 | goto unlink_description; | ||
387 | } | ||
388 | |||
389 | /* adds the description part */ | ||
390 | |||
391 | r = mailmime_smart_add_part(multipart, description_mime); | ||
392 | if (r != MAIL_NO_ERROR) { | ||
393 | mailprivacy_mime_clear(description_mime); | ||
394 | mailmime_free(description_mime); | ||
395 | mailprivacy_mime_clear(multipart); | ||
396 | mailmime_free(multipart); | ||
397 | res = MAIL_ERROR_MEMORY; | ||
398 | goto unlink_description; | ||
399 | } | ||
400 | |||
401 | /* building the decrypted part */ | ||
402 | |||
403 | r = mailprivacy_get_part_from_file(privacy, 1, | ||
404 | decrypted_filename, &decrypted_mime); | ||
405 | if (r == MAIL_NO_ERROR) { | ||
406 | /* adds the decrypted part */ | ||
407 | |||
408 | r = mailmime_smart_add_part(multipart, decrypted_mime); | ||
409 | if (r != MAIL_NO_ERROR) { | ||
410 | mailprivacy_mime_clear(decrypted_mime); | ||
411 | mailmime_free(decrypted_mime); | ||
412 | mailprivacy_mime_clear(multipart); | ||
413 | mailmime_free(multipart); | ||
414 | res = MAIL_ERROR_MEMORY; | ||
415 | goto unlink_description; | ||
416 | } | ||
417 | } | ||
418 | |||
419 | unlink(description_filename); | ||
420 | unlink(decrypted_filename); | ||
421 | unlink(smime_filename); | ||
422 | |||
423 | * result = multipart; | ||
424 | |||
425 | return MAIL_NO_ERROR; | ||
426 | |||
427 | unlink_description: | ||
428 | unlink(description_filename); | ||
429 | unlink_decrypted: | ||
430 | unlink(decrypted_filename); | ||
431 | unlink_smime: | ||
432 | unlink(smime_filename); | ||
433 | err: | ||
434 | return res; | ||
435 | } | ||
436 | |||
437 | |||
438 | static int get_cert_from_sig(struct mailprivacy * privacy, | ||
439 | mailmessage * msg, | ||
440 | struct mailmime * mime); | ||
441 | |||
442 | #define SMIME_VERIFY_DESCRIPTION "S/MIME verify signed message\r\n" | ||
443 | #define SMIME_VERIFY_FAILED "S/MIME verification FAILED\r\n" | ||
444 | #define SMIME_VERIFY_SUCCESS "S/MIME verification success\r\n" | ||
445 | |||
446 | static int | ||
447 | smime_verify(struct mailprivacy * privacy, | ||
448 | mailmessage * msg, | ||
449 | struct mailmime * mime, struct mailmime ** result) | ||
450 | { | ||
451 | char smime_filename[PATH_MAX]; | ||
452 | char quoted_smime_filename[PATH_MAX]; | ||
453 | int res; | ||
454 | int r; | ||
455 | char command[PATH_MAX]; | ||
456 | int sign_ok; | ||
457 | struct mailmime * description_mime; | ||
458 | FILE * description_f; | ||
459 | char description_filename[PATH_MAX]; | ||
460 | struct mailmime * multipart; | ||
461 | char stripped_filename[PATH_MAX]; | ||
462 | struct mailmime * stripped_mime; | ||
463 | char quoted_stripped_filename[PATH_MAX]; | ||
464 | FILE * stripped_f; | ||
465 | char check_CA[PATH_MAX]; | ||
466 | char quoted_CAfile[PATH_MAX]; | ||
467 | char noverify[PATH_MAX]; | ||
468 | |||
469 | if (store_cert) | ||
470 | get_cert_from_sig(privacy, msg, mime); | ||
471 | |||
472 | * check_CA = '\0'; | ||
473 | if (CAfile != NULL) { | ||
474 | r = mail_quote_filename(quoted_CAfile, sizeof(quoted_CAfile), CAfile); | ||
475 | if (r < 0) { | ||
476 | res = MAIL_ERROR_MEMORY; | ||
477 | goto err; | ||
478 | } | ||
479 | |||
480 | snprintf(check_CA, sizeof(check_CA), "-CAfile %s", quoted_CAfile); | ||
481 | } | ||
482 | |||
483 | * noverify = '\0'; | ||
484 | if (!CA_check) { | ||
485 | snprintf(noverify, sizeof(noverify), "-noverify"); | ||
486 | } | ||
487 | |||
488 | /* fetch the whole multipart and write it to a file */ | ||
489 | |||
490 | r = mailprivacy_fetch_mime_body_to_file(privacy, | ||
491 | smime_filename, sizeof(smime_filename), | ||
492 | msg, mime); | ||
493 | if (r != MAIL_NO_ERROR) { | ||
494 | res = r; | ||
495 | goto err; | ||
496 | } | ||
497 | |||
498 | stripped_f = mailprivacy_get_tmp_file(privacy, | ||
499 | stripped_filename, | ||
500 | sizeof(stripped_filename)); | ||
501 | if (stripped_f == NULL) { | ||
502 | res = MAIL_ERROR_FILE; | ||
503 | goto unlink_smime; | ||
504 | } | ||
505 | fclose(stripped_f); | ||
506 | |||
507 | /* description */ | ||
508 | |||
509 | description_f = mailprivacy_get_tmp_file(privacy, | ||
510 | description_filename, | ||
511 | sizeof(description_filename)); | ||
512 | if (description_f == NULL) { | ||
513 | res = MAIL_ERROR_FILE; | ||
514 | goto unlink_stripped; | ||
515 | } | ||
516 | |||
517 | fprintf(description_f, SMIME_VERIFY_DESCRIPTION); | ||
518 | |||
519 | /* run the command */ | ||
520 | |||
521 | r = mail_quote_filename(quoted_smime_filename, | ||
522 | sizeof(quoted_smime_filename), smime_filename); | ||
523 | if (r < 0) { | ||
524 | fclose(description_f); | ||
525 | res = MAIL_ERROR_MEMORY; | ||
526 | goto unlink_description; | ||
527 | } | ||
528 | |||
529 | r = mail_quote_filename(quoted_stripped_filename, | ||
530 | sizeof(quoted_stripped_filename), stripped_filename); | ||
531 | if (r < 0) { | ||
532 | fclose(description_f); | ||
533 | res = MAIL_ERROR_MEMORY; | ||
534 | goto unlink_description; | ||
535 | } | ||
536 | |||
537 | sign_ok = 0; | ||
538 | snprintf(command, PATH_MAX, "openssl smime -verify -in %s -out %s %s %s", | ||
539 | quoted_smime_filename, quoted_stripped_filename, check_CA, noverify); | ||
540 | |||
541 | r = get_smime_output(description_f, command); | ||
542 | switch (r) { | ||
543 | case NO_ERROR_SMIME: | ||
544 | sign_ok = 1; | ||
545 | break; | ||
546 | case ERROR_SMIME_CHECK: | ||
547 | sign_ok = 0; | ||
548 | break; | ||
549 | case ERROR_SMIME_COMMAND: | ||
550 | fclose(description_f); | ||
551 | res = MAIL_ERROR_COMMAND; | ||
552 | goto unlink_description; | ||
553 | case ERROR_SMIME_FILE: | ||
554 | fclose(description_f); | ||
555 | res = MAIL_ERROR_FILE; | ||
556 | goto unlink_description; | ||
557 | } | ||
558 | if (sign_ok) | ||
559 | fprintf(description_f, SMIME_VERIFY_SUCCESS); | ||
560 | else | ||
561 | fprintf(description_f, SMIME_VERIFY_FAILED); | ||
562 | fclose(description_f); | ||
563 | |||
564 | /* building multipart */ | ||
565 | |||
566 | r = mailmime_new_with_content("multipart/x-verified", NULL, &multipart); | ||
567 | if (r != MAILIMF_NO_ERROR) { | ||
568 | res = MAIL_ERROR_MEMORY; | ||
569 | goto unlink_description; | ||
570 | } | ||
571 | |||
572 | /* building the description part */ | ||
573 | |||
574 | description_mime = mailprivacy_new_file_part(privacy, | ||
575 | description_filename, | ||
576 | "text/plain", MAILMIME_MECHANISM_8BIT); | ||
577 | if (description_mime == NULL) { | ||
578 | mailprivacy_mime_clear(multipart); | ||
579 | mailmime_free(multipart); | ||
580 | res = MAIL_ERROR_MEMORY; | ||
581 | goto unlink_description; | ||
582 | } | ||
583 | |||
584 | /* adds the description part */ | ||
585 | |||
586 | r = mailmime_smart_add_part(multipart, description_mime); | ||
587 | if (r != MAIL_NO_ERROR) { | ||
588 | mailprivacy_mime_clear(description_mime); | ||
589 | mailmime_free(description_mime); | ||
590 | mailprivacy_mime_clear(multipart); | ||
591 | mailmime_free(multipart); | ||
592 | res = MAIL_ERROR_MEMORY; | ||
593 | goto unlink_description; | ||
594 | } | ||
595 | |||
596 | r = mailprivacy_get_part_from_file(privacy, 1, | ||
597 | stripped_filename, &stripped_mime); | ||
598 | if (r != MAIL_NO_ERROR) { | ||
599 | mailprivacy_mime_clear(multipart); | ||
600 | mailmime_free(multipart); | ||
601 | res = r; | ||
602 | goto unlink_description; | ||
603 | } | ||
604 | |||
605 | r = mailmime_smart_add_part(multipart, stripped_mime); | ||
606 | if (r != MAIL_NO_ERROR) { | ||
607 | mailprivacy_mime_clear(stripped_mime); | ||
608 | mailmime_free(stripped_mime); | ||
609 | mailprivacy_mime_clear(multipart); | ||
610 | mailmime_free(multipart); | ||
611 | res = MAIL_ERROR_MEMORY; | ||
612 | goto unlink_description; | ||
613 | } | ||
614 | |||
615 | unlink(description_filename); | ||
616 | unlink(stripped_filename); | ||
617 | unlink(smime_filename); | ||
618 | |||
619 | * result = multipart; | ||
620 | |||
621 | return MAIL_NO_ERROR; | ||
622 | |||
623 | unlink_description: | ||
624 | unlink(description_filename); | ||
625 | unlink_stripped: | ||
626 | unlink(stripped_filename); | ||
627 | unlink_smime: | ||
628 | unlink(smime_filename); | ||
629 | err: | ||
630 | return res; | ||
631 | } | ||
632 | |||
633 | static int smime_test_encrypted(struct mailprivacy * privacy, | ||
634 | mailmessage * msg, | ||
635 | struct mailmime * mime) | ||
636 | { | ||
637 | switch (mime->mm_type) { | ||
638 | case MAILMIME_MULTIPLE: | ||
639 | return smime_is_signed(mime); | ||
640 | |||
641 | case MAILMIME_SINGLE: | ||
642 | return smime_is_encrypted(mime); | ||
643 | } | ||
644 | |||
645 | return 0; | ||
646 | } | ||
647 | |||
648 | static int smime_handler(struct mailprivacy * privacy, | ||
649 | mailmessage * msg, | ||
650 | struct mailmime * mime, struct mailmime ** result) | ||
651 | { | ||
652 | int r; | ||
653 | struct mailmime * alternative_mime; | ||
654 | |||
655 | alternative_mime = NULL; | ||
656 | switch (mime->mm_type) { | ||
657 | case MAILMIME_MULTIPLE: | ||
658 | r = MAIL_ERROR_INVAL; | ||
659 | if (smime_is_signed(mime)) | ||
660 | r = smime_verify(privacy, msg, mime, &alternative_mime); | ||
661 | |||
662 | if (r != MAIL_NO_ERROR) | ||
663 | return r; | ||
664 | |||
665 | * result = alternative_mime; | ||
666 | |||
667 | return MAIL_NO_ERROR; | ||
668 | |||
669 | case MAILMIME_SINGLE: | ||
670 | r = MAIL_ERROR_INVAL; | ||
671 | if (smime_is_encrypted(mime)) | ||
672 | r = smime_decrypt(privacy, msg, mime, &alternative_mime); | ||
673 | |||
674 | if (r != MAIL_NO_ERROR) | ||
675 | return r; | ||
676 | |||
677 | * result = alternative_mime; | ||
678 | |||
679 | return MAIL_NO_ERROR; | ||
680 | } | ||
681 | |||
682 | return MAIL_ERROR_INVAL; | ||
683 | } | ||
684 | |||
685 | |||
686 | |||
687 | |||
688 | static void strip_mime_headers(struct mailmime * mime) | ||
689 | { | ||
690 | struct mailmime_fields * fields; | ||
691 | clistiter * cur; | ||
692 | |||
693 | fields = mime->mm_mime_fields; | ||
694 | |||
695 | if (fields == NULL) | ||
696 | return; | ||
697 | |||
698 | for(cur = clist_begin(fields->fld_list) ; cur != NULL ; | ||
699 | cur = clist_next(cur)) { | ||
700 | struct mailmime_field * field; | ||
701 | |||
702 | field = clist_content(cur); | ||
703 | |||
704 | if (field->fld_type == MAILMIME_FIELD_VERSION) { | ||
705 | mailmime_field_free(field); | ||
706 | clist_delete(fields->fld_list, cur); | ||
707 | break; | ||
708 | } | ||
709 | } | ||
710 | } | ||
711 | |||
712 | static int smime_sign(struct mailprivacy * privacy, | ||
713 | struct mailmime * mime, struct mailmime ** result) | ||
714 | { | ||
715 | char signed_filename[PATH_MAX]; | ||
716 | FILE * signed_f; | ||
717 | int res; | ||
718 | int r; | ||
719 | int col; | ||
720 | char signature_filename[PATH_MAX]; | ||
721 | FILE * signature_f; | ||
722 | char command[PATH_MAX]; | ||
723 | char quoted_signature_filename[PATH_MAX]; | ||
724 | char quoted_signed_filename[PATH_MAX]; | ||
725 | struct mailmime * signed_mime; | ||
726 | char * smime_cert; | ||
727 | char * smime_key; | ||
728 | char quoted_smime_cert[PATH_MAX]; | ||
729 | char quoted_smime_key[PATH_MAX]; | ||
730 | char * email; | ||
731 | |||
732 | /* get signing key */ | ||
733 | |||
734 | email = get_first_from_addr(mime); | ||
735 | if (email == NULL) { | ||
736 | res = MAIL_ERROR_INVAL; | ||
737 | goto err; | ||
738 | } | ||
739 | |||
740 | smime_key = get_private_key_file(email); | ||
741 | smime_cert = get_cert_file(email); | ||
742 | if ((smime_cert == NULL) || (smime_key == NULL)) { | ||
743 | res = MAIL_ERROR_INVAL; | ||
744 | goto err; | ||
745 | } | ||
746 | |||
747 | /* part to sign */ | ||
748 | |||
749 | /* encode quoted printable all text parts */ | ||
750 | |||
751 | mailprivacy_prepare_mime(mime); | ||
752 | |||
753 | signed_f = mailprivacy_get_tmp_file(privacy, | ||
754 | signed_filename, sizeof(signed_filename)); | ||
755 | if (signed_f == NULL) { | ||
756 | res = MAIL_ERROR_FILE; | ||
757 | goto err; | ||
758 | } | ||
759 | |||
760 | col = 0; | ||
761 | r = mailmime_write(signed_f, &col, mime); | ||
762 | if (r != MAILIMF_NO_ERROR) { | ||
763 | fclose(signed_f); | ||
764 | res = MAIL_ERROR_FILE; | ||
765 | goto unlink_signed; | ||
766 | } | ||
767 | |||
768 | fclose(signed_f); | ||
769 | |||
770 | /* prepare destination file for signature */ | ||
771 | |||
772 | signature_f = mailprivacy_get_tmp_file(privacy, | ||
773 | signature_filename, | ||
774 | sizeof(signature_filename)); | ||
775 | if (signature_f == NULL) { | ||
776 | res = MAIL_ERROR_FILE; | ||
777 | goto unlink_signed; | ||
778 | } | ||
779 | fclose(signature_f); | ||
780 | |||
781 | r = mail_quote_filename(quoted_signed_filename, | ||
782 | sizeof(quoted_signed_filename), signed_filename); | ||
783 | if (r < 0) { | ||
784 | res = MAIL_ERROR_MEMORY; | ||
785 | goto unlink_signature; | ||
786 | } | ||
787 | |||
788 | r = mail_quote_filename(quoted_signature_filename, | ||
789 | sizeof(quoted_signature_filename), signature_filename); | ||
790 | if (r < 0) { | ||
791 | res = MAIL_ERROR_MEMORY; | ||
792 | goto unlink_signature; | ||
793 | } | ||
794 | |||
795 | r = mail_quote_filename(quoted_smime_key, | ||
796 | sizeof(quoted_smime_key), smime_key); | ||
797 | if (r < 0) { | ||
798 | res = MAIL_ERROR_MEMORY; | ||
799 | goto unlink_signature; | ||
800 | } | ||
801 | |||
802 | r = mail_quote_filename(quoted_smime_cert, | ||
803 | sizeof(quoted_smime_cert), smime_cert); | ||
804 | if (r < 0) { | ||
805 | res = MAIL_ERROR_MEMORY; | ||
806 | goto unlink_signature; | ||
807 | } | ||
808 | |||
809 | snprintf(command, sizeof(command), | ||
810 | "openssl smime -sign -in %s -out %s -signer %s -inkey %s 2>/dev/null", | ||
811 | quoted_signed_filename, quoted_signature_filename, | ||
812 | quoted_smime_cert, quoted_smime_key); | ||
813 | |||
814 | r = system(command); | ||
815 | if (WEXITSTATUS(r) != 0) { | ||
816 | res = MAIL_ERROR_COMMAND; | ||
817 | goto unlink_signature; | ||
818 | } | ||
819 | |||
820 | /* signature part */ | ||
821 | |||
822 | r = mailprivacy_get_part_from_file(privacy, 0, | ||
823 | signature_filename, &signed_mime); | ||
824 | if (r != MAIL_NO_ERROR) { | ||
825 | res = r; | ||
826 | goto unlink_signature; | ||
827 | } | ||
828 | strip_mime_headers(signed_mime); | ||
829 | |||
830 | unlink(signature_filename); | ||
831 | unlink(signed_filename); | ||
832 | |||
833 | * result = signed_mime; | ||
834 | |||
835 | return MAIL_NO_ERROR; | ||
836 | |||
837 | unlink_signature: | ||
838 | unlink(signature_filename); | ||
839 | unlink_signed: | ||
840 | unlink(signed_filename); | ||
841 | err: | ||
842 | return res; | ||
843 | } | ||
844 | |||
845 | |||
846 | /* ********************************************************************* */ | ||
847 | /* find S/MIME recipient */ | ||
848 | |||
849 | static int recipient_add_mb(char * recipient, size_t * len, | ||
850 | struct mailimf_mailbox * mb) | ||
851 | { | ||
852 | char * filename; | ||
853 | char quoted_filename[PATH_MAX]; | ||
854 | size_t buflen; | ||
855 | int r; | ||
856 | |||
857 | if (mb->mb_addr_spec == NULL) | ||
858 | return MAIL_NO_ERROR; | ||
859 | |||
860 | filename = get_cert_file(mb->mb_addr_spec); | ||
861 | if (filename == NULL) | ||
862 | return MAIL_ERROR_INVAL; | ||
863 | |||
864 | r = mail_quote_filename(quoted_filename, sizeof(quoted_filename), | ||
865 | filename); | ||
866 | if (r < 0) | ||
867 | return MAIL_ERROR_MEMORY; | ||
868 | |||
869 | buflen = strlen(quoted_filename + 1); | ||
870 | if (buflen >= * len) | ||
871 | return MAIL_ERROR_MEMORY; | ||
872 | |||
873 | strncat(recipient, quoted_filename, * len); | ||
874 | (* len) -= buflen; | ||
875 | strncat(recipient, " ", * len); | ||
876 | (* len) --; | ||
877 | |||
878 | return MAIL_NO_ERROR; | ||
879 | } | ||
880 | |||
881 | static int recipient_add_mb_list(char * recipient, size_t * len, | ||
882 | struct mailimf_mailbox_list * mb_list) | ||
883 | { | ||
884 | clistiter * cur; | ||
885 | int r; | ||
886 | |||
887 | for(cur = clist_begin(mb_list->mb_list) ; cur != NULL ; | ||
888 | cur = clist_next(cur)) { | ||
889 | struct mailimf_mailbox * mb; | ||
890 | |||
891 | mb = clist_content(cur); | ||
892 | |||
893 | r = recipient_add_mb(recipient, len, mb); | ||
894 | if (r != MAIL_NO_ERROR) | ||
895 | return r; | ||
896 | } | ||
897 | |||
898 | return MAIL_NO_ERROR; | ||
899 | } | ||
900 | |||
901 | static int recipient_add_group(char * recipient, size_t * len, | ||
902 | struct mailimf_group * group) | ||
903 | { | ||
904 | return recipient_add_mb_list(recipient, len, group->grp_mb_list); | ||
905 | } | ||
906 | |||
907 | static int recipient_add_addr(char * recipient, size_t * len, | ||
908 | struct mailimf_address * addr) | ||
909 | { | ||
910 | int r; | ||
911 | |||
912 | switch (addr->ad_type) { | ||
913 | case MAILIMF_ADDRESS_MAILBOX: | ||
914 | r = recipient_add_mb(recipient, len, addr->ad_data.ad_mailbox); | ||
915 | break; | ||
916 | case MAILIMF_ADDRESS_GROUP: | ||
917 | r = recipient_add_group(recipient, len, addr->ad_data.ad_group); | ||
918 | break; | ||
919 | default: | ||
920 | r = MAIL_ERROR_INVAL; | ||
921 | } | ||
922 | |||
923 | return r; | ||
924 | } | ||
925 | |||
926 | static int recipient_add_addr_list(char * recipient, size_t * len, | ||
927 | struct mailimf_address_list * addr_list) | ||
928 | { | ||
929 | clistiter * cur; | ||
930 | int r; | ||
931 | |||
932 | for(cur = clist_begin(addr_list->ad_list) ; cur != NULL ; | ||
933 | cur = clist_next(cur)) { | ||
934 | struct mailimf_address * addr; | ||
935 | |||
936 | addr = clist_content(cur); | ||
937 | |||
938 | r = recipient_add_addr(recipient, len, addr); | ||
939 | if (r != MAIL_NO_ERROR) | ||
940 | return r; | ||
941 | } | ||
942 | |||
943 | return MAIL_NO_ERROR; | ||
944 | } | ||
945 | |||
946 | static int collect_smime_cert(char * recipient, size_t size, | ||
947 | struct mailimf_fields * fields) | ||
948 | { | ||
949 | struct mailimf_single_fields single_fields; | ||
950 | int r; | ||
951 | size_t remaining; | ||
952 | int res; | ||
953 | |||
954 | * recipient = '\0'; | ||
955 | remaining = size; | ||
956 | |||
957 | if (fields != NULL) | ||
958 | mailimf_single_fields_init(&single_fields, fields); | ||
959 | |||
960 | if (single_fields.fld_to != NULL) { | ||
961 | r = recipient_add_addr_list(recipient, &remaining, | ||
962 | single_fields.fld_to->to_addr_list); | ||
963 | if (r != MAIL_NO_ERROR) { | ||
964 | res = r; | ||
965 | goto err; | ||
966 | } | ||
967 | } | ||
968 | |||
969 | if (single_fields.fld_cc != NULL) { | ||
970 | r = recipient_add_addr_list(recipient, &remaining, | ||
971 | single_fields.fld_cc->cc_addr_list); | ||
972 | if (r != MAIL_NO_ERROR) { | ||
973 | res = r; | ||
974 | goto err; | ||
975 | } | ||
976 | } | ||
977 | |||
978 | if (single_fields.fld_bcc != NULL) { | ||
979 | if (single_fields.fld_bcc->bcc_addr_list != NULL) { | ||
980 | r = recipient_add_addr_list(recipient, &remaining, | ||
981 | single_fields.fld_bcc->bcc_addr_list); | ||
982 | if (r < 0) { | ||
983 | res = r; | ||
984 | goto err; | ||
985 | } | ||
986 | } | ||
987 | } | ||
988 | |||
989 | return MAIL_NO_ERROR; | ||
990 | |||
991 | err: | ||
992 | return res; | ||
993 | } | ||
994 | |||
995 | |||
996 | |||
997 | static int smime_encrypt(struct mailprivacy * privacy, | ||
998 | struct mailmime * mime, struct mailmime ** result) | ||
999 | { | ||
1000 | char encrypted_filename[PATH_MAX]; | ||
1001 | FILE * encrypted_f; | ||
1002 | int res; | ||
1003 | int r; | ||
1004 | int col; | ||
1005 | char decrypted_filename[PATH_MAX]; | ||
1006 | FILE * decrypted_f; | ||
1007 | char command[PATH_MAX]; | ||
1008 | char quoted_decrypted_filename[PATH_MAX]; | ||
1009 | char quoted_encrypted_filename[PATH_MAX]; | ||
1010 | struct mailmime * encrypted_mime; | ||
1011 | struct mailmime * root; | ||
1012 | struct mailimf_fields * fields; | ||
1013 | char recipient[PATH_MAX]; | ||
1014 | |||
1015 | root = mime; | ||
1016 | while (root->mm_parent != NULL) | ||
1017 | root = root->mm_parent; | ||
1018 | |||
1019 | fields = NULL; | ||
1020 | if (root->mm_type == MAILMIME_MESSAGE) | ||
1021 | fields = root->mm_data.mm_message.mm_fields; | ||
1022 | |||
1023 | /* recipient */ | ||
1024 | r = collect_smime_cert(recipient, sizeof(recipient), fields); | ||
1025 | if (r != MAIL_NO_ERROR) { | ||
1026 | res = r; | ||
1027 | goto err; | ||
1028 | } | ||
1029 | |||
1030 | /* part to encrypt */ | ||
1031 | |||
1032 | /* encode quoted printable all text parts */ | ||
1033 | |||
1034 | mailprivacy_prepare_mime(mime); | ||
1035 | |||
1036 | decrypted_f = mailprivacy_get_tmp_file(privacy, | ||
1037 | decrypted_filename, | ||
1038 | sizeof(decrypted_filename)); | ||
1039 | if (decrypted_f == NULL) { | ||
1040 | res = MAIL_ERROR_FILE; | ||
1041 | goto err; | ||
1042 | } | ||
1043 | |||
1044 | col = 0; | ||
1045 | r = mailmime_write(decrypted_f, &col, mime); | ||
1046 | if (r != MAILIMF_NO_ERROR) { | ||
1047 | fclose(decrypted_f); | ||
1048 | res = MAIL_ERROR_FILE; | ||
1049 | goto unlink_decrypted; | ||
1050 | } | ||
1051 | |||
1052 | fclose(decrypted_f); | ||
1053 | |||
1054 | /* prepare destination file for encryption */ | ||
1055 | |||
1056 | encrypted_f = mailprivacy_get_tmp_file(privacy, | ||
1057 | encrypted_filename, | ||
1058 | sizeof(encrypted_filename)); | ||
1059 | if (encrypted_f == NULL) { | ||
1060 | res = MAIL_ERROR_FILE; | ||
1061 | goto unlink_decrypted; | ||
1062 | } | ||
1063 | fclose(encrypted_f); | ||
1064 | |||
1065 | r = mail_quote_filename(quoted_decrypted_filename, | ||
1066 | sizeof(quoted_decrypted_filename), decrypted_filename); | ||
1067 | if (r < 0) { | ||
1068 | res = MAIL_ERROR_MEMORY; | ||
1069 | goto unlink_encrypted; | ||
1070 | } | ||
1071 | |||
1072 | r = mail_quote_filename(quoted_encrypted_filename, | ||
1073 | sizeof(quoted_encrypted_filename), encrypted_filename); | ||
1074 | if (r < 0) { | ||
1075 | res = MAIL_ERROR_MEMORY; | ||
1076 | goto unlink_encrypted; | ||
1077 | } | ||
1078 | |||
1079 | snprintf(command, sizeof(command), | ||
1080 | "openssl smime -encrypt -in %s -out %s %s 2>/dev/null", | ||
1081 | quoted_decrypted_filename, quoted_encrypted_filename, recipient); | ||
1082 | |||
1083 | r = system(command); | ||
1084 | if (WEXITSTATUS(r) != 0) { | ||
1085 | res = MAIL_ERROR_COMMAND; | ||
1086 | goto unlink_encrypted; | ||
1087 | } | ||
1088 | |||
1089 | /* encrypted part */ | ||
1090 | |||
1091 | r = mailprivacy_get_part_from_file(privacy, 0, | ||
1092 | encrypted_filename, &encrypted_mime); | ||
1093 | if (r != MAIL_NO_ERROR) { | ||
1094 | res = r; | ||
1095 | goto unlink_encrypted; | ||
1096 | } | ||
1097 | strip_mime_headers(encrypted_mime); | ||
1098 | |||
1099 | unlink(encrypted_filename); | ||
1100 | unlink(decrypted_filename); | ||
1101 | |||
1102 | * result = encrypted_mime; | ||
1103 | |||
1104 | return MAIL_NO_ERROR; | ||
1105 | |||
1106 | unlink_encrypted: | ||
1107 | unlink(encrypted_filename); | ||
1108 | unlink_decrypted: | ||
1109 | unlink(decrypted_filename); | ||
1110 | err: | ||
1111 | return res; | ||
1112 | } | ||
1113 | |||
1114 | |||
1115 | static int smime_sign_encrypt(struct mailprivacy * privacy, | ||
1116 | struct mailmime * mime, struct mailmime ** result) | ||
1117 | { | ||
1118 | char encrypted_filename[PATH_MAX]; | ||
1119 | FILE * encrypted_f; | ||
1120 | int res; | ||
1121 | int r; | ||
1122 | int col; | ||
1123 | char signature_filename[PATH_MAX]; | ||
1124 | FILE * signature_f; | ||
1125 | char decrypted_filename[PATH_MAX]; | ||
1126 | FILE * decrypted_f; | ||
1127 | char command[PATH_MAX]; | ||
1128 | char quoted_decrypted_filename[PATH_MAX]; | ||
1129 | char quoted_encrypted_filename[PATH_MAX]; | ||
1130 | char quoted_signature_filename[PATH_MAX]; | ||
1131 | struct mailmime * encrypted_mime; | ||
1132 | struct mailmime * root; | ||
1133 | struct mailimf_fields * fields; | ||
1134 | char recipient[PATH_MAX]; | ||
1135 | char * smime_cert; | ||
1136 | char * smime_key; | ||
1137 | char quoted_smime_cert[PATH_MAX]; | ||
1138 | char quoted_smime_key[PATH_MAX]; | ||
1139 | char * email; | ||
1140 | |||
1141 | root = mime; | ||
1142 | while (root->mm_parent != NULL) | ||
1143 | root = root->mm_parent; | ||
1144 | |||
1145 | fields = NULL; | ||
1146 | if (root->mm_type == MAILMIME_MESSAGE) | ||
1147 | fields = root->mm_data.mm_message.mm_fields; | ||
1148 | |||
1149 | /* recipient */ | ||
1150 | r = collect_smime_cert(recipient, sizeof(recipient), fields); | ||
1151 | if (r != MAIL_NO_ERROR) { | ||
1152 | res = r; | ||
1153 | goto err; | ||
1154 | } | ||
1155 | |||
1156 | /* get signing key */ | ||
1157 | |||
1158 | email = get_first_from_addr(mime); | ||
1159 | if (email == NULL) { | ||
1160 | res = MAIL_ERROR_INVAL; | ||
1161 | goto err; | ||
1162 | } | ||
1163 | |||
1164 | smime_key = get_private_key_file(email); | ||
1165 | smime_cert = get_cert_file(email); | ||
1166 | if ((smime_cert == NULL) || (smime_key == NULL)) { | ||
1167 | res = MAIL_ERROR_INVAL; | ||
1168 | goto err; | ||
1169 | } | ||
1170 | |||
1171 | /* part to encrypt */ | ||
1172 | |||
1173 | /* encode quoted printable all text parts */ | ||
1174 | |||
1175 | mailprivacy_prepare_mime(mime); | ||
1176 | |||
1177 | decrypted_f = mailprivacy_get_tmp_file(privacy, | ||
1178 | decrypted_filename, sizeof(decrypted_filename)); | ||
1179 | if (decrypted_f == NULL) { | ||
1180 | res = MAIL_ERROR_FILE; | ||
1181 | goto err; | ||
1182 | } | ||
1183 | |||
1184 | col = 0; | ||
1185 | r = mailmime_write(decrypted_f, &col, mime); | ||
1186 | if (r != MAILIMF_NO_ERROR) { | ||
1187 | fclose(decrypted_f); | ||
1188 | res = MAIL_ERROR_FILE; | ||
1189 | goto unlink_decrypted; | ||
1190 | } | ||
1191 | |||
1192 | fclose(decrypted_f); | ||
1193 | |||
1194 | /* prepare destination file for signature */ | ||
1195 | |||
1196 | signature_f = mailprivacy_get_tmp_file(privacy, | ||
1197 | signature_filename, | ||
1198 | sizeof(signature_filename)); | ||
1199 | if (signature_f == NULL) { | ||
1200 | res = MAIL_ERROR_FILE; | ||
1201 | goto unlink_decrypted; | ||
1202 | } | ||
1203 | fclose(signature_f); | ||
1204 | |||
1205 | r = mail_quote_filename(quoted_decrypted_filename, | ||
1206 | sizeof(quoted_decrypted_filename), decrypted_filename); | ||
1207 | if (r < 0) { | ||
1208 | res = MAIL_ERROR_MEMORY; | ||
1209 | goto unlink_signature; | ||
1210 | } | ||
1211 | |||
1212 | r = mail_quote_filename(quoted_signature_filename, | ||
1213 | sizeof(quoted_signature_filename), signature_filename); | ||
1214 | if (r < 0) { | ||
1215 | res = MAIL_ERROR_MEMORY; | ||
1216 | goto unlink_signature; | ||
1217 | } | ||
1218 | |||
1219 | r = mail_quote_filename(quoted_smime_key, | ||
1220 | sizeof(quoted_smime_key), smime_key); | ||
1221 | if (r < 0) { | ||
1222 | res = MAIL_ERROR_MEMORY; | ||
1223 | goto unlink_signature; | ||
1224 | } | ||
1225 | |||
1226 | r = mail_quote_filename(quoted_smime_cert, | ||
1227 | sizeof(quoted_smime_cert), smime_cert); | ||
1228 | if (r < 0) { | ||
1229 | res = MAIL_ERROR_MEMORY; | ||
1230 | goto unlink_signature; | ||
1231 | } | ||
1232 | |||
1233 | snprintf(command, sizeof(command), | ||
1234 | "openssl smime -sign -in %s -out %s -signer %s -inkey %s 2>/dev/null", | ||
1235 | quoted_decrypted_filename, quoted_signature_filename, | ||
1236 | quoted_smime_cert, quoted_smime_key); | ||
1237 | |||
1238 | r = system(command); | ||
1239 | if (WEXITSTATUS(r) != 0) { | ||
1240 | res = MAIL_ERROR_COMMAND; | ||
1241 | goto unlink_signature; | ||
1242 | } | ||
1243 | |||
1244 | |||
1245 | /* prepare destination file for encryption */ | ||
1246 | |||
1247 | encrypted_f = mailprivacy_get_tmp_file(privacy, | ||
1248 | encrypted_filename, | ||
1249 | sizeof(encrypted_filename)); | ||
1250 | if (encrypted_f == NULL) { | ||
1251 | res = MAIL_ERROR_FILE; | ||
1252 | goto unlink_signature; | ||
1253 | } | ||
1254 | fclose(encrypted_f); | ||
1255 | |||
1256 | r = mail_quote_filename(quoted_encrypted_filename, | ||
1257 | sizeof(quoted_encrypted_filename), encrypted_filename); | ||
1258 | if (r < 0) { | ||
1259 | res = MAIL_ERROR_MEMORY; | ||
1260 | goto unlink_encrypted; | ||
1261 | } | ||
1262 | |||
1263 | snprintf(command, sizeof(command), | ||
1264 | "openssl smime -encrypt -in %s -out %s %s 2>/dev/null", | ||
1265 | quoted_signature_filename, quoted_encrypted_filename, recipient); | ||
1266 | |||
1267 | r = system(command); | ||
1268 | if (WEXITSTATUS(r) != 0) { | ||
1269 | res = MAIL_ERROR_COMMAND; | ||
1270 | goto unlink_encrypted; | ||
1271 | } | ||
1272 | |||
1273 | /* encrypted part */ | ||
1274 | |||
1275 | r = mailprivacy_get_part_from_file(privacy, 0, | ||
1276 | encrypted_filename, &encrypted_mime); | ||
1277 | if (r != MAIL_NO_ERROR) { | ||
1278 | res = r; | ||
1279 | goto unlink_encrypted; | ||
1280 | } | ||
1281 | strip_mime_headers(encrypted_mime); | ||
1282 | |||
1283 | unlink(encrypted_filename); | ||
1284 | unlink(signature_filename); | ||
1285 | unlink(decrypted_filename); | ||
1286 | |||
1287 | * result = encrypted_mime; | ||
1288 | |||
1289 | return MAIL_NO_ERROR; | ||
1290 | |||
1291 | unlink_encrypted: | ||
1292 | unlink(encrypted_filename); | ||
1293 | unlink_signature: | ||
1294 | unlink(signature_filename); | ||
1295 | unlink_decrypted: | ||
1296 | unlink(decrypted_filename); | ||
1297 | err: | ||
1298 | return res; | ||
1299 | } | ||
1300 | |||
1301 | |||
1302 | |||
1303 | static struct mailprivacy_encryption smime_encryption_tab[] = { | ||
1304 | /* S/MIME signed part */ | ||
1305 | { | ||
1306 | .name = "signed", | ||
1307 | .description = "S/MIME signed part", | ||
1308 | .encrypt = smime_sign, | ||
1309 | }, | ||
1310 | |||
1311 | /* S/MIME encrypted part */ | ||
1312 | |||
1313 | { | ||
1314 | .name = "encrypted", | ||
1315 | .description = "S/MIME encrypted part", | ||
1316 | .encrypt = smime_encrypt, | ||
1317 | }, | ||
1318 | |||
1319 | /* S/MIME signed & encrypted part */ | ||
1320 | |||
1321 | { | ||
1322 | .name = "signed-encrypted", | ||
1323 | .description = "S/MIME signed & encrypted part", | ||
1324 | .encrypt = smime_sign_encrypt, | ||
1325 | }, | ||
1326 | }; | ||
1327 | |||
1328 | static struct mailprivacy_protocol smime_protocol = { | ||
1329 | .name = "smime", | ||
1330 | .description = "S/MIME", | ||
1331 | |||
1332 | .is_encrypted = smime_test_encrypted, | ||
1333 | .decrypt = smime_handler, | ||
1334 | |||
1335 | .encryption_count = | ||
1336 | (sizeof(smime_encryption_tab) / sizeof(smime_encryption_tab[0])), | ||
1337 | |||
1338 | .encryption_tab = smime_encryption_tab, | ||
1339 | }; | ||
1340 | |||
1341 | int mailprivacy_smime_init(struct mailprivacy * privacy) | ||
1342 | { | ||
1343 | certificates = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYALL); | ||
1344 | if (certificates == NULL) | ||
1345 | goto err; | ||
1346 | |||
1347 | private_keys = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYALL); | ||
1348 | if (private_keys == NULL) | ||
1349 | goto free_cert; | ||
1350 | |||
1351 | CAcert_dir[0] = '\0'; | ||
1352 | |||
1353 | return mailprivacy_register(privacy, &smime_protocol); | ||
1354 | |||
1355 | free_cert: | ||
1356 | chash_free(certificates); | ||
1357 | err: | ||
1358 | return MAIL_ERROR_MEMORY; | ||
1359 | } | ||
1360 | |||
1361 | void mailprivacy_smime_done(struct mailprivacy * privacy) | ||
1362 | { | ||
1363 | mailprivacy_unregister(privacy, &smime_protocol); | ||
1364 | chash_free(private_keys); | ||
1365 | private_keys = NULL; | ||
1366 | chash_free(certificates); | ||
1367 | certificates = NULL; | ||
1368 | if (CAfile != NULL) { | ||
1369 | unlink(CAfile); | ||
1370 | free(CAfile); | ||
1371 | } | ||
1372 | CAfile = NULL; | ||
1373 | CAcert_dir[0] = '\0'; | ||
1374 | } | ||
1375 | |||
1376 | |||
1377 | static void strip_string(char * str) | ||
1378 | { | ||
1379 | char * p; | ||
1380 | size_t len; | ||
1381 | |||
1382 | p = strchr(str, '\r'); | ||
1383 | if (p != NULL) | ||
1384 | * p = 0; | ||
1385 | |||
1386 | p = strchr(str, '\n'); | ||
1387 | if (p != NULL) | ||
1388 | * p = 0; | ||
1389 | |||
1390 | p = str; | ||
1391 | while ((* p == ' ') || (* p == '\t')) { | ||
1392 | p ++; | ||
1393 | } | ||
1394 | |||
1395 | len = strlen(p); | ||
1396 | memmove(str, p, len); | ||
1397 | str[len] = 0; | ||
1398 | |||
1399 | if (len == 0) | ||
1400 | return; | ||
1401 | |||
1402 | p = str; | ||
1403 | len = len - 1; | ||
1404 | while ((p[len] == ' ') || (p[len] == '\t')) { | ||
1405 | p[len] = '\0'; | ||
1406 | |||
1407 | if (len == 0) | ||
1408 | break; | ||
1409 | |||
1410 | len --; | ||
1411 | } | ||
1412 | } | ||
1413 | |||
1414 | |||
1415 | |||
1416 | #define MAX_EMAIL_SIZE 1024 | ||
1417 | |||
1418 | static void set_file(chash * hash, char * email, char * filename) | ||
1419 | { | ||
1420 | char * n; | ||
1421 | char buf[MAX_EMAIL_SIZE]; | ||
1422 | chashdatum key; | ||
1423 | chashdatum data; | ||
1424 | |||
1425 | strncpy(buf, email, sizeof(buf)); | ||
1426 | buf[sizeof(buf) - 1] = '\0'; | ||
1427 | for(n = buf ; * n != '\0' ; n ++) | ||
1428 | * n = toupper((unsigned char) * n); | ||
1429 | strip_string(buf); | ||
1430 | |||
1431 | key.data = buf; | ||
1432 | key.len = strlen(buf); | ||
1433 | data.data = filename; | ||
1434 | data.len = strlen(filename) + 1; | ||
1435 | |||
1436 | chash_set(hash, &key, &data, NULL); | ||
1437 | } | ||
1438 | |||
1439 | static char * get_file(chash * hash, char * email) | ||
1440 | { | ||
1441 | chashdatum key; | ||
1442 | chashdatum data; | ||
1443 | char buf[MAX_EMAIL_SIZE]; | ||
1444 | char * n; | ||
1445 | int r; | ||
1446 | |||
1447 | strncpy(buf, email, sizeof(buf)); | ||
1448 | buf[sizeof(buf) - 1] = '\0'; | ||
1449 | for(n = buf ; * n != '\0' ; n ++) | ||
1450 | * n = toupper((unsigned char) * n); | ||
1451 | |||
1452 | strip_string(buf); | ||
1453 | key.data = buf; | ||
1454 | key.len = strlen(buf); | ||
1455 | r = chash_get(hash, &key, &data); | ||
1456 | if (r < 0) | ||
1457 | return NULL; | ||
1458 | |||
1459 | return data.data; | ||
1460 | } | ||
1461 | |||
1462 | void mailprivacy_smime_set_cert_dir(struct mailprivacy * privacy, | ||
1463 | char * directory) | ||
1464 | { | ||
1465 | DIR * dir; | ||
1466 | struct dirent * ent; | ||
1467 | |||
1468 | chash_clear(certificates); | ||
1469 | |||
1470 | if (directory == NULL) | ||
1471 | return; | ||
1472 | |||
1473 | if (* directory == '\0') | ||
1474 | return; | ||
1475 | |||
1476 | strncpy(cert_dir, directory, sizeof(cert_dir)); | ||
1477 | cert_dir[sizeof(cert_dir) - 1] = '\0'; | ||
1478 | |||
1479 | dir = opendir(directory); | ||
1480 | if (dir == NULL) | ||
1481 | return; | ||
1482 | |||
1483 | while ((ent = readdir(dir)) != NULL) { | ||
1484 | char filename[PATH_MAX]; | ||
1485 | char command[PATH_MAX]; | ||
1486 | char buf[MAX_EMAIL_SIZE]; | ||
1487 | FILE * p; | ||
1488 | |||
1489 | snprintf(filename, sizeof(filename), | ||
1490 | "%s/%s", directory, ent->d_name); | ||
1491 | |||
1492 | snprintf(command, sizeof(command), | ||
1493 | "openssl x509 -email -noout -in %s 2>/dev/null", filename); | ||
1494 | |||
1495 | p = popen(command, "r"); | ||
1496 | if (p == NULL) | ||
1497 | continue; | ||
1498 | |||
1499 | while (fgets(buf, sizeof(buf), p) != NULL) | ||
1500 | set_file(certificates, buf, filename); | ||
1501 | |||
1502 | pclose(p); | ||
1503 | } | ||
1504 | closedir(dir); | ||
1505 | } | ||
1506 | |||
1507 | static char * get_cert_file(char * email) | ||
1508 | { | ||
1509 | return get_file(certificates, email); | ||
1510 | } | ||
1511 | |||
1512 | static char * get_private_key_file(char * email) | ||
1513 | { | ||
1514 | return get_file(private_keys, email); | ||
1515 | } | ||
1516 | |||
1517 | void mail_private_smime_clear_private_keys(struct mailprivacy * privacy) | ||
1518 | { | ||
1519 | chash_clear(private_keys); | ||
1520 | } | ||
1521 | |||
1522 | #define MAX_BUF 1024 | ||
1523 | |||
1524 | void mailprivacy_smime_set_CA_dir(struct mailprivacy * privacy, | ||
1525 | char * directory) | ||
1526 | { | ||
1527 | DIR * dir; | ||
1528 | struct dirent * ent; | ||
1529 | FILE * f_CA; | ||
1530 | char CA_filename[PATH_MAX]; | ||
1531 | |||
1532 | if (directory == NULL) | ||
1533 | return; | ||
1534 | |||
1535 | if (* directory == '\0') | ||
1536 | return; | ||
1537 | |||
1538 | /* make a temporary file that contains all the CAs */ | ||
1539 | |||
1540 | if (CAfile != NULL) { | ||
1541 | unlink(CAfile); | ||
1542 | free(CAfile); | ||
1543 | CAfile = NULL; | ||
1544 | } | ||
1545 | |||
1546 | f_CA = mailprivacy_get_tmp_file(privacy, CA_filename, sizeof(CA_filename)); | ||
1547 | if (f_CA == NULL) | ||
1548 | return; | ||
1549 | |||
1550 | strncpy(CAcert_dir, directory, sizeof(CAcert_dir)); | ||
1551 | CAcert_dir[sizeof(CAcert_dir) - 1] = '\0'; | ||
1552 | |||
1553 | dir = opendir(directory); | ||
1554 | if (dir == NULL) { | ||
1555 | fclose(f_CA); | ||
1556 | goto unlink_CA; | ||
1557 | } | ||
1558 | |||
1559 | while ((ent = readdir(dir)) != NULL) { | ||
1560 | char filename[PATH_MAX]; | ||
1561 | char command[PATH_MAX]; | ||
1562 | char buf[MAX_BUF]; | ||
1563 | FILE * f; | ||
1564 | |||
1565 | snprintf(filename, sizeof(filename), | ||
1566 | "%s/%s", directory, ent->d_name); | ||
1567 | |||
1568 | f = fopen(filename, "r"); | ||
1569 | if (f == NULL) | ||
1570 | continue; | ||
1571 | |||
1572 | while (fgets(buf, sizeof(buf), f) != NULL) | ||
1573 | fputs(buf, f_CA); | ||
1574 | |||
1575 | fclose(f); | ||
1576 | } | ||
1577 | |||
1578 | closedir(dir); | ||
1579 | |||
1580 | fclose(f_CA); | ||
1581 | |||
1582 | CAfile = strdup(CA_filename); | ||
1583 | if (CAfile == NULL) | ||
1584 | goto unlink_CA; | ||
1585 | |||
1586 | return; | ||
1587 | |||
1588 | unlink_CA: | ||
1589 | unlink(CA_filename); | ||
1590 | } | ||
1591 | |||
1592 | void mailprivacy_smime_set_CA_check(struct mailprivacy * privacy, | ||
1593 | int enabled) | ||
1594 | { | ||
1595 | CA_check = enabled; | ||
1596 | } | ||
1597 | |||
1598 | void mailprivacy_smime_set_store_cert(struct mailprivacy * privacy, | ||
1599 | int enabled) | ||
1600 | { | ||
1601 | store_cert = enabled; | ||
1602 | } | ||
1603 | |||
1604 | static int get_cert_from_sig(struct mailprivacy * privacy, | ||
1605 | mailmessage * msg, | ||
1606 | struct mailmime * mime) | ||
1607 | { | ||
1608 | clistiter * cur; | ||
1609 | struct mailmime * signed_mime; | ||
1610 | struct mailmime * signature_mime; | ||
1611 | int res; | ||
1612 | char signature_filename[PATH_MAX]; | ||
1613 | char quoted_signature_filename[PATH_MAX]; | ||
1614 | char * email; | ||
1615 | char * cert_file; | ||
1616 | char store_cert_filename[PATH_MAX]; | ||
1617 | char quoted_store_cert_filename[PATH_MAX]; | ||
1618 | int r; | ||
1619 | char command[PATH_MAX]; | ||
1620 | |||
1621 | if (* cert_dir == '\0') | ||
1622 | return MAIL_ERROR_INVAL; | ||
1623 | |||
1624 | if (mime->mm_type != MAILMIME_MULTIPLE) | ||
1625 | return MAIL_ERROR_INVAL; | ||
1626 | |||
1627 | email = get_first_from_addr(mime); | ||
1628 | if (email == NULL) | ||
1629 | return MAIL_ERROR_INVAL; | ||
1630 | |||
1631 | cert_file = get_cert_file(email); | ||
1632 | if (cert_file != NULL) | ||
1633 | return MAIL_NO_ERROR; | ||
1634 | |||
1635 | /* get the two parts of the S/MIME message */ | ||
1636 | |||
1637 | cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list); | ||
1638 | if (cur == NULL) { | ||
1639 | res = MAIL_ERROR_INVAL; | ||
1640 | goto err; | ||
1641 | } | ||
1642 | |||
1643 | signed_mime = cur->data; | ||
1644 | cur = clist_next(cur); | ||
1645 | if (cur == NULL) { | ||
1646 | res = MAIL_ERROR_INVAL; | ||
1647 | goto err; | ||
1648 | } | ||
1649 | |||
1650 | signature_mime = cur->data; | ||
1651 | |||
1652 | r = mailprivacy_fetch_decoded_to_file(privacy, | ||
1653 | signature_filename, sizeof(signature_filename), | ||
1654 | msg, signature_mime); | ||
1655 | if (r != MAILIMF_NO_ERROR) { | ||
1656 | res = r; | ||
1657 | goto err; | ||
1658 | } | ||
1659 | |||
1660 | r = mail_quote_filename(quoted_signature_filename, | ||
1661 | sizeof(quoted_signature_filename), signature_filename); | ||
1662 | if (r < 0) { | ||
1663 | res = MAIL_ERROR_MEMORY; | ||
1664 | goto unlink_signature; | ||
1665 | } | ||
1666 | |||
1667 | snprintf(store_cert_filename, sizeof(store_cert_filename), | ||
1668 | "%s/%s-cert.pem", cert_dir, email); | ||
1669 | |||
1670 | r = mail_quote_filename(quoted_store_cert_filename, | ||
1671 | sizeof(quoted_store_cert_filename), store_cert_filename); | ||
1672 | if (r < 0) { | ||
1673 | res = MAIL_ERROR_MEMORY; | ||
1674 | goto unlink_signature; | ||
1675 | } | ||
1676 | |||
1677 | snprintf(command, sizeof(command), | ||
1678 | "openssl pkcs7 -inform DER -in %s -out %s -print_certs 2>/dev/null", | ||
1679 | quoted_signature_filename, quoted_store_cert_filename); | ||
1680 | |||
1681 | r = system(command); | ||
1682 | if (WEXITSTATUS(r) != 0) { | ||
1683 | res = MAIL_ERROR_COMMAND; | ||
1684 | goto unlink_signature; | ||
1685 | } | ||
1686 | |||
1687 | unlink(signature_filename); | ||
1688 | |||
1689 | set_file(certificates, email, store_cert_filename); | ||
1690 | |||
1691 | return MAIL_NO_ERROR; | ||
1692 | |||
1693 | unlink_signature: | ||
1694 | unlink(signature_filename); | ||
1695 | err: | ||
1696 | return res; | ||
1697 | } | ||
1698 | |||
1699 | |||
1700 | static void set_private_key(struct mailprivacy * privacy, | ||
1701 | char * email, char * file) | ||
1702 | { | ||
1703 | set_file(private_keys, email, file); | ||
1704 | } | ||
1705 | |||
1706 | #define PRIVATE_KEY_SUFFIX "-private-key.pem" | ||
1707 | |||
1708 | void mailprivacy_smime_set_private_keys_dir(struct mailprivacy * privacy, | ||
1709 | char * directory) | ||
1710 | { | ||
1711 | DIR * dir; | ||
1712 | struct dirent * ent; | ||
1713 | |||
1714 | chash_clear(private_keys); | ||
1715 | |||
1716 | if (directory == NULL) | ||
1717 | return; | ||
1718 | |||
1719 | if (* directory == '\0') | ||
1720 | return; | ||
1721 | |||
1722 | strncpy(private_keys_dir, directory, sizeof(private_keys_dir)); | ||
1723 | private_keys_dir[sizeof(private_keys_dir) - 1] = '\0'; | ||
1724 | |||
1725 | dir = opendir(directory); | ||
1726 | if (dir == NULL) | ||
1727 | return; | ||
1728 | |||
1729 | while ((ent = readdir(dir)) != NULL) { | ||
1730 | char filename[PATH_MAX]; | ||
1731 | char email[PATH_MAX]; | ||
1732 | char * p; | ||
1733 | |||
1734 | snprintf(filename, sizeof(filename), | ||
1735 | "%s/%s", directory, ent->d_name); | ||
1736 | |||
1737 | strncpy(email, ent->d_name, sizeof(email)); | ||
1738 | email[sizeof(email) - 1] = '\0'; | ||
1739 | |||
1740 | p = strstr(email, PRIVATE_KEY_SUFFIX); | ||
1741 | if (p == NULL) | ||
1742 | continue; | ||
1743 | |||
1744 | if (strlen(p) != sizeof(PRIVATE_KEY_SUFFIX) - 1) | ||
1745 | continue; | ||
1746 | |||
1747 | * p = 0; | ||
1748 | |||
1749 | if (* email == '\0') | ||
1750 | continue; | ||
1751 | |||
1752 | set_private_key(privacy, email, filename); | ||
1753 | } | ||
1754 | closedir(dir); | ||
1755 | } | ||