Bump s-nail v14.6.1
[s-mailx.git] / openssl.c
blobb107888679120ebf3288f8397756850f2fef54d5
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ OpenSSL functions. TODO this needs an overhaul -- there _are_ stack leaks!?
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 2002
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 EMPTY_FILE(openssl)
45 #ifdef HAVE_OPENSSL
46 #include <sys/socket.h>
48 #include <dirent.h>
49 #include <netdb.h>
51 #include <netinet/in.h>
53 #include <openssl/crypto.h>
54 #include <openssl/ssl.h>
55 #include <openssl/err.h>
56 #include <openssl/x509v3.h>
57 #include <openssl/x509.h>
58 #include <openssl/pem.h>
59 #include <openssl/rand.h>
61 #ifdef HAVE_ARPA_INET_H
62 # include <arpa/inet.h>
63 #endif
66 * OpenSSL client implementation according to: John Viega, Matt Messier,
67 * Pravir Chandra: Network Security with OpenSSL. Sebastopol, CA 2002.
70 #ifdef HAVE_STACK_OF
71 # define _STACKOF(X) STACK_OF(X)
72 #else
73 # define _STACKOF(X) /*X*/STACK
74 #endif
76 struct ssl_method {
77 char const sm_name[8];
78 SSL_METHOD const * (*sm_fun)(void);
81 struct smime_cipher {
82 char const sc_name[8];
83 EVP_CIPHER const * (*sc_fun)(void);
86 /* Supported SSL/TLS methods: update manual on change! */
87 static struct ssl_method const _ssl_methods[] = {
88 {"auto", &SSLv23_client_method},
89 #define _SSL_DEFAULT_METHOD SSLv23_client_method
90 #ifndef OPENSSL_NO_TLS1
91 # ifdef TLS1_2_VERSION
92 {"tls1.2", &TLSv1_2_client_method},
93 # endif
94 # ifdef TLS1_1_VERSION
95 {"tls1.1", &TLSv1_1_client_method},
96 # endif
97 {"tls1", &TLSv1_client_method},
98 #endif
99 #ifndef OPENSSL_NO_SSL3
100 {"ssl3", &SSLv3_client_method},
101 #endif
102 #ifndef OPENSSL_NO_SSL2
103 {"ssl2", &SSLv2_client_method}
104 #endif
107 /* Supported S/MIME cipher algorithms: update manual on change! */
108 static struct smime_cipher const _smime_ciphers[] = {
109 #ifndef OPENSSL_NO_AES
110 # define _SMIME_DEFAULT_CIPHER EVP_aes_128_cbc /* According to RFC 5751 */
111 {"aes-128", &EVP_aes_128_cbc},
112 {"aes-256", &EVP_aes_256_cbc},
113 {"aes-192", &EVP_aes_192_cbc},
114 #endif
115 #ifndef OPENSSL_NO_DES
116 # ifndef _SMIME_DEFAULT_CIPHER
117 # define _SMIME_DEFAULT_CIPHER EVP_des_ede3_cbc
118 # endif
119 {"des3", &EVP_des_ede3_cbc},
120 {"des", &EVP_des_cbc},
121 #endif
122 #ifndef OPENSSL_NO_RC2
123 {"rc2-40", &EVP_rc2_40_cbc},
124 {"rc2-64", &EVP_rc2_64_cbc},
125 #endif
127 #ifndef _SMIME_DEFAULT_CIPHER
128 # error Your OpenSSL library does not include the necessary
129 # error cipher algorithms that are required to support S/MIME
130 #endif
132 static int initialized;
133 static int rand_init;
134 static int message_number;
135 static int verify_error_found;
137 static int ssl_rand_init(void);
138 static void ssl_init(void);
139 static int ssl_verify_cb(int success, X509_STORE_CTX *store);
140 static const SSL_METHOD *ssl_select_method(char const *uhp);
141 static void ssl_load_verifications(struct sock *sp);
142 static void ssl_certificate(struct sock *sp, char const *uhp);
143 static enum okay ssl_check_host(char const *server, struct sock *sp);
144 static int smime_verify(struct message *m, int n, _STACKOF(X509) *chain,
145 X509_STORE *store);
146 static EVP_CIPHER const * _smime_cipher(char const *name);
147 static int ssl_password_cb(char *buf, int size, int rwflag,
148 void *userdata);
149 static FILE * smime_sign_cert(char const *xname, char const *xname2,
150 bool_t dowarn);
151 static char * smime_sign_include_certs(char const *name);
152 static int smime_sign_include_chain_creat(_STACKOF(X509) **chain,
153 char const *cfiles);
154 #if defined X509_V_FLAG_CRL_CHECK && defined X509_V_FLAG_CRL_CHECK_ALL
155 static enum okay load_crl1(X509_STORE *store, char const *name);
156 #endif
157 static enum okay load_crls(X509_STORE *store, enum okeys fok, enum okeys dok);
159 static int
160 ssl_rand_init(void)
162 char *cp, *x;
163 int state = 0;
164 NYD_ENTER;
166 if ((cp = ok_vlook(ssl_rand_egd)) != NULL) {
167 if ((x = file_expand(cp)) == NULL || RAND_egd(cp = x) == -1)
168 fprintf(stderr, tr(245, "entropy daemon at \"%s\" not available\n"),
169 cp);
170 else
171 state = 1;
172 } else if ((cp = ok_vlook(ssl_rand_file)) != NULL) {
173 if ((x = file_expand(cp)) == NULL || RAND_load_file(cp = x, 1024) == -1)
174 fprintf(stderr, tr(246, "entropy file at \"%s\" not available\n"), cp);
175 else {
176 struct stat st;
178 if (!stat(cp, &st) && S_ISREG(st.st_mode) && !access(cp, W_OK)) {
179 if (RAND_write_file(cp) == -1) {
180 fprintf(stderr, tr(247,
181 "writing entropy data to \"%s\" failed\n"), cp);
184 state = 1;
187 NYD_LEAVE;
188 return state;
191 static void
192 ssl_init(void)
194 NYD_ENTER;
195 if (initialized == 0) {
196 SSL_library_init();
197 initialized = 1;
199 if (rand_init == 0)
200 rand_init = ssl_rand_init();
201 NYD_LEAVE;
204 static int
205 ssl_verify_cb(int success, X509_STORE_CTX *store)
207 int rv = TRU1;
208 NYD_ENTER;
210 if (success == 0) {
211 char data[256];
212 X509 *cert = X509_STORE_CTX_get_current_cert(store);
213 int depth = X509_STORE_CTX_get_error_depth(store);
214 int err = X509_STORE_CTX_get_error(store);
216 verify_error_found = 1;
217 if (message_number)
218 fprintf(stderr, "Message %d: ", message_number);
219 fprintf(stderr, tr(229, "Error with certificate at depth: %i\n"), depth);
220 X509_NAME_oneline(X509_get_issuer_name(cert), data, sizeof data);
221 fprintf(stderr, tr(230, " issuer = %s\n"), data);
222 X509_NAME_oneline(X509_get_subject_name(cert), data, sizeof data);
223 fprintf(stderr, tr(231, " subject = %s\n"), data);
224 fprintf(stderr, tr(232, " err %i: %s\n"),
225 err, X509_verify_cert_error_string(err));
226 if (ssl_verify_decide() != OKAY)
227 rv = FAL0;
229 NYD_LEAVE;
230 return rv;
233 static SSL_METHOD const *
234 ssl_select_method(char const *uhp)
236 SSL_METHOD const *method;
237 char *cp;
238 size_t i;
239 NYD_ENTER;
241 if ((cp = ssl_method_string(uhp)) != NULL) {
242 method = NULL;
243 for (i = 0; i < NELEM(_ssl_methods); ++i)
244 if (!strcmp(_ssl_methods[i].sm_name, cp)) {
245 method = (*_ssl_methods[i].sm_fun)();
246 goto jleave;
248 fprintf(stderr, tr(244, "Invalid SSL method \"%s\"\n"), cp);
250 method = _SSL_DEFAULT_METHOD();
251 jleave:
252 NYD_LEAVE;
253 return method;
256 static void
257 ssl_load_verifications(struct sock *sp)
259 char *ca_dir, *ca_file;
260 X509_STORE *store;
261 NYD_ENTER;
263 if (ssl_verify_level == SSL_VERIFY_IGNORE)
264 goto jleave;
266 if ((ca_dir = ok_vlook(ssl_ca_dir)) != NULL)
267 ca_dir = file_expand(ca_dir);
268 if ((ca_file = ok_vlook(ssl_ca_file)) != NULL)
269 ca_file = file_expand(ca_file);
271 if (ca_dir != NULL || ca_file != NULL) {
272 if (SSL_CTX_load_verify_locations(sp->s_ctx, ca_file, ca_dir) != 1) {
273 fprintf(stderr, tr(233, "Error loading "));
274 if (ca_dir) {
275 fputs(ca_dir, stderr);
276 if (ca_file)
277 fputs(tr(234, " or "), stderr);
279 if (ca_file)
280 fputs(ca_file, stderr);
281 fputs("\n", stderr);
285 if (!ok_blook(ssl_no_default_ca)) {
286 if (SSL_CTX_set_default_verify_paths(sp->s_ctx) != 1)
287 fprintf(stderr, tr(243, "Error loading default CA locations\n"));
290 verify_error_found = 0;
291 message_number = 0;
292 SSL_CTX_set_verify(sp->s_ctx, SSL_VERIFY_PEER, ssl_verify_cb);
293 store = SSL_CTX_get_cert_store(sp->s_ctx);
294 load_crls(store, ok_v_ssl_crl_file, ok_v_ssl_crl_dir);
295 jleave:
296 NYD_LEAVE;
299 static void
300 ssl_certificate(struct sock *sp, char const *uhp)
302 size_t i;
303 char *certvar, *keyvar, *cert, *key, *x;
304 NYD_ENTER;
306 i = strlen(uhp);
307 certvar = ac_alloc(i + 9 +1);
308 memcpy(certvar, "ssl-cert-", 9);
309 memcpy(certvar + 9, uhp, i +1);
311 if ((cert = vok_vlook(certvar)) != NULL ||
312 (cert = ok_vlook(ssl_cert)) != NULL) {
313 x = cert;
314 if ((cert = file_expand(cert)) == NULL) {
315 cert = x;
316 goto jbcert;
317 } else if (SSL_CTX_use_certificate_chain_file(sp->s_ctx, cert) == 1) {
318 keyvar = ac_alloc(strlen(uhp) + 8 +1);
319 memcpy(keyvar, "ssl-key-", 8);
320 memcpy(keyvar + 8, uhp, i +1);
321 if ((key = vok_vlook(keyvar)) == NULL &&
322 (key = ok_vlook(ssl_key)) == NULL)
323 key = cert;
324 else if ((x = key, key = file_expand(key)) == NULL) {
325 key = x;
326 goto jbkey;
328 if (SSL_CTX_use_PrivateKey_file(sp->s_ctx, key, SSL_FILETYPE_PEM) != 1)
329 jbkey:
330 fprintf(stderr, tr(238, "cannot load private key from file %s\n"),
331 key);
332 ac_free(keyvar);
333 } else
334 jbcert:
335 fprintf(stderr, tr(239, "cannot load certificate from file %s\n"),
336 cert);
338 ac_free(certvar);
339 NYD_LEAVE;
342 static enum okay
343 ssl_check_host(char const *server, struct sock *sp)
345 char data[256];
346 X509 *cert;
347 X509_NAME *subj;
348 _STACKOF(GENERAL_NAME) *gens;
349 GENERAL_NAME *gen;
350 int i;
351 enum okay rv = STOP;
352 NYD_ENTER;
354 if ((cert = SSL_get_peer_certificate(sp->s_ssl)) == NULL) {
355 fprintf(stderr, tr(248, "no certificate from \"%s\"\n"), server);
356 goto jleave;
359 gens = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
360 if (gens != NULL) {
361 for (i = 0; i < sk_GENERAL_NAME_num(gens); ++i) {
362 gen = sk_GENERAL_NAME_value(gens, i);
363 if (gen->type == GEN_DNS) {
364 if (options & OPT_VERBOSE)
365 fprintf(stderr, "Comparing DNS name: \"%s\"\n",
366 gen->d.ia5->data);
367 rv = rfc2595_hostname_match(server, (char*)gen->d.ia5->data);
368 if (rv == OKAY)
369 goto jdone;
374 if ((subj = X509_get_subject_name(cert)) != NULL &&
375 X509_NAME_get_text_by_NID(subj, NID_commonName, data, sizeof data)
376 > 0) {
377 data[sizeof data - 1] = '\0';
378 if (options & OPT_VERBOSE)
379 fprintf(stderr, "Comparing common name: \"%s\"\n", data);
380 rv = rfc2595_hostname_match(server, data);
382 jdone:
383 X509_free(cert);
384 jleave:
385 NYD_LEAVE;
386 return rv;
389 static int
390 smime_verify(struct message *m, int n, _STACKOF(X509) *chain, X509_STORE *store)
392 char data[LINESIZE], *sender, *to, *cc, *cnttype;
393 int rv, c, i, j;
394 struct message *x;
395 FILE *fp, *ip;
396 off_t size;
397 BIO *fb, *pb;
398 PKCS7 *pkcs7;
399 _STACKOF(X509) *certs;
400 _STACKOF(GENERAL_NAME) *gens;
401 X509 *cert;
402 X509_NAME *subj;
403 GENERAL_NAME *gen;
404 NYD_ENTER;
406 rv = 1;
407 fp = NULL;
408 fb = NULL;
409 verify_error_found = 0;
410 message_number = n;
412 for (;;) {
413 sender = getsender(m);
414 to = hfield1("to", m);
415 cc = hfield1("cc", m);
416 cnttype = hfield1("content-type", m);
417 if ((ip = setinput(&mb, m, NEED_BODY)) == NULL)
418 goto jleave;
419 if (cnttype && !strncmp(cnttype, "application/x-pkcs7-mime", 24)) {
420 if ((x = smime_decrypt(m, to, cc, 1)) == NULL)
421 goto jleave;
422 if (x != (struct message*)-1) {
423 m = x;
424 continue;
427 size = m->m_size;
428 break;
431 if ((fp = Ftmp(NULL, "smimever", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
432 NULL) {
433 perror("tempfile");
434 goto jleave;
436 while (size-- > 0) {
437 c = getc(ip);
438 putc(c, fp);
440 fflush_rewind(fp);
442 if ((fb = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) {
443 ssl_gen_err(tr(537,
444 "Error creating BIO verification object for message %d"), n);
445 goto jleave;
448 if ((pkcs7 = SMIME_read_PKCS7(fb, &pb)) == NULL) {
449 ssl_gen_err(tr(538, "Error reading PKCS#7 object for message %d"), n);
450 goto jleave;
452 if (PKCS7_verify(pkcs7, chain, store, pb, NULL, 0) != 1) {
453 ssl_gen_err(tr(539, "Error verifying message %d"), n);
454 goto jleave;
457 if (sender == NULL) {
458 fprintf(stderr, tr(540, "Warning: Message %d has no sender.\n"), n);
459 rv = 0;
460 goto jleave;
463 certs = PKCS7_get0_signers(pkcs7, chain, 0);
464 if (certs == NULL) {
465 fprintf(stderr, tr(541, "No certificates found in message %d.\n"), n);
466 goto jleave;
469 for (i = 0; i < sk_X509_num(certs); ++i) {
470 cert = sk_X509_value(certs, i);
471 gens = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
472 if (gens != NULL) {
473 for (j = 0; j < sk_GENERAL_NAME_num(gens); ++j) {
474 gen = sk_GENERAL_NAME_value(gens, j);
475 if (gen->type == GEN_EMAIL) {
476 if (options & OPT_VERBOSE)
477 fprintf(stderr, "Comparing alt. address: %s\"\n", data);
478 if (!asccasecmp((char*)gen->d.ia5->data, sender))
479 goto jfound;
484 if ((subj = X509_get_subject_name(cert)) != NULL &&
485 X509_NAME_get_text_by_NID(subj, NID_pkcs9_emailAddress,
486 data, sizeof data) > 0) {
487 data[sizeof data -1] = '\0';
488 if (options & OPT_VERBOSE)
489 fprintf(stderr, "Comparing address: \"%s\"\n", data);
490 if (!asccasecmp(data, sender))
491 goto jfound;
494 fprintf(stderr, tr(542, "Message %d: certificate does not match <%s>\n"),
495 n, sender);
496 goto jleave;
497 jfound:
498 if (verify_error_found == 0)
499 printf(tr(543, "Message %d was verified successfully.\n"), n);
500 rv = verify_error_found;
501 jleave:
502 if (fb != NULL)
503 BIO_free(fb);
504 if (fp != NULL)
505 Fclose(fp);
506 NYD_LEAVE;
507 return rv;
510 static EVP_CIPHER const *
511 _smime_cipher(char const *name)
513 EVP_CIPHER const *cipher;
514 char *vn, *cp;
515 size_t i;
516 NYD_ENTER;
518 vn = ac_alloc(i = strlen(name) + 13 +1);
519 snprintf(vn, (int)i, "smime-cipher-%s", name);
520 cp = vok_vlook(vn);
521 ac_free(vn);
523 if (cp != NULL) {
524 cipher = NULL;
525 for (i = 0; i < NELEM(_smime_ciphers); ++i)
526 if (!strcmp(_smime_ciphers[i].sc_name, cp)) {
527 cipher = (*_smime_ciphers[i].sc_fun)();
528 goto jleave;
530 fprintf(stderr, tr(240, "Invalid cipher(s): %s\n"), cp);
531 } else
532 cipher = _SMIME_DEFAULT_CIPHER();
533 jleave:
534 NYD_LEAVE;
535 return cipher;
538 static int
539 ssl_password_cb(char *buf, int size, int rwflag, void *userdata)
541 char *pass;
542 size_t len;
543 NYD_ENTER;
544 UNUSED(rwflag);
545 UNUSED(userdata);
547 if ((pass = getpassword("PEM pass phrase:")) != NULL) {
548 len = strlen(pass);
549 if (UICMP(z, len, >=, size))
550 len = size -1;
551 memcpy(buf, pass, len);
552 buf[len] = '\0';
553 } else
554 len = 0;
555 NYD_LEAVE;
556 return (int)len;
559 static FILE *
560 smime_sign_cert(char const *xname, char const *xname2, bool_t dowarn)
562 char *vn, *cp;
563 int vs;
564 struct name *np;
565 char const *name = xname, *name2 = xname2;
566 FILE *fp = NULL;
567 NYD_ENTER;
569 jloop:
570 if (name) {
571 np = lextract(name, GTO | GSKIN);
572 while (np != NULL) {
573 /* This needs to be more intelligent since it will currently take the
574 * first name for which a private key is available regardless of
575 * whether it is the right one for the message */
576 vn = ac_alloc(vs = strlen(np->n_name) + 30);
577 snprintf(vn, vs, "smime-sign-cert-%s", np->n_name);
578 cp = vok_vlook(vn);
579 ac_free(vn);
580 if (cp != NULL)
581 goto jopen;
582 np = np->n_flink;
584 if (name2 != NULL) {
585 name = name2;
586 name2 = NULL;
587 goto jloop;
591 if ((cp = ok_vlook(smime_sign_cert)) == NULL)
592 goto jerr;
593 jopen:
594 if ((cp = file_expand(cp)) == NULL)
595 goto jleave;
596 if ((fp = Fopen(cp, "r")) == NULL)
597 perror(cp);
598 jleave:
599 NYD_LEAVE;
600 return fp;
601 jerr:
602 if (dowarn) {
603 fprintf(stderr, tr(558, "Could not find a certificate for %s"), xname);
604 if (xname2)
605 fprintf(stderr, tr(559, "or %s"), xname2);
606 fputc('\n', stderr);
608 goto jleave;
611 static char *
612 smime_sign_include_certs(char const *name)
614 char *rv;
615 NYD_ENTER;
617 /* See comments in smime_sign_cert() for algorithm pitfalls */
618 if (name != NULL) {
619 struct name *np;
621 for (np = lextract(name, GTO | GSKIN); np != NULL; np = np->n_flink) {
622 int vs;
623 char *vn = ac_alloc(vs = strlen(np->n_name) + 30);
624 snprintf(vn, vs, "smime-sign-include-certs-%s", np->n_name);
625 rv = vok_vlook(vn);
626 ac_free(vn);
627 if (rv != NULL)
628 goto jleave;
631 rv = ok_vlook(smime_sign_include_certs);
632 jleave:
633 NYD_LEAVE;
634 return rv;
637 static int
638 smime_sign_include_chain_creat(_STACKOF(X509) **chain, char const *cfiles)
640 X509 *tmp;
641 FILE *fp;
642 char *rest, *x, *ncf;
643 NYD_ENTER;
645 *chain = sk_X509_new_null();
647 for (rest = savestr(cfiles);;) {
648 ncf = strchr(rest, ',');/* FIXME use n_strsep(): this FIXES behaviour! */
649 if (ncf != NULL)
650 *ncf++ = '\0';
651 /* This fails for '=,file' constructs, but those are sick */
652 if (*rest == '\0')
653 break;
655 if ((x = file_expand(rest)) == NULL ||
656 (fp = Fopen(rest = x, "r")) == NULL) {
657 perror(cfiles);
658 goto jerr;
660 if ((tmp = PEM_read_X509(fp, NULL, &ssl_password_cb, NULL)) == NULL) {
661 ssl_gen_err(tr(560, "Error reading certificate from \"%s\""), rest);
662 Fclose(fp);
663 goto jerr;
665 sk_X509_push(*chain, tmp);
666 Fclose(fp);
668 if (ncf == NULL)
669 break;
670 rest = ncf;
673 if (sk_X509_num(*chain) == 0) {
674 fprintf(stderr, tr(561, "smime-sign-include-certs defined but empty\n"));
675 goto jerr;
677 jleave:
678 NYD_LEAVE;
679 return (*chain != NULL);
680 jerr:
681 sk_X509_pop_free(*chain, X509_free);
682 *chain = NULL;
683 goto jleave;
686 #if defined X509_V_FLAG_CRL_CHECK && defined X509_V_FLAG_CRL_CHECK_ALL
687 static enum okay
688 load_crl1(X509_STORE *store, char const *name)
690 X509_LOOKUP *lookup;
691 enum okay rv = STOP;
692 NYD_ENTER;
694 if (options & OPT_VERBOSE)
695 printf("Loading CRL from \"%s\".\n", name);
696 if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) == NULL) {
697 ssl_gen_err(tr(565, "Error creating X509 lookup object"));
698 goto jleave;
700 if (X509_load_crl_file(lookup, name, X509_FILETYPE_PEM) != 1) {
701 ssl_gen_err(tr(566, "Error loading CRL from \"%s\""), name);
702 goto jleave;
704 rv = OKAY;
705 jleave:
706 NYD_LEAVE;
707 return rv;
709 #endif /* new OpenSSL */
711 static enum okay
712 load_crls(X509_STORE *store, enum okeys fok, enum okeys dok)
714 char *crl_file, *crl_dir;
715 #if defined X509_V_FLAG_CRL_CHECK && defined X509_V_FLAG_CRL_CHECK_ALL
716 DIR *dirp;
717 struct dirent *dp;
718 char *fn = NULL;
719 int fs = 0, ds, es;
720 #endif
721 enum okay rv = STOP;
722 NYD_ENTER;
724 if ((crl_file = _var_oklook(fok)) != NULL) {
725 #if defined X509_V_FLAG_CRL_CHECK && defined X509_V_FLAG_CRL_CHECK_ALL
726 if ((crl_file = file_expand(crl_file)) == NULL ||
727 load_crl1(store, crl_file) != OKAY)
728 goto jleave;
729 #else
730 fprintf(stderr, tr(567,
731 "This OpenSSL version is too old to use CRLs.\n"));
732 goto jleave;
733 #endif
736 if ((crl_dir = _var_oklook(dok)) != NULL) {
737 #if defined X509_V_FLAG_CRL_CHECK && defined X509_V_FLAG_CRL_CHECK_ALL
738 char *x;
739 if ((x = file_expand(crl_dir)) == NULL ||
740 (dirp = opendir(crl_dir = x)) == NULL) {
741 perror(crl_dir);
742 goto jleave;
745 ds = strlen(crl_dir);
746 fn = smalloc(fs = ds + 20);
747 memcpy(fn, crl_dir, ds);
748 fn[ds] = '/';
749 while ((dp = readdir(dirp)) != NULL) {
750 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
751 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
752 continue;
753 if (dp->d_name[0] == '.')
754 continue;
755 if (ds + (es = strlen(dp->d_name)) + 2 < fs)
756 fn = srealloc(fn, fs = ds + es + 20);
757 memcpy(fn + ds + 1, dp->d_name, es + 1);
758 if (load_crl1(store, fn) != OKAY) {
759 closedir(dirp);
760 free(fn);
761 goto jleave;
764 closedir(dirp);
765 free(fn);
766 #else /* old OpenSSL */
767 fprintf(stderr, tr(567,
768 "This OpenSSL version is too old to use CRLs.\n"));
769 goto jleave;
770 #endif
772 #if defined X509_V_FLAG_CRL_CHECK && defined X509_V_FLAG_CRL_CHECK_ALL
773 if (crl_file || crl_dir)
774 X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK |
775 X509_V_FLAG_CRL_CHECK_ALL);
776 #endif
777 rv = OKAY;
778 jleave:
779 NYD_LEAVE;
780 return rv;
783 FL enum okay
784 ssl_open(char const *server, struct sock *sp, char const *uhp)
786 char *cp;
787 long opts;
788 enum okay rv = STOP;
789 NYD_ENTER;
791 ssl_init();
792 ssl_set_verify_level(uhp);
793 if ((sp->s_ctx = SSL_CTX_new(UNCONST(ssl_select_method(uhp)))) == NULL) {
794 ssl_gen_err(tr(261, "SSL_CTX_new() failed"));
795 goto jleave;
798 #ifdef SSL_MODE_AUTO_RETRY
799 /* available with OpenSSL 0.9.6 or later */
800 SSL_CTX_set_mode(sp->s_ctx, SSL_MODE_AUTO_RETRY);
801 #endif /* SSL_MODE_AUTO_RETRY */
802 opts = SSL_OP_ALL;
803 if (!ok_blook(ssl_v2_allow))
804 opts |= SSL_OP_NO_SSLv2;
805 SSL_CTX_set_options(sp->s_ctx, opts);
806 ssl_load_verifications(sp);
807 ssl_certificate(sp, uhp);
808 if ((cp = ok_vlook(ssl_cipher_list)) != NULL) {
809 if (SSL_CTX_set_cipher_list(sp->s_ctx, cp) != 1)
810 fprintf(stderr, tr(240, "Invalid cipher(s): %s\n"), cp);
813 if ((sp->s_ssl = SSL_new(sp->s_ctx)) == NULL) {
814 ssl_gen_err(tr(262, "SSL_new() failed"));
815 goto jleave;
817 SSL_set_fd(sp->s_ssl, sp->s_fd);
818 if (SSL_connect(sp->s_ssl) < 0) {
819 ssl_gen_err(tr(263, "could not initiate SSL/TLS connection"));
820 goto jleave;
822 if (ssl_verify_level != SSL_VERIFY_IGNORE) {
823 if (ssl_check_host(server, sp) != OKAY) {
824 fprintf(stderr, tr(249, "host certificate does not match \"%s\"\n"),
825 server);
826 if (ssl_verify_decide() != OKAY)
827 goto jleave;
830 sp->s_use_ssl = 1;
831 rv = OKAY;
832 jleave:
833 NYD_LEAVE;
834 return rv;
837 FL void
838 ssl_gen_err(char const *fmt, ...)
840 va_list ap;
841 NYD_ENTER;
843 va_start(ap, fmt);
844 vfprintf(stderr, fmt, ap);
845 va_end(ap);
846 SSL_load_error_strings();
847 fprintf(stderr, ": %s\n", ERR_error_string(ERR_get_error(), NULL));
848 NYD_LEAVE;
851 FL int
852 c_verify(void *vp)
854 int *msgvec = vp, *ip, ec = 0, rv = 1;
855 _STACKOF(X509) *chain = NULL;
856 X509_STORE *store;
857 char *ca_dir, *ca_file;
858 NYD_ENTER;
860 ssl_init();
862 ssl_verify_level = SSL_VERIFY_STRICT;
863 if ((store = X509_STORE_new()) == NULL) {
864 ssl_gen_err(tr(544, "Error creating X509 store"));
865 goto jleave;
867 X509_STORE_set_verify_cb_func(store, ssl_verify_cb);
869 if ((ca_dir = ok_vlook(smime_ca_dir)) != NULL)
870 ca_dir = file_expand(ca_dir);
871 if ((ca_file = ok_vlook(smime_ca_file)) != NULL)
872 ca_file = file_expand(ca_file);
874 if (ca_dir != NULL || ca_file != NULL) {
875 if (X509_STORE_load_locations(store, ca_file, ca_dir) != 1) {
876 ssl_gen_err(tr(545, "Error loading %s"),
877 (ca_file != NULL) ? ca_file : ca_dir);
878 goto jleave;
881 if (!ok_blook(smime_no_default_ca)) {
882 if (X509_STORE_set_default_paths(store) != 1) {
883 ssl_gen_err(tr(546, "Error loading default CA locations"));
884 goto jleave;
888 if (load_crls(store, ok_v_smime_crl_file, ok_v_smime_crl_dir) != OKAY)
889 goto jleave;
890 for (ip = msgvec; *ip != 0; ++ip) {
891 struct message *mp = message + *ip - 1;
892 setdot(mp);
893 ec |= smime_verify(mp, *ip, chain, store);
895 if ((rv = ec) != 0)
896 exit_status |= EXIT_ERR;
897 jleave:
898 NYD_LEAVE;
899 return rv;
902 FL FILE *
903 smime_sign(FILE *ip, struct header *headp)
905 FILE *rv = NULL, *sp = NULL, *fp = NULL, *bp, *hp;
906 char const *addr;
907 X509 *cert = NULL;
908 _STACKOF(X509) *chain = NULL;
909 PKCS7 *pkcs7;
910 EVP_PKEY *pkey = NULL;
911 BIO *bb, *sb;
912 bool_t bail = FAL0;
913 NYD_ENTER;
915 ssl_init();
917 if ((addr = myorigin(headp)) == NULL) {
918 fprintf(stderr, tr(531, "No \"from\" address for signing specified\n"));
919 goto jleave;
921 if ((fp = smime_sign_cert(addr, NULL, 1)) == NULL)
922 goto jleave;
924 if ((pkey = PEM_read_PrivateKey(fp, NULL, &ssl_password_cb, NULL)) == NULL) {
925 ssl_gen_err(tr(532, "Error reading private key from"));
926 goto jleave;
929 rewind(fp);
930 if ((cert = PEM_read_X509(fp, NULL, &ssl_password_cb, NULL)) == NULL) {
931 ssl_gen_err(tr(533, "Error reading signer certificate from"));
932 goto jleave;
934 Fclose(fp);
935 fp = NULL;
937 if ((addr = smime_sign_include_certs(addr)) != NULL &&
938 !smime_sign_include_chain_creat(&chain, addr))
939 goto jleave;
941 if ((sp = Ftmp(NULL, "smimesign", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600))
942 == NULL) {
943 perror("tempfile");
944 goto jleave;
947 rewind(ip);
948 if (smime_split(ip, &hp, &bp, -1, 0) == STOP) {
949 bail = TRU1;
950 goto jerr1;
953 sb = NULL;
954 if ((bb = BIO_new_fp(bp, BIO_NOCLOSE)) == NULL ||
955 (sb = BIO_new_fp(sp, BIO_NOCLOSE)) == NULL) {
956 ssl_gen_err(tr(534, "Error creating BIO signing objects"));
957 bail = TRU1;
958 goto jerr;
961 if ((pkcs7 = PKCS7_sign(cert, pkey, chain, bb,
962 PKCS7_DETACHED)) == NULL) {
963 ssl_gen_err(tr(535, "Error creating the PKCS#7 signing object"));
964 bail = TRU1;
965 goto jerr;
967 if (PEM_write_bio_PKCS7(sb, pkcs7) == 0) {
968 ssl_gen_err(tr(536, "Error writing signed S/MIME data"));
969 bail = TRU1;
970 /*goto jerr*/
972 jerr:
973 if (sb != NULL)
974 BIO_free(sb);
975 if (bb != NULL)
976 BIO_free(bb);
977 if (!bail) {
978 rewind(bp);
979 fflush_rewind(sp);
980 rv = smime_sign_assemble(hp, bp, sp);
981 } else
982 jerr1:
983 Fclose(sp);
985 jleave:
986 if (chain != NULL)
987 sk_X509_pop_free(chain, X509_free);
988 if (cert != NULL)
989 X509_free(cert);
990 if (pkey != NULL)
991 EVP_PKEY_free(pkey);
992 if (fp != NULL)
993 Fclose(fp);
994 NYD_LEAVE;
995 return rv;
998 FL FILE *
999 smime_encrypt(FILE *ip, char const *xcertfile, char const *to)
1001 char *certfile = UNCONST(xcertfile);
1002 FILE *rv = NULL, *yp, *fp, *bp, *hp;
1003 X509 *cert;
1004 PKCS7 *pkcs7;
1005 BIO *bb, *yb;
1006 _STACKOF(X509) *certs;
1007 EVP_CIPHER const *cipher;
1008 bool_t bail = FAL0;
1009 NYD_ENTER;
1011 if ((certfile = file_expand(certfile)) == NULL)
1012 goto jleave;
1014 ssl_init();
1015 if ((cipher = _smime_cipher(to)) == NULL)
1016 goto jleave;
1017 if ((fp = Fopen(certfile, "r")) == NULL) {
1018 perror(certfile);
1019 goto jleave;
1022 if ((cert = PEM_read_X509(fp, NULL, &ssl_password_cb, NULL)) == NULL) {
1023 ssl_gen_err(tr(547, "Error reading encryption certificate from \"%s\""),
1024 certfile);
1025 bail = TRU1;
1027 Fclose(fp);
1028 if (bail)
1029 goto jleave;
1030 bail = FAL0;
1032 certs = sk_X509_new_null();
1033 sk_X509_push(certs, cert);
1035 if ((yp = Ftmp(NULL, "smimeenc", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
1036 NULL) {
1037 perror("tempfile");
1038 goto jleave;
1041 rewind(ip);
1042 if (smime_split(ip, &hp, &bp, -1, 0) == STOP) {
1043 Fclose(yp);
1044 goto jleave;
1047 yb = NULL;
1048 if ((bb = BIO_new_fp(bp, BIO_NOCLOSE)) == NULL ||
1049 (yb = BIO_new_fp(yp, BIO_NOCLOSE)) == NULL) {
1050 ssl_gen_err(tr(548, "Error creating BIO encryption objects"));
1051 bail = TRU1;
1052 goto jerr;
1054 if ((pkcs7 = PKCS7_encrypt(certs, bb, cipher, 0)) == NULL) {
1055 ssl_gen_err(tr(549, "Error creating the PKCS#7 encryption object"));
1056 bail = TRU1;
1057 goto jerr;
1059 if (PEM_write_bio_PKCS7(yb, pkcs7) == 0) {
1060 ssl_gen_err(tr(550, "Error writing encrypted S/MIME data"));
1061 bail = TRU1;
1062 /* goto jerr */
1064 jerr:
1065 if (bb != NULL)
1066 BIO_free(bb);
1067 if (yb != NULL)
1068 BIO_free(yb);
1069 Fclose(bp);
1070 if (bail)
1071 Fclose(yp);
1072 else {
1073 fflush_rewind(yp);
1074 rv = smime_encrypt_assemble(hp, yp);
1076 jleave:
1077 NYD_LEAVE;
1078 return rv;
1081 FL struct message *
1082 smime_decrypt(struct message *m, char const *to, char const *cc, int signcall)
1084 struct message *rv;
1085 FILE *fp, *bp, *hp, *op;
1086 X509 *cert;
1087 PKCS7 *pkcs7;
1088 EVP_PKEY *pkey;
1089 BIO *bb, *pb, *ob;
1090 long size;
1091 FILE *yp;
1092 NYD_ENTER;
1094 rv = NULL;
1095 cert = NULL;
1096 pkey = NULL;
1097 size = m->m_size;
1099 if ((yp = setinput(&mb, m, NEED_BODY)) == NULL)
1100 goto jleave;
1102 ssl_init();
1103 if ((fp = smime_sign_cert(to, cc, 0)) != NULL) {
1104 pkey = PEM_read_PrivateKey(fp, NULL, &ssl_password_cb, NULL);
1105 if (pkey == NULL) {
1106 ssl_gen_err(tr(551, "Error reading private key"));
1107 Fclose(fp);
1108 goto jleave;
1110 rewind(fp);
1112 if ((cert = PEM_read_X509(fp, NULL, &ssl_password_cb, NULL)) == NULL) {
1113 ssl_gen_err(tr(552, "Error reading decryption certificate"));
1114 Fclose(fp);
1115 EVP_PKEY_free(pkey);
1116 goto jleave;
1118 Fclose(fp);
1121 if ((op = Ftmp(NULL, "smimedec", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
1122 NULL) {
1123 perror("tempfile");
1124 goto j_ferr;
1127 if (smime_split(yp, &hp, &bp, size, 1) == STOP)
1128 goto jferr;
1130 if ((ob = BIO_new_fp(op, BIO_NOCLOSE)) == NULL ||
1131 (bb = BIO_new_fp(bp, BIO_NOCLOSE)) == NULL) {
1132 ssl_gen_err(tr(553, "Error creating BIO decryption objects"));
1133 goto jferr;
1135 if ((pkcs7 = SMIME_read_PKCS7(bb, &pb)) == NULL) {
1136 ssl_gen_err(tr(554, "Error reading PKCS#7 object"));
1137 jferr:
1138 Fclose(op);
1139 j_ferr:
1140 if (cert)
1141 X509_free(cert);
1142 if (pkey)
1143 EVP_PKEY_free(pkey);
1144 goto jleave;
1147 if (PKCS7_type_is_signed(pkcs7)) {
1148 if (signcall) {
1149 setinput(&mb, m, NEED_BODY);
1150 rv = (struct message*)-1;
1151 goto jerr2;
1153 if (PKCS7_verify(pkcs7, NULL, NULL, NULL, ob,
1154 PKCS7_NOVERIFY | PKCS7_NOSIGS) != 1)
1155 goto jerr;
1156 fseek(hp, 0L, SEEK_END);
1157 fprintf(hp, "X-Encryption-Cipher: none\n");
1158 fflush(hp);
1159 rewind(hp);
1160 } else if (pkey == NULL) {
1161 fprintf(stderr, tr(555, "No appropriate private key found.\n"));
1162 goto jerr2;
1163 } else if (cert == NULL) {
1164 fprintf(stderr, tr(556, "No appropriate certificate found.\n"));
1165 goto jerr2;
1166 } else if (PKCS7_decrypt(pkcs7, pkey, cert, ob, 0) != 1) {
1167 jerr:
1168 ssl_gen_err(tr(557, "Error decrypting PKCS#7 object"));
1169 jerr2:
1170 BIO_free(bb);
1171 BIO_free(ob);
1172 Fclose(op);
1173 Fclose(bp);
1174 Fclose(hp);
1175 if (cert != NULL)
1176 X509_free(cert);
1177 if (pkey != NULL)
1178 EVP_PKEY_free(pkey);
1179 goto jleave;
1181 BIO_free(bb);
1182 BIO_free(ob);
1183 if (cert)
1184 X509_free(cert);
1185 if (pkey)
1186 EVP_PKEY_free(pkey);
1187 fflush_rewind(op);
1188 Fclose(bp);
1190 rv = smime_decrypt_assemble(m, hp, op);
1191 jleave:
1192 NYD_LEAVE;
1193 return rv;
1196 FL enum okay
1197 smime_certsave(struct message *m, int n, FILE *op)
1199 struct message *x;
1200 char *to, *cc, *cnttype;
1201 int c, i;
1202 FILE *fp, *ip;
1203 off_t size;
1204 BIO *fb, *pb;
1205 PKCS7 *pkcs7;
1206 _STACKOF(X509) *certs, *chain = NULL;
1207 X509 *cert;
1208 enum okay rv = STOP;
1209 NYD_ENTER;
1211 message_number = n;
1212 jloop:
1213 to = hfield1("to", m);
1214 cc = hfield1("cc", m);
1215 cnttype = hfield1("content-type", m);
1216 if ((ip = setinput(&mb, m, NEED_BODY)) == NULL)
1217 goto jleave;
1218 if (cnttype && !strncmp(cnttype, "application/x-pkcs7-mime", 24)) {
1219 if ((x = smime_decrypt(m, to, cc, 1)) == NULL)
1220 goto jleave;
1221 if (x != (struct message*)-1) {
1222 m = x;
1223 goto jloop;
1226 size = m->m_size;
1228 if ((fp = Ftmp(NULL, "smimecert", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600))
1229 == NULL) {
1230 perror("tempfile");
1231 goto jleave;
1234 while (size-- > 0) {
1235 c = getc(ip);
1236 putc(c, fp);
1238 fflush(fp);
1240 rewind(fp);
1241 if ((fb = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) {
1242 ssl_gen_err("Error creating BIO object for message %d", n);
1243 Fclose(fp);
1244 goto jleave;
1247 if ((pkcs7 = SMIME_read_PKCS7(fb, &pb)) == NULL) {
1248 ssl_gen_err(tr(562, "Error reading PKCS#7 object for message %d"), n);
1249 BIO_free(fb);
1250 Fclose(fp);
1251 goto jleave;
1253 BIO_free(fb);
1254 Fclose(fp);
1255 certs = PKCS7_get0_signers(pkcs7, chain, 0);
1256 if (certs == NULL) {
1257 fprintf(stderr, tr(563, "No certificates found in message %d\n"), n);
1258 goto jleave;
1261 for (i = 0; i < sk_X509_num(certs); ++i) {
1262 cert = sk_X509_value(certs, i);
1263 if (X509_print_fp(op, cert) == 0 || PEM_write_X509(op, cert) == 0) {
1264 ssl_gen_err(tr(564, "Error writing certificate %d from message %d"),
1265 i, n);
1266 goto jleave;
1269 rv = OKAY;
1270 jleave:
1271 NYD_LEAVE;
1272 return rv;
1274 #endif /* HAVE_OPENSSL */
1276 /* vim:set fenc=utf-8:s-it-mode */