uagent,version are "char const" not "const char"
[s-mailx.git] / nss.c
blob2c5eb1d83be69ffdd8d71ccbf6f29f5f0ec07b6e
1 /*
2 * S-nail - 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.
33 #include "config.h"
35 #ifndef USE_NSS
36 typedef int avoid_empty_file_compiler_warning;
37 #else
39 #include "rcv.h"
41 #include <setjmp.h>
42 #include <termios.h>
43 #include <stdio.h>
45 static int verbose;
46 static int reset_tio;
47 static struct termios otio;
48 static sigjmp_buf nssjmp;
50 #include <stdarg.h>
52 #include <nss.h>
53 #include <ssl.h>
54 #include <prinit.h>
55 #include <prmem.h>
56 #include <pk11func.h>
57 #include <prtypes.h>
58 #include <prerror.h>
59 #include <secerr.h>
60 #include <smime.h>
61 #include <ciferfam.h>
62 #ifdef HAVE_XCONST_H
63 #include <xconst.h>
64 #endif
65 #ifdef HAVE_GENNAME_H
66 #include <genname.h>
67 #endif
68 #include <private/pprio.h>
70 #include "extern.h"
72 #ifndef HAVE_CERTAltNameEncodedContext
74 * NSS 3.11.5 neither installs genname.h nor provides this
75 * structure otherwise, so define it here.
77 typedef struct CERTAltNameEncodedContextStr {
78 SECItem **encodedGenName;
79 } CERTAltNameEncodedContext;
80 #endif /* !HAVE_CERTAltNameEncodedContext */
82 #include "nsserr.c"
84 static char *password_cb(PK11SlotInfo *slot, PRBool retry, void *arg);
85 static SECStatus bad_cert_cb(void *arg, PRFileDesc *fd);
86 static enum okay nss_check_host(const char *server, struct sock *sp);
87 static const char *bad_cert_str(void);
88 static enum okay nss_init(void);
89 static void nss_select_method(const char *uhp);
90 static CERTCertificate *get_signer_cert(char *addr);
91 static FILE *encode(FILE *ip, FILE **hp, FILE **bp, NSSCMSMessage *msg,
92 void (*cb)(void *, const char *, unsigned long));
93 static void decoder_cb(void *arg, const char *buf, unsigned long len);
94 static void base64_cb(void *arg, const char *buf, unsigned long len);
95 static int verify1(struct message *m, int n);
96 static struct message *getsig(struct message *m, int n, NSSCMSMessage **msg);
97 static enum okay getdig(struct message *m, int n, SECItem ***digests,
98 PLArenaPool **poolp, SECAlgorithmID **algids);
99 static void nsscatch(int s);
100 static void dumpcert(CERTCertificate *cert, FILE *op);
101 static enum okay getcipher(const char *to, SECOidTag *alg, int *key);
103 static char *
104 password_cb(PK11SlotInfo *slot, PRBool retry, void *arg)
106 sighandler_type saveint;
107 char *pass = NULL;
109 (void)&saveint;
110 (void)&pass;
111 saveint = safe_signal(SIGINT, SIG_IGN);
112 if (sigsetjmp(nssjmp, 1) == 0) {
113 if (saveint != SIG_IGN)
114 safe_signal(SIGINT, nsscatch);
115 pass = getpassword(&otio, &reset_tio, arg);
117 safe_signal(SIGINT, saveint);
118 if (pass == NULL)
119 return NULL;
120 return PL_strdup(pass);
123 static SECStatus
124 bad_cert_cb(void *arg, PRFileDesc *fd)
126 if (PORT_GetError() == SSL_ERROR_BAD_CERT_DOMAIN)
128 * We must not use this result. NSS verifies host names
129 * according to RFC 2818, but we must verify host names
130 * according to RFC 2595. The rules are different:
132 * - RFC 2818 says that if both a dNSName and a CN are
133 * contained in the peer certificate, only the dNSName
134 * is used. RFC 2595 encourages to use both.
136 * - RFC 2818 allows the wildcard '*' in any component
137 * of the host name. RFC 2595 allows it only as the
138 * "left-most name component".
140 * So ignore it and verify separately.
142 return SECSuccess;
143 fprintf(stderr, "Error in certificate: %s.\n", bad_cert_str());
144 return ssl_vrfy_decide() == OKAY ? SECSuccess : SECFailure;
148 * Host name checking according to RFC 2595.
150 static enum okay
151 nss_check_host(const char *server, struct sock *sp)
153 CERTCertificate *cert;
154 char *cn = NULL;
155 enum okay ok = STOP;
156 PRArenaPool *arena;
157 CERTGeneralName *gn;
158 SECItem altname;
159 CERTAltNameEncodedContext ec;
160 int i;
161 const SEC_ASN1Template gntempl[] = {
162 { SEC_ASN1_SEQUENCE_OF, 0, SEC_AnyTemplate }
165 if ((cert = SSL_PeerCertificate(sp->s_prfd)) == NULL) {
166 fprintf(stderr, "no certificate from \"%s\"\n", server);
167 return STOP;
169 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
170 if (CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME,
171 &altname) == SECSuccess &&
172 SEC_ASN1DecodeItem(arena, &ec, gntempl,
173 &altname) == SECSuccess &&
174 ec.encodedGenName != NULL) {
175 for (i = 0; ec.encodedGenName[i] != NULL; i++) {
176 gn = CERT_DecodeGeneralName(arena, ec.encodedGenName[i],
177 NULL);
178 if (gn->type == certDNSName) {
179 char *dn = ac_alloc(gn->name.other.len + 1);
180 memcpy(dn, gn->name.other.data,
181 gn->name.other.len);
182 dn[gn->name.other.len] = '\0';
183 if (verbose)
184 fprintf(stderr,
185 "Comparing DNS name: \"%s\"\n",
186 dn);
187 if (rfc2595_hostname_match(server, dn)
188 == OKAY) {
189 ac_free(dn);
190 goto out;
192 ac_free(dn);
196 if ((cn = CERT_GetCommonName(&cert->subject)) != NULL) {
197 if (verbose)
198 fprintf(stderr, "Comparing common name: \"%s\"\n", cn);
199 ok = rfc2595_hostname_match(server, cn);
201 if (ok == STOP)
202 fprintf(stderr, "host certificate does not match \"%s\"\n",
203 server);
204 out: if (cn)
205 PORT_Free(cn);
206 PORT_FreeArena(arena, PR_FALSE);
207 CERT_DestroyCertificate(cert);
208 return ok;
211 static const char *
212 bad_cert_str(void)
214 int ec;
216 ec = PORT_GetError();
217 return nss_strerror(ec);
220 static enum okay
221 nss_init(void)
223 static int initialized;
224 char *cp;
226 verbose = value("verbose") != NULL;
227 if (initialized == 0) {
228 if ((cp = value("nss-config-dir")) == NULL) {
229 fputs("Missing \"nss-config-dir\" variable.\n", stderr);
230 return STOP;
232 cp = file_expand(cp);
233 PR_Init(0, 0, 0);
234 PK11_SetPasswordFunc(password_cb);
235 if (NSS_Init(cp) == SECSuccess) {
236 NSS_SetDomesticPolicy();
237 initialized = 1;
238 return OKAY;
240 nss_gen_err("Error initializing NSS");
241 return STOP;
243 return OKAY;
246 static void
247 nss_select_method(const char *uhp)
249 char *cp;
250 enum {
251 SSL2 = 01,
252 SSL3 = 02,
253 TLS1 = 03
254 } methods;
256 methods = SSL2|SSL3|TLS1;
257 cp = ssl_method_string(uhp);
258 if (cp != NULL) {
259 if (strcmp(cp, "ssl2") == 0)
260 methods = SSL2;
261 else if (strcmp(cp, "ssl3") == 0)
262 methods = SSL3;
263 else if (strcmp(cp, "tls1") == 0)
264 methods = TLS1;
265 else {
266 fprintf(stderr, tr(244, "Invalid SSL method \"%s\"\n"),
267 cp);
270 if (value("ssl-v2-allow") == NULL)
271 methods &= ~SSL2;
272 SSL_OptionSetDefault(SSL_ENABLE_SSL2, methods&SSL2 ? PR_TRUE:PR_FALSE);
273 SSL_OptionSetDefault(SSL_ENABLE_SSL3, methods&SSL3 ? PR_TRUE:PR_FALSE);
274 SSL_OptionSetDefault(SSL_ENABLE_TLS, methods&TLS1 ? PR_TRUE:PR_FALSE);
277 enum okay
278 ssl_open(const char *server, struct sock *sp, const char *uhp)
280 PRFileDesc *fdp, *fdc;
282 if (nss_init() == STOP)
283 return STOP;
284 ssl_set_vrfy_level(uhp);
285 nss_select_method(uhp);
286 if ((fdp = PR_ImportTCPSocket(sp->s_fd)) == NULL) {
287 nss_gen_err("Error importing OS file descriptor");
288 return STOP;
290 if ((fdc = SSL_ImportFD(NULL, fdp)) == NULL) {
291 nss_gen_err("Error importing NSPR file descriptor");
292 PR_Close(fdp);
293 return STOP;
295 SSL_SetURL(fdc, server);
296 SSL_SetPKCS11PinArg(fdc, NULL);
297 SSL_BadCertHook(fdc, bad_cert_cb, NULL);
298 if (SSL_ResetHandshake(fdc, PR_FALSE) != SECSuccess) {
299 nss_gen_err("Cannot reset NSS handshake");
300 PR_Close(fdc);
301 return STOP;
303 if (SSL_ForceHandshake(fdc) != 0) {
304 nss_gen_err("SSL/TLS handshake failed");
305 PR_Close(fdc);
306 return STOP;
308 sp->s_prfd = fdc;
309 if (nss_check_host(server, sp) != OKAY && ssl_vrfy_decide() != OKAY) {
310 PR_Close(fdc);
311 sp->s_prfd = NULL;
312 return STOP;
314 sp->s_use_ssl = 1;
315 if (verbose) {
316 char *cipher, *issuer, *subject;
317 int keysize, secretkeysize;
319 if (SSL_SecurityStatus(fdc, NULL, &cipher,
320 &keysize, &secretkeysize,
321 &issuer, &subject) == SECSuccess) {
322 fprintf(stderr, "SSL parameters: cipher=%s, "
323 "keysize=%d, secretkeysize=%d,\n"
324 "issuer=%s\n"
325 "subject=%s\n",
326 cipher, keysize, secretkeysize,
327 issuer, subject);
328 PR_Free(cipher);
329 PR_Free(issuer);
330 PR_Free(subject);
331 } else
332 nss_gen_err("Could not read status information");
334 return OKAY;
337 void
338 nss_gen_err(const char *fmt, ...)
340 va_list ap;
341 char *text;
342 int len;
344 va_start(ap, fmt);
345 vfprintf(stderr, fmt, ap);
346 va_end(ap);
347 if ((len = PR_GetErrorTextLength()) > 0) {
348 text = ac_alloc(len);
349 if (PR_GetErrorText(text) > 0)
350 fprintf(stderr, ": %s\n", text);
351 ac_free(text);
352 } else
353 fprintf(stderr, ": %s.\n", nss_strerror(PR_GetError()));
356 FILE *
357 smime_sign(FILE *ip, struct header *headp)
359 NSSCMSMessage *msg;
360 NSSCMSContentInfo *content;
361 NSSCMSSignedData *data;
362 NSSCMSSignerInfo *info;
363 CERTCertificate *cert;
364 CERTCertDBHandle *handle;
365 FILE *hp, *bp, *sp;
366 char *addr;
368 if (nss_init() != OKAY)
369 return NULL;
370 if ((addr = myorigin(headp)) == NULL) {
371 fprintf(stderr, "No \"from\" address for signing specified\n");
372 return NULL;
374 if ((cert = get_signer_cert(addr)) == NULL)
375 return NULL;
376 handle = CERT_GetDefaultCertDB();
377 if ((msg = NSS_CMSMessage_Create(NULL)) == NULL) {
378 fprintf(stderr, "Cannot create CMS message.\n");
379 return NULL;
381 if ((data = NSS_CMSSignedData_Create(msg)) == NULL) {
382 fprintf(stderr, "Cannot create CMS signed data.\n");
383 return NULL;
385 content = NSS_CMSMessage_GetContentInfo(msg);
386 if (NSS_CMSContentInfo_SetContent_SignedData(msg, content, data)
387 != SECSuccess) {
388 fprintf(stderr, "Cannot attach CMS signed data.\n");
389 return NULL;
391 content = NSS_CMSSignedData_GetContentInfo(data);
392 if (NSS_CMSContentInfo_SetContent_Data(msg, content, NULL, PR_TRUE)
393 != SECSuccess) {
394 fprintf(stderr, "Cannot attach CMS data.\n");
395 return NULL;
397 if ((info = NSS_CMSSignerInfo_Create(msg, cert, SEC_OID_SHA1)) == 0) {
398 fprintf(stderr, "Cannot create signed information.\n");
399 return NULL;
401 if (NSS_CMSSignerInfo_IncludeCerts(info, NSSCMSCM_CertOnly,
402 certUsageEmailSigner) != SECSuccess) {
403 fprintf(stderr, "Cannot include certificate.\n");
404 return NULL;
406 if (NSS_CMSSignerInfo_AddSigningTime(info, PR_Now()) != SECSuccess) {
407 fprintf(stderr, "Cannot add signing time.\n");
408 return NULL;
410 if (NSS_CMSSignerInfo_AddSMIMECaps(info) != SECSuccess) {
411 fprintf(stderr, "Cannot add S/MIME capabilities.\n");
412 return NULL;
414 NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(info, cert, handle);
415 NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(info, cert, handle);
416 if (NSS_CMSSignedData_AddCertificate(data, cert) != SECSuccess) {
417 fprintf(stderr, "Cannot add encryption certificate.\n");
418 return NULL;
420 if (NSS_CMSSignedData_AddSignerInfo(data, info) != SECSuccess) {
421 fprintf(stderr, "Cannot add signer information.\n");
422 return NULL;
424 CERT_DestroyCertificate(cert);
425 if ((sp = encode(ip, &hp, &bp, msg, base64_cb)) == NULL) {
426 NSS_CMSMessage_Destroy(msg);
427 return NULL;
429 NSS_CMSMessage_Destroy(msg);
430 return smime_sign_assemble(hp, bp, sp);
434 cverify(void *vp)
436 int *msgvec = vp, *ip;
437 int ec = 0;
439 if (nss_init() != OKAY)
440 return 1;
441 ssl_vrfy_level = VRFY_STRICT;
442 for (ip = msgvec; *ip; ip++) {
443 setdot(&message[*ip-1]);
444 ec |= verify1(&message[*ip-1], *ip);
446 return ec;
449 FILE *
450 smime_encrypt(FILE *ip, const char *ignored, const char *to)
452 NSSCMSMessage *msg;
453 NSSCMSContentInfo *content;
454 NSSCMSEnvelopedData *data;
455 NSSCMSRecipientInfo *info;
456 CERTCertificate *cert[2];
457 CERTCertDBHandle *handle;
458 SECOidTag tag;
459 FILE *hp, *pp, *yp;
460 int keysize;
461 char *nickname, *vn;
462 int vs;
464 if (nss_init() != OKAY)
465 return NULL;
466 handle = CERT_GetDefaultCertDB();
467 vn = ac_alloc(vs = strlen(to) + 30);
468 snprintf(vn, vs, "smime-nickname-%s", to);
469 nickname = value(vn);
470 ac_free(vn);
471 if ((cert[0] = CERT_FindCertByNicknameOrEmailAddr(handle,
472 nickname ? nickname : (char *)to)) == NULL) {
473 if (nickname)
474 fprintf(stderr, "Cannot find certificate \"%s\".\n",
475 nickname);
476 else
477 fprintf(stderr, "Cannot find certificate for <%s>.\n",
478 to);
479 return NULL;
481 cert[1] = NULL;
482 if (getcipher(to, &tag, &keysize) != OKAY)
483 return NULL;
484 if ((msg = NSS_CMSMessage_Create(NULL)) == NULL) {
485 fprintf(stderr, "Cannot create CMS message.\n");
486 return NULL;
488 if ((data = NSS_CMSEnvelopedData_Create(msg, tag, keysize)) == NULL) {
489 fprintf(stderr, "Cannot create enveloped data.\n");
490 return NULL;
492 content = NSS_CMSMessage_GetContentInfo(msg);
493 if (NSS_CMSContentInfo_SetContent_EnvelopedData(msg, content, data)
494 != SECSuccess) {
495 fprintf(stderr, "Cannot attach enveloped data.\n");
496 return NULL;
498 content = NSS_CMSEnvelopedData_GetContentInfo(data);
499 if (NSS_CMSContentInfo_SetContent_Data(msg, content, NULL, PR_FALSE)
500 != SECSuccess) {
501 fprintf(stderr, "Cannot attach CMS data.\n");
502 return NULL;
504 if ((info = NSS_CMSRecipientInfo_Create(msg, cert[0])) == NULL) {
505 fprintf(stderr, "Cannot create CMS recipient information.\n");
506 return NULL;
508 if (NSS_CMSEnvelopedData_AddRecipient(data, info) != SECSuccess) {
509 fprintf(stderr, "Cannot add CMS recipient information.\n");
510 return NULL;
512 CERT_DestroyCertificate(cert[0]);
513 if ((yp = encode(ip, &hp, &pp, msg, base64_cb)) == NULL)
514 return NULL;
515 NSS_CMSMessage_Destroy(msg);
516 return smime_encrypt_assemble(hp, yp);
519 struct message *
520 smime_decrypt(struct message *m, const char *to, const char *cc, int signcall)
522 NSSCMSDecoderContext *ctx;
523 NSSCMSMessage *msg;
524 FILE *op, *hp, *bp;
525 char *buf = NULL;
526 size_t bufsize = 0, buflen, count;
527 char *cp;
528 struct str in, out;
529 FILE *yp;
530 long size;
531 int i, nlevels;
532 int binary = 0;
534 if ((yp = setinput(&mb, m, NEED_BODY)) == NULL)
535 return NULL;
536 if (nss_init() != OKAY)
537 return NULL;
538 if ((op = Ftemp(&cp, "Rp", "w+", 0600, 1)) == NULL) {
539 perror("tempfile");
540 return NULL;
542 rm(cp);
543 Ftfree(&cp);
544 if ((ctx = NSS_CMSDecoder_Start(NULL,
545 decoder_cb, op,
546 password_cb, "Pass phrase:",
547 NULL, NULL)) == NULL) {
548 fprintf(stderr, "Cannot start decoder.\n");
549 return NULL;
551 size = m->m_size;
552 if ((smime_split(yp, &hp, &bp, size, 1)) == STOP)
553 return NULL;
554 count = fsize(bp);
555 while (fgetline(&buf, &bufsize, &count, &buflen, bp, 0) != NULL) {
556 if (buf[0] == '\n')
557 break;
558 if ((cp = thisfield(buf, "content-transfer-encoding")) != NULL)
559 if (ascncasecmp(cp, "binary", 7) == 0)
560 binary = 1;
562 while (fgetline(&buf, &bufsize, &count, &buflen, bp, 0) != NULL) {
563 if (binary)
564 NSS_CMSDecoder_Update(ctx, buf, buflen);
565 else {
566 in.s = buf;
567 in.l = buflen;
568 mime_fromb64_b(&in, &out, 0, bp);
569 NSS_CMSDecoder_Update(ctx, out.s, out.l);
570 free(out.s);
573 free(buf);
574 if ((msg = NSS_CMSDecoder_Finish(ctx)) == NULL) {
575 fprintf(stderr, "Failed to decode message.\n");
576 Fclose(hp);
577 Fclose(bp);
578 return NULL;
580 nlevels = NSS_CMSMessage_ContentLevelCount(msg);
581 for (i = 0; i < nlevels; i++) {
582 NSSCMSContentInfo *content;
583 SECOidTag tag;
585 content = NSS_CMSMessage_ContentLevel(msg, i);
586 tag = NSS_CMSContentInfo_GetContentTypeTag(content);
587 if (tag == SEC_OID_PKCS7_DATA) {
588 const char *fld = "X-Encryption-Cipher";
589 SECOidTag alg;
590 int keysize;
592 alg = NSS_CMSContentInfo_GetContentEncAlgTag(content);
593 keysize = NSS_CMSContentInfo_GetBulkKeySize(content);
594 fseek(hp, 0L, SEEK_END);
595 switch (alg) {
596 case 0:
597 if (signcall) {
598 NSS_CMSMessage_Destroy(msg);
599 Fclose(hp);
600 Fclose(bp);
601 setinput(&mb, m, NEED_BODY);
602 return (struct message *)-1;
604 fprintf(hp, "%s: none\n", fld);
605 break;
606 case SEC_OID_RC2_CBC:
607 fprintf(hp, "%s: RC2, %d bits\n", fld, keysize);
608 break;
609 case SEC_OID_DES_CBC:
610 fprintf(hp, "%s: DES, 56 bits\n", fld);
611 break;
612 case SEC_OID_DES_EDE3_CBC:
613 fprintf(hp, "%s: 3DES, 112/168 bits\n", fld);
614 break;
615 case SEC_OID_FORTEZZA_SKIPJACK:
616 fprintf(hp, "%s: Fortezza\n", fld);
617 break;
618 default:
619 fprintf(hp, "%s: unknown type %lu\n", fld,
620 (unsigned long)alg);
622 fflush(hp);
623 rewind(hp);
626 NSS_CMSMessage_Destroy(msg);
627 fflush(op);
628 rewind(op);
629 Fclose(bp);
630 return smime_decrypt_assemble(m, hp, op);
633 static CERTCertificate *
634 get_signer_cert(char *addr)
636 CERTCertDBHandle *handle;
637 CERTCertList *list;
638 CERTCertListNode *node;
639 CERTCertificate *cert = NULL;
640 const char *cp;
641 char *nick;
642 char *vn;
643 int vs, found = 0;
645 addr = skin(addr);
646 vn = ac_alloc(vs = strlen(addr) + 30);
647 snprintf(vn, vs, "smime-sign-nickname-%s", addr);
648 if ((nick = value(vn)) == NULL)
649 nick = value("smime-sign-nickname");
650 ac_free(vn);
651 handle = CERT_GetDefaultCertDB();
652 if (nick) {
653 cert = CERT_FindCertByNickname(handle, nick);
654 if (cert == NULL)
655 fprintf(stderr, "No certificate \"%s\" found.\n", nick);
656 return cert;
658 if ((list = CERT_FindUserCertsByUsage(handle, certUsageEmailSigner,
659 PR_TRUE, PR_TRUE, NULL)) == NULL) {
660 fprintf(stderr, "Cannot find any certificates for signing.\n");
661 return NULL;
663 for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list);
664 node = CERT_LIST_NEXT(node)) {
665 if ((cp = CERT_GetCertEmailAddress(&node->cert->subject))
666 != NULL && asccasecmp(cp, addr) == 0) {
667 cert = node->cert;
668 found++;
671 if (cert == NULL) {
672 for (node = CERT_LIST_HEAD(list);
673 !CERT_LIST_END(node, list) && cert == NULL;
674 node = CERT_LIST_NEXT(node)) {
675 cp = CERT_GetFirstEmailAddress(node->cert);
676 while (cp) {
677 if (asccasecmp(cp, addr) == 0) {
678 cert = node->cert;
679 found++;
681 cp = CERT_GetNextEmailAddress(node->cert, cp);
685 if (found > 1) {
686 fprintf(stderr,
687 "More than one signing certificate found for <%s>.\n"
688 "Use the smime-sign-nickname variable.\n", addr);
689 return NULL;
691 if (cert == NULL)
692 fprintf(stderr,
693 "Cannot find a signing certificate for <%s>.\n",
694 addr);
695 return cert;
698 static FILE *
699 encode(FILE *ip, FILE **hp, FILE **bp, NSSCMSMessage *msg,
700 void (*cb)(void *, const char *, unsigned long))
702 NSSCMSEncoderContext *ctx;
703 char *buf = NULL, *cp;
704 size_t bufsize = 0, buflen, count;
705 FILE *op;
707 if (smime_split(ip, hp, bp, -1, 0) == STOP)
708 return NULL;
709 if ((op = Ftemp(&cp, "Ry", "w+", 0600, 1)) == NULL) {
710 perror("tempfile");
711 return NULL;
713 rm(cp);
714 Ftfree(&cp);
715 if ((ctx = NSS_CMSEncoder_Start(msg,
716 cb, op,
717 NULL, NULL,
718 password_cb, "Pass phrase:",
719 NULL, NULL,
720 NULL, NULL)) == NULL) {
721 fprintf(stderr, "Cannot create encoder context.\n");
722 Fclose(op);
723 return NULL;
725 count = fsize(*bp);
726 while (fgetline(&buf, &bufsize, &count, &buflen, *bp, 0) != NULL) {
727 buf[buflen-1] = '\r';
728 buf[buflen] = '\n';
729 if (NSS_CMSEncoder_Update(ctx, buf, buflen+1) != 0) {
730 fprintf(stderr, "Failed to add data to encoder.\n");
731 Fclose(op);
732 return NULL;
735 free(buf);
736 if (NSS_CMSEncoder_Finish(ctx) != 0) {
737 fprintf(stderr, "Failed to encode data.\n");
738 Fclose(op);
739 return NULL;
741 rewind(*bp);
742 cb(op, (void *)-1, 0);
743 fflush(op);
744 if (ferror(op)) {
745 perror("tempfile");
746 Fclose(op);
747 return NULL;
749 rewind(op);
750 return op;
753 static void
754 decoder_cb(void *arg, const char *buf, unsigned long len)
756 if (arg && buf)
757 fwrite(buf, 1, len, arg);
760 static void
761 base64_cb(void *arg, const char *buf, unsigned long len)
763 static char back[972];
764 static int fill;
765 unsigned long pos;
767 if (arg && buf && buf != (void *)-1) {
768 pos = 0;
769 while (len - pos >= sizeof back - fill) {
770 memcpy(&back[fill], &buf[pos], sizeof back - fill);
771 mime_write(back, sizeof back, arg,
772 CONV_TOB64, TD_NONE, NULL, 0,
773 NULL, NULL);
774 pos += sizeof back - fill;
775 fill = 0;
777 memcpy(&back[fill], &buf[pos], len - pos);
778 fill += len - pos;
779 } else if (buf == (void *)-1) {
780 mime_write(back, fill, arg,
781 CONV_TOB64, TD_NONE, NULL, 0,
782 NULL, NULL);
783 fill = 0;
787 static int
788 verify1(struct message *m, int n)
790 SECItem **digests;
791 NSSCMSMessage *msg;
792 PLArenaPool *poolp;
793 SECAlgorithmID **algids;
794 CERTCertDBHandle *handle;
795 int nlevels, i;
796 int status = 0;
797 int foundsender = 0;
798 char *sender;
800 if ((m = getsig(m, n, &msg)) == NULL)
801 return 1;
802 sender = getsender(m);
803 handle = CERT_GetDefaultCertDB();
804 nlevels = NSS_CMSMessage_ContentLevelCount(msg);
805 for (i = 0; i < nlevels; i++) {
806 NSSCMSContentInfo *content;
807 SECOidTag tag;
809 content = NSS_CMSMessage_ContentLevel(msg, i);
810 tag = NSS_CMSContentInfo_GetContentTypeTag(content);
811 if (tag == SEC_OID_PKCS7_SIGNED_DATA) {
812 NSSCMSSignedData *data;
813 int nsigners, j;
815 if ((data = NSS_CMSContentInfo_GetContent(content))
816 == NULL) {
817 fprintf(stderr, "Signed data missing for "
818 "message %d.\n", n);
819 status = -1;
820 break;
822 if (!NSS_CMSSignedData_HasDigests(data)) {
823 algids = NSS_CMSSignedData_GetDigestAlgs(data);
824 if (getdig(m, n, &digests, &poolp, algids)
825 != OKAY) {
826 status = -1;
827 break;
829 if (NSS_CMSSignedData_SetDigests(data, algids,
830 digests)
831 != SECSuccess) {
832 fprintf(stderr, "Cannot set digests "
833 "for message %d.\n", n);
834 status = -1;
835 break;
837 PORT_FreeArena(poolp, PR_FALSE);
839 if (NSS_CMSSignedData_ImportCerts(data, handle,
840 certUsageEmailSigner,
841 PR_FALSE) != SECSuccess) {
842 fprintf(stderr, "Cannot temporarily import "
843 "certificates for "
844 "message %d.\n", n);
845 status = -1;
846 break;
848 nsigners = NSS_CMSSignedData_SignerInfoCount(data);
849 if (nsigners == 0) {
850 fprintf(stderr, "Message %d has no signers.\n",
852 status = -1;
853 break;
855 if (!NSS_CMSSignedData_HasDigests(data)) {
856 fprintf(stderr, "Message %d has no digests.\n",
858 status = -1;
859 break;
861 for (j = 0; j < nsigners; j++) {
862 const char *svs;
863 NSSCMSSignerInfo *info;
864 NSSCMSVerificationStatus vs;
865 SECStatus bad;
866 CERTCertificate *cert;
867 const char *addr;
868 int passed = 0;
870 info = NSS_CMSSignedData_GetSignerInfo(data, j);
871 cert = NSS_CMSSignerInfo_GetSigningCertificate
872 (info, handle);
873 bad = NSS_CMSSignedData_VerifySignerInfo(data,
874 j, handle,
875 certUsageEmailSigner);
876 vs = NSS_CMSSignerInfo_GetVerificationStatus
877 (info);
878 svs = NSS_CMSUtil_VerificationStatusToString
879 (vs);
880 addr = CERT_GetCertEmailAddress(&cert->subject);
881 if (sender != NULL && addr != NULL &&
882 asccasecmp(sender, addr) == 0)
883 foundsender++;
884 else {
885 addr = CERT_GetFirstEmailAddress(cert);
886 while (sender && addr) {
887 if (!asccasecmp(sender, addr)) {
888 foundsender++;
889 break;
891 addr = CERT_GetNextEmailAddress
892 (cert, addr);
895 if (CERT_VerifyCertNow(handle,
896 cert, PR_TRUE,
897 certUsageEmailSigner,
898 NULL) != SECSuccess)
899 fprintf(stderr, "Bad certificate for "
900 "signer <%s> of "
901 "message %d: %s.\n",
902 addr ? addr : "?", n,
903 bad_cert_str());
904 else
905 passed++;
906 if (bad)
907 fprintf(stderr, "Bad status for "
908 "signer <%s> of "
909 "message %d: %s.\n",
910 addr ? addr : "?",
911 n, svs);
912 else
913 passed++;
914 if (passed < 2)
915 status = -1;
916 else if (status == 0)
917 status = 1;
921 if (foundsender == 0) {
922 if (sender) {
923 fprintf(stderr, "Signers of message "
924 "%d do not include the sender <%s>\n",
925 n, sender);
926 status = -1;
927 } else
928 fprintf(stderr, "Warning: Message %d has no From: "
929 "header field.\n", n);
930 } else if (status == 1)
931 printf("Message %d was verified successfully.\n", n);
932 if (status == 0)
933 fprintf(stderr, "No verification information found in "
934 "message %d.\n", n);
935 NSS_CMSMessage_Destroy(msg);
936 return status != 1;
939 static struct message *
940 getsig(struct message *m, int n, NSSCMSMessage **msg)
942 struct message *x;
943 char *ct, *pt, *boundary = NULL, *cte;
944 char *buf = NULL;
945 size_t bufsize = 0, buflen, count, boundlen = -1;
946 int part;
947 FILE *fp;
948 NSSCMSDecoderContext *decctx;
949 struct str in, out;
950 char *to, *cc;
951 int inhdr, binary;
952 int detached = 1;
954 loop: if ((ct = hfield1("content-type", m)) == NULL)
955 goto not;
956 if (strncmp(ct, "application/x-pkcs7-mime", 24) == 0 ||
957 strncmp(ct, "application/pkcs7-mime", 22) == 0) {
958 to = hfield1("to", m);
959 cc = hfield1("cc", m);
960 if ((x = smime_decrypt(m, to, cc, 1)) == NULL)
961 return NULL;
962 if (x != (struct message *)-1) {
963 m = x;
964 goto loop;
966 detached = 0;
967 } else if (strncmp(ct, "multipart/signed", 16) ||
968 (pt = mime_getparam("protocol", ct)) == NULL ||
969 strcmp(pt, "application/x-pkcs7-signature") &&
970 strcmp(pt, "application/pkcs7-signature") ||
971 (boundary = mime_getboundary(ct)) == NULL) {
972 not: fprintf(stderr,
973 "Message %d is not an S/MIME signed message.\n", n);
974 return NULL;
975 } else
976 boundlen = strlen(boundary);
977 if ((decctx = NSS_CMSDecoder_Start(NULL, NULL, NULL,
978 password_cb, "Pass phrase:",
979 NULL, NULL)) == NULL) {
980 fprintf(stderr, "Cannot start decoder.\n");
981 return NULL;
983 if ((fp = setinput(&mb, m, NEED_BODY)) == NULL) {
984 return NULL;
986 count = m->m_size;
987 part = 0;
988 inhdr = 1;
989 binary = 0;
990 while (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) != NULL) {
991 if (detached && boundary && buflen >= boundlen + 1 &&
992 strncmp(buf, boundary, boundlen) == 0) {
993 if (buf[boundlen] == '\n') {
994 part++;
995 inhdr = 1;
996 binary = 0;
997 if (part >= 3) {
998 fprintf(stderr, "Message %d has too "
999 "many parts.\n", n);
1000 free(buf);
1001 return NULL;
1003 continue;
1005 if (buf[boundlen] == '-' && buf[boundlen+1] == '-' &&
1006 buf[boundlen+2] == '\n')
1007 break;
1008 } else if (buf[0] == '\n') {
1009 inhdr = 0;
1010 continue;
1012 if ((!detached || part == 2) && inhdr == 0) {
1013 if (binary)
1014 NSS_CMSDecoder_Update(decctx, buf, buflen);
1015 else {
1016 in.s = buf;
1017 in.l = buflen;
1018 mime_fromb64_b(&in, &out, 0, fp);
1019 NSS_CMSDecoder_Update(decctx, out.s, out.l);
1020 free(out.s);
1023 if (buflen == 1 && buf[0] == '\n')
1024 inhdr = 0;
1025 if (inhdr && (cte = thisfield(buf, "content-transfer-encoding"))
1026 != NULL && ascncasecmp(cte, "binary", 7) == 0)
1027 binary = 1;
1029 free(buf);
1030 if ((*msg = NSS_CMSDecoder_Finish(decctx)) == NULL) {
1031 fprintf(stderr, "Failed to decode signature for message %d.\n",
1033 return NULL;
1035 return m;
1038 static enum okay
1039 getdig(struct message *m, int n, SECItem ***digests,
1040 PLArenaPool **poolp, SECAlgorithmID **algids)
1042 char *ct, *pt, *boundary;
1043 char *buf = NULL;
1044 size_t bufsize = 0, buflen, count, boundlen;
1045 int part;
1046 int nl;
1047 FILE *fp;
1048 NSSCMSDigestContext *digctx;
1050 *poolp = PORT_NewArena(1024);
1051 if ((ct = hfield1("content-type", m)) == NULL ||
1052 strncmp(ct, "multipart/signed", 16) ||
1053 (pt = mime_getparam("protocol", ct)) == NULL ||
1054 strcmp(pt, "application/x-pkcs7-signature") &&
1055 strcmp(pt, "application/pkcs7-signature") ||
1056 (boundary = mime_getboundary(ct)) == NULL) {
1057 fprintf(stderr,
1058 "Message %d is not an S/MIME signed message.\n", n);
1059 return STOP;
1061 boundlen = strlen(boundary);
1062 if ((digctx = NSS_CMSDigestContext_StartMultiple(algids)) == NULL) {
1063 fprintf(stderr, "Cannot start digest computation.\n");
1064 return STOP;
1066 if ((fp = setinput(&mb, m, NEED_BODY)) == NULL) {
1067 return STOP;
1069 count = m->m_size;
1070 part = 0;
1071 nl = 0;
1072 while (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) != NULL) {
1073 if (buflen >= boundlen + 1 &&
1074 strncmp(buf, boundary, boundlen) == 0) {
1075 if (buf[boundlen] == '\n') {
1076 if (++part >= 2)
1077 break;
1078 continue;
1080 if (buf[boundlen] == '-' && buf[boundlen+1] == '-' &&
1081 buf[boundlen+2] == '\n')
1082 break;
1084 if (part == 1) {
1085 if (nl) {
1086 NSS_CMSDigestContext_Update(digctx,
1087 (unsigned char *)"\r\n", 2);
1088 nl = 0;
1090 if (buf[buflen-1] == '\n') {
1091 nl = 1;
1092 buflen--;
1094 NSS_CMSDigestContext_Update(digctx,
1095 (unsigned char *)buf, buflen);
1096 continue;
1099 free(buf);
1100 if (NSS_CMSDigestContext_FinishMultiple(digctx,
1101 *poolp, digests) != SECSuccess) {
1102 fprintf(stderr, "Error creating digest for message %d\n", n);
1103 return STOP;
1105 return OKAY;
1108 static void
1109 nsscatch(int s)
1111 if (reset_tio)
1112 tcsetattr(0, TCSADRAIN, &otio);
1113 siglongjmp(nssjmp, s);
1116 enum okay
1117 smime_certsave(struct message *m, int n, FILE *op)
1119 NSSCMSMessage *msg;
1120 CERTCertDBHandle *handle;
1121 int nlevels, i, cnt = 0;
1122 enum okay ok = OKAY;
1124 if (nss_init() == STOP)
1125 return STOP;
1126 if ((m = getsig(m, n, &msg)) == NULL)
1127 return 1;
1128 handle = CERT_GetDefaultCertDB();
1129 nlevels = NSS_CMSMessage_ContentLevelCount(msg);
1130 for (i = 0; i < nlevels; i++) {
1131 NSSCMSContentInfo *content;
1132 SECOidTag tag;
1134 content = NSS_CMSMessage_ContentLevel(msg, i);
1135 tag = NSS_CMSContentInfo_GetContentTypeTag(content);
1136 if (tag == SEC_OID_PKCS7_SIGNED_DATA) {
1137 NSSCMSSignedData *data;
1138 int nsigners, j;
1140 if ((data = NSS_CMSContentInfo_GetContent(content))
1141 == NULL) {
1142 fprintf(stderr, "Signed data missing for "
1143 "message %d.\n", n);
1144 ok = STOP;
1145 break;
1147 if (NSS_CMSSignedData_ImportCerts(data, handle,
1148 certUsageEmailSigner,
1149 PR_FALSE) != SECSuccess) {
1150 fprintf(stderr, "Cannot temporarily import "
1151 "certificates for "
1152 "message %d.\n", n);
1153 ok = STOP;
1154 break;
1156 nsigners = NSS_CMSSignedData_SignerInfoCount(data);
1157 if (nsigners == 0) {
1158 fprintf(stderr, "Message %d has no signers.\n",
1160 ok = STOP;
1161 break;
1163 for (j = 0; j < nsigners; j++) {
1164 NSSCMSSignerInfo *info;
1165 CERTCertificateList *list;
1166 CERTCertificate *cert;
1167 int k;
1169 info = NSS_CMSSignedData_GetSignerInfo(data, j);
1170 list = NSS_CMSSignerInfo_GetCertList(info);
1171 if (list) {
1172 for (k = 0; k < list->len; k++) {
1173 cert = (CERTCertificate *)
1174 &list->certs[k];
1175 dumpcert(cert, op);
1176 cnt++;
1179 cert = NSS_CMSSignerInfo_GetSigningCertificate
1180 (info, handle);
1181 if (cert) {
1182 dumpcert(cert, op);
1183 cnt++;
1188 NSS_CMSMessage_Destroy(msg);
1189 if (cnt == 0) {
1190 fprintf(stderr, "No certificates found in message %d.\n", n);
1191 ok = STOP;
1193 return ok;
1196 static void
1197 dumpcert(CERTCertificate *cert, FILE *op)
1199 fprintf(op, "subject=%s\n", cert->subjectName);
1200 fprintf(op, "issuer=%s\n", cert->issuerName);
1201 fputs("-----BEGIN CERTIFICATE-----\n", op);
1202 mime_write(cert->derCert.data,
1203 cert->derCert.len, op,
1204 CONV_TOB64, TD_NONE, NULL, 0,
1205 NULL, NULL);
1206 fputs("-----END CERTIFICATE-----\n", op);
1209 static enum okay
1210 getcipher(const char *to, SECOidTag *alg, int *key)
1212 char *vn, *cp;
1213 int vs;
1215 *key = 0;
1216 *alg = SEC_OID_DES_EDE3_CBC;
1217 vn = ac_alloc(vs = strlen(to) + 30);
1218 snprintf(vn, vs, "smime-cipher-%s", to);
1219 if ((cp = value(vn)) != NULL) {
1220 if (strcmp(cp, "rc2-40") == 0) {
1221 *alg = SEC_OID_RC2_CBC;
1222 *key = 40;
1223 } else if (strcmp(cp, "rc2-64") == 0) {
1224 *alg = SEC_OID_RC2_CBC;
1225 *key = 64;
1226 } else if (strcmp(cp, "rc2-128") == 0) {
1227 *alg = SEC_OID_RC2_CBC;
1228 *key = 128;
1229 } else if (strcmp(cp, "des") == 0)
1230 *alg = SEC_OID_DES_CBC;
1231 else if (strcmp(cp, "fortezza") == 0)
1232 *alg = SEC_OID_FORTEZZA_SKIPJACK;
1233 else if (strcmp(cp, "des-ede3") == 0)
1234 /*EMPTY*/;
1235 else {
1236 fprintf(stderr, "Invalid cipher \"%s\".\n", cp);
1237 return STOP;
1240 ac_free(vn);
1241 return OKAY;
1243 #endif /* USE_NSS */