xref: /titanic_51/usr/src/cmd/svr4pkg/pkgadm/certs.c (revision 2bc987325e3ded1865bff043128661815c4690b9)
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 
28 #include <stdio.h>
29 #include <limits.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <pkglocs.h>
34 #include <locale.h>
35 #include <libintl.h>
36 #include <signal.h>
37 #include <sys/stat.h>
38 #include <sys/statvfs.h>
39 #include <sys/types.h>
40 #include <fcntl.h>
41 #include <libintl.h>
42 #include <dirent.h>
43 #include <openssl/err.h>
44 #include <openssl/pkcs7.h>
45 #include <openssl/pkcs12.h>
46 #include <openssl/x509.h>
47 #include <openssl/pem.h>
48 #include <openssl/x509v3.h>
49 
50 #include <pkglib.h>
51 #include <p12lib.h>
52 #include <install.h>
53 #include <libadm.h>
54 #include <libinst.h>
55 #include "pkgadm.h"
56 #include "pkgadm_msgs.h"
57 
58 
59 /*
60  * Function:	load_cert_and_key
61  * Description:	Loads a public key certificate and associated private key
62  *		from a stream.
63  * Parameters:	err	- Where to write errors to for underlying library calls
64  *		incert - File to read certs and keys from
65  *		format - The format of the file
66  *		passarg - How to collect password if needed to decrypt file
67  *		key - Location to store resulting key if found
68  *		cert - Location to store resulting cert if found.
69  *
70  * Returns:	f one or more certificates are found in the file,
71  *		and one or more keys are found, then the first
72  *		certificate is used, and the keys are searched for a
73  *		match.  If no key matches the cert, then only the cert
74  *		is returned.  If no certs are found, but one or more
75  *		keys are found, then the first key is returned.
76  */
77 int
78 load_cert_and_key(PKG_ERR *err, FILE *incert,
79     keystore_encoding_format_t format, char *passarg, EVP_PKEY **key,
80     X509 **cert)
81 {
82 	X509 *tmpcert = NULL;
83 	EVP_PKEY *tmpkey = NULL;
84 	STACK_OF(EVP_PKEY)	*keys = NULL;
85 	STACK_OF(X509)		*certs = NULL;
86 	int i, ret = 0;
87 	keystore_passphrase_data	data;
88 	unsigned long crypto_err;
89 
90 	if (key) *key = NULL;
91 	if (cert) *cert = NULL;
92 
93 	switch (format) {
94 	case KEYSTORE_FORMAT_DER:
95 		/* first try to load a DER cert, which cannot contain a key */
96 		if ((tmpcert = d2i_X509_fp(incert, NULL)) == NULL) {
97 			log_msg(LOG_MSG_ERR, MSG_PARSE);
98 			ret = 1;
99 		}
100 		break;
101 	case KEYSTORE_FORMAT_PEM:
102 	default:
103 		data.err = err;
104 		set_passphrase_passarg(passarg);
105 		set_passphrase_prompt(gettext("Enter PEM passphrase:"));
106 		if (sunw_PEM_contents(incert, pkg_passphrase_cb,
107 		    &data, &keys, &certs) < 0) {
108 			/* print out openssl-generated PEM errors */
109 			while ((crypto_err = ERR_get_error()) != 0) {
110 				log_msg(LOG_MSG_ERR,
111 				    ERR_reason_error_string(crypto_err));
112 			}
113 			ret = 1;
114 			goto cleanup;
115 		}
116 
117 		/* take the first cert in the file, if any */
118 		if (cert && (certs != NULL)) {
119 			if (sk_X509_num(certs) != 1) {
120 				log_msg(LOG_MSG_ERR, MSG_MULTIPLE_CERTS);
121 				ret = 1;
122 				goto cleanup;
123 			} else {
124 				tmpcert = sk_X509_value(certs, 0);
125 			}
126 		}
127 
128 		if (key && (keys != NULL)) {
129 			if (tmpcert != NULL) {
130 				/*
131 				 * if we found a cert and some keys,
132 				 * only return the key that
133 				 * matches the cert
134 				 */
135 				for (i = 0; i < sk_EVP_PKEY_num(keys); i++) {
136 					if (X509_check_private_key(tmpcert,
137 					    sk_EVP_PKEY_value(keys, i))) {
138 						tmpkey =
139 						    sk_EVP_PKEY_value(keys, i);
140 						break;
141 					}
142 				}
143 			} else {
144 				if (sk_EVP_PKEY_num(keys) > 0) {
145 					tmpkey = sk_EVP_PKEY_value(keys, 0);
146 				}
147 			}
148 		}
149 		break;
150 	}
151 
152 	/* set results */
153 	if (key && tmpkey) {
154 		*key = tmpkey;
155 		tmpkey = NULL;
156 	}
157 
158 	if (cert && tmpcert) {
159 		*cert = tmpcert;
160 		tmpcert = NULL;
161 	}
162 
163 cleanup:
164 	if (tmpcert != NULL) {
165 		X509_free(tmpcert);
166 	}
167 	if (tmpkey != NULL) {
168 		sunw_evp_pkey_free(tmpkey);
169 	}
170 	return (ret);
171 }
172 
173 /*
174  * Function:	load_all_certs
175  * Description:	Loads alll certificates from a stream.
176  * Parameters:	err	- Where to write errors to for underlying library calls
177  *		incert - File to read certs and keys from
178  *		format - The format of the file
179  *		passarg - How to collect password if needed to decrypt file
180  *		certs - Location to store resulting cert if found.
181  *
182  * Returns:	0 - success, all certs placed in ''certs'
183  *		non-zero failure, errors in 'err'
184  */
185 int
186 load_all_certs(PKG_ERR *err, FILE *incert,
187     keystore_encoding_format_t format, char *passarg, STACK_OF(X509) **certs)
188 {
189 	X509 *tmpcert = NULL;
190 	STACK_OF(X509) *tmpcerts = NULL;
191 	int ret = 0;
192 	keystore_passphrase_data	data;
193 	unsigned long crypto_err;
194 	if (certs) *certs = NULL;
195 
196 	switch (format) {
197 	case KEYSTORE_FORMAT_DER:
198 		/* first try to load a DER cert, which cannot contain a key */
199 		if ((tmpcert = d2i_X509_fp(incert, NULL)) == NULL) {
200 		    log_msg(LOG_MSG_ERR, MSG_PARSE);
201 			ret = 1;
202 			goto cleanup;
203 		}
204 
205 		if ((tmpcerts = sk_X509_new_null()) == NULL) {
206 			log_msg(LOG_MSG_ERR, MSG_MEM);
207 			ret = 1;
208 			goto cleanup;
209 		}
210 		sk_X509_push(tmpcerts, tmpcert);
211 		break;
212 	case KEYSTORE_FORMAT_PEM:
213 	default:
214 		data.err = err;
215 		set_passphrase_prompt(MSG_PEM_PASSPROMPT);
216 		set_passphrase_passarg(passarg);
217 		if (sunw_PEM_contents(incert, pkg_passphrase_cb,
218 		    &data, NULL, &tmpcerts) < 0) {
219 			/* print out openssl-generated PEM errors */
220 			while ((crypto_err = ERR_get_error()) != 0) {
221 				log_msg(LOG_MSG_ERR,
222 				    ERR_reason_error_string(crypto_err));
223 			}
224 		}
225 		break;
226 	}
227 
228 	/* set results */
229 	if (certs && tmpcerts) {
230 		*certs = tmpcerts;
231 		tmpcerts = NULL;
232 	}
233 
234 cleanup:
235 	if (tmpcerts != NULL) {
236 		sk_X509_free(tmpcerts);
237 	}
238 	return (ret);
239 }
240