fio.c: fix compiler warning if ! USE_SSL
[s-mailx.git] / nss.c
blobbc72badfaa603d83d3dfd3a36807f760b540a947
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
6 */
7 /*
8 * Changes Copyright (c) 2004
9 * Gunnar Ritter. All rights reserved.
12 * Parts of this file are derived from the Mozilla NSS 3.9.2 source,
13 * mozilla/security/nss/cmd/smimetools/cmsutil.c. Therefore:
15 * The contents of this file are subject to the Mozilla Public License
16 * Version 1.1 (the "License"); you may not use this file except in
17 * compliance with the License. You may obtain a copy of the License
18 * at http://www.mozilla.org/MPL/
20 * Software distributed under the License is distributed on an "AS
21 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
22 * implied. See the License for the specific language governing
23 * rights and limitations under the License.
25 * The Original Code is the Netscape security libraries.
27 * The Initial Developer of the Original Code is Netscape
28 * Communications Corporation. Portions created by Netscape are
29 * Copyright (C) 1994-2000 Netscape Communications Corporation. All
30 * Rights Reserved.
32 * Contributor(s):
35 #ifndef lint
36 #ifdef DOSCCS
37 static char sccsid[] = "@(#)nss.c 1.48 (gritter) 8/4/07";
38 #endif
39 #endif /* not lint */
41 #include "config.h"
43 #ifndef USE_NSS
44 typedef int avoid_empty_file_compiler_warning;
45 #else
47 #include "rcv.h"
49 #include <setjmp.h>
50 #include <termios.h>
51 #include <stdio.h>
53 static int verbose;
54 static int reset_tio;
55 static struct termios otio;
56 static sigjmp_buf nssjmp;
58 #include <stdarg.h>
60 #include <nss.h>
61 #include <ssl.h>
62 #include <prinit.h>
63 #include <prmem.h>
64 #include <pk11func.h>
65 #include <prtypes.h>
66 #include <prerror.h>
67 #include <secerr.h>
68 #include <smime.h>
69 #include <ciferfam.h>
70 #ifdef HAVE_XCONST_H
71 #include <xconst.h>
72 #endif
73 #ifdef HAVE_GENNAME_H
74 #include <genname.h>
75 #endif
76 #include <private/pprio.h>
78 #include "extern.h"
80 #ifndef HAVE_CERTAltNameEncodedContext
82 * NSS 3.11.5 neither installs genname.h nor provides this
83 * structure otherwise, so define it here.
85 typedef struct CERTAltNameEncodedContextStr {
86 SECItem **encodedGenName;
87 } CERTAltNameEncodedContext;
88 #endif /* !HAVE_CERTAltNameEncodedContext */
90 #include "nsserr.c"
92 static char *password_cb(PK11SlotInfo *slot, PRBool retry, void *arg);
93 static SECStatus bad_cert_cb(void *arg, PRFileDesc *fd);
94 static enum okay nss_check_host(const char *server, struct sock *sp);
95 static const char *bad_cert_str(void);
96 static enum okay nss_init(void);
97 static void nss_select_method(const char *uhp);
98 static CERTCertificate *get_signer_cert(char *addr);
99 static FILE *encode(FILE *ip, FILE **hp, FILE **bp, NSSCMSMessage *msg,
100 void (*cb)(void *, const char *, unsigned long));
101 static void decoder_cb(void *arg, const char *buf, unsigned long len);
102 static void base64_cb(void *arg, const char *buf, unsigned long len);
103 static int verify1(struct message *m, int n);
104 static struct message *getsig(struct message *m, int n, NSSCMSMessage **msg);
105 static enum okay getdig(struct message *m, int n, SECItem ***digests,
106 PLArenaPool **poolp, SECAlgorithmID **algids);
107 static void nsscatch(int s);
108 static void dumpcert(CERTCertificate *cert, FILE *op);
109 static enum okay getcipher(const char *to, SECOidTag *alg, int *key);
111 static char *
112 password_cb(PK11SlotInfo *slot, PRBool retry, void *arg)
114 sighandler_type saveint;
115 char *pass = NULL;
117 (void)&saveint;
118 (void)&pass;
119 saveint = safe_signal(SIGINT, SIG_IGN);
120 if (sigsetjmp(nssjmp, 1) == 0) {
121 if (saveint != SIG_IGN)
122 safe_signal(SIGINT, nsscatch);
123 pass = getpassword(&otio, &reset_tio, arg);
125 safe_signal(SIGINT, saveint);
126 if (pass == NULL)
127 return NULL;
128 return PL_strdup(pass);
131 static SECStatus
132 bad_cert_cb(void *arg, PRFileDesc *fd)
134 if (PORT_GetError() == SSL_ERROR_BAD_CERT_DOMAIN)
136 * We must not use this result. NSS verifies host names
137 * according to RFC 2818, but we must verify host names
138 * according to RFC 2595. The rules are different:
140 * - RFC 2818 says that if both a dNSName and a CN are
141 * contained in the peer certificate, only the dNSName
142 * is used. RFC 2595 encourages to use both.
144 * - RFC 2818 allows the wildcard '*' in any component
145 * of the host name. RFC 2595 allows it only as the
146 * "left-most name component".
148 * So ignore it and verify separately.
150 return SECSuccess;
151 fprintf(stderr, "Error in certificate: %s.\n", bad_cert_str());
152 return ssl_vrfy_decide() == OKAY ? SECSuccess : SECFailure;
156 * Host name checking according to RFC 2595.
158 static enum okay
159 nss_check_host(const char *server, struct sock *sp)
161 CERTCertificate *cert;
162 char *cn = NULL;
163 enum okay ok = STOP;
164 PRArenaPool *arena;
165 CERTGeneralName *gn;
166 SECItem altname;
167 CERTAltNameEncodedContext ec;
168 int i;
169 const SEC_ASN1Template gntempl[] = {
170 { SEC_ASN1_SEQUENCE_OF, 0, SEC_AnyTemplate }
173 if ((cert = SSL_PeerCertificate(sp->s_prfd)) == NULL) {
174 fprintf(stderr, "no certificate from \"%s\"\n", server);
175 return STOP;
177 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
178 if (CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME,
179 &altname) == SECSuccess &&
180 SEC_ASN1DecodeItem(arena, &ec, gntempl,
181 &altname) == SECSuccess &&
182 ec.encodedGenName != NULL) {
183 for (i = 0; ec.encodedGenName[i] != NULL; i++) {
184 gn = CERT_DecodeGeneralName(arena, ec.encodedGenName[i],
185 NULL);
186 if (gn->type == certDNSName) {
187 char *dn = ac_alloc(gn->name.other.len + 1);
188 memcpy(dn, gn->name.other.data,
189 gn->name.other.len);
190 dn[gn->name.other.len] = '\0';
191 if (verbose)
192 fprintf(stderr,
193 "Comparing DNS name: \"%s\"\n",
194 dn);
195 if (rfc2595_hostname_match(server, dn)
196 == OKAY) {
197 ac_free(dn);
198 goto out;
200 ac_free(dn);
204 if ((cn = CERT_GetCommonName(&cert->subject)) != NULL) {
205 if (verbose)
206 fprintf(stderr, "Comparing common name: \"%s\"\n", cn);
207 ok = rfc2595_hostname_match(server, cn);
209 if (ok == STOP)
210 fprintf(stderr, "host certificate does not match \"%s\"\n",
211 server);
212 out: if (cn)
213 PORT_Free(cn);
214 PORT_FreeArena(arena, PR_FALSE);
215 CERT_DestroyCertificate(cert);
216 return ok;
219 static const char *
220 bad_cert_str(void)
222 int ec;
224 ec = PORT_GetError();
225 return nss_strerror(ec);
228 static enum okay
229 nss_init(void)
231 static int initialized;
232 char *cp;
234 verbose = value("verbose") != NULL;
235 if (initialized == 0) {
236 if ((cp = value("nss-config-dir")) == NULL) {
237 fputs("Missing \"nss-config-dir\" variable.\n", stderr);
238 return STOP;
240 cp = expand(cp);
241 PR_Init(0, 0, 0);
242 PK11_SetPasswordFunc(password_cb);
243 if (NSS_Init(cp) == SECSuccess) {
244 NSS_SetDomesticPolicy();
245 initialized = 1;
246 return OKAY;
248 nss_gen_err("Error initializing NSS");
249 return STOP;
251 return OKAY;
254 static void
255 nss_select_method(const char *uhp)
257 char *cp;
258 enum {
259 SSL2 = 01,
260 SSL3 = 02,
261 TLS1 = 03
262 } methods;
264 methods = SSL2|SSL3|TLS1;
265 cp = ssl_method_string(uhp);
266 if (cp != NULL) {
267 if (equal(cp, "ssl2"))
268 methods = SSL2;
269 else if (equal(cp, "ssl3"))
270 methods = SSL3;
271 else if (equal(cp, "tls1"))
272 methods = TLS1;
273 else {
274 fprintf(stderr, catgets(catd, CATSET, 244,
275 "Invalid SSL method \"%s\"\n"), cp);
278 if (value("ssl-v2-allow") == NULL)
279 methods &= ~SSL2;
280 SSL_OptionSetDefault(SSL_ENABLE_SSL2, methods&SSL2 ? PR_TRUE:PR_FALSE);
281 SSL_OptionSetDefault(SSL_ENABLE_SSL3, methods&SSL3 ? PR_TRUE:PR_FALSE);
282 SSL_OptionSetDefault(SSL_ENABLE_TLS, methods&TLS1 ? PR_TRUE:PR_FALSE);
285 enum okay
286 ssl_open(const char *server, struct sock *sp, const char *uhp)
288 PRFileDesc *fdp, *fdc;
290 if (nss_init() == STOP)
291 return STOP;
292 ssl_set_vrfy_level(uhp);
293 nss_select_method(uhp);
294 if ((fdp = PR_ImportTCPSocket(sp->s_fd)) == NULL) {
295 nss_gen_err("Error importing OS file descriptor");
296 return STOP;
298 if ((fdc = SSL_ImportFD(NULL, fdp)) == NULL) {
299 nss_gen_err("Error importing NSPR file descriptor");
300 PR_Close(fdp);
301 return STOP;
303 SSL_SetURL(fdc, server);
304 SSL_SetPKCS11PinArg(fdc, NULL);
305 SSL_BadCertHook(fdc, bad_cert_cb, NULL);
306 if (SSL_ResetHandshake(fdc, PR_FALSE) != SECSuccess) {
307 nss_gen_err("Cannot reset NSS handshake");
308 PR_Close(fdc);
309 return STOP;
311 if (SSL_ForceHandshake(fdc) != 0) {
312 nss_gen_err("SSL/TLS handshake failed");
313 PR_Close(fdc);
314 return STOP;
316 sp->s_prfd = fdc;
317 if (nss_check_host(server, sp) != OKAY && ssl_vrfy_decide() != OKAY) {
318 PR_Close(fdc);
319 sp->s_prfd = NULL;
320 return STOP;
322 sp->s_use_ssl = 1;
323 if (verbose) {
324 char *cipher, *issuer, *subject;
325 int keysize, secretkeysize;
327 if (SSL_SecurityStatus(fdc, NULL, &cipher,
328 &keysize, &secretkeysize,
329 &issuer, &subject) == SECSuccess) {
330 fprintf(stderr, "SSL parameters: cipher=%s, "
331 "keysize=%d, secretkeysize=%d,\n"
332 "issuer=%s\n"
333 "subject=%s\n",
334 cipher, keysize, secretkeysize,
335 issuer, subject);
336 PR_Free(cipher);
337 PR_Free(issuer);
338 PR_Free(subject);
339 } else
340 nss_gen_err("Could not read status information");
342 return OKAY;
345 void
346 nss_gen_err(const char *fmt, ...)
348 va_list ap;
349 char *text;
350 int len;
352 va_start(ap, fmt);
353 vfprintf(stderr, fmt, ap);
354 va_end(ap);
355 if ((len = PR_GetErrorTextLength()) > 0) {
356 text = ac_alloc(len);
357 if (PR_GetErrorText(text) > 0)
358 fprintf(stderr, ": %s\n", text);
359 ac_free(text);
360 } else
361 fprintf(stderr, ": %s.\n", nss_strerror(PR_GetError()));
364 FILE *
365 smime_sign(FILE *ip, struct header *headp)
367 NSSCMSMessage *msg;
368 NSSCMSContentInfo *content;
369 NSSCMSSignedData *data;
370 NSSCMSSignerInfo *info;
371 CERTCertificate *cert;
372 CERTCertDBHandle *handle;
373 FILE *hp, *bp, *sp;
374 char *addr;
376 if (nss_init() != OKAY)
377 return NULL;
378 if ((addr = myorigin(headp)) == NULL) {
379 fprintf(stderr, "No \"from\" address for signing specified\n");
380 return NULL;
382 if ((cert = get_signer_cert(addr)) == NULL)
383 return NULL;
384 handle = CERT_GetDefaultCertDB();
385 if ((msg = NSS_CMSMessage_Create(NULL)) == NULL) {
386 fprintf(stderr, "Cannot create CMS message.\n");
387 return NULL;
389 if ((data = NSS_CMSSignedData_Create(msg)) == NULL) {
390 fprintf(stderr, "Cannot create CMS signed data.\n");
391 return NULL;
393 content = NSS_CMSMessage_GetContentInfo(msg);
394 if (NSS_CMSContentInfo_SetContent_SignedData(msg, content, data)
395 != SECSuccess) {
396 fprintf(stderr, "Cannot attach CMS signed data.\n");
397 return NULL;
399 content = NSS_CMSSignedData_GetContentInfo(data);
400 if (NSS_CMSContentInfo_SetContent_Data(msg, content, NULL, PR_TRUE)
401 != SECSuccess) {
402 fprintf(stderr, "Cannot attach CMS data.\n");
403 return NULL;
405 if ((info = NSS_CMSSignerInfo_Create(msg, cert, SEC_OID_SHA1)) == 0) {
406 fprintf(stderr, "Cannot create signed information.\n");
407 return NULL;
409 if (NSS_CMSSignerInfo_IncludeCerts(info, NSSCMSCM_CertOnly,
410 certUsageEmailSigner) != SECSuccess) {
411 fprintf(stderr, "Cannot include certificate.\n");
412 return NULL;
414 if (NSS_CMSSignerInfo_AddSigningTime(info, PR_Now()) != SECSuccess) {
415 fprintf(stderr, "Cannot add signing time.\n");
416 return NULL;
418 if (NSS_CMSSignerInfo_AddSMIMECaps(info) != SECSuccess) {
419 fprintf(stderr, "Cannot add S/MIME capabilities.\n");
420 return NULL;
422 NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(info, cert, handle);
423 NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(info, cert, handle);
424 if (NSS_CMSSignedData_AddCertificate(data, cert) != SECSuccess) {
425 fprintf(stderr, "Cannot add encryption certificate.\n");
426 return NULL;
428 if (NSS_CMSSignedData_AddSignerInfo(data, info) != SECSuccess) {
429 fprintf(stderr, "Cannot add signer information.\n");
430 return NULL;
432 CERT_DestroyCertificate(cert);
433 if ((sp = encode(ip, &hp, &bp, msg, base64_cb)) == NULL) {
434 NSS_CMSMessage_Destroy(msg);
435 return NULL;
437 NSS_CMSMessage_Destroy(msg);
438 return smime_sign_assemble(hp, bp, sp);
442 cverify(void *vp)
444 int *msgvec = vp, *ip;
445 int ec = 0;
447 if (nss_init() != OKAY)
448 return 1;
449 ssl_vrfy_level = VRFY_STRICT;
450 for (ip = msgvec; *ip; ip++) {
451 setdot(&message[*ip-1]);
452 ec |= verify1(&message[*ip-1], *ip);
454 return ec;
457 FILE *
458 smime_encrypt(FILE *ip, const char *ignored, const char *to)
460 NSSCMSMessage *msg;
461 NSSCMSContentInfo *content;
462 NSSCMSEnvelopedData *data;
463 NSSCMSRecipientInfo *info;
464 CERTCertificate *cert[2];
465 CERTCertDBHandle *handle;
466 SECOidTag tag;
467 FILE *hp, *pp, *yp;
468 int keysize;
469 char *nickname, *vn;
470 int vs;
472 if (nss_init() != OKAY)
473 return NULL;
474 handle = CERT_GetDefaultCertDB();
475 vn = ac_alloc(vs = strlen(to) + 30);
476 snprintf(vn, vs, "smime-nickname-%s", to);
477 nickname = value(vn);
478 ac_free(vn);
479 if ((cert[0] = CERT_FindCertByNicknameOrEmailAddr(handle,
480 nickname ? nickname : (char *)to)) == NULL) {
481 if (nickname)
482 fprintf(stderr, "Cannot find certificate \"%s\".\n",
483 nickname);
484 else
485 fprintf(stderr, "Cannot find certificate for <%s>.\n",
486 to);
487 return NULL;
489 cert[1] = NULL;
490 if (getcipher(to, &tag, &keysize) != OKAY)
491 return NULL;
492 if ((msg = NSS_CMSMessage_Create(NULL)) == NULL) {
493 fprintf(stderr, "Cannot create CMS message.\n");
494 return NULL;
496 if ((data = NSS_CMSEnvelopedData_Create(msg, tag, keysize)) == NULL) {
497 fprintf(stderr, "Cannot create enveloped data.\n");
498 return NULL;
500 content = NSS_CMSMessage_GetContentInfo(msg);
501 if (NSS_CMSContentInfo_SetContent_EnvelopedData(msg, content, data)
502 != SECSuccess) {
503 fprintf(stderr, "Cannot attach enveloped data.\n");
504 return NULL;
506 content = NSS_CMSEnvelopedData_GetContentInfo(data);
507 if (NSS_CMSContentInfo_SetContent_Data(msg, content, NULL, PR_FALSE)
508 != SECSuccess) {
509 fprintf(stderr, "Cannot attach CMS data.\n");
510 return NULL;
512 if ((info = NSS_CMSRecipientInfo_Create(msg, cert[0])) == NULL) {
513 fprintf(stderr, "Cannot create CMS recipient information.\n");
514 return NULL;
516 if (NSS_CMSEnvelopedData_AddRecipient(data, info) != SECSuccess) {
517 fprintf(stderr, "Cannot add CMS recipient information.\n");
518 return NULL;
520 CERT_DestroyCertificate(cert[0]);
521 if ((yp = encode(ip, &hp, &pp, msg, base64_cb)) == NULL)
522 return NULL;
523 NSS_CMSMessage_Destroy(msg);
524 return smime_encrypt_assemble(hp, yp);
527 struct message *
528 smime_decrypt(struct message *m, const char *to, const char *cc, int signcall)
530 NSSCMSDecoderContext *ctx;
531 NSSCMSMessage *msg;
532 FILE *op, *hp, *bp;
533 char *buf = NULL;
534 size_t bufsize = 0, buflen, count;
535 char *cp;
536 struct str in, out;
537 FILE *yp;
538 long size;
539 int i, nlevels;
540 int binary = 0;
542 if ((yp = setinput(&mb, m, NEED_BODY)) == NULL)
543 return NULL;
544 if (nss_init() != OKAY)
545 return NULL;
546 if ((op = Ftemp(&cp, "Rp", "w+", 0600, 1)) == NULL) {
547 perror("tempfile");
548 return NULL;
550 rm(cp);
551 Ftfree(&cp);
552 if ((ctx = NSS_CMSDecoder_Start(NULL,
553 decoder_cb, op,
554 password_cb, "Pass phrase:",
555 NULL, NULL)) == NULL) {
556 fprintf(stderr, "Cannot start decoder.\n");
557 return NULL;
559 size = m->m_size;
560 if ((smime_split(yp, &hp, &bp, size, 1)) == STOP)
561 return NULL;
562 count = fsize(bp);
563 while (fgetline(&buf, &bufsize, &count, &buflen, bp, 0) != NULL) {
564 if (buf[0] == '\n')
565 break;
566 if ((cp = thisfield(buf, "content-transfer-encoding")) != NULL)
567 if (ascncasecmp(cp, "binary", 7) == 0)
568 binary = 1;
570 while (fgetline(&buf, &bufsize, &count, &buflen, bp, 0) != NULL) {
571 if (binary)
572 NSS_CMSDecoder_Update(ctx, buf, buflen);
573 else {
574 in.s = buf;
575 in.l = buflen;
576 mime_fromb64_b(&in, &out, 0, bp);
577 NSS_CMSDecoder_Update(ctx, out.s, out.l);
578 free(out.s);
581 free(buf);
582 if ((msg = NSS_CMSDecoder_Finish(ctx)) == NULL) {
583 fprintf(stderr, "Failed to decode message.\n");
584 Fclose(hp);
585 Fclose(bp);
586 return NULL;
588 nlevels = NSS_CMSMessage_ContentLevelCount(msg);
589 for (i = 0; i < nlevels; i++) {
590 NSSCMSContentInfo *content;
591 SECOidTag tag;
593 content = NSS_CMSMessage_ContentLevel(msg, i);
594 tag = NSS_CMSContentInfo_GetContentTypeTag(content);
595 if (tag == SEC_OID_PKCS7_DATA) {
596 const char *fld = "X-Encryption-Cipher";
597 SECOidTag alg;
598 int keysize;
600 alg = NSS_CMSContentInfo_GetContentEncAlgTag(content);
601 keysize = NSS_CMSContentInfo_GetBulkKeySize(content);
602 fseek(hp, 0L, SEEK_END);
603 switch (alg) {
604 case 0:
605 if (signcall) {
606 NSS_CMSMessage_Destroy(msg);
607 Fclose(hp);
608 Fclose(bp);
609 setinput(&mb, m, NEED_BODY);
610 return (struct message *)-1;
612 fprintf(hp, "%s: none\n", fld);
613 break;
614 case SEC_OID_RC2_CBC:
615 fprintf(hp, "%s: RC2, %d bits\n", fld, keysize);
616 break;
617 case SEC_OID_DES_CBC:
618 fprintf(hp, "%s: DES, 56 bits\n", fld);
619 break;
620 case SEC_OID_DES_EDE3_CBC:
621 fprintf(hp, "%s: 3DES, 112/168 bits\n", fld);
622 break;
623 case SEC_OID_FORTEZZA_SKIPJACK:
624 fprintf(hp, "%s: Fortezza\n", fld);
625 break;
626 default:
627 fprintf(hp, "%s: unknown type %lu\n", fld,
628 (unsigned long)alg);
630 fflush(hp);
631 rewind(hp);
634 NSS_CMSMessage_Destroy(msg);
635 fflush(op);
636 rewind(op);
637 Fclose(bp);
638 return smime_decrypt_assemble(m, hp, op);
641 static CERTCertificate *
642 get_signer_cert(char *addr)
644 CERTCertDBHandle *handle;
645 CERTCertList *list;
646 CERTCertListNode *node;
647 CERTCertificate *cert = NULL;
648 const char *cp;
649 char *nick;
650 char *vn;
651 int vs, found = 0;
653 addr = skin(addr);
654 vn = ac_alloc(vs = strlen(addr) + 30);
655 snprintf(vn, vs, "smime-sign-nickname-%s", addr);
656 if ((nick = value(vn)) == NULL)
657 nick = value("smime-sign-nickname");
658 ac_free(vn);
659 handle = CERT_GetDefaultCertDB();
660 if (nick) {
661 cert = CERT_FindCertByNickname(handle, nick);
662 if (cert == NULL)
663 fprintf(stderr, "No certificate \"%s\" found.\n", nick);
664 return cert;
666 if ((list = CERT_FindUserCertsByUsage(handle, certUsageEmailSigner,
667 PR_TRUE, PR_TRUE, NULL)) == NULL) {
668 fprintf(stderr, "Cannot find any certificates for signing.\n");
669 return NULL;
671 for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list);
672 node = CERT_LIST_NEXT(node)) {
673 if ((cp = CERT_GetCertEmailAddress(&node->cert->subject))
674 != NULL && asccasecmp(cp, addr) == 0) {
675 cert = node->cert;
676 found++;
679 if (cert == NULL) {
680 for (node = CERT_LIST_HEAD(list);
681 !CERT_LIST_END(node, list) && cert == NULL;
682 node = CERT_LIST_NEXT(node)) {
683 cp = CERT_GetFirstEmailAddress(node->cert);
684 while (cp) {
685 if (asccasecmp(cp, addr) == 0) {
686 cert = node->cert;
687 found++;
689 cp = CERT_GetNextEmailAddress(node->cert, cp);
693 if (found > 1) {
694 fprintf(stderr,
695 "More than one signing certificate found for <%s>.\n"
696 "Use the smime-sign-nickname variable.\n", addr);
697 return NULL;
699 if (cert == NULL)
700 fprintf(stderr,
701 "Cannot find a signing certificate for <%s>.\n",
702 addr);
703 return cert;
706 static FILE *
707 encode(FILE *ip, FILE **hp, FILE **bp, NSSCMSMessage *msg,
708 void (*cb)(void *, const char *, unsigned long))
710 NSSCMSEncoderContext *ctx;
711 char *buf = NULL, *cp;
712 size_t bufsize = 0, buflen, count;
713 FILE *op;
715 if (smime_split(ip, hp, bp, -1, 0) == STOP)
716 return NULL;
717 if ((op = Ftemp(&cp, "Ry", "w+", 0600, 1)) == NULL) {
718 perror("tempfile");
719 return NULL;
721 rm(cp);
722 Ftfree(&cp);
723 if ((ctx = NSS_CMSEncoder_Start(msg,
724 cb, op,
725 NULL, NULL,
726 password_cb, "Pass phrase:",
727 NULL, NULL,
728 NULL, NULL)) == NULL) {
729 fprintf(stderr, "Cannot create encoder context.\n");
730 Fclose(op);
731 return NULL;
733 count = fsize(*bp);
734 while (fgetline(&buf, &bufsize, &count, &buflen, *bp, 0) != NULL) {
735 buf[buflen-1] = '\r';
736 buf[buflen] = '\n';
737 if (NSS_CMSEncoder_Update(ctx, buf, buflen+1) != 0) {
738 fprintf(stderr, "Failed to add data to encoder.\n");
739 Fclose(op);
740 return NULL;
743 free(buf);
744 if (NSS_CMSEncoder_Finish(ctx) != 0) {
745 fprintf(stderr, "Failed to encode data.\n");
746 Fclose(op);
747 return NULL;
749 rewind(*bp);
750 cb(op, (void *)-1, 0);
751 fflush(op);
752 if (ferror(op)) {
753 perror("tempfile");
754 Fclose(op);
755 return NULL;
757 rewind(op);
758 return op;
761 static void
762 decoder_cb(void *arg, const char *buf, unsigned long len)
764 if (arg && buf)
765 fwrite(buf, 1, len, arg);
768 static void
769 base64_cb(void *arg, const char *buf, unsigned long len)
771 static char back[972];
772 static int fill;
773 unsigned long pos;
775 if (arg && buf && buf != (void *)-1) {
776 pos = 0;
777 while (len - pos >= sizeof back - fill) {
778 memcpy(&back[fill], &buf[pos], sizeof back - fill);
779 mime_write(back, sizeof back, arg,
780 CONV_TOB64, TD_NONE, NULL, 0,
781 NULL, NULL);
782 pos += sizeof back - fill;
783 fill = 0;
785 memcpy(&back[fill], &buf[pos], len - pos);
786 fill += len - pos;
787 } else if (buf == (void *)-1) {
788 mime_write(back, fill, arg,
789 CONV_TOB64, TD_NONE, NULL, 0,
790 NULL, NULL);
791 fill = 0;
795 static int
796 verify1(struct message *m, int n)
798 SECItem **digests;
799 NSSCMSMessage *msg;
800 PLArenaPool *poolp;
801 SECAlgorithmID **algids;
802 CERTCertDBHandle *handle;
803 int nlevels, i;
804 int status = 0;
805 int foundsender = 0;
806 char *sender;
808 if ((m = getsig(m, n, &msg)) == NULL)
809 return 1;
810 sender = getsender(m);
811 handle = CERT_GetDefaultCertDB();
812 nlevels = NSS_CMSMessage_ContentLevelCount(msg);
813 for (i = 0; i < nlevels; i++) {
814 NSSCMSContentInfo *content;
815 SECOidTag tag;
817 content = NSS_CMSMessage_ContentLevel(msg, i);
818 tag = NSS_CMSContentInfo_GetContentTypeTag(content);
819 if (tag == SEC_OID_PKCS7_SIGNED_DATA) {
820 NSSCMSSignedData *data;
821 int nsigners, j;
823 if ((data = NSS_CMSContentInfo_GetContent(content))
824 == NULL) {
825 fprintf(stderr, "Signed data missing for "
826 "message %d.\n", n);
827 status = -1;
828 break;
830 if (!NSS_CMSSignedData_HasDigests(data)) {
831 algids = NSS_CMSSignedData_GetDigestAlgs(data);
832 if (getdig(m, n, &digests, &poolp, algids)
833 != OKAY) {
834 status = -1;
835 break;
837 if (NSS_CMSSignedData_SetDigests(data, algids,
838 digests)
839 != SECSuccess) {
840 fprintf(stderr, "Cannot set digests "
841 "for message %d.\n", n);
842 status = -1;
843 break;
845 PORT_FreeArena(poolp, PR_FALSE);
847 if (NSS_CMSSignedData_ImportCerts(data, handle,
848 certUsageEmailSigner,
849 PR_FALSE) != SECSuccess) {
850 fprintf(stderr, "Cannot temporarily import "
851 "certificates for "
852 "message %d.\n", n);
853 status = -1;
854 break;
856 nsigners = NSS_CMSSignedData_SignerInfoCount(data);
857 if (nsigners == 0) {
858 fprintf(stderr, "Message %d has no signers.\n",
860 status = -1;
861 break;
863 if (!NSS_CMSSignedData_HasDigests(data)) {
864 fprintf(stderr, "Message %d has no digests.\n",
866 status = -1;
867 break;
869 for (j = 0; j < nsigners; j++) {
870 const char *svs;
871 NSSCMSSignerInfo *info;
872 NSSCMSVerificationStatus vs;
873 SECStatus bad;
874 CERTCertificate *cert;
875 const char *addr;
876 int passed = 0;
878 info = NSS_CMSSignedData_GetSignerInfo(data, j);
879 cert = NSS_CMSSignerInfo_GetSigningCertificate
880 (info, handle);
881 bad = NSS_CMSSignedData_VerifySignerInfo(data,
882 j, handle,
883 certUsageEmailSigner);
884 vs = NSS_CMSSignerInfo_GetVerificationStatus
885 (info);
886 svs = NSS_CMSUtil_VerificationStatusToString
887 (vs);
888 addr = CERT_GetCertEmailAddress(&cert->subject);
889 if (sender != NULL && addr != NULL &&
890 asccasecmp(sender, addr) == 0)
891 foundsender++;
892 else {
893 addr = CERT_GetFirstEmailAddress(cert);
894 while (sender && addr) {
895 if (!asccasecmp(sender, addr)) {
896 foundsender++;
897 break;
899 addr = CERT_GetNextEmailAddress
900 (cert, addr);
903 if (CERT_VerifyCertNow(handle,
904 cert, PR_TRUE,
905 certUsageEmailSigner,
906 NULL) != SECSuccess)
907 fprintf(stderr, "Bad certificate for "
908 "signer <%s> of "
909 "message %d: %s.\n",
910 addr ? addr : "?", n,
911 bad_cert_str());
912 else
913 passed++;
914 if (bad)
915 fprintf(stderr, "Bad status for "
916 "signer <%s> of "
917 "message %d: %s.\n",
918 addr ? addr : "?",
919 n, svs);
920 else
921 passed++;
922 if (passed < 2)
923 status = -1;
924 else if (status == 0)
925 status = 1;
929 if (foundsender == 0) {
930 if (sender) {
931 fprintf(stderr, "Signers of message "
932 "%d do not include the sender <%s>\n",
933 n, sender);
934 status = -1;
935 } else
936 fprintf(stderr, "Warning: Message %d has no From: "
937 "header field.\n", n);
938 } else if (status == 1)
939 printf("Message %d was verified successfully.\n", n);
940 if (status == 0)
941 fprintf(stderr, "No verification information found in "
942 "message %d.\n", n);
943 NSS_CMSMessage_Destroy(msg);
944 return status != 1;
947 static struct message *
948 getsig(struct message *m, int n, NSSCMSMessage **msg)
950 struct message *x;
951 char *ct, *pt, *boundary = NULL, *cte;
952 char *buf = NULL;
953 size_t bufsize = 0, buflen, count, boundlen = -1;
954 int part;
955 FILE *fp;
956 NSSCMSDecoderContext *decctx;
957 struct str in, out;
958 char *to, *cc;
959 int inhdr, binary;
960 int detached = 1;
962 loop: if ((ct = hfield("content-type", m)) == NULL)
963 goto not;
964 if (strncmp(ct, "application/x-pkcs7-mime", 24) == 0 ||
965 strncmp(ct, "application/pkcs7-mime", 22) == 0) {
966 to = hfield("to", m);
967 cc = hfield("cc", m);
968 if ((x = smime_decrypt(m, to, cc, 1)) == NULL)
969 return NULL;
970 if (x != (struct message *)-1) {
971 m = x;
972 goto loop;
974 detached = 0;
975 } else if (strncmp(ct, "multipart/signed", 16) ||
976 (pt = mime_getparam("protocol", ct)) == NULL ||
977 strcmp(pt, "application/x-pkcs7-signature") &&
978 strcmp(pt, "application/pkcs7-signature") ||
979 (boundary = mime_getboundary(ct)) == NULL) {
980 not: fprintf(stderr,
981 "Message %d is not an S/MIME signed message.\n", n);
982 return NULL;
983 } else
984 boundlen = strlen(boundary);
985 if ((decctx = NSS_CMSDecoder_Start(NULL, NULL, NULL,
986 password_cb, "Pass phrase:",
987 NULL, NULL)) == NULL) {
988 fprintf(stderr, "Cannot start decoder.\n");
989 return NULL;
991 if ((fp = setinput(&mb, m, NEED_BODY)) == NULL) {
992 return NULL;
994 count = m->m_size;
995 part = 0;
996 inhdr = 1;
997 binary = 0;
998 while (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) != NULL) {
999 if (detached && boundary && buflen >= boundlen + 1 &&
1000 strncmp(buf, boundary, boundlen) == 0) {
1001 if (buf[boundlen] == '\n') {
1002 part++;
1003 inhdr = 1;
1004 binary = 0;
1005 if (part >= 3) {
1006 fprintf(stderr, "Message %d has too "
1007 "many parts.\n", n);
1008 free(buf);
1009 return NULL;
1011 continue;
1013 if (buf[boundlen] == '-' && buf[boundlen+1] == '-' &&
1014 buf[boundlen+2] == '\n')
1015 break;
1016 } else if (buf[0] == '\n') {
1017 inhdr = 0;
1018 continue;
1020 if ((!detached || part == 2) && inhdr == 0) {
1021 if (binary)
1022 NSS_CMSDecoder_Update(decctx, buf, buflen);
1023 else {
1024 in.s = buf;
1025 in.l = buflen;
1026 mime_fromb64_b(&in, &out, 0, fp);
1027 NSS_CMSDecoder_Update(decctx, out.s, out.l);
1028 free(out.s);
1031 if (buflen == 1 && buf[0] == '\n')
1032 inhdr = 0;
1033 if (inhdr && (cte = thisfield(buf, "content-transfer-encoding"))
1034 != NULL && ascncasecmp(cte, "binary", 7) == 0)
1035 binary = 1;
1037 free(buf);
1038 if ((*msg = NSS_CMSDecoder_Finish(decctx)) == NULL) {
1039 fprintf(stderr, "Failed to decode signature for message %d.\n",
1041 return NULL;
1043 return m;
1046 static enum okay
1047 getdig(struct message *m, int n, SECItem ***digests,
1048 PLArenaPool **poolp, SECAlgorithmID **algids)
1050 char *ct, *pt, *boundary;
1051 char *buf = NULL;
1052 size_t bufsize = 0, buflen, count, boundlen;
1053 int part;
1054 int nl;
1055 FILE *fp;
1056 NSSCMSDigestContext *digctx;
1058 *poolp = PORT_NewArena(1024);
1059 if ((ct = hfield("content-type", m)) == NULL ||
1060 strncmp(ct, "multipart/signed", 16) ||
1061 (pt = mime_getparam("protocol", ct)) == NULL ||
1062 strcmp(pt, "application/x-pkcs7-signature") &&
1063 strcmp(pt, "application/pkcs7-signature") ||
1064 (boundary = mime_getboundary(ct)) == NULL) {
1065 fprintf(stderr,
1066 "Message %d is not an S/MIME signed message.\n", n);
1067 return STOP;
1069 boundlen = strlen(boundary);
1070 if ((digctx = NSS_CMSDigestContext_StartMultiple(algids)) == NULL) {
1071 fprintf(stderr, "Cannot start digest computation.\n");
1072 return STOP;
1074 if ((fp = setinput(&mb, m, NEED_BODY)) == NULL) {
1075 return STOP;
1077 count = m->m_size;
1078 part = 0;
1079 nl = 0;
1080 while (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) != NULL) {
1081 if (buflen >= boundlen + 1 &&
1082 strncmp(buf, boundary, boundlen) == 0) {
1083 if (buf[boundlen] == '\n') {
1084 if (++part >= 2)
1085 break;
1086 continue;
1088 if (buf[boundlen] == '-' && buf[boundlen+1] == '-' &&
1089 buf[boundlen+2] == '\n')
1090 break;
1092 if (part == 1) {
1093 if (nl) {
1094 NSS_CMSDigestContext_Update(digctx,
1095 (unsigned char *)"\r\n", 2);
1096 nl = 0;
1098 if (buf[buflen-1] == '\n') {
1099 nl = 1;
1100 buflen--;
1102 NSS_CMSDigestContext_Update(digctx,
1103 (unsigned char *)buf, buflen);
1104 continue;
1107 free(buf);
1108 if (NSS_CMSDigestContext_FinishMultiple(digctx,
1109 *poolp, digests) != SECSuccess) {
1110 fprintf(stderr, "Error creating digest for message %d\n", n);
1111 return STOP;
1113 return OKAY;
1116 static void
1117 nsscatch(int s)
1119 if (reset_tio)
1120 tcsetattr(0, TCSADRAIN, &otio);
1121 siglongjmp(nssjmp, s);
1124 enum okay
1125 smime_certsave(struct message *m, int n, FILE *op)
1127 NSSCMSMessage *msg;
1128 CERTCertDBHandle *handle;
1129 int nlevels, i, cnt = 0;
1130 enum okay ok = OKAY;
1132 if (nss_init() == STOP)
1133 return STOP;
1134 if ((m = getsig(m, n, &msg)) == NULL)
1135 return 1;
1136 handle = CERT_GetDefaultCertDB();
1137 nlevels = NSS_CMSMessage_ContentLevelCount(msg);
1138 for (i = 0; i < nlevels; i++) {
1139 NSSCMSContentInfo *content;
1140 SECOidTag tag;
1142 content = NSS_CMSMessage_ContentLevel(msg, i);
1143 tag = NSS_CMSContentInfo_GetContentTypeTag(content);
1144 if (tag == SEC_OID_PKCS7_SIGNED_DATA) {
1145 NSSCMSSignedData *data;
1146 int nsigners, j;
1148 if ((data = NSS_CMSContentInfo_GetContent(content))
1149 == NULL) {
1150 fprintf(stderr, "Signed data missing for "
1151 "message %d.\n", n);
1152 ok = STOP;
1153 break;
1155 if (NSS_CMSSignedData_ImportCerts(data, handle,
1156 certUsageEmailSigner,
1157 PR_FALSE) != SECSuccess) {
1158 fprintf(stderr, "Cannot temporarily import "
1159 "certificates for "
1160 "message %d.\n", n);
1161 ok = STOP;
1162 break;
1164 nsigners = NSS_CMSSignedData_SignerInfoCount(data);
1165 if (nsigners == 0) {
1166 fprintf(stderr, "Message %d has no signers.\n",
1168 ok = STOP;
1169 break;
1171 for (j = 0; j < nsigners; j++) {
1172 NSSCMSSignerInfo *info;
1173 CERTCertificateList *list;
1174 CERTCertificate *cert;
1175 int k;
1177 info = NSS_CMSSignedData_GetSignerInfo(data, j);
1178 list = NSS_CMSSignerInfo_GetCertList(info);
1179 if (list) {
1180 for (k = 0; k < list->len; k++) {
1181 cert = (CERTCertificate *)
1182 &list->certs[k];
1183 dumpcert(cert, op);
1184 cnt++;
1187 cert = NSS_CMSSignerInfo_GetSigningCertificate
1188 (info, handle);
1189 if (cert) {
1190 dumpcert(cert, op);
1191 cnt++;
1196 NSS_CMSMessage_Destroy(msg);
1197 if (cnt == 0) {
1198 fprintf(stderr, "No certificates found in message %d.\n", n);
1199 ok = STOP;
1201 return ok;
1204 static void
1205 dumpcert(CERTCertificate *cert, FILE *op)
1207 fprintf(op, "subject=%s\n", cert->subjectName);
1208 fprintf(op, "issuer=%s\n", cert->issuerName);
1209 fputs("-----BEGIN CERTIFICATE-----\n", op);
1210 mime_write(cert->derCert.data,
1211 cert->derCert.len, op,
1212 CONV_TOB64, TD_NONE, NULL, 0,
1213 NULL, NULL);
1214 fputs("-----END CERTIFICATE-----\n", op);
1217 static enum okay
1218 getcipher(const char *to, SECOidTag *alg, int *key)
1220 char *vn, *cp;
1221 int vs;
1223 *key = 0;
1224 *alg = SEC_OID_DES_EDE3_CBC;
1225 vn = ac_alloc(vs = strlen(to) + 30);
1226 snprintf(vn, vs, "smime-cipher-%s", to);
1227 if ((cp = value(vn)) != NULL) {
1228 if (strcmp(cp, "rc2-40") == 0) {
1229 *alg = SEC_OID_RC2_CBC;
1230 *key = 40;
1231 } else if (strcmp(cp, "rc2-64") == 0) {
1232 *alg = SEC_OID_RC2_CBC;
1233 *key = 64;
1234 } else if (strcmp(cp, "rc2-128") == 0) {
1235 *alg = SEC_OID_RC2_CBC;
1236 *key = 128;
1237 } else if (strcmp(cp, "des") == 0)
1238 *alg = SEC_OID_DES_CBC;
1239 else if (strcmp(cp, "fortezza") == 0)
1240 *alg = SEC_OID_FORTEZZA_SKIPJACK;
1241 else if (strcmp(cp, "des-ede3") == 0)
1242 /*EMPTY*/;
1243 else {
1244 fprintf(stderr, "Invalid cipher \"%s\".\n", cp);
1245 return STOP;
1248 ac_free(vn);
1249 return OKAY;
1251 #endif /* USE_NSS */