1 /*
2  * GnuTLS PKCS#11 support
3  * Copyright (C) 2010-2016 Free Software Foundation, Inc.
4  * Copyright (C) 2016 Red Hat
5  *
6  * Authors: Nikos Mavrogiannopoulos
7  *
8  * GnuTLS is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program.  If not, see <https://www.gnu.org/licenses/>
20  */
21 
22 #include "gnutls_int.h"
23 #include <gnutls/pkcs11.h>
24 #include <global.h>
25 #include "errors.h"
26 #include "x509/common.h"
27 
28 #include <pkcs11_int.h>
29 #include <p11-kit/p11-kit.h>
30 #include "pkcs11x.h"
31 
32 struct find_ext_data_st {
33 	/* in */
34 	gnutls_pkcs11_obj_t obj;
35 	gnutls_datum_t spki;
36 
37 	/* out */
38 	gnutls_x509_ext_st *exts;
39 	unsigned int exts_size;
40 };
41 
override_ext(gnutls_x509_crt_t crt,gnutls_datum_t * ext)42 static int override_ext(gnutls_x509_crt_t crt, gnutls_datum_t *ext)
43 {
44 	gnutls_x509_ext_st parsed;
45 	int ret;
46 
47 	ret = _gnutls_x509_decode_ext(ext, &parsed);
48 	if (ret < 0)
49 		return gnutls_assert_val(ret);
50 
51 	/* set the new extension */
52 	ret = _gnutls_x509_crt_set_extension(crt, parsed.oid, &parsed.data, parsed.critical);
53 	if (ret < 0) {
54 		gnutls_assert();
55 		goto cleanup;
56 	}
57 
58 	ret = 0;
59  cleanup:
60 	gnutls_x509_ext_deinit(&parsed);
61 	return ret;
62 }
63 
64 /* This function re-encodes a certificate to contain its stapled extensions.
65  * That assumes that the certificate is not in the distrusted list.
66  */
pkcs11_override_cert_exts(struct pkcs11_session_info * sinfo,gnutls_datum_t * spki,gnutls_datum_t * der)67 int pkcs11_override_cert_exts(struct pkcs11_session_info *sinfo, gnutls_datum_t *spki, gnutls_datum_t *der)
68 {
69 	int ret;
70 	gnutls_datum_t new_der = {NULL, 0};
71 	struct ck_attribute a[2];
72 	struct ck_attribute b[1];
73 	unsigned long count;
74 	unsigned ext_data_size = der->size;
75 	uint8_t *ext_data = NULL;
76 	ck_object_class_t class = -1;
77 	gnutls_x509_crt_t crt = NULL;
78 	unsigned finalize = 0;
79 	ck_rv_t rv;
80 	ck_object_handle_t obj;
81 
82 	if (sinfo->trusted == 0) {
83 		_gnutls_debug_log("p11: cannot override extensions on a non-p11-kit trust module\n");
84 		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
85 	}
86 
87 	/* retrieve the extensions */
88 	class = CKO_X_CERTIFICATE_EXTENSION;
89 	a[0].type = CKA_CLASS;
90 	a[0].value = &class;
91 	a[0].value_len = sizeof class;
92 
93 	a[1].type = CKA_PUBLIC_KEY_INFO;
94 	a[1].value = spki->data;
95 	a[1].value_len = spki->size;
96 
97 	rv = pkcs11_find_objects_init(sinfo->module, sinfo->pks, a, 2);
98 	if (rv != CKR_OK) {
99 		gnutls_assert();
100 		_gnutls_debug_log
101 		    ("p11: FindObjectsInit failed for cert extensions.\n");
102 		ret = pkcs11_rv_to_err(rv);
103 		goto cleanup;
104 	}
105 	finalize = 1;
106 
107 	rv = pkcs11_find_objects(sinfo->module, sinfo->pks, &obj, 1, &count);
108 	if (rv == CKR_OK && count == 1) {
109 		ext_data = gnutls_malloc(ext_data_size);
110 		if (ext_data == NULL) {
111 			gnutls_assert();
112 			ret = GNUTLS_E_MEMORY_ERROR;
113 			goto cleanup;
114 		}
115 
116 		ret = gnutls_x509_crt_init(&crt);
117 		if (ret < 0) {
118 			gnutls_assert();
119 			goto cleanup;
120 		}
121 
122 		ret = gnutls_x509_crt_import(crt, der, GNUTLS_X509_FMT_DER);
123 		if (ret < 0) {
124 			gnutls_assert();
125 			goto cleanup;
126 		}
127 
128 		do {
129 
130 			b[0].type = CKA_VALUE;
131 			b[0].value = ext_data;
132 			b[0].value_len = ext_data_size;
133 
134 			if (pkcs11_get_attribute_value
135 			    (sinfo->module, sinfo->pks, obj, b, 1) == CKR_OK) {
136 				gnutls_datum_t data = { b[0].value, b[0].value_len };
137 
138 				ret = override_ext(crt, &data);
139 				if (ret < 0) {
140 					gnutls_assert();
141 					goto cleanup;
142 				}
143 			}
144 		} while (pkcs11_find_objects(sinfo->module, sinfo->pks, &obj, 1, &count) == CKR_OK && count == 1);
145 
146 		/* overwrite the old certificate with the new */
147 		ret = gnutls_x509_crt_export2(crt, GNUTLS_X509_FMT_DER, &new_der);
148 		if (ret < 0) {
149 			gnutls_assert();
150 			goto cleanup;
151 		}
152 
153 		gnutls_free(der->data);
154 		der->data = new_der.data;
155 		der->size = new_der.size;
156 	}
157 
158 	ret = 0;
159  cleanup:
160 	if (crt != NULL)
161 		gnutls_x509_crt_deinit(crt);
162 	if (finalize != 0)
163 		pkcs11_find_objects_final(sinfo);
164 	gnutls_free(ext_data);
165 	return ret;
166 
167 }
168 
169 static int
find_ext_cb(struct ck_function_list * module,struct pkcs11_session_info * sinfo,struct ck_token_info * tinfo,struct ck_info * lib_info,void * input)170 find_ext_cb(struct ck_function_list *module, struct pkcs11_session_info *sinfo,
171 	    struct ck_token_info *tinfo, struct ck_info *lib_info,
172 	    void *input)
173 {
174 	struct find_ext_data_st *find_data = input;
175 	struct ck_attribute a[4];
176 	ck_object_class_t class = -1;
177 	unsigned long count;
178 	ck_rv_t rv;
179 	ck_object_handle_t obj;
180 	int ret;
181 	gnutls_datum_t ext;
182 
183 	if (tinfo == NULL) {	/* we don't support multiple calls */
184 		gnutls_assert();
185 		return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
186 	}
187 
188 	/* do not bother reading the token if basic fields do not match
189 	 */
190 	if (!p11_kit_uri_match_token_info
191 	    (find_data->obj->info, tinfo)
192 	    || !p11_kit_uri_match_module_info(find_data->obj->info,
193 					      lib_info)) {
194 		gnutls_assert();
195 		return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
196 	}
197 
198 	/* retrieve the extensions */
199 	class = CKO_X_CERTIFICATE_EXTENSION;
200 	a[0].type = CKA_CLASS;
201 	a[0].value = &class;
202 	a[0].value_len = sizeof class;
203 
204 	a[1].type = CKA_PUBLIC_KEY_INFO;
205 	a[1].value = find_data->spki.data;
206 	a[1].value_len = find_data->spki.size;
207 
208 	rv = pkcs11_find_objects_init(sinfo->module, sinfo->pks, a, 2);
209 	if (rv != CKR_OK) {
210 		gnutls_assert();
211 		_gnutls_debug_log
212 		    ("p11: FindObjectsInit failed for cert extensions.\n");
213 		return pkcs11_rv_to_err(rv);
214 	}
215 
216 	while(pkcs11_find_objects(sinfo->module, sinfo->pks, &obj, 1, &count) == CKR_OK && count == 1) {
217 		rv = pkcs11_get_attribute_avalue(sinfo->module, sinfo->pks, obj, CKA_VALUE, &ext);
218 		if (rv == CKR_OK) {
219 
220 			find_data->exts = gnutls_realloc_fast(find_data->exts, (1+find_data->exts_size)*sizeof(find_data->exts[0]));
221 			if (find_data->exts == NULL) {
222 				gnutls_assert();
223 				ret = pkcs11_rv_to_err(rv);
224 				goto cleanup;
225 			}
226 
227 			if (_gnutls_x509_decode_ext(&ext, &find_data->exts[find_data->exts_size]) == 0) {
228 				find_data->exts_size++;
229 			}
230 			gnutls_free(ext.data);
231 		}
232 	}
233 
234 	ret = 0;
235  cleanup:
236 	pkcs11_find_objects_final(sinfo);
237 	return ret;
238 }
239 
240 /**
241  * gnutls_pkcs11_obj_get_exts:
242  * @obj: should contain a #gnutls_pkcs11_obj_t type
243  * @exts: a pointer to a %gnutls_x509_ext_st pointer
244  * @exts_size: will be updated with the number of @exts
245  * @flags: Or sequence of %GNUTLS_PKCS11_OBJ_* flags
246  *
247  * This function will return information about attached extensions
248  * that associate to the provided object (which should be a certificate).
249  * The extensions are the attached p11-kit trust module extensions.
250  *
251  * Each element of @exts must be deinitialized using gnutls_x509_ext_deinit()
252  * while @exts should be deallocated using gnutls_free().
253  *
254  * Returns: %GNUTLS_E_SUCCESS (0) on success or a negative error code on error.
255  *
256  * Since: 3.3.8
257  **/
258 int
gnutls_pkcs11_obj_get_exts(gnutls_pkcs11_obj_t obj,gnutls_x509_ext_st ** exts,unsigned int * exts_size,unsigned int flags)259 gnutls_pkcs11_obj_get_exts(gnutls_pkcs11_obj_t obj,
260 			   gnutls_x509_ext_st **exts, unsigned int *exts_size,
261 			   unsigned int flags)
262 {
263 	int ret;
264 	gnutls_datum_t spki = {NULL, 0};
265 	struct find_ext_data_st find_data;
266 	unsigned deinit_spki = 0;
267 
268 	PKCS11_CHECK_INIT;
269 	memset(&find_data, 0, sizeof(find_data));
270 
271 	*exts_size = 0;
272 
273 	if (obj->type != GNUTLS_PKCS11_OBJ_X509_CRT && obj->type != GNUTLS_PKCS11_OBJ_PUBKEY)
274 		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
275 
276 	if (obj->type == GNUTLS_PKCS11_OBJ_PUBKEY) {
277 		spki.data = obj->raw.data;
278 		spki.size = obj->raw.size;
279 	} else {
280 		ret = _gnutls_x509_raw_crt_to_raw_pubkey(&obj->raw, &spki);
281 		if (ret < 0)
282 			return gnutls_assert_val(ret);
283 		deinit_spki = 1;
284 	}
285 
286 	find_data.spki.data = spki.data;
287 	find_data.spki.size = spki.size;
288 	find_data.obj = obj;
289 	ret =
290 	    _pkcs11_traverse_tokens(find_ext_cb, &find_data, obj->info,
291 				    &obj->pin,
292 				    pkcs11_obj_flags_to_int(flags));
293 	if (ret < 0) {
294 		gnutls_assert();
295 		goto cleanup;
296 	}
297 
298 	*exts = find_data.exts;
299 	*exts_size = find_data.exts_size;
300 
301 	ret = 0;
302  cleanup:
303 	if (deinit_spki)
304 		gnutls_free(spki.data);
305 	return ret;
306 }
307 
308