xref: /illumos-gate/usr/src/lib/libelfsign/common/elfcertlib.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <limits.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <dirent.h>
33 #include <strings.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <errno.h>
37 #include <sys/mman.h>
38 #include <md5.h>
39 #include <pthread.h>
40 
41 #include <cryptoutil.h>
42 
43 #include <kmfapi.h>
44 #include <sys/crypto/elfsign.h>
45 #include <libelfsign.h>
46 
47 #include <synch.h>
48 
49 const char _PATH_ELFSIGN_CRYPTO_CERTS[] = CRYPTO_CERTS_DIR;
50 const char _PATH_ELFSIGN_ETC_CERTS[] = ETC_CERTS_DIR;
51 
52 /*
53  * The CACERT and OBJCACERT are the Cryptographic Trust Anchors
54  * for the Solaris Cryptographic Framework.
55  */
56 static const char _PATH_CRYPTO_CACERT[] = CRYPTO_CERTS_DIR "/CA";
57 static const char _PATH_CRYPTO_OBJCACERT[] = CRYPTO_CERTS_DIR "/SUNWObjectCA";
58 static ELFCert_t CACERT = NULL;
59 static ELFCert_t OBJCACERT = NULL;
60 static pthread_mutex_t ca_mutex = PTHREAD_MUTEX_INITIALIZER;
61 
62 static void elfcertlib_freecert(ELFsign_t, ELFCert_t);
63 static ELFCert_t elfcertlib_allocatecert(void);
64 
65 /*
66  * elfcertlib_verifycert - Verify the Cert with a Trust Anchor
67  *
68  * IN	ess		- elfsign context structure
69  *	cert
70  * OUT	NONE
71  * RETURN	TRUE/FALSE
72  *
73  * We first setup the Trust Anchor (CA and SUNWObjectCA) certs
74  * if it hasn't been done already.  We verify that the files on disk
75  * are those we expected.
76  *
77  * We then verify the given cert using the publickey of a TA.
78  * If the passed in cert is a TA or it has been verified already we
79  * short cut and return TRUE without futher validation.
80  */
81 /*ARGSUSED*/
82 boolean_t
83 elfcertlib_verifycert(ELFsign_t ess, ELFCert_t cert)
84 {
85 	KMF_RETURN rv;
86 	if ((cert->c_verified == E_OK) || (cert->c_verified == E_IS_TA)) {
87 		return (B_TRUE);
88 	}
89 
90 	(void) pthread_mutex_lock(&ca_mutex);
91 	if (CACERT == NULL) {
92 		(void) elfcertlib_getcert(ess, (char *)_PATH_CRYPTO_CACERT,
93 		    NULL, &CACERT, ES_GET);
94 	}
95 	if (OBJCACERT == NULL) {
96 		(void) elfcertlib_getcert(ess, (char *)_PATH_CRYPTO_OBJCACERT,
97 		    NULL, &OBJCACERT, ES_GET);
98 	}
99 	(void) pthread_mutex_unlock(&ca_mutex);
100 
101 	if (CACERT != NULL) {
102 		rv = KMF_VerifyCertWithCert(ess->es_kmfhandle,
103 		    (const KMF_DATA *)&cert->c_cert,
104 		    (const KMF_DATA *)&CACERT->c_cert.certificate);
105 		if (rv == KMF_OK) {
106 			if (ess->es_certCAcallback != NULL)
107 				(ess->es_certvercallback)(ess->es_callbackctx,
108 				    cert, CACERT);
109 			cert->c_verified = E_OK;
110 			return (B_TRUE);
111 		}
112 	}
113 
114 	if (OBJCACERT != NULL) {
115 		rv = KMF_VerifyCertWithCert(ess->es_kmfhandle,
116 		    (const KMF_DATA *)&cert->c_cert,
117 		    (const KMF_DATA *)&OBJCACERT->c_cert.certificate);
118 		if (rv == KMF_OK) {
119 			if (ess->es_certCAcallback != NULL)
120 				(ess->es_certvercallback)(ess->es_callbackctx,
121 				    cert, OBJCACERT);
122 			cert->c_verified = E_OK;
123 			return (B_TRUE);
124 		}
125 	}
126 
127 	return (B_FALSE);
128 }
129 
130 /*
131  * elfcertlib_getcert - Get the certificate for signer_DN
132  *
133  * IN	ess		- elfsign context structure
134  *	cert_pathname	- path to cert (May be NULL)
135  *	signer_DN	- The DN we are looking for (May be NULL)
136  *      action		- indicates crypto verification call
137  * OUT  certp		- allocated/loaded ELFCert_t
138  *
139  * If the cert_pathname is passed use it and don't search.
140  * Otherwise, go looking in certificate directories
141  */
142 boolean_t
143 elfcertlib_getcert(ELFsign_t ess, char *cert_pathname,
144     char *signer_DN, ELFCert_t *certp, enum ES_ACTION action)
145 {
146 	KMF_RETURN rv;
147 	ELFCert_t	cert = NULL;
148 	KMF_FINDCERT_PARAMS fcparams;
149 	KMF_X509_DER_CERT certbuf[2];
150 	uint32_t ncerts;
151 	boolean_t ret = B_FALSE;
152 	char	*pathlist[3], **plp;
153 
154 	cryptodebug("elfcertlib_getcert: path=%s, DN=%s",
155 	    cert_pathname ? cert_pathname : "-none-",
156 	    signer_DN ? signer_DN : "-none-");
157 	*certp = NULL;
158 	if (cert_pathname == NULL && signer_DN == NULL) {
159 		cryptodebug("elfcertlib_getcert: lack of specificity");
160 		return (ret);
161 	}
162 
163 	plp = pathlist;
164 	if (cert_pathname != NULL) {
165 		/* look in the specified object */
166 		*plp++ = cert_pathname;
167 	} else {
168 		/* look in the certificate directories */
169 		*plp++ = (char *)_PATH_ELFSIGN_CRYPTO_CERTS;
170 		/*
171 		 * crypto verifications don't search beyond
172 		 * _PATH_ELFSIGN_CRYPTO_CERTS
173 		 */
174 		if (action != ES_GET_CRYPTO)
175 			*plp++ = (char *)_PATH_ELFSIGN_ETC_CERTS;
176 	}
177 	*plp = NULL;
178 
179 	if ((cert = elfcertlib_allocatecert()) == NULL) {
180 		return (ret);
181 	}
182 
183 	for (plp = pathlist; *plp; plp++) {
184 		(void) memset(&fcparams, 0, sizeof (fcparams));
185 		fcparams.kstype = KMF_KEYSTORE_OPENSSL;
186 		fcparams.sslparms.certfile = *plp;
187 		fcparams.subject = signer_DN;
188 		ncerts = 2;
189 
190 		rv = KMF_FindCert(ess->es_kmfhandle, &fcparams, certbuf,
191 		    &ncerts);
192 		if (rv != KMF_OK)
193 			continue;
194 		if (ncerts > 1 && signer_DN == NULL) {
195 			/* There can be only one */
196 			cryptodebug("elfcertlib_getcert: "
197 			    "too many certificates found in %s",
198 			    cert_pathname);
199 			goto cleanup;
200 		}
201 		/* found it, cache subject and issuer */
202 		cert->c_cert = certbuf[0];
203 		rv = KMF_GetCertSubjectNameString(ess->es_kmfhandle,
204 		    &cert->c_cert.certificate, &cert->c_subject);
205 		if (rv != KMF_OK)
206 			goto cleanup;
207 
208 		rv = KMF_GetCertIssuerNameString(ess->es_kmfhandle,
209 		    &cert->c_cert.certificate, &cert->c_issuer);
210 		if (rv != KMF_OK)
211 			goto cleanup;
212 		break;
213 	}
214 	if (*plp == NULL) {
215 		cryptodebug("elfcertlib_getcert: no certificate found");
216 		goto cleanup;
217 	}
218 
219 	cert->c_verified = E_UNCHECKED;
220 
221 	/*
222 	 * If the cert we are loading is the trust anchor (ie the CA) then
223 	 * we mark it as such in cert.  This is so that we don't attempt
224 	 * to verify it later.  The CA is always implicitly verified.
225 	 */
226 	if (cert_pathname != NULL && (
227 	    strcmp(cert_pathname, _PATH_CRYPTO_CACERT) == 0 ||
228 	    strcmp(cert_pathname, _PATH_CRYPTO_OBJCACERT) == 0)) {
229 		if (ess->es_certCAcallback != NULL)
230 			(ess->es_certCAcallback)(ess->es_callbackctx, cert,
231 			    cert_pathname);
232 		cert->c_verified = E_IS_TA;
233 	}
234 
235 	ret = B_TRUE;
236 
237 cleanup:
238 	if (ret) {
239 		*certp = cert;
240 	} else {
241 		if (cert != NULL)
242 			elfcertlib_freecert(ess, cert);
243 		if (signer_DN != NULL)
244 			cryptoerror(LOG_ERR, "unable to find a certificate "
245 			    "for DN: %s", signer_DN);
246 		else
247 			cryptoerror(LOG_ERR, "unable to load certificate "
248 			    "from %s", cert_pathname);
249 	}
250 	return (ret);
251 }
252 
253 /*
254  * elfcertlib_loadprivatekey - Load the private key from path
255  *
256  * IN	ess		- elfsign context structure
257  *	cert
258  *	pathname
259  * OUT	cert
260  * RETURNS	TRUE/FALSE
261  */
262 boolean_t
263 elfcertlib_loadprivatekey(ELFsign_t ess, ELFCert_t cert, const char *pathname)
264 {
265 	KMF_RETURN rv = KMF_OK;
266 	uint32_t nkeys = 2;
267 	KMF_FINDKEY_PARAMS fkparams;
268 	KMF_KEY_HANDLE	keybuf[2];
269 
270 	(void) memset(&fkparams, 0, sizeof (fkparams));
271 	fkparams.keyclass = KMF_ASYM_PRI;
272 	fkparams.kstype = KMF_KEYSTORE_OPENSSL;
273 	fkparams.sslparms.keyfile = (char *)pathname;
274 
275 	rv = KMF_FindKey(ess->es_kmfhandle, &fkparams, keybuf, &nkeys);
276 	if (rv != KMF_OK)
277 		return (B_FALSE);
278 	if (nkeys != 1) {
279 		/* lack of specificity */
280 		cryptodebug("found %d keys at %s", nkeys, pathname);
281 		return (B_FALSE);
282 	}
283 	cert->c_privatekey = keybuf[0];
284 	cryptodebug("key %s loaded", pathname);
285 	return (B_TRUE);
286 }
287 
288 /*
289  * elfcertlib_loadtokenkey - Load the private key from token
290  *
291  * IN	ess		- elfsign context structure
292  *	cert
293  *	token_label
294  *	pin
295  * OUT	cert
296  * RETURNS	TRUE/FALSE
297  */
298 boolean_t
299 elfcertlib_loadtokenkey(ELFsign_t ess, ELFCert_t cert,
300     const char *token_label, const char *pin)
301 {
302 	KMF_RETURN rv = KMF_OK;
303 	KMF_FINDKEY_PARAMS fkparams;
304 	KMF_CONFIG_PARAMS cfgparams;
305 	uint32_t nkeys = 1;
306 	char *idstr = NULL;
307 	char *err = NULL;
308 
309 	(void) memset(&fkparams, 0, sizeof (fkparams));
310 	(void) memset(&cfgparams, 0, sizeof (cfgparams));
311 
312 	cfgparams.kstype = KMF_KEYSTORE_PK11TOKEN;
313 	cfgparams.pkcs11config.label = (char *)token_label;
314 	cfgparams.pkcs11config.readonly = B_TRUE;
315 	rv = KMF_ConfigureKeystore(ess->es_kmfhandle, &cfgparams);
316 	if (rv != KMF_OK) {
317 		if (KMF_GetKMFErrorString(rv, &err) == KMF_OK) {
318 			cryptodebug("Error configuring token access:"
319 			    " %s\n", err);
320 			free(err);
321 		}
322 		return (B_FALSE);
323 	}
324 
325 	fkparams.idstr = idstr;
326 	fkparams.kstype = KMF_KEYSTORE_PK11TOKEN;
327 	fkparams.keyclass = KMF_ASYM_PRI;
328 	fkparams.cred.cred = (char *)pin;
329 	fkparams.cred.credlen = (pin != NULL ? strlen(pin) : 0);
330 	fkparams.pkcs11parms.private = B_TRUE;
331 
332 	/*
333 	 * We will search for the key based on the ID attribute
334 	 * which was added when the key was created.  ID is
335 	 * a SHA-1 hash of the public modulus shared by the
336 	 * key and the certificate.
337 	 */
338 	rv = KMF_GetCertIDString(&cert->c_cert.certificate, &idstr);
339 	if (rv != KMF_OK) {
340 		if (KMF_GetKMFErrorString(rv, &err) == KMF_OK) {
341 			cryptodebug("Error getting ID from cert: %s\n", err);
342 			free(err);
343 		}
344 		return (B_FALSE);
345 	}
346 	fkparams.idstr = idstr;
347 
348 	rv = KMF_FindKey(ess->es_kmfhandle, &fkparams,
349 	    &cert->c_privatekey, &nkeys);
350 	if (rv != KMF_OK || nkeys != 1) {
351 		if (KMF_GetKMFErrorString(rv, &err) == KMF_OK) {
352 			cryptodebug("Error finding private key: %s\n", err);
353 			free(err);
354 		}
355 		free(idstr);
356 		return (B_FALSE);
357 	}
358 	cryptodebug("key found in %s", token_label);
359 	cryptodebug("elfcertlib_loadprivatekey = 0x%.8X",
360 	    &cert->c_privatekey);
361 
362 	free(idstr);
363 	return (B_TRUE);
364 }
365 
366 static const CK_BYTE MD5_DER_PREFIX[] = {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08,
367 	0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10};
368 
369 /*
370  * elfcertlib_sign - sign the given DATA using the privatekey in cert
371  *
372  * IN	ess		- elfsign context structure
373  *	cert
374  *	data
375  *	data_len
376  * OUT	sig	- must be big enough to hold the signature of data
377  *		  Caller must allocate
378  *	sig_len	- actual length used; 0 on failure.
379  * RETURNS	TRUE/FALSE
380  */
381 /*ARGSUSED*/
382 boolean_t
383 elfcertlib_sign(ELFsign_t ess, ELFCert_t cert,
384 	const uchar_t *data, size_t data_len,
385 	uchar_t *sig, size_t *sig_len)
386 {
387 	KMF_RETURN ret = KMF_OK;
388 	KMF_DATA tobesigned;
389 	KMF_DATA signature;
390 	uchar_t	 der_data[sizeof (MD5_DER_PREFIX) + MD5_DIGEST_LENGTH];
391 
392 	if (ess->es_version <= FILESIG_VERSION2) {
393 		/* compatibility: take MD5 hash of SHA1 hash */
394 		size_t	derlen = MD5_DIGEST_LENGTH;
395 		MD5_CTX ctx;
396 
397 		/*
398 		 * first: digest using software-based methods, don't
399 		 * rely on the token for hashing.
400 		 */
401 		MD5Init(&ctx);
402 		MD5Update(&ctx, data, data_len);
403 		MD5Final(&der_data[sizeof (MD5_DER_PREFIX)], &ctx);
404 
405 		/*
406 		 * second: insert prefix
407 		 */
408 		(void) memcpy(der_data, MD5_DER_PREFIX,
409 		    sizeof (MD5_DER_PREFIX));
410 		/*
411 		 * prepare to sign the local buffer
412 		 */
413 		tobesigned.Data = (uchar_t *)der_data;
414 		tobesigned.Length = sizeof (MD5_DER_PREFIX) + derlen;
415 	} else {
416 		tobesigned.Data = (uchar_t *)data;
417 		tobesigned.Length = data_len;
418 	}
419 
420 	signature.Data = (uchar_t *)sig;
421 	signature.Length = *sig_len;
422 
423 	ret = KMF_SignDataWithKey(ess->es_kmfhandle,
424 	    &cert->c_privatekey, (KMF_OID *)&KMFOID_RSA,
425 	    &tobesigned, &signature);
426 
427 	if (ret != KMF_OK) {
428 		char *err;
429 		if (KMF_GetKMFErrorString(ret, &err) == KMF_OK &&
430 		    err != NULL) {
431 			cryptodebug("Error signing data: %s\n", err);
432 			free(err);
433 		}
434 		*sig_len = 0;
435 		return (B_FALSE);
436 	}
437 	*sig_len = signature.Length;
438 	return (B_TRUE);
439 }
440 
441 /*
442  * elfcertlib_verifysig - verify the given DATA using the public key in cert
443  *
444  * IN	ess		- elfsign context structure
445  *	cert
446  *	signature
447  *	sig_len
448  *	data
449  *	data_len
450  * OUT	N/A
451  * RETURNS	TRUE/FALSE
452  */
453 boolean_t
454 elfcertlib_verifysig(ELFsign_t ess, ELFCert_t cert,
455 	const uchar_t *signature, size_t sig_len,
456 	const uchar_t *data, size_t data_len)
457 {
458 	KMF_RETURN	rv;
459 	KMF_DATA	indata;
460 	KMF_DATA	insig;
461 	KMF_ALGORITHM_INDEX algid;
462 
463 	indata.Data = (uchar_t *)data;
464 	indata.Length = data_len;
465 	insig.Data = (uchar_t *)signature;
466 	insig.Length = sig_len;
467 
468 	if (ess->es_version <= FILESIG_VERSION2)
469 		algid = KMF_ALGID_MD5WithRSA;
470 	else
471 		algid = KMF_ALGID_RSA;
472 
473 	/*
474 	 * We tell KMF to use the PKCS11 verification APIs
475 	 * here to prevent the use of OpenSSL and to keep
476 	 * all validation within the FIPS-140 boundary for
477 	 * the Cryptographic Framework.
478 	 */
479 	rv = KMF_VerifyDataWithCert(ess->es_kmfhandle,
480 	    KMF_KEYSTORE_PK11TOKEN, algid,
481 	    &indata, &insig, &cert->c_cert.certificate);
482 
483 	return ((rv == KMF_OK));
484 }
485 
486 /*
487  * elfcertlib_getdn
488  *
489  * IN	cert
490  * OUT	NONE
491  * RETURN 	dn or NULL
492  */
493 char *
494 elfcertlib_getdn(ELFCert_t cert)
495 {
496 	cryptodebug("elfcertlib_getdn");
497 
498 	return (cert->c_subject);
499 }
500 
501 /*
502  * elfcertlib_getissuer
503  *
504  * IN	cert
505  * OUT	NONE
506  * RETURN 	dn or NULL
507  */
508 char *
509 elfcertlib_getissuer(ELFCert_t cert)
510 {
511 	cryptodebug("elfcertlib_issuer");
512 
513 	return (cert->c_issuer);
514 }
515 
516 boolean_t
517 elfcertlib_init(ELFsign_t ess)
518 {
519 	boolean_t rc = B_TRUE;
520 	KMF_RETURN rv;
521 	if (ess->es_kmfhandle == NULL) {
522 		rv = KMF_Initialize(&ess->es_kmfhandle, NULL, NULL);
523 		if (rv != KMF_OK) {
524 			cryptoerror(LOG_ERR,
525 			    "unable to initialize KMF library");
526 			rc = B_FALSE;
527 		}
528 	}
529 	return (rc);
530 }
531 
532 void
533 elfcertlib_fini(ELFsign_t ess)
534 {
535 	(void) KMF_Finalize(ess->es_kmfhandle);
536 }
537 
538 /*
539  * set the token device
540  */
541 boolean_t
542 elfcertlib_settoken(ELFsign_t ess, char *token)
543 {
544 	boolean_t rc = B_TRUE;
545 	KMF_RETURN rv;
546 	KMF_CONFIG_PARAMS cfgparams;
547 
548 	(void) memset(&cfgparams, 0, sizeof (cfgparams));
549 	cfgparams.kstype = KMF_KEYSTORE_PK11TOKEN;
550 	cfgparams.pkcs11config.label = token;
551 	cfgparams.pkcs11config.readonly = B_TRUE;
552 	rv = KMF_ConfigureKeystore(ess->es_kmfhandle, &cfgparams);
553 	if (rv != KMF_OK) {
554 		cryptoerror(LOG_ERR, "unable to select token\n");
555 		rc = B_FALSE;
556 	}
557 
558 	return (rc);
559 }
560 
561 /*
562  * set the certificate CA identification callback
563  */
564 void
565 elfcertlib_setcertCAcallback(ELFsign_t ess,
566     void (*cb)(void *, ELFCert_t, char *))
567 {
568 	ess->es_certCAcallback = cb;
569 }
570 
571 /*
572  * set the certificate verification callback
573  */
574 void
575 elfcertlib_setcertvercallback(ELFsign_t ess,
576     void (*cb)(void *, ELFCert_t, ELFCert_t))
577 {
578 	ess->es_certvercallback = cb;
579 }
580 
581 
582 /*
583  * elfcertlib_releasecert - release a cert
584  *
585  * IN cert
586  * OUT cert
587  * RETURN	N/A
588  *
589  */
590 void
591 elfcertlib_releasecert(ELFsign_t ess, ELFCert_t cert)
592 {
593 	elfcertlib_freecert(ess, cert);
594 }
595 
596 /*
597  * elfcertlib_allocatecert - create a new ELFCert_t
598  *
599  * IN N/A
600  * OUT	N/A
601  * RETURN 	ELFCert_t, NULL on failure.
602  */
603 static ELFCert_t
604 elfcertlib_allocatecert(void)
605 {
606 	ELFCert_t cert = NULL;
607 
608 	cert = malloc(sizeof (struct ELFCert_s));
609 	if (cert == NULL) {
610 		cryptoerror(LOG_ERR,
611 		    "elfcertlib_allocatecert: malloc failed %s",
612 		    strerror(errno));
613 		return (NULL);
614 	}
615 	(void) memset(cert, 0, sizeof (struct ELFCert_s));
616 	cert->c_verified = E_UNCHECKED;
617 	cert->c_subject = NULL;
618 	cert->c_issuer = NULL;
619 	return (cert);
620 }
621 
622 /*
623  * elfcertlib_freecert - freeup the memory of a cert
624  *
625  * IN cert
626  * OUT cert
627  * RETURN	N/A
628  *
629  */
630 static void
631 elfcertlib_freecert(ELFsign_t ess, ELFCert_t cert)
632 {
633 	if (cert == NULL)
634 		return;
635 
636 	free(cert->c_subject);
637 	free(cert->c_issuer);
638 
639 	KMF_FreeKMFCert(ess->es_kmfhandle, &cert->c_cert);
640 	KMF_FreeKMFKey(ess->es_kmfhandle, &cert->c_privatekey);
641 
642 	free(cert);
643 }
644