1 /*
2  * Heirloom mailx - a mail user agent derived from Berkeley Mail.
3  *
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  */
10 /*
11  * Parts of this file are derived from the Mozilla NSS 3.9.2 source,
12  * mozilla/security/nss/cmd/smimetools/cmsutil.c. Therefore:
13  *
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/
18  *
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.
23  *
24  * The Original Code is the Netscape security libraries.
25  *
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.
30  *
31  * Contributor(s):
32  */
33 
34 #ifndef lint
35 #ifdef	DOSCCS
36 static char sccsid[] = "@(#)nss.c	1.48 (gritter) 8/4/07";
37 #endif
38 #endif /* not lint */
39 
40 #include "config.h"
41 
42 #ifdef	USE_NSS
43 
44 #include "rcv.h"
45 
46 #include <setjmp.h>
47 #include <termios.h>
48 #include <stdio.h>
49 
50 static int	verbose;
51 static int	reset_tio;
52 static struct termios	otio;
53 static sigjmp_buf	nssjmp;
54 
55 #include <stdarg.h>
56 
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>
74 
75 #include "extern.h"
76 
77 #ifndef	HAVE_CERTAltNameEncodedContext
78 /*
79  * NSS 3.11.5 neither installs genname.h nor provides this
80  * structure otherwise, so define it here.
81  */
82 typedef struct CERTAltNameEncodedContextStr {
83 	SECItem	**encodedGenName;
84 } CERTAltNameEncodedContext;
85 #endif	/* !HAVE_CERTAltNameEncodedContext */
86 
87 #include "nsserr.c"
88 
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);
107 
108 static char *
password_cb(PK11SlotInfo * slot,PRBool retry,void * arg)109 password_cb(PK11SlotInfo *slot, PRBool retry, void *arg)
110 {
111 	sighandler_type	saveint;
112 	char	*pass = NULL;
113 
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);
121 	}
122 	safe_signal(SIGINT, saveint);
123 	if (pass == NULL)
124 		return NULL;
125 	return PL_strdup(pass);
126 }
127 
128 static SECStatus
bad_cert_cb(void * arg,PRFileDesc * fd)129 bad_cert_cb(void *arg, PRFileDesc *fd)
130 {
131 	if (PORT_GetError() == SSL_ERROR_BAD_CERT_DOMAIN)
132 		/*
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:
136 		 *
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.
140 		 *
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".
144 		 *
145 		 * So ignore it and verify separately.
146 		 */
147 		return SECSuccess;
148 	fprintf(stderr, "Error in certificate: %s.\n", bad_cert_str());
149 	return ssl_vrfy_decide() == OKAY ? SECSuccess : SECFailure;
150 }
151 
152 /*
153  * Host name checking according to RFC 2595.
154  */
155 static enum okay
nss_check_host(const char * server,struct sock * sp)156 nss_check_host(const char *server, struct sock *sp)
157 {
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 }
168 	};
169 
170 	if ((cert = SSL_PeerCertificate(sp->s_prfd)) == NULL) {
171 		fprintf(stderr, "no certificate from \"%s\"\n", server);
172 		return STOP;
173 	}
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;
196 				}
197 				ac_free(dn);
198 			}
199 		}
200 	}
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);
205 	}
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;
214 }
215 
216 static const char *
bad_cert_str(void)217 bad_cert_str(void)
218 {
219 	int	ec;
220 
221 	ec = PORT_GetError();
222 	return nss_strerror(ec);
223 }
224 
225 static enum okay
nss_init(void)226 nss_init(void)
227 {
228 	static int	initialized;
229 	char	*cp;
230 
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;
236 		}
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;
244 		}
245 		nss_gen_err("Error initializing NSS");
246 		return STOP;
247 	}
248 	return OKAY;
249 }
250 
251 static void
nss_select_method(const char * uhp)252 nss_select_method(const char *uhp)
253 {
254 	char	*cp;
255 	enum {
256 		SSL2 = 01,
257 		SSL3 = 02,
258 		TLS1 = 03
259 	} methods;
260 
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);
273 		}
274 	}
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);
280 }
281 
282 enum okay
ssl_open(const char * server,struct sock * sp,const char * uhp)283 ssl_open(const char *server, struct sock *sp, const char *uhp)
284 {
285 	PRFileDesc	*fdp, *fdc;
286 
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;
294 	}
295 	if ((fdc = SSL_ImportFD(NULL, fdp)) == NULL) {
296 		nss_gen_err("Error importing NSPR file descriptor");
297 		PR_Close(fdp);
298 		return STOP;
299 	}
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;
307 	}
308 	if (SSL_ForceHandshake(fdc) != 0) {
309 		nss_gen_err("SSL/TLS handshake failed");
310 		PR_Close(fdc);
311 		return STOP;
312 	}
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;
318 	}
319 	sp->s_use_ssl = 1;
320 	if (verbose) {
321 		char	*cipher, *issuer, *subject;
322 		int	keysize, secretkeysize;
323 
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");
338 	}
339 	return OKAY;
340 }
341 
342 void
nss_gen_err(const char * fmt,...)343 nss_gen_err(const char *fmt, ...)
344 {
345 	va_list	ap;
346 	char	*text;
347 	int	len;
348 
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()));
359 }
360 
361 FILE *
smime_sign(FILE * ip,struct header * headp)362 smime_sign(FILE *ip, struct header *headp)
363 {
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;
372 
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;
378 	}
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;
385 	}
386 	if ((data = NSS_CMSSignedData_Create(msg)) == NULL) {
387 		fprintf(stderr, "Cannot create CMS signed data.\n");
388 		return NULL;
389 	}
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;
395 	}
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;
401 	}
402 	if ((info = NSS_CMSSignerInfo_Create(msg, cert, SEC_OID_SHA1)) == 0) {
403 		fprintf(stderr, "Cannot create signed information.\n");
404 		return NULL;
405 	}
406 	if (NSS_CMSSignerInfo_IncludeCerts(info, NSSCMSCM_CertOnly,
407 				certUsageEmailSigner) != SECSuccess) {
408 		fprintf(stderr, "Cannot include certificate.\n");
409 		return NULL;
410 	}
411 	if (NSS_CMSSignerInfo_AddSigningTime(info, PR_Now()) != SECSuccess) {
412 		fprintf(stderr, "Cannot add signing time.\n");
413 		return NULL;
414 	}
415 	if (NSS_CMSSignerInfo_AddSMIMECaps(info) != SECSuccess) {
416 		fprintf(stderr, "Cannot add S/MIME capabilities.\n");
417 		return NULL;
418 	}
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;
424 	}
425 	if (NSS_CMSSignedData_AddSignerInfo(data, info) != SECSuccess) {
426 		fprintf(stderr, "Cannot add signer information.\n");
427 		return NULL;
428 	}
429 	CERT_DestroyCertificate(cert);
430 	if ((sp = encode(ip, &hp, &bp, msg, base64_cb)) == NULL) {
431 		NSS_CMSMessage_Destroy(msg);
432 		return NULL;
433 	}
434 	NSS_CMSMessage_Destroy(msg);
435 	return smime_sign_assemble(hp, bp, sp);
436 }
437 
438 int
cverify(void * vp)439 cverify(void *vp)
440 {
441 	int	*msgvec = vp, *ip;
442 	int	ec = 0;
443 
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);
450 	}
451 	return ec;
452 }
453 
454 FILE *
smime_encrypt(FILE * ip,const char * ignored,const char * to)455 smime_encrypt(FILE *ip, const char *ignored, const char *to)
456 {
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;
468 
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;
485 	}
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;
492 	}
493 	if ((data = NSS_CMSEnvelopedData_Create(msg, tag, keysize)) == NULL) {
494 		fprintf(stderr, "Cannot create enveloped data.\n");
495 		return NULL;
496 	}
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;
502 	}
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;
508 	}
509 	if ((info = NSS_CMSRecipientInfo_Create(msg, cert[0])) == NULL) {
510 		fprintf(stderr, "Cannot create CMS recipient information.\n");
511 		return NULL;
512 	}
513 	if (NSS_CMSEnvelopedData_AddRecipient(data, info) != SECSuccess) {
514 		fprintf(stderr, "Cannot add CMS recipient information.\n");
515 		return NULL;
516 	}
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);
522 }
523 
524 struct message *
smime_decrypt(struct message * m,const char * to,const char * cc,int signcall)525 smime_decrypt(struct message *m, const char *to, const char *cc, int signcall)
526 {
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;
538 
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;
546 	}
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;
555 	}
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;
566 	}
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);
576 		}
577 	}
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;
584 	}
585 	nlevels = NSS_CMSMessage_ContentLevelCount(msg);
586 	for (i = 0; i < nlevels; i++) {
587 		NSSCMSContentInfo	*content;
588 		SECOidTag	tag;
589 
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;
596 
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;
608 				}
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);
626 			}
627 			fflush(hp);
628 			rewind(hp);
629 		}
630 	}
631 	NSS_CMSMessage_Destroy(msg);
632 	fflush(op);
633 	rewind(op);
634 	Fclose(bp);
635 	return smime_decrypt_assemble(m, hp, op);
636 }
637 
638 static CERTCertificate *
get_signer_cert(char * addr)639 get_signer_cert(char *addr)
640 {
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;
649 
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;
662 	}
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;
667 	}
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++;
674 		}
675 	}
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++;
685 				}
686 				cp = CERT_GetNextEmailAddress(node->cert, cp);
687 			}
688 		}
689 	}
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;
695 	}
696 	if (cert == NULL)
697 		fprintf(stderr,
698 			"Cannot find a signing certificate for <%s>.\n",
699 			addr);
700 	return cert;
701 }
702 
703 static FILE *
encode(FILE * ip,FILE ** hp,FILE ** bp,NSSCMSMessage * msg,void (* cb)(void *,const char *,unsigned long))704 encode(FILE *ip, FILE **hp, FILE **bp, NSSCMSMessage *msg,
705 		void    (*cb)(void *, const char *, unsigned long))
706 {
707 	NSSCMSEncoderContext	*ctx;
708 	char	*buf = NULL, *cp;
709 	size_t	bufsize = 0, buflen, count;
710 	FILE	*op;
711 
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;
717 	}
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;
729 	}
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;
738 		}
739 	}
740 	free(buf);
741 	if (NSS_CMSEncoder_Finish(ctx) != 0) {
742 		fprintf(stderr, "Failed to encode data.\n");
743 		Fclose(op);
744 		return NULL;
745 	}
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;
753 	}
754 	rewind(op);
755 	return op;
756 }
757 
758 static void
decoder_cb(void * arg,const char * buf,unsigned long len)759 decoder_cb(void *arg, const char *buf, unsigned long len)
760 {
761 	if (arg && buf)
762 		fwrite(buf, 1, len, arg);
763 }
764 
765 static void
base64_cb(void * arg,const char * buf,unsigned long len)766 base64_cb(void *arg, const char *buf, unsigned long len)
767 {
768 	static char	back[972];
769 	static int	fill;
770 	unsigned long	pos;
771 
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;
781 		}
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;
789 	}
790 }
791 
792 static int
verify1(struct message * m,int n)793 verify1(struct message *m, int n)
794 {
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;
804 
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;
813 
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;
819 
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;
826 			}
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;
833 				}
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;
841 				}
842 				PORT_FreeArena(poolp, PR_FALSE);
843 			}
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;
852 			}
853 			nsigners = NSS_CMSSignedData_SignerInfoCount(data);
854 			if (nsigners == 0) {
855 				fprintf(stderr, "Message %d has no signers.\n",
856 						n);
857 				status = -1;
858 				break;
859 			}
860 			if (!NSS_CMSSignedData_HasDigests(data)) {
861 				fprintf(stderr, "Message %d has no digests.\n",
862 						n);
863 				status = -1;
864 				break;
865 			}
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;
874 
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;
895 						}
896 						addr = CERT_GetNextEmailAddress
897 							(cert, addr);
898 					}
899 				}
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;
923 			}
924 		}
925 	}
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;
942 }
943 
944 static struct message *
getsig(struct message * m,int n,NSSCMSMessage ** msg)945 getsig(struct message *m, int n, NSSCMSMessage **msg)
946 {
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;
958 
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;
970 		}
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;
987 	}
988 	if ((fp = setinput(&mb, m, NEED_BODY)) == NULL) {
989 		return NULL;
990 	}
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;
1007 				}
1008 				continue;
1009 			}
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;
1016 		}
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);
1026 			}
1027 		}
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;
1033 	}
1034 	free(buf);
1035 	if ((*msg = NSS_CMSDecoder_Finish(decctx)) == NULL) {
1036 		fprintf(stderr, "Failed to decode signature for message %d.\n",
1037 				n);
1038 		return NULL;
1039 	}
1040 	return m;
1041 }
1042 
1043 static enum okay
getdig(struct message * m,int n,SECItem *** digests,PLArenaPool ** poolp,SECAlgorithmID ** algids)1044 getdig(struct message *m, int n, SECItem ***digests,
1045 		PLArenaPool **poolp, SECAlgorithmID **algids)
1046 {
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;
1054 
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;
1065 	}
1066 	boundlen = strlen(boundary);
1067 	if ((digctx = NSS_CMSDigestContext_StartMultiple(algids)) == NULL) {
1068 		fprintf(stderr, "Cannot start digest computation.\n");
1069 		return STOP;
1070 	}
1071 	if ((fp = setinput(&mb, m, NEED_BODY)) == NULL) {
1072 		return STOP;
1073 	}
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;
1084 			}
1085 			if (buf[boundlen] == '-' && buf[boundlen+1] == '-' &&
1086 					buf[boundlen+2] == '\n')
1087 				break;
1088 		}
1089 		if (part == 1) {
1090 			if (nl) {
1091 				NSS_CMSDigestContext_Update(digctx,
1092 						(unsigned char *)"\r\n", 2);
1093 				nl = 0;
1094 			}
1095 			if (buf[buflen-1] == '\n') {
1096 				nl = 1;
1097 				buflen--;
1098 			}
1099 			NSS_CMSDigestContext_Update(digctx,
1100 					(unsigned char *)buf, buflen);
1101 			continue;
1102 		}
1103 	}
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;
1109 	}
1110 	return OKAY;
1111 }
1112 
1113 static void
nsscatch(int s)1114 nsscatch(int s)
1115 {
1116 	if (reset_tio)
1117 		tcsetattr(0, TCSADRAIN, &otio);
1118 	siglongjmp(nssjmp, s);
1119 }
1120 
1121 enum okay
smime_certsave(struct message * m,int n,FILE * op)1122 smime_certsave(struct message *m, int n, FILE *op)
1123 {
1124 	NSSCMSMessage	*msg;
1125 	CERTCertDBHandle	*handle;
1126 	int	nlevels, i, cnt = 0;
1127 	enum okay	ok = OKAY;
1128 
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;
1138 
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;
1144 
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;
1151 			}
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;
1160 			}
1161 			nsigners = NSS_CMSSignedData_SignerInfoCount(data);
1162 			if (nsigners == 0) {
1163 				fprintf(stderr, "Message %d has no signers.\n",
1164 						n);
1165 				ok = STOP;
1166 				break;
1167 			}
1168 			for (j = 0; j < nsigners; j++) {
1169 				NSSCMSSignerInfo	*info;
1170 				CERTCertificateList	*list;
1171 				CERTCertificate	*cert;
1172 				int	k;
1173 
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++;
1182 					}
1183 				}
1184 				cert = NSS_CMSSignerInfo_GetSigningCertificate
1185 					(info, handle);
1186 				if (cert) {
1187 					dumpcert(cert, op);
1188 					cnt++;
1189 				}
1190 			}
1191 		}
1192 	}
1193 	NSS_CMSMessage_Destroy(msg);
1194 	if (cnt == 0) {
1195 		fprintf(stderr, "No certificates found in message %d.\n", n);
1196 		ok = STOP;
1197 	}
1198 	return ok;
1199 }
1200 
1201 static void
dumpcert(CERTCertificate * cert,FILE * op)1202 dumpcert(CERTCertificate *cert, FILE *op)
1203 {
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);
1212 }
1213 
1214 static enum okay
getcipher(const char * to,SECOidTag * alg,int * key)1215 getcipher(const char *to, SECOidTag *alg, int *key)
1216 {
1217 	char	*vn, *cp;
1218 	int	vs;
1219 
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;
1243 		}
1244 	}
1245 	ac_free(vn);
1246 	return OKAY;
1247 }
1248 #endif	/* USE_NSS */
1249