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