/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 2002 * Gunnar Ritter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Gunnar Ritter * and his contributors. * 4. Neither the name of Gunnar Ritter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)openssl.c 1.24 (gritter) 3/4/06"; #endif #endif /* not lint */ #include "config.h" #ifndef USE_NSS #ifdef USE_OPENSSL #include #include #include static int verbose; static int reset_tio; static struct termios otio; static sigjmp_buf ssljmp; #include #include #include #include #include #include #include #include "rcv.h" #include #include #include #include #include #include #include #ifdef HAVE_ARPA_INET_H #include #endif /* HAVE_ARPA_INET_H */ #include #include "extern.h" /* * Mail -- a mail program * * SSL functions */ /* * OpenSSL client implementation according to: John Viega, Matt Messier, * Pravir Chandra: Network Security with OpenSSL. Sebastopol, CA 2002. */ static int initialized; static int rand_init; static int message_number; static int verify_error_found; static void sslcatch(int s); static int ssl_rand_init(void); static void ssl_init(void); static int ssl_verify_cb(int success, X509_STORE_CTX *store); static SSL_METHOD *ssl_select_method(const char *uhp); static void ssl_load_verifications(struct sock *sp); static void ssl_certificate(struct sock *sp, const char *uhp); static enum okay ssl_check_host(const char *server, struct sock *sp); static int smime_verify(struct message *m, int n, STACK *chain, X509_STORE *store); static EVP_CIPHER *smime_cipher(const char *name); static int ssl_password_cb(char *buf, int size, int rwflag, void *userdata); static FILE *smime_sign_cert(const char *xname, const char *xname2, int warn); #if defined (X509_V_FLAG_CRL_CHECK) && defined (X509_V_FLAG_CRL_CHECK_ALL) static enum okay load_crl1(X509_STORE *store, const char *name); #endif static enum okay load_crls(X509_STORE *store, const char *vfile, const char *vdir); static void sslcatch(int s) { if (reset_tio) tcsetattr(0, TCSADRAIN, &otio); siglongjmp(ssljmp, s); } static int ssl_rand_init(void) { char *cp; int state = 0; if ((cp = value("ssl-rand-egd")) != NULL) { cp = expand(cp); if (RAND_egd(cp) == -1) { fprintf(stderr, catgets(catd, CATSET, 245, "entropy daemon at \"%s\" not available\n"), cp); } else state = 1; } else if ((cp = value("ssl-rand-file")) != NULL) { cp = expand(cp); if (RAND_load_file(cp, 1024) == -1) { fprintf(stderr, catgets(catd, CATSET, 246, "entropy file at \"%s\" not available\n"), cp); } else { struct stat st; if (stat(cp, &st) == 0 && S_ISREG(st.st_mode) && access(cp, W_OK) == 0) { if (RAND_write_file(cp) == -1) { fprintf(stderr, catgets(catd, CATSET, 247, "writing entropy data to \"%s\" failed\n"), cp); } } state = 1; } } return state; } static void ssl_init(void) { verbose = value("verbose") != NULL; if (initialized == 0) { SSL_library_init(); initialized = 1; } if (rand_init == 0) rand_init = ssl_rand_init(); } static int ssl_verify_cb(int success, X509_STORE_CTX *store) { if (success == 0) { char data[256]; X509 *cert = X509_STORE_CTX_get_current_cert(store); int depth = X509_STORE_CTX_get_error_depth(store); int err = X509_STORE_CTX_get_error(store); verify_error_found = 1; if (message_number) fprintf(stderr, "Message %d: ", message_number); fprintf(stderr, catgets(catd, CATSET, 229, "Error with certificate at depth: %i\n"), depth); X509_NAME_oneline(X509_get_issuer_name(cert), data, sizeof data); fprintf(stderr, catgets(catd, CATSET, 230, " issuer = %s\n"), data); X509_NAME_oneline(X509_get_subject_name(cert), data, sizeof data); fprintf(stderr, catgets(catd, CATSET, 231, " subject = %s\n"), data); fprintf(stderr, catgets(catd, CATSET, 232, " err %i: %s\n"), err, X509_verify_cert_error_string(err)); if (ssl_vrfy_decide() != OKAY) return 0; } return 1; } static SSL_METHOD * ssl_select_method(const char *uhp) { SSL_METHOD *method; char *cp; cp = ssl_method_string(uhp); if (cp != NULL) { if (equal(cp, "ssl2")) method = SSLv2_client_method(); else if (equal(cp, "ssl3")) method = SSLv3_client_method(); else if (equal(cp, "tls1")) method = TLSv1_client_method(); else { fprintf(stderr, catgets(catd, CATSET, 244, "Invalid SSL method \"%s\"\n"), cp); method = SSLv23_client_method(); } } else method = SSLv23_client_method(); return method; } static void ssl_load_verifications(struct sock *sp) { char *ca_dir, *ca_file; X509_STORE *store; if (ssl_vrfy_level == VRFY_IGNORE) return; if ((ca_dir = value("ssl-ca-dir")) != NULL) ca_dir = expand(ca_dir); if ((ca_file = value("ssl-ca-file")) != NULL) ca_file = expand(ca_file); if (ca_dir || ca_file) { if (SSL_CTX_load_verify_locations(sp->s_ctx, ca_file, ca_dir) != 1) { fprintf(stderr, catgets(catd, CATSET, 233, "Error loading")); if (ca_dir) { fprintf(stderr, catgets(catd, CATSET, 234, " %s"), ca_dir); if (ca_file) fprintf(stderr, catgets(catd, CATSET, 235, " or")); } if (ca_file) fprintf(stderr, catgets(catd, CATSET, 236, " %s"), ca_file); fprintf(stderr, catgets(catd, CATSET, 237, "\n")); } } if (value("ssl-no-default-ca") == NULL) { if (SSL_CTX_set_default_verify_paths(sp->s_ctx) != 1) fprintf(stderr, catgets(catd, CATSET, 243, "Error loading default CA locations\n")); } verify_error_found = 0; message_number = 0; SSL_CTX_set_verify(sp->s_ctx, SSL_VERIFY_PEER, ssl_verify_cb); store = SSL_CTX_get_cert_store(sp->s_ctx); load_crls(store, "ssl-crl-file", "ssl-crl-dir"); } static void ssl_certificate(struct sock *sp, const char *uhp) { char *certvar, *keyvar, *cert, *key; certvar = ac_alloc(strlen(uhp) + 10); strcpy(certvar, "ssl-cert-"); strcpy(&certvar[9], uhp); if ((cert = value(certvar)) != NULL || (cert = value("ssl-cert")) != NULL) { cert = expand(cert); if (SSL_CTX_use_certificate_chain_file(sp->s_ctx, cert) == 1) { keyvar = ac_alloc(strlen(uhp) + 9); strcpy(keyvar, "ssl-key-"); if ((key = value(keyvar)) == NULL && (key = value("ssl-key")) == NULL) key = cert; else key = expand(key); if (SSL_CTX_use_PrivateKey_file(sp->s_ctx, key, SSL_FILETYPE_PEM) != 1) fprintf(stderr, catgets(catd, CATSET, 238, "cannot load private key from file %s\n"), key); ac_free(keyvar); } else fprintf(stderr, catgets(catd, CATSET, 239, "cannot load certificate from file %s\n"), cert); } ac_free(certvar); } static enum okay ssl_check_host(const char *server, struct sock *sp) { X509 *cert; X509_NAME *subj; char data[256]; /*GENERAL_NAMES*/STACK *gens; GENERAL_NAME *gen; int i; if ((cert = SSL_get_peer_certificate(sp->s_ssl)) == NULL) { fprintf(stderr, catgets(catd, CATSET, 248, "no certificate from \"%s\"\n"), server); return STOP; } gens = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (gens != NULL) { for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { gen = sk_GENERAL_NAME_value(gens, i); if (gen->type == GEN_DNS) { if (verbose) fprintf(stderr, "Comparing DNS name: \"%s\"\n", gen->d.ia5->data); if (rfc2595_hostname_match(server, (char *)gen->d.ia5->data) == OKAY) goto found; } } } if ((subj = X509_get_subject_name(cert)) != NULL && X509_NAME_get_text_by_NID(subj, NID_commonName, data, sizeof data) > 0) { data[sizeof data - 1] = 0; if (verbose) fprintf(stderr, "Comparing common name: \"%s\"\n", data); if (rfc2595_hostname_match(server, data) == OKAY) goto found; } X509_free(cert); return STOP; found: X509_free(cert); return OKAY; } enum okay ssl_open(const char *server, struct sock *sp, const char *uhp) { char *cp; long options; ssl_init(); ssl_set_vrfy_level(uhp); if ((sp->s_ctx = SSL_CTX_new(ssl_select_method(uhp))) == NULL) { ssl_gen_err(catgets(catd, CATSET, 261, "SSL_CTX_new() failed")); return STOP; } #ifdef SSL_MODE_AUTO_RETRY /* available with OpenSSL 0.9.6 or later */ SSL_CTX_set_mode(sp->s_ctx, SSL_MODE_AUTO_RETRY); #endif /* SSL_MODE_AUTO_RETRY */ options = SSL_OP_ALL; if (value("ssl-v2-allow") == NULL) options |= SSL_OP_NO_SSLv2; SSL_CTX_set_options(sp->s_ctx, options); ssl_load_verifications(sp); ssl_certificate(sp, uhp); if ((cp = value("ssl-cipher-list")) != NULL) { if (SSL_CTX_set_cipher_list(sp->s_ctx, cp) != 1) fprintf(stderr, catgets(catd, CATSET, 240, "invalid ciphers: %s\n"), cp); } if ((sp->s_ssl = SSL_new(sp->s_ctx)) == NULL) { ssl_gen_err(catgets(catd, CATSET, 262, "SSL_new() failed")); return STOP; } SSL_set_fd(sp->s_ssl, sp->s_fd); if (SSL_connect(sp->s_ssl) < 0) { ssl_gen_err(catgets(catd, CATSET, 263, "could not initiate SSL/TLS connection")); return STOP; } if (ssl_vrfy_level != VRFY_IGNORE) { if (ssl_check_host(server, sp) != OKAY) { fprintf(stderr, catgets(catd, CATSET, 249, "host certificate does not match \"%s\"\n"), server); if (ssl_vrfy_decide() != OKAY) return STOP; } } sp->s_use_ssl = 1; return OKAY; } void ssl_gen_err(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); SSL_load_error_strings(); fprintf(stderr, ": %s\n", (ERR_error_string(ERR_get_error(), NULL))); } FILE * smime_sign(FILE *ip, struct header *headp) { FILE *sp, *fp, *bp, *hp; char *cp, *addr; X509 *cert; PKCS7 *pkcs7; EVP_PKEY *pkey; BIO *bb, *sb; ssl_init(); if ((addr = myorigin(headp)) == NULL) return NULL; if ((fp = smime_sign_cert(addr, NULL, 1)) == NULL) return NULL; if ((pkey = PEM_read_PrivateKey(fp, NULL, ssl_password_cb, NULL)) == NULL) { ssl_gen_err("Error reading private key from"); Fclose(fp); return NULL; } rewind(fp); if ((cert = PEM_read_X509(fp, NULL, ssl_password_cb, NULL)) == NULL) { ssl_gen_err("Error reading signer certificate from"); Fclose(fp); EVP_PKEY_free(pkey); return NULL; } Fclose(fp); if ((sp = Ftemp(&cp, "Rs", "w+", 0600, 1)) == NULL) { perror("tempfile"); X509_free(cert); EVP_PKEY_free(pkey); return NULL; } rm(cp); Ftfree(&cp); rewind(ip); if (smime_split(ip, &hp, &bp, -1, 0) == STOP) { Fclose(sp); X509_free(cert); EVP_PKEY_free(pkey); return NULL; } if ((bb = BIO_new_fp(bp, BIO_NOCLOSE)) == NULL || (sb = BIO_new_fp(sp, BIO_NOCLOSE)) == NULL) { ssl_gen_err("Error creating BIO signing objects"); Fclose(sp); X509_free(cert); EVP_PKEY_free(pkey); return NULL; } if ((pkcs7 = PKCS7_sign(cert, pkey, NULL, bb, PKCS7_DETACHED)) == NULL) { ssl_gen_err("Error creating the PKCS#7 signing object"); BIO_free(bb); BIO_free(sb); Fclose(sp); X509_free(cert); EVP_PKEY_free(pkey); return NULL; } if (PEM_write_bio_PKCS7(sb, pkcs7) == 0) { ssl_gen_err("Error writing signed S/MIME data"); BIO_free(bb); BIO_free(sb); Fclose(sp); X509_free(cert); EVP_PKEY_free(pkey); return NULL; } BIO_free(bb); BIO_free(sb); X509_free(cert); EVP_PKEY_free(pkey); rewind(bp); fflush(sp); rewind(sp); return smime_sign_assemble(hp, bp, sp); } static int smime_verify(struct message *m, int n, STACK *chain, X509_STORE *store) { struct message *x; char *cp, *sender, *to, *cc, *cnttype; int c, i, j; FILE *fp, *ip; off_t size; BIO *fb, *pb; PKCS7 *pkcs7; STACK *certs, *gens; X509 *cert; X509_NAME *subj; char data[LINESIZE]; GENERAL_NAME *gen; verify_error_found = 0; message_number = n; loop: sender = getsender(m); to = hfield("to", m); cc = hfield("cc", m); cnttype = hfield("content-type", m); if ((ip = setinput(&mb, m, NEED_BODY)) == NULL) return 1; if (cnttype && strncmp(cnttype, "application/x-pkcs7-mime", 24) == 0) { if ((x = smime_decrypt(m, to, cc, 1)) == NULL) return 1; if (x != (struct message *)-1) { m = x; goto loop; } } size = m->m_size; if ((fp = Ftemp(&cp, "Rv", "w+", 0600, 1)) == NULL) { perror("tempfile"); return 1; } rm(cp); Ftfree(&cp); while (size-- > 0) { c = getc(ip); putc(c, fp); } fflush(fp); rewind(fp); if ((fb = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) { ssl_gen_err("Error creating BIO verification object " "for message %d", n); Fclose(fp); return 1; } if ((pkcs7 = SMIME_read_PKCS7(fb, &pb)) == NULL) { ssl_gen_err("Error reading PKCS#7 object for message %d", n); BIO_free(fb); Fclose(fp); return 1; } if (PKCS7_verify(pkcs7, chain, store, pb, NULL, 0) != 1) { ssl_gen_err("Error verifying message %d", n); BIO_free(fb); Fclose(fp); return 1; } BIO_free(fb); Fclose(fp); if (sender == NULL) { fprintf(stderr, "Warning: Message %d has no sender.\n", n); return 0; } certs = PKCS7_get0_signers(pkcs7, chain, 0); if (certs == NULL) { fprintf(stderr, "No certificates found in message %d.\n", n); return 1; } for (i = 0; i < sk_X509_num(certs); i++) { cert = sk_X509_value(certs, i); gens = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (gens != NULL) { for (j = 0; j < sk_GENERAL_NAME_num(gens); j++) { gen = sk_GENERAL_NAME_value(gens, j); if (gen->type == GEN_EMAIL) { if (verbose) fprintf(stderr, "Comparing alt. " "address: %s\"\n", data); if (!asccasecmp((char *) gen->d.ia5->data, sender)) goto found; } } } if ((subj = X509_get_subject_name(cert)) != NULL && X509_NAME_get_text_by_NID(subj, NID_pkcs9_emailAddress, data, sizeof data) > 0) { data[sizeof data - 1] = 0; if (verbose) fprintf(stderr, "Comparing address: \"%s\"\n", data); if (asccasecmp(data, sender) == 0) goto found; } } fprintf(stderr, "Message %d: certificate does not match <%s>\n", n, sender); return 1; found: if (verify_error_found == 0) printf("Message %d was verified successfully.\n", n); return verify_error_found; } int cverify(void *vp) { int *msgvec = vp, *ip; int ec = 0; STACK *chain = NULL; X509_STORE *store; char *ca_dir, *ca_file; ssl_init(); ssl_vrfy_level = VRFY_STRICT; if ((store = X509_STORE_new()) == NULL) { ssl_gen_err("Error creating X509 store"); return 1; } X509_STORE_set_verify_cb_func(store, ssl_verify_cb); if ((ca_dir = value("smime-ca-dir")) != NULL) ca_dir = expand(ca_dir); if ((ca_file = value("smime-ca-file")) != NULL) ca_file = expand(ca_file); if (ca_dir || ca_file) { if (X509_STORE_load_locations(store, ca_file, ca_dir) != 1) { ssl_gen_err("Error loading %s", ca_file ? ca_file : ca_dir); return 1; } } if (value("smime-no-default-ca") == NULL) { if (X509_STORE_set_default_paths(store) != 1) { ssl_gen_err("Error loading default CA locations"); return 1; } } if (load_crls(store, "smime-crl-file", "smime-crl-dir") != OKAY) return 1; for (ip = msgvec; *ip; ip++) { setdot(&message[*ip-1]); ec |= smime_verify(&message[*ip-1], *ip, chain, store); } return ec; } static EVP_CIPHER * smime_cipher(const char *name) { const EVP_CIPHER *cipher; char *vn, *cp; int vs; vn = ac_alloc(vs = strlen(name) + 30); snprintf(vn, vs, "smime-cipher-%s", name); if ((cp = value(vn)) != NULL) { if (strcmp(cp, "rc2-40") == 0) cipher = EVP_rc2_40_cbc(); else if (strcmp(cp, "rc2-64") == 0) cipher = EVP_rc2_64_cbc(); else if (strcmp(cp, "des") == 0) cipher = EVP_des_cbc(); else if (strcmp(cp, "des-ede3") == 0) cipher = EVP_des_ede3_cbc(); else { fprintf(stderr, "Invalid cipher \"%s\".\n", cp); cipher = NULL; } } else cipher = EVP_des_ede3_cbc(); ac_free(vn); return (EVP_CIPHER *)cipher; } FILE * smime_encrypt(FILE *ip, const char *certfile, const char *to) { FILE *yp, *fp, *bp, *hp; char *cp; X509 *cert; PKCS7 *pkcs7; BIO *bb, *yb; STACK *certs; EVP_CIPHER *cipher; certfile = expand((char *)certfile); ssl_init(); if ((cipher = smime_cipher(to)) == NULL) return NULL; if ((fp = Fopen(certfile, "r")) == NULL) { perror(certfile); return NULL; } if ((cert = PEM_read_X509(fp, NULL, ssl_password_cb, NULL)) == NULL) { ssl_gen_err("Error reading encryption certificate from \"%s\"", certfile); Fclose(fp); return NULL; } Fclose(fp); certs = sk_X509_new_null(); sk_X509_push(certs, cert); if ((yp = Ftemp(&cp, "Ry", "w+", 0600, 1)) == NULL) { perror("tempfile"); return NULL; } rm(cp); Ftfree(&cp); rewind(ip); if (smime_split(ip, &hp, &bp, -1, 0) == STOP) { Fclose(yp); return NULL; } if ((bb = BIO_new_fp(bp, BIO_NOCLOSE)) == NULL || (yb = BIO_new_fp(yp, BIO_NOCLOSE)) == NULL) { ssl_gen_err("Error creating BIO encryption objects"); Fclose(yp); return NULL; } if ((pkcs7 = PKCS7_encrypt(certs, bb, cipher, 0)) == NULL) { ssl_gen_err("Error creating the PKCS#7 encryption object"); BIO_free(bb); BIO_free(yb); Fclose(yp); return NULL; } if (PEM_write_bio_PKCS7(yb, pkcs7) == 0) { ssl_gen_err("Error writing encrypted S/MIME data"); BIO_free(bb); BIO_free(yb); Fclose(yp); return NULL; } BIO_free(bb); BIO_free(yb); Fclose(bp); fflush(yp); rewind(yp); return smime_encrypt_assemble(hp, yp); } struct message * smime_decrypt(struct message *m, const char *to, const char *cc, int signcall) { FILE *fp, *bp, *hp, *op; char *cp; X509 *cert = NULL; PKCS7 *pkcs7; EVP_PKEY *pkey = NULL; BIO *bb, *pb, *ob; long size = m->m_size; FILE *yp; if ((yp = setinput(&mb, m, NEED_BODY)) == NULL) return NULL; ssl_init(); if ((fp = smime_sign_cert(to, cc, 0)) != NULL) { if ((pkey = PEM_read_PrivateKey(fp, NULL, ssl_password_cb, NULL)) == NULL) { ssl_gen_err("Error reading private key"); Fclose(fp); return NULL; } rewind(fp); if ((cert = PEM_read_X509(fp, NULL, ssl_password_cb, NULL)) == NULL) { ssl_gen_err("Error reading decryption certificate"); Fclose(fp); EVP_PKEY_free(pkey); return NULL; } Fclose(fp); } if ((op = Ftemp(&cp, "Rp", "w+", 0600, 1)) == NULL) { perror("tempfile"); if (cert) X509_free(cert); if (pkey) EVP_PKEY_free(pkey); return NULL; } rm(cp); Ftfree(&cp); if (smime_split(yp, &hp, &bp, size, 1) == STOP) { Fclose(op); if (cert) X509_free(cert); if (pkey) EVP_PKEY_free(pkey); return NULL; } if ((ob = BIO_new_fp(op, BIO_NOCLOSE)) == NULL || (bb = BIO_new_fp(bp, BIO_NOCLOSE)) == NULL) { ssl_gen_err("Error creating BIO decryption objects"); Fclose(op); if (cert) X509_free(cert); if (pkey) EVP_PKEY_free(pkey); return NULL; } if ((pkcs7 = SMIME_read_PKCS7(bb, &pb)) == NULL) { ssl_gen_err("Error reading PKCS#7 object"); Fclose(op); if (cert) X509_free(cert); if (pkey) EVP_PKEY_free(pkey); return NULL; } if (PKCS7_type_is_signed(pkcs7)) { if (signcall) { BIO_free(bb); BIO_free(ob); if (cert) X509_free(cert); if (pkey) EVP_PKEY_free(pkey); Fclose(op); Fclose(bp); Fclose(hp); setinput(&mb, m, NEED_BODY); return (struct message *)-1; } if (PKCS7_verify(pkcs7, NULL, NULL, NULL, ob, PKCS7_NOVERIFY|PKCS7_NOSIGS) != 1) goto err; fseek(hp, 0L, SEEK_END); fprintf(hp, "X-Encryption-Cipher: none\n"); fflush(hp); rewind(hp); } else if (pkey == NULL) { fprintf(stderr, "No appropriate private key found.\n"); goto err2; } else if (cert == NULL) { fprintf(stderr, "No appropriate certificate found.\n"); goto err2; } else if (PKCS7_decrypt(pkcs7, pkey, cert, ob, 0) != 1) { err: ssl_gen_err("Error decrypting PKCS#7 object"); err2: BIO_free(bb); BIO_free(ob); Fclose(op); Fclose(bp); Fclose(hp); if (cert) X509_free(cert); if (pkey) EVP_PKEY_free(pkey); return NULL; } BIO_free(bb); BIO_free(ob); if (cert) X509_free(cert); if (pkey) EVP_PKEY_free(pkey); fflush(op); rewind(op); Fclose(bp); return smime_decrypt_assemble(m, hp, op); } /*ARGSUSED4*/ static int ssl_password_cb(char *buf, int size, int rwflag, void *userdata) { sighandler_type saveint; char *pass = NULL; int len; (void)&saveint; (void)&pass; saveint = safe_signal(SIGINT, SIG_IGN); if (sigsetjmp(ssljmp, 1) == 0) { if (saveint != SIG_IGN) safe_signal(SIGINT, sslcatch); pass = getpassword(&otio, &reset_tio, "PEM pass phrase:"); } safe_signal(SIGINT, saveint); if (pass == NULL) return 0; len = strlen(pass); if (len > size) len = size; memcpy(buf, pass, len); return len; } static FILE * smime_sign_cert(const char *xname, const char *xname2, int warn) { char *vn, *cp; int vs; FILE *fp; struct name *np; const char *name = xname, *name2 = xname2; loop: if (name) { np = sextract(savestr(name), GTO|GSKIN); while (np) { /* * This needs to be more intelligent since it will * currently take the first name for which a private * key is available regardless of whether it is the * right one for the message. */ vn = ac_alloc(vs = strlen(np->n_name) + 30); snprintf(vn, vs, "smime-sign-cert-%s", np->n_name); if ((cp = value(vn)) != NULL) goto open; np = np->n_flink; } if (name2) { name = name2; name2 = NULL; goto loop; } } if ((cp = value("smime-sign-cert")) != NULL) goto open; if (warn) { fprintf(stderr, "Could not find a certificate for %s", xname); if (xname2) fprintf(stderr, "or %s", xname2); fputc('\n', stderr); } return NULL; open: cp = expand(cp); if ((fp = Fopen(cp, "r")) == NULL) { perror(cp); return NULL; } return fp; } enum okay smime_certsave(struct message *m, int n, FILE *op) { struct message *x; char *cp, *to, *cc, *cnttype; int c, i; FILE *fp, *ip; off_t size; BIO *fb, *pb; PKCS7 *pkcs7; STACK *certs; X509 *cert; STACK *chain = NULL; enum okay ok = OKAY; message_number = n; loop: to = hfield("to", m); cc = hfield("cc", m); cnttype = hfield("content-type", m); if ((ip = setinput(&mb, m, NEED_BODY)) == NULL) return STOP; if (cnttype && strncmp(cnttype, "application/x-pkcs7-mime", 24) == 0) { if ((x = smime_decrypt(m, to, cc, 1)) == NULL) return STOP; if (x != (struct message *)-1) { m = x; goto loop; } } size = m->m_size; if ((fp = Ftemp(&cp, "Rv", "w+", 0600, 1)) == NULL) { perror("tempfile"); return STOP; } rm(cp); Ftfree(&cp); while (size-- > 0) { c = getc(ip); putc(c, fp); } fflush(fp); rewind(fp); if ((fb = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) { ssl_gen_err("Error creating BIO object for message %d", n); Fclose(fp); return STOP; } if ((pkcs7 = SMIME_read_PKCS7(fb, &pb)) == NULL) { ssl_gen_err("Error reading PKCS#7 object for message %d", n); BIO_free(fb); Fclose(fp); return STOP; } BIO_free(fb); Fclose(fp); certs = PKCS7_get0_signers(pkcs7, chain, 0); if (certs == NULL) { fprintf(stderr, "No certificates found in message %d.\n", n); return STOP; } for (i = 0; i < sk_X509_num(certs); i++) { cert = sk_X509_value(certs, i); if (X509_print_fp(op, cert) == 0 || PEM_write_X509(op, cert) == 0) { ssl_gen_err("Error writing certificate %d from " "message %d", i, n); ok = STOP; } } return ok; } #if defined (X509_V_FLAG_CRL_CHECK) && defined (X509_V_FLAG_CRL_CHECK_ALL) static enum okay load_crl1(X509_STORE *store, const char *name) { X509_LOOKUP *lookup; if (verbose) printf("Loading CRL from \"%s\".\n", name); if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) == NULL) { ssl_gen_err("Error creating X509 lookup object"); return STOP; } if (X509_load_crl_file(lookup, name, X509_FILETYPE_PEM) != 1) { ssl_gen_err("Error loading CRL from \"%s\"", name); return STOP; } return OKAY; } #endif /* new OpenSSL */ static enum okay load_crls(X509_STORE *store, const char *vfile, const char *vdir) { char *crl_file, *crl_dir; #if defined (X509_V_FLAG_CRL_CHECK) && defined (X509_V_FLAG_CRL_CHECK_ALL) DIR *dirfd; struct dirent *dp; char *fn = NULL; int fs = 0, ds, es; #endif /* new OpenSSL */ if ((crl_file = value(vfile)) != NULL) { #if defined (X509_V_FLAG_CRL_CHECK) && defined (X509_V_FLAG_CRL_CHECK_ALL) crl_file = expand(crl_file); if (load_crl1(store, crl_file) != OKAY) return STOP; #else /* old OpenSSL */ fprintf(stderr, "This OpenSSL version is too old to use CRLs.\n"); return STOP; #endif /* old OpenSSL */ } if ((crl_dir = value(vdir)) != NULL) { #if defined (X509_V_FLAG_CRL_CHECK) && defined (X509_V_FLAG_CRL_CHECK_ALL) crl_dir = expand(crl_dir); ds = strlen(crl_dir); if ((dirfd = opendir(crl_dir)) == NULL) { perror(crl_dir); return STOP; } fn = smalloc(fs = ds + 20); strcpy(fn, crl_dir); fn[ds] = '/'; while ((dp = readdir(dirfd)) != NULL) { if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) continue; if (dp->d_name[0] == '.') continue; if (ds + (es = strlen(dp->d_name)) + 2 < fs) fn = srealloc(fn, fs = ds + es + 20); strcpy(&fn[ds+1], dp->d_name); if (load_crl1(store, fn) != OKAY) { closedir(dirfd); free(fn); return STOP; } } closedir(dirfd); free(fn); #else /* old OpenSSL */ fprintf(stderr, "This OpenSSL version is too old to use CRLs.\n"); return STOP; #endif /* old OpenSSL */ } #if defined (X509_V_FLAG_CRL_CHECK) && defined (X509_V_FLAG_CRL_CHECK_ALL) if (crl_file || crl_dir) X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); #endif /* old OpenSSL */ return OKAY; } #else /* !USE_OPENSSL */ #include static void nosmime(void) { fprintf(stderr, "No S/MIME support compiled in.\n"); } /*ARGSUSED*/ FILE * smime_sign(FILE *fp) { nosmime(); return NULL; } /*ARGSUSED*/ int cverify(void *vp) { nosmime(); return 1; } /*ARGSUSED*/ FILE * smime_encrypt(FILE *fp, const char *certfile, const char *to) { nosmime(); return NULL; } /*ARGSUSED*/ struct message * smime_decrypt(struct message *m, const char *to, const char *cc, int signcall) { nosmime(); return NULL; } /*ARGSUSED*/ int ccertsave(void *v) { nosmime(); return 1; } #endif /* !USE_OPENSSL */ #endif /* !USE_NSS */