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