xref: /netbsd/external/bsd/pkg_install/dist/lib/pkcs7.c (revision 6550d01e)
1 /*	$NetBSD: pkcs7.c,v 1.1.1.4 2009/08/06 16:55:27 joerg Exp $	*/
2 #if HAVE_CONFIG_H
3 #include "config.h"
4 #endif
5 #include <nbcompat.h>
6 #if HAVE_SYS_CDEFS_H
7 #include <sys/cdefs.h>
8 #endif
9 
10 __RCSID("$NetBSD: pkcs7.c,v 1.1.1.4 2009/08/06 16:55:27 joerg Exp $");
11 
12 /*-
13  * Copyright (c) 2004, 2008 The NetBSD Foundation, Inc.
14  * All rights reserved.
15  *
16  * This code is derived from software contributed to The NetBSD Foundation
17  * by Love H�rnquist �strand <lha@it.su.se>
18  *
19  * Redistribution and use in source and binary forms, with or without
20  * modification, are permitted provided that the following conditions
21  * are met:
22  * 1. Redistributions of source code must retain the above copyright
23  *    notice, this list of conditions and the following disclaimer.
24  * 2. Redistributions in binary form must reproduce the above copyright
25  *    notice, this list of conditions and the following disclaimer in the
26  *    documentation and/or other materials provided with the distribution.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
32  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39  */
40 
41 #if HAVE_ERR_H
42 #include <err.h>
43 #endif
44 
45 #include <openssl/pkcs7.h>
46 #include <openssl/evp.h>
47 #include <openssl/x509.h>
48 #include <openssl/x509v3.h>
49 #include <openssl/pem.h>
50 #include <openssl/err.h>
51 
52 #include "lib.h"
53 
54 #ifndef NS_ANY_CA
55 #define NS_ANY_CA		(NS_SSL_CA|NS_SMIME_CA|NS_OBJSIGN_CA)
56 #endif
57 
58 static const unsigned int pkg_key_usage = XKU_CODE_SIGN | XKU_SMIME;
59 
60 static int
61 check_ca(X509 *cert)
62 {
63 	if ((cert->ex_flags & EXFLAG_KUSAGE) != 0 &&
64 	    (cert->ex_kusage & KU_KEY_CERT_SIGN) != KU_KEY_CERT_SIGN)
65 		return 0;
66 	if ((cert->ex_flags & EXFLAG_BCONS) != 0)
67 		return (cert->ex_flags & EXFLAG_CA) == EXFLAG_CA;
68 	if ((cert->ex_flags & (EXFLAG_V1|EXFLAG_SS)) == (EXFLAG_V1|EXFLAG_SS))
69 		return 1;
70 	if ((cert->ex_flags & EXFLAG_KUSAGE) != 0)
71 		return 1;
72 	if ((cert->ex_flags & EXFLAG_NSCERT) != 0 &&
73 	    (cert->ex_nscert & NS_ANY_CA) != 0)
74 		return 1;
75 	return 0;
76 }
77 
78 static STACK_OF(X509) *
79 file_to_certs(const char *file)
80 {
81 	unsigned long ret;
82 	STACK_OF(X509) *certs;
83 	FILE *f;
84 
85 	if ((f = fopen(file, "r")) == NULL) {
86 		warn("open failed %s", file);
87 		return NULL;
88 	}
89 
90 	certs = sk_X509_new_null();
91 	for (;;) {
92 		X509 *cert;
93 
94 		cert = PEM_read_X509(f, NULL, NULL, NULL);
95 		if (cert == NULL) {
96 			ret = ERR_GET_REASON(ERR_peek_error());
97 			if (ret == PEM_R_NO_START_LINE) {
98 				/* End of file reached. no error */
99 				ERR_clear_error();
100 				break;
101 			}
102 			sk_X509_free(certs);
103 			warnx("Can't read certificate in file: %s", file);
104 			fclose(f);
105 			return NULL;
106 		}
107 		sk_X509_insert(certs, cert, sk_X509_num(certs));
108 	}
109 
110 	fclose(f);
111 
112 	if (sk_X509_num(certs) == 0) {
113 		sk_X509_free(certs);
114 		certs = NULL;
115 		warnx("No certificate found in file %s", file);
116 	}
117 
118 	return certs;
119 }
120 
121 int
122 easy_pkcs7_verify(const char *content, size_t len,
123     const char *signature, size_t signature_len,
124     const char *anchor, int is_pkg)
125 {
126 	STACK_OF(X509) *cert_chain, *signers;
127 	X509_STORE *store;
128 	BIO *sig, *in;
129 	PKCS7 *p7;
130 	int i, status;
131 	X509_NAME *name;
132 	char *subject;
133 
134 	OpenSSL_add_all_algorithms();
135 	ERR_load_crypto_strings();
136 
137 	status = -1;
138 
139 	if (cert_chain_file)
140 		cert_chain = file_to_certs(cert_chain_file);
141 	else
142 		cert_chain = NULL;
143 
144 	store = X509_STORE_new();
145 	if (store == NULL) {
146 		sk_X509_free(cert_chain);
147 		warnx("Failed to create certificate store");
148 		return -1;
149 	}
150 
151 	X509_STORE_load_locations(store, anchor, NULL);
152 
153 	in = BIO_new_mem_buf(__UNCONST(content), len);
154 	sig = BIO_new_mem_buf(__UNCONST(signature), signature_len);
155 	signers = NULL;
156 
157 	p7 = PEM_read_bio_PKCS7(sig, NULL, NULL, NULL);
158 	if (p7 == NULL) {
159 		warnx("Failed to parse the signature");
160 		goto cleanup;
161 	}
162 
163 	if (PKCS7_verify(p7, cert_chain, store, in, NULL, 0) != 1) {
164 		warnx("Failed to verify signature");
165 		goto cleanup;
166 	}
167 
168 	signers = PKCS7_get0_signers(p7, NULL, 0);
169 	if (signers == NULL) {
170 		warnx("Failed to get signers");
171 		goto cleanup;
172 	}
173 
174 	if (sk_X509_num(signers) == 0) {
175 		warnx("No signers found");
176 		goto cleanup;
177 	}
178 
179 	for (i = 0; i < sk_X509_num(signers); i++) {
180 		/* Compute ex_xkusage */
181 		X509_check_purpose(sk_X509_value(signers, i), -1, -1);
182 
183 		if (check_ca(sk_X509_value(signers, i))) {
184 			warnx("CA keys are not valid for signatures");
185 			goto cleanup;
186 		}
187 		if (is_pkg) {
188 			if (sk_X509_value(signers, i)->ex_xkusage != pkg_key_usage) {
189 				warnx("Certificate must have CODE SIGNING "
190 				    "and EMAIL PROTECTION property");
191 				goto cleanup;
192 			}
193 		} else {
194 			if (sk_X509_value(signers, i)->ex_xkusage != 0) {
195 				warnx("Certificate must not have any property");
196 				goto cleanup;
197 			}
198 		}
199 	}
200 
201 	printf("Sigature ok, signed by:\n");
202 
203 	for (i = 0; i < sk_X509_num(signers); i++) {
204 		name = X509_get_subject_name(sk_X509_value(signers, i));
205 		subject = X509_NAME_oneline(name, NULL, 0);
206 
207 		printf("\t%s\n", subject);
208 
209 		OPENSSL_free(subject);
210 	}
211 
212 	status = 0;
213 
214 cleanup:
215 	sk_X509_free(cert_chain);
216 	sk_X509_free(signers);
217 	X509_STORE_free(store);
218 
219 	PKCS7_free(p7);
220 	BIO_free(in);
221 	BIO_free(sig);
222 
223 	return status;
224 }
225 
226 static int
227 ssl_pass_cb(char *buf, int size, int rwflag, void *u)
228 {
229 
230 	if (EVP_read_pw_string(buf, size, "Passphrase :", 0)) {
231 #if OPENSSL_VERSION >= 0x0090608fL
232 		OPENSSL_cleanse(buf, size);
233 #else
234 		memset(buf, 0, size);
235 #endif
236 		return 0;
237 	}
238 	return strlen(buf);
239 }
240 
241 int
242 easy_pkcs7_sign(const char *content, size_t len,
243     char **signature, size_t *signature_len,
244     const char *key_file, const char *cert_file)
245 {
246 	FILE *f;
247 	X509 *certificate;
248 	STACK_OF(X509) *c, *cert_chain;
249 	EVP_PKEY *private_key;
250 	char *tmp_sig;
251 	BIO *out, *in;
252 	PKCS7 *p7;
253 	int status;
254 
255 	OpenSSL_add_all_algorithms();
256 	ERR_load_crypto_strings();
257 
258 	status = -1;
259 	private_key = NULL;
260 	cert_chain = NULL;
261 	in = NULL;
262 
263 	c = file_to_certs(cert_file);
264 
265 	if (sk_X509_num(c) != 1) {
266 		warnx("More then one certificate in the certificate file");
267 		goto cleanup;
268 	}
269 	certificate = sk_X509_value(c, 0);
270 
271 	/* Compute ex_kusage */
272 	X509_check_purpose(certificate, -1, 0);
273 
274 	if (check_ca(certificate)) {
275 		warnx("CA keys are not valid for signatures");
276 		goto cleanup;
277 	}
278 
279 	if (certificate->ex_xkusage != pkg_key_usage) {
280 		warnx("Certificate must have CODE SIGNING "
281 		    "and EMAIL PROTECTION property");
282 		goto cleanup;
283 	}
284 
285 	if (cert_chain_file)
286 		cert_chain = file_to_certs(cert_chain_file);
287 
288 	if ((f = fopen(key_file, "r")) == NULL) {
289 		warn("Failed to open private key file %s", key_file);
290 		goto cleanup;
291 	}
292 	private_key = PEM_read_PrivateKey(f, NULL, ssl_pass_cb, NULL);
293 	fclose(f);
294 	if (private_key == NULL) {
295 		warnx("Can't read private key: %s", key_file);
296 		goto cleanup;
297 	}
298 
299 	if (X509_check_private_key(certificate, private_key) != 1) {
300 		warnx("The private key %s doesn't match the certificate %s",
301 		    key_file, cert_file);
302 		goto cleanup;
303 	}
304 
305 	in = BIO_new_mem_buf(__UNCONST(content), len);
306 
307 	p7 = PKCS7_sign(certificate, private_key, cert_chain, in,
308 	    PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY);
309 	if (p7 == NULL) {
310 		warnx("Failed to create signature structure");
311 		goto cleanup;
312 	}
313 
314 	out = BIO_new(BIO_s_mem());
315 	PEM_write_bio_PKCS7(out, p7);
316 	*signature_len = BIO_get_mem_data(out, &tmp_sig);
317 	*signature = xmalloc(*signature_len);
318 	memcpy(*signature, tmp_sig, *signature_len);
319 	BIO_free_all(out);
320 
321 	PKCS7_free(p7);
322 
323 	status = 0;
324 
325 cleanup:
326 	sk_X509_free(c);
327 	sk_X509_free(cert_chain);
328 	EVP_PKEY_free(private_key);
329 	BIO_free(in);
330 
331 	return status;
332 }
333