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.
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
36 typedef int avoid_empty_file_compiler_warning
;
47 static struct termios otio
;
48 static sigjmp_buf nssjmp
;
68 #include <private/pprio.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 */
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
);
104 password_cb(PK11SlotInfo
*slot
, PRBool retry
, void *arg
)
106 sighandler_type saveint
;
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
);
120 return PL_strdup(pass
);
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.
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.
151 nss_check_host(const char *server
, struct sock
*sp
)
153 CERTCertificate
*cert
;
159 CERTAltNameEncodedContext ec
;
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
);
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
],
178 if (gn
->type
== certDNSName
) {
179 char *dn
= ac_alloc(gn
->name
.other
.len
+ 1);
180 memcpy(dn
, gn
->name
.other
.data
,
182 dn
[gn
->name
.other
.len
] = '\0';
185 "Comparing DNS name: \"%s\"\n",
187 if (rfc2595_hostname_match(server
, dn
)
196 if ((cn
= CERT_GetCommonName(&cert
->subject
)) != NULL
) {
198 fprintf(stderr
, "Comparing common name: \"%s\"\n", cn
);
199 ok
= rfc2595_hostname_match(server
, cn
);
202 fprintf(stderr
, "host certificate does not match \"%s\"\n",
206 PORT_FreeArena(arena
, PR_FALSE
);
207 CERT_DestroyCertificate(cert
);
216 ec
= PORT_GetError();
217 return nss_strerror(ec
);
223 static int initialized
;
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
);
234 PK11_SetPasswordFunc(password_cb
);
235 if (NSS_Init(cp
) == SECSuccess
) {
236 NSS_SetDomesticPolicy();
240 nss_gen_err("Error initializing NSS");
247 nss_select_method(const char *uhp
)
256 methods
= SSL2
|SSL3
|TLS1
;
257 cp
= ssl_method_string(uhp
);
259 if (equal(cp
, "ssl2"))
261 else if (equal(cp
, "ssl3"))
263 else if (equal(cp
, "tls1"))
266 fprintf(stderr
, catgets(catd
, CATSET
, 244,
267 "Invalid SSL method \"%s\"\n"), cp
);
270 if (value("ssl-v2-allow") == NULL
)
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
);
278 ssl_open(const char *server
, struct sock
*sp
, const char *uhp
)
280 PRFileDesc
*fdp
, *fdc
;
282 if (nss_init() == 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");
290 if ((fdc
= SSL_ImportFD(NULL
, fdp
)) == NULL
) {
291 nss_gen_err("Error importing NSPR file descriptor");
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");
303 if (SSL_ForceHandshake(fdc
) != 0) {
304 nss_gen_err("SSL/TLS handshake failed");
309 if (nss_check_host(server
, sp
) != OKAY
&& ssl_vrfy_decide() != OKAY
) {
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"
326 cipher
, keysize
, secretkeysize
,
332 nss_gen_err("Could not read status information");
338 nss_gen_err(const char *fmt
, ...)
345 vfprintf(stderr
, fmt
, ap
);
347 if ((len
= PR_GetErrorTextLength()) > 0) {
348 text
= ac_alloc(len
);
349 if (PR_GetErrorText(text
) > 0)
350 fprintf(stderr
, ": %s\n", text
);
353 fprintf(stderr
, ": %s.\n", nss_strerror(PR_GetError()));
357 smime_sign(FILE *ip
, struct header
*headp
)
360 NSSCMSContentInfo
*content
;
361 NSSCMSSignedData
*data
;
362 NSSCMSSignerInfo
*info
;
363 CERTCertificate
*cert
;
364 CERTCertDBHandle
*handle
;
368 if (nss_init() != OKAY
)
370 if ((addr
= myorigin(headp
)) == NULL
) {
371 fprintf(stderr
, "No \"from\" address for signing specified\n");
374 if ((cert
= get_signer_cert(addr
)) == NULL
)
376 handle
= CERT_GetDefaultCertDB();
377 if ((msg
= NSS_CMSMessage_Create(NULL
)) == NULL
) {
378 fprintf(stderr
, "Cannot create CMS message.\n");
381 if ((data
= NSS_CMSSignedData_Create(msg
)) == NULL
) {
382 fprintf(stderr
, "Cannot create CMS signed data.\n");
385 content
= NSS_CMSMessage_GetContentInfo(msg
);
386 if (NSS_CMSContentInfo_SetContent_SignedData(msg
, content
, data
)
388 fprintf(stderr
, "Cannot attach CMS signed data.\n");
391 content
= NSS_CMSSignedData_GetContentInfo(data
);
392 if (NSS_CMSContentInfo_SetContent_Data(msg
, content
, NULL
, PR_TRUE
)
394 fprintf(stderr
, "Cannot attach CMS data.\n");
397 if ((info
= NSS_CMSSignerInfo_Create(msg
, cert
, SEC_OID_SHA1
)) == 0) {
398 fprintf(stderr
, "Cannot create signed information.\n");
401 if (NSS_CMSSignerInfo_IncludeCerts(info
, NSSCMSCM_CertOnly
,
402 certUsageEmailSigner
) != SECSuccess
) {
403 fprintf(stderr
, "Cannot include certificate.\n");
406 if (NSS_CMSSignerInfo_AddSigningTime(info
, PR_Now()) != SECSuccess
) {
407 fprintf(stderr
, "Cannot add signing time.\n");
410 if (NSS_CMSSignerInfo_AddSMIMECaps(info
) != SECSuccess
) {
411 fprintf(stderr
, "Cannot add S/MIME capabilities.\n");
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");
420 if (NSS_CMSSignedData_AddSignerInfo(data
, info
) != SECSuccess
) {
421 fprintf(stderr
, "Cannot add signer information.\n");
424 CERT_DestroyCertificate(cert
);
425 if ((sp
= encode(ip
, &hp
, &bp
, msg
, base64_cb
)) == NULL
) {
426 NSS_CMSMessage_Destroy(msg
);
429 NSS_CMSMessage_Destroy(msg
);
430 return smime_sign_assemble(hp
, bp
, sp
);
436 int *msgvec
= vp
, *ip
;
439 if (nss_init() != OKAY
)
441 ssl_vrfy_level
= VRFY_STRICT
;
442 for (ip
= msgvec
; *ip
; ip
++) {
443 setdot(&message
[*ip
-1]);
444 ec
|= verify1(&message
[*ip
-1], *ip
);
450 smime_encrypt(FILE *ip
, const char *ignored
, const char *to
)
453 NSSCMSContentInfo
*content
;
454 NSSCMSEnvelopedData
*data
;
455 NSSCMSRecipientInfo
*info
;
456 CERTCertificate
*cert
[2];
457 CERTCertDBHandle
*handle
;
464 if (nss_init() != OKAY
)
466 handle
= CERT_GetDefaultCertDB();
467 vn
= ac_alloc(vs
= strlen(to
) + 30);
468 snprintf(vn
, vs
, "smime-nickname-%s", to
);
469 nickname
= value(vn
);
471 if ((cert
[0] = CERT_FindCertByNicknameOrEmailAddr(handle
,
472 nickname
? nickname
: (char *)to
)) == NULL
) {
474 fprintf(stderr
, "Cannot find certificate \"%s\".\n",
477 fprintf(stderr
, "Cannot find certificate for <%s>.\n",
482 if (getcipher(to
, &tag
, &keysize
) != OKAY
)
484 if ((msg
= NSS_CMSMessage_Create(NULL
)) == NULL
) {
485 fprintf(stderr
, "Cannot create CMS message.\n");
488 if ((data
= NSS_CMSEnvelopedData_Create(msg
, tag
, keysize
)) == NULL
) {
489 fprintf(stderr
, "Cannot create enveloped data.\n");
492 content
= NSS_CMSMessage_GetContentInfo(msg
);
493 if (NSS_CMSContentInfo_SetContent_EnvelopedData(msg
, content
, data
)
495 fprintf(stderr
, "Cannot attach enveloped data.\n");
498 content
= NSS_CMSEnvelopedData_GetContentInfo(data
);
499 if (NSS_CMSContentInfo_SetContent_Data(msg
, content
, NULL
, PR_FALSE
)
501 fprintf(stderr
, "Cannot attach CMS data.\n");
504 if ((info
= NSS_CMSRecipientInfo_Create(msg
, cert
[0])) == NULL
) {
505 fprintf(stderr
, "Cannot create CMS recipient information.\n");
508 if (NSS_CMSEnvelopedData_AddRecipient(data
, info
) != SECSuccess
) {
509 fprintf(stderr
, "Cannot add CMS recipient information.\n");
512 CERT_DestroyCertificate(cert
[0]);
513 if ((yp
= encode(ip
, &hp
, &pp
, msg
, base64_cb
)) == NULL
)
515 NSS_CMSMessage_Destroy(msg
);
516 return smime_encrypt_assemble(hp
, yp
);
520 smime_decrypt(struct message
*m
, const char *to
, const char *cc
, int signcall
)
522 NSSCMSDecoderContext
*ctx
;
526 size_t bufsize
= 0, buflen
, count
;
534 if ((yp
= setinput(&mb
, m
, NEED_BODY
)) == NULL
)
536 if (nss_init() != OKAY
)
538 if ((op
= Ftemp(&cp
, "Rp", "w+", 0600, 1)) == NULL
) {
544 if ((ctx
= NSS_CMSDecoder_Start(NULL
,
546 password_cb
, "Pass phrase:",
547 NULL
, NULL
)) == NULL
) {
548 fprintf(stderr
, "Cannot start decoder.\n");
552 if ((smime_split(yp
, &hp
, &bp
, size
, 1)) == STOP
)
555 while (fgetline(&buf
, &bufsize
, &count
, &buflen
, bp
, 0) != NULL
) {
558 if ((cp
= thisfield(buf
, "content-transfer-encoding")) != NULL
)
559 if (ascncasecmp(cp
, "binary", 7) == 0)
562 while (fgetline(&buf
, &bufsize
, &count
, &buflen
, bp
, 0) != NULL
) {
564 NSS_CMSDecoder_Update(ctx
, buf
, buflen
);
568 mime_fromb64_b(&in
, &out
, 0, bp
);
569 NSS_CMSDecoder_Update(ctx
, out
.s
, out
.l
);
574 if ((msg
= NSS_CMSDecoder_Finish(ctx
)) == NULL
) {
575 fprintf(stderr
, "Failed to decode message.\n");
580 nlevels
= NSS_CMSMessage_ContentLevelCount(msg
);
581 for (i
= 0; i
< nlevels
; i
++) {
582 NSSCMSContentInfo
*content
;
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";
592 alg
= NSS_CMSContentInfo_GetContentEncAlgTag(content
);
593 keysize
= NSS_CMSContentInfo_GetBulkKeySize(content
);
594 fseek(hp
, 0L, SEEK_END
);
598 NSS_CMSMessage_Destroy(msg
);
601 setinput(&mb
, m
, NEED_BODY
);
602 return (struct message
*)-1;
604 fprintf(hp
, "%s: none\n", fld
);
606 case SEC_OID_RC2_CBC
:
607 fprintf(hp
, "%s: RC2, %d bits\n", fld
, keysize
);
609 case SEC_OID_DES_CBC
:
610 fprintf(hp
, "%s: DES, 56 bits\n", fld
);
612 case SEC_OID_DES_EDE3_CBC
:
613 fprintf(hp
, "%s: 3DES, 112/168 bits\n", fld
);
615 case SEC_OID_FORTEZZA_SKIPJACK
:
616 fprintf(hp
, "%s: Fortezza\n", fld
);
619 fprintf(hp
, "%s: unknown type %lu\n", fld
,
626 NSS_CMSMessage_Destroy(msg
);
630 return smime_decrypt_assemble(m
, hp
, op
);
633 static CERTCertificate
*
634 get_signer_cert(char *addr
)
636 CERTCertDBHandle
*handle
;
638 CERTCertListNode
*node
;
639 CERTCertificate
*cert
= NULL
;
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");
651 handle
= CERT_GetDefaultCertDB();
653 cert
= CERT_FindCertByNickname(handle
, nick
);
655 fprintf(stderr
, "No certificate \"%s\" found.\n", nick
);
658 if ((list
= CERT_FindUserCertsByUsage(handle
, certUsageEmailSigner
,
659 PR_TRUE
, PR_TRUE
, NULL
)) == NULL
) {
660 fprintf(stderr
, "Cannot find any certificates for signing.\n");
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) {
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
);
677 if (asccasecmp(cp
, addr
) == 0) {
681 cp
= CERT_GetNextEmailAddress(node
->cert
, cp
);
687 "More than one signing certificate found for <%s>.\n"
688 "Use the smime-sign-nickname variable.\n", addr
);
693 "Cannot find a signing certificate for <%s>.\n",
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
;
707 if (smime_split(ip
, hp
, bp
, -1, 0) == STOP
)
709 if ((op
= Ftemp(&cp
, "Ry", "w+", 0600, 1)) == NULL
) {
715 if ((ctx
= NSS_CMSEncoder_Start(msg
,
718 password_cb
, "Pass phrase:",
720 NULL
, NULL
)) == NULL
) {
721 fprintf(stderr
, "Cannot create encoder context.\n");
726 while (fgetline(&buf
, &bufsize
, &count
, &buflen
, *bp
, 0) != NULL
) {
727 buf
[buflen
-1] = '\r';
729 if (NSS_CMSEncoder_Update(ctx
, buf
, buflen
+1) != 0) {
730 fprintf(stderr
, "Failed to add data to encoder.\n");
736 if (NSS_CMSEncoder_Finish(ctx
) != 0) {
737 fprintf(stderr
, "Failed to encode data.\n");
742 cb(op
, (void *)-1, 0);
754 decoder_cb(void *arg
, const char *buf
, unsigned long len
)
757 fwrite(buf
, 1, len
, arg
);
761 base64_cb(void *arg
, const char *buf
, unsigned long len
)
763 static char back
[972];
767 if (arg
&& buf
&& buf
!= (void *)-1) {
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,
774 pos
+= sizeof back
- fill
;
777 memcpy(&back
[fill
], &buf
[pos
], len
- pos
);
779 } else if (buf
== (void *)-1) {
780 mime_write(back
, fill
, arg
,
781 CONV_TOB64
, TD_NONE
, NULL
, 0,
788 verify1(struct message
*m
, int n
)
793 SECAlgorithmID
**algids
;
794 CERTCertDBHandle
*handle
;
800 if ((m
= getsig(m
, n
, &msg
)) == NULL
)
802 sender
= getsender(m
);
803 handle
= CERT_GetDefaultCertDB();
804 nlevels
= NSS_CMSMessage_ContentLevelCount(msg
);
805 for (i
= 0; i
< nlevels
; i
++) {
806 NSSCMSContentInfo
*content
;
809 content
= NSS_CMSMessage_ContentLevel(msg
, i
);
810 tag
= NSS_CMSContentInfo_GetContentTypeTag(content
);
811 if (tag
== SEC_OID_PKCS7_SIGNED_DATA
) {
812 NSSCMSSignedData
*data
;
815 if ((data
= NSS_CMSContentInfo_GetContent(content
))
817 fprintf(stderr
, "Signed data missing for "
822 if (!NSS_CMSSignedData_HasDigests(data
)) {
823 algids
= NSS_CMSSignedData_GetDigestAlgs(data
);
824 if (getdig(m
, n
, &digests
, &poolp
, algids
)
829 if (NSS_CMSSignedData_SetDigests(data
, algids
,
832 fprintf(stderr
, "Cannot set digests "
833 "for message %d.\n", n
);
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 "
848 nsigners
= NSS_CMSSignedData_SignerInfoCount(data
);
850 fprintf(stderr
, "Message %d has no signers.\n",
855 if (!NSS_CMSSignedData_HasDigests(data
)) {
856 fprintf(stderr
, "Message %d has no digests.\n",
861 for (j
= 0; j
< nsigners
; j
++) {
863 NSSCMSSignerInfo
*info
;
864 NSSCMSVerificationStatus vs
;
866 CERTCertificate
*cert
;
870 info
= NSS_CMSSignedData_GetSignerInfo(data
, j
);
871 cert
= NSS_CMSSignerInfo_GetSigningCertificate
873 bad
= NSS_CMSSignedData_VerifySignerInfo(data
,
875 certUsageEmailSigner
);
876 vs
= NSS_CMSSignerInfo_GetVerificationStatus
878 svs
= NSS_CMSUtil_VerificationStatusToString
880 addr
= CERT_GetCertEmailAddress(&cert
->subject
);
881 if (sender
!= NULL
&& addr
!= NULL
&&
882 asccasecmp(sender
, addr
) == 0)
885 addr
= CERT_GetFirstEmailAddress(cert
);
886 while (sender
&& addr
) {
887 if (!asccasecmp(sender
, addr
)) {
891 addr
= CERT_GetNextEmailAddress
895 if (CERT_VerifyCertNow(handle
,
897 certUsageEmailSigner
,
899 fprintf(stderr
, "Bad certificate for "
902 addr
? addr
: "?", n
,
907 fprintf(stderr
, "Bad status for "
916 else if (status
== 0)
921 if (foundsender
== 0) {
923 fprintf(stderr
, "Signers of message "
924 "%d do not include the sender <%s>\n",
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
);
933 fprintf(stderr
, "No verification information found in "
935 NSS_CMSMessage_Destroy(msg
);
939 static struct message
*
940 getsig(struct message
*m
, int n
, NSSCMSMessage
**msg
)
943 char *ct
, *pt
, *boundary
= NULL
, *cte
;
945 size_t bufsize
= 0, buflen
, count
, boundlen
= -1;
948 NSSCMSDecoderContext
*decctx
;
954 loop
: if ((ct
= hfield("content-type", m
)) == NULL
)
956 if (strncmp(ct
, "application/x-pkcs7-mime", 24) == 0 ||
957 strncmp(ct
, "application/pkcs7-mime", 22) == 0) {
958 to
= hfield("to", m
);
959 cc
= hfield("cc", m
);
960 if ((x
= smime_decrypt(m
, to
, cc
, 1)) == NULL
)
962 if (x
!= (struct message
*)-1) {
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
) {
973 "Message %d is not an S/MIME signed message.\n", n
);
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");
983 if ((fp
= setinput(&mb
, m
, NEED_BODY
)) == NULL
) {
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') {
998 fprintf(stderr
, "Message %d has too "
1005 if (buf
[boundlen
] == '-' && buf
[boundlen
+1] == '-' &&
1006 buf
[boundlen
+2] == '\n')
1008 } else if (buf
[0] == '\n') {
1012 if ((!detached
|| part
== 2) && inhdr
== 0) {
1014 NSS_CMSDecoder_Update(decctx
, buf
, buflen
);
1018 mime_fromb64_b(&in
, &out
, 0, fp
);
1019 NSS_CMSDecoder_Update(decctx
, out
.s
, out
.l
);
1023 if (buflen
== 1 && buf
[0] == '\n')
1025 if (inhdr
&& (cte
= thisfield(buf
, "content-transfer-encoding"))
1026 != NULL
&& ascncasecmp(cte
, "binary", 7) == 0)
1030 if ((*msg
= NSS_CMSDecoder_Finish(decctx
)) == NULL
) {
1031 fprintf(stderr
, "Failed to decode signature for message %d.\n",
1039 getdig(struct message
*m
, int n
, SECItem
***digests
,
1040 PLArenaPool
**poolp
, SECAlgorithmID
**algids
)
1042 char *ct
, *pt
, *boundary
;
1044 size_t bufsize
= 0, buflen
, count
, boundlen
;
1048 NSSCMSDigestContext
*digctx
;
1050 *poolp
= PORT_NewArena(1024);
1051 if ((ct
= hfield("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
) {
1058 "Message %d is not an S/MIME signed message.\n", n
);
1061 boundlen
= strlen(boundary
);
1062 if ((digctx
= NSS_CMSDigestContext_StartMultiple(algids
)) == NULL
) {
1063 fprintf(stderr
, "Cannot start digest computation.\n");
1066 if ((fp
= setinput(&mb
, m
, NEED_BODY
)) == NULL
) {
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') {
1080 if (buf
[boundlen
] == '-' && buf
[boundlen
+1] == '-' &&
1081 buf
[boundlen
+2] == '\n')
1086 NSS_CMSDigestContext_Update(digctx
,
1087 (unsigned char *)"\r\n", 2);
1090 if (buf
[buflen
-1] == '\n') {
1094 NSS_CMSDigestContext_Update(digctx
,
1095 (unsigned char *)buf
, buflen
);
1100 if (NSS_CMSDigestContext_FinishMultiple(digctx
,
1101 *poolp
, digests
) != SECSuccess
) {
1102 fprintf(stderr
, "Error creating digest for message %d\n", n
);
1112 tcsetattr(0, TCSADRAIN
, &otio
);
1113 siglongjmp(nssjmp
, s
);
1117 smime_certsave(struct message
*m
, int n
, FILE *op
)
1120 CERTCertDBHandle
*handle
;
1121 int nlevels
, i
, cnt
= 0;
1122 enum okay ok
= OKAY
;
1124 if (nss_init() == STOP
)
1126 if ((m
= getsig(m
, n
, &msg
)) == NULL
)
1128 handle
= CERT_GetDefaultCertDB();
1129 nlevels
= NSS_CMSMessage_ContentLevelCount(msg
);
1130 for (i
= 0; i
< nlevels
; i
++) {
1131 NSSCMSContentInfo
*content
;
1134 content
= NSS_CMSMessage_ContentLevel(msg
, i
);
1135 tag
= NSS_CMSContentInfo_GetContentTypeTag(content
);
1136 if (tag
== SEC_OID_PKCS7_SIGNED_DATA
) {
1137 NSSCMSSignedData
*data
;
1140 if ((data
= NSS_CMSContentInfo_GetContent(content
))
1142 fprintf(stderr
, "Signed data missing for "
1143 "message %d.\n", n
);
1147 if (NSS_CMSSignedData_ImportCerts(data
, handle
,
1148 certUsageEmailSigner
,
1149 PR_FALSE
) != SECSuccess
) {
1150 fprintf(stderr
, "Cannot temporarily import "
1152 "message %d.\n", n
);
1156 nsigners
= NSS_CMSSignedData_SignerInfoCount(data
);
1157 if (nsigners
== 0) {
1158 fprintf(stderr
, "Message %d has no signers.\n",
1163 for (j
= 0; j
< nsigners
; j
++) {
1164 NSSCMSSignerInfo
*info
;
1165 CERTCertificateList
*list
;
1166 CERTCertificate
*cert
;
1169 info
= NSS_CMSSignedData_GetSignerInfo(data
, j
);
1170 list
= NSS_CMSSignerInfo_GetCertList(info
);
1172 for (k
= 0; k
< list
->len
; k
++) {
1173 cert
= (CERTCertificate
*)
1179 cert
= NSS_CMSSignerInfo_GetSigningCertificate
1188 NSS_CMSMessage_Destroy(msg
);
1190 fprintf(stderr
, "No certificates found in message %d.\n", n
);
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,
1206 fputs("-----END CERTIFICATE-----\n", op
);
1210 getcipher(const char *to
, SECOidTag
*alg
, int *key
)
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
;
1223 } else if (strcmp(cp
, "rc2-64") == 0) {
1224 *alg
= SEC_OID_RC2_CBC
;
1226 } else if (strcmp(cp
, "rc2-128") == 0) {
1227 *alg
= SEC_OID_RC2_CBC
;
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)
1236 fprintf(stderr
, "Invalid cipher \"%s\".\n", cp
);
1243 #endif /* USE_NSS */