/src/samba/third_party/heimdal/lib/hx509/ca.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2006 - 2010 Kungliga Tekniska Högskolan |
3 | | * (Royal Institute of Technology, Stockholm, Sweden). |
4 | | * All rights reserved. |
5 | | * |
6 | | * Redistribution and use in source and binary forms, with or without |
7 | | * modification, are permitted provided that the following conditions |
8 | | * are met: |
9 | | * |
10 | | * 1. Redistributions of source code must retain the above copyright |
11 | | * notice, this list of conditions and the following disclaimer. |
12 | | * |
13 | | * 2. Redistributions in binary form must reproduce the above copyright |
14 | | * notice, this list of conditions and the following disclaimer in the |
15 | | * documentation and/or other materials provided with the distribution. |
16 | | * |
17 | | * 3. Neither the name of the Institute nor the names of its contributors |
18 | | * may be used to endorse or promote products derived from this software |
19 | | * without specific prior written permission. |
20 | | * |
21 | | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND |
22 | | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
23 | | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
24 | | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE |
25 | | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
26 | | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
27 | | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
28 | | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
29 | | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
30 | | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
31 | | * SUCH DAMAGE. |
32 | | */ |
33 | | |
34 | | #include "hx_locl.h" |
35 | | |
36 | | /** |
37 | | * @page page_ca Hx509 CA functions |
38 | | * |
39 | | * See the library functions here: @ref hx509_ca |
40 | | */ |
41 | | |
42 | | struct hx509_ca_tbs { |
43 | | hx509_name subject; |
44 | | SubjectPublicKeyInfo spki; |
45 | | KeyUsage ku; |
46 | | ExtKeyUsage eku; |
47 | | GeneralNames san; |
48 | | CertificatePolicies cps; |
49 | | PolicyMappings pms; |
50 | | heim_integer serial; |
51 | | struct { |
52 | | unsigned int proxy:1; |
53 | | unsigned int ca:1; |
54 | | unsigned int key:1; |
55 | | unsigned int serial:1; |
56 | | unsigned int domaincontroller:1; |
57 | | unsigned int xUniqueID:1; |
58 | | } flags; |
59 | | time_t notBefore; |
60 | | time_t notAfter; |
61 | | HeimPkinitPrincMaxLifeSecs pkinitTicketMaxLife; |
62 | | int pathLenConstraint; /* both for CA and Proxy */ |
63 | | CRLDistributionPoints crldp; |
64 | | heim_bit_string subjectUniqueID; |
65 | | heim_bit_string issuerUniqueID; |
66 | | AlgorithmIdentifier *sigalg; |
67 | | }; |
68 | | |
69 | | /** |
70 | | * Allocate an to-be-signed certificate object that will be converted |
71 | | * into an certificate. |
72 | | * |
73 | | * @param context A hx509 context. |
74 | | * @param tbs returned to-be-signed certicate object, free with |
75 | | * hx509_ca_tbs_free(). |
76 | | * |
77 | | * @return An hx509 error code, see hx509_get_error_string(). |
78 | | * |
79 | | * @ingroup hx509_ca |
80 | | */ |
81 | | |
82 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
83 | | hx509_ca_tbs_init(hx509_context context, hx509_ca_tbs *tbs) |
84 | 0 | { |
85 | 0 | *tbs = calloc(1, sizeof(**tbs)); |
86 | 0 | if (*tbs == NULL) |
87 | 0 | return ENOMEM; |
88 | | |
89 | 0 | return 0; |
90 | 0 | } |
91 | | |
92 | | /** |
93 | | * Free an To Be Signed object. |
94 | | * |
95 | | * @param tbs object to free. |
96 | | * |
97 | | * @ingroup hx509_ca |
98 | | */ |
99 | | |
100 | | HX509_LIB_FUNCTION void HX509_LIB_CALL |
101 | | hx509_ca_tbs_free(hx509_ca_tbs *tbs) |
102 | 0 | { |
103 | 0 | if (tbs == NULL || *tbs == NULL) |
104 | 0 | return; |
105 | | |
106 | 0 | free_SubjectPublicKeyInfo(&(*tbs)->spki); |
107 | 0 | free_CertificatePolicies(&(*tbs)->cps); |
108 | 0 | free_PolicyMappings(&(*tbs)->pms); |
109 | 0 | free_GeneralNames(&(*tbs)->san); |
110 | 0 | free_ExtKeyUsage(&(*tbs)->eku); |
111 | 0 | der_free_heim_integer(&(*tbs)->serial); |
112 | 0 | free_CRLDistributionPoints(&(*tbs)->crldp); |
113 | 0 | der_free_bit_string(&(*tbs)->subjectUniqueID); |
114 | 0 | der_free_bit_string(&(*tbs)->issuerUniqueID); |
115 | 0 | if ((*tbs)->subject) |
116 | 0 | hx509_name_free(&(*tbs)->subject); |
117 | 0 | if ((*tbs)->sigalg) { |
118 | 0 | free_AlgorithmIdentifier((*tbs)->sigalg); |
119 | 0 | free((*tbs)->sigalg); |
120 | 0 | } |
121 | |
|
122 | 0 | memset(*tbs, 0, sizeof(**tbs)); |
123 | 0 | free(*tbs); |
124 | 0 | *tbs = NULL; |
125 | 0 | } |
126 | | |
127 | | /** |
128 | | * Set the absolute time when the certificate is valid from. If not |
129 | | * set the current time will be used. |
130 | | * |
131 | | * @param context A hx509 context. |
132 | | * @param tbs object to be signed. |
133 | | * @param t time the certificated will start to be valid |
134 | | * |
135 | | * @return An hx509 error code, see hx509_get_error_string(). |
136 | | * |
137 | | * @ingroup hx509_ca |
138 | | */ |
139 | | |
140 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
141 | | hx509_ca_tbs_set_notBefore(hx509_context context, |
142 | | hx509_ca_tbs tbs, |
143 | | time_t t) |
144 | 0 | { |
145 | 0 | tbs->notBefore = t; |
146 | 0 | return 0; |
147 | 0 | } |
148 | | |
149 | | /** |
150 | | * Set the absolute time when the certificate is valid to. |
151 | | * |
152 | | * @param context A hx509 context. |
153 | | * @param tbs object to be signed. |
154 | | * @param t time when the certificate will expire |
155 | | * |
156 | | * @return An hx509 error code, see hx509_get_error_string(). |
157 | | * |
158 | | * @ingroup hx509_ca |
159 | | */ |
160 | | |
161 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
162 | | hx509_ca_tbs_set_notAfter(hx509_context context, |
163 | | hx509_ca_tbs tbs, |
164 | | time_t t) |
165 | 0 | { |
166 | 0 | tbs->notAfter = t; |
167 | 0 | return 0; |
168 | 0 | } |
169 | | |
170 | | /** |
171 | | * Set the relative time when the certificiate is going to expire. |
172 | | * |
173 | | * @param context A hx509 context. |
174 | | * @param tbs object to be signed. |
175 | | * @param delta seconds to the certificate is going to expire. |
176 | | * |
177 | | * @return An hx509 error code, see hx509_get_error_string(). |
178 | | * |
179 | | * @ingroup hx509_ca |
180 | | */ |
181 | | |
182 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
183 | | hx509_ca_tbs_set_notAfter_lifetime(hx509_context context, |
184 | | hx509_ca_tbs tbs, |
185 | | time_t delta) |
186 | 0 | { |
187 | 0 | return hx509_ca_tbs_set_notAfter(context, tbs, time(NULL) + delta); |
188 | 0 | } |
189 | | |
190 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
191 | | hx509_ca_tbs_set_pkinit_max_life(hx509_context context, |
192 | | hx509_ca_tbs tbs, |
193 | | time_t max_life) |
194 | 0 | { |
195 | 0 | tbs->pkinitTicketMaxLife = max_life; |
196 | 0 | return 0; |
197 | 0 | } |
198 | | |
199 | | static const struct units templatebits[] = { |
200 | | { "ExtendedKeyUsage", HX509_CA_TEMPLATE_EKU }, |
201 | | { "KeyUsage", HX509_CA_TEMPLATE_KU }, |
202 | | { "SPKI", HX509_CA_TEMPLATE_SPKI }, |
203 | | { "notAfter", HX509_CA_TEMPLATE_NOTAFTER }, |
204 | | { "notBefore", HX509_CA_TEMPLATE_NOTBEFORE }, |
205 | | { "serial", HX509_CA_TEMPLATE_SERIAL }, |
206 | | { "subject", HX509_CA_TEMPLATE_SUBJECT }, |
207 | | { "pkinitMaxLife", HX509_CA_TEMPLATE_PKINIT_MAX_LIFE }, |
208 | | { NULL, 0 } |
209 | | }; |
210 | | |
211 | | /** |
212 | | * Make of template units, use to build flags argument to |
213 | | * hx509_ca_tbs_set_template() with parse_units(). |
214 | | * |
215 | | * @return an units structure. |
216 | | * |
217 | | * @ingroup hx509_ca |
218 | | */ |
219 | | |
220 | | HX509_LIB_FUNCTION const struct units * HX509_LIB_CALL |
221 | | hx509_ca_tbs_template_units(void) |
222 | 0 | { |
223 | 0 | return templatebits; |
224 | 0 | } |
225 | | |
226 | | /** |
227 | | * Initialize the to-be-signed certificate object from a template certificate. |
228 | | * |
229 | | * @param context A hx509 context. |
230 | | * @param tbs object to be signed. |
231 | | * @param flags bit field selecting what to copy from the template |
232 | | * certificate. |
233 | | * @param cert template certificate. |
234 | | * |
235 | | * @return An hx509 error code, see hx509_get_error_string(). |
236 | | * |
237 | | * @ingroup hx509_ca |
238 | | */ |
239 | | |
240 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
241 | | hx509_ca_tbs_set_template(hx509_context context, |
242 | | hx509_ca_tbs tbs, |
243 | | int flags, |
244 | | hx509_cert cert) |
245 | 0 | { |
246 | 0 | int ret; |
247 | |
|
248 | 0 | if (flags & HX509_CA_TEMPLATE_SUBJECT) { |
249 | 0 | if (tbs->subject) |
250 | 0 | hx509_name_free(&tbs->subject); |
251 | 0 | ret = hx509_cert_get_subject(cert, &tbs->subject); |
252 | 0 | if (ret) { |
253 | 0 | hx509_set_error_string(context, 0, ret, |
254 | 0 | "Failed to get subject from template"); |
255 | 0 | return ret; |
256 | 0 | } |
257 | 0 | } |
258 | 0 | if (flags & HX509_CA_TEMPLATE_SERIAL) { |
259 | 0 | der_free_heim_integer(&tbs->serial); |
260 | 0 | ret = hx509_cert_get_serialnumber(cert, &tbs->serial); |
261 | 0 | tbs->flags.serial = !ret; |
262 | 0 | if (ret) { |
263 | 0 | hx509_set_error_string(context, 0, ret, |
264 | 0 | "Failed to copy serial number"); |
265 | 0 | return ret; |
266 | 0 | } |
267 | 0 | } |
268 | 0 | if (flags & HX509_CA_TEMPLATE_NOTBEFORE) |
269 | 0 | tbs->notBefore = hx509_cert_get_notBefore(cert); |
270 | 0 | if (flags & HX509_CA_TEMPLATE_NOTAFTER) |
271 | 0 | tbs->notAfter = hx509_cert_get_notAfter(cert); |
272 | 0 | if (flags & HX509_CA_TEMPLATE_SPKI) { |
273 | 0 | free_SubjectPublicKeyInfo(&tbs->spki); |
274 | 0 | ret = hx509_cert_get_SPKI(context, cert, &tbs->spki); |
275 | 0 | tbs->flags.key = !ret; |
276 | 0 | if (ret) |
277 | 0 | return ret; |
278 | 0 | } |
279 | 0 | if (flags & HX509_CA_TEMPLATE_KU) { |
280 | 0 | ret = _hx509_cert_get_keyusage(context, cert, &tbs->ku); |
281 | 0 | if (ret) |
282 | 0 | return ret; |
283 | 0 | } |
284 | 0 | if (flags & HX509_CA_TEMPLATE_EKU) { |
285 | 0 | ExtKeyUsage eku; |
286 | 0 | size_t i; |
287 | 0 | ret = _hx509_cert_get_eku(context, cert, &eku); |
288 | 0 | if (ret) |
289 | 0 | return ret; |
290 | 0 | for (i = 0; i < eku.len; i++) { |
291 | 0 | ret = hx509_ca_tbs_add_eku(context, tbs, &eku.val[i]); |
292 | 0 | if (ret) { |
293 | 0 | free_ExtKeyUsage(&eku); |
294 | 0 | return ret; |
295 | 0 | } |
296 | 0 | } |
297 | 0 | free_ExtKeyUsage(&eku); |
298 | 0 | } |
299 | 0 | if (flags & HX509_CA_TEMPLATE_PKINIT_MAX_LIFE) { |
300 | 0 | time_t max_life; |
301 | |
|
302 | 0 | if ((max_life = hx509_cert_get_pkinit_max_life(context, cert, 0)) > 0) |
303 | 0 | hx509_ca_tbs_set_pkinit_max_life(context, tbs, max_life); |
304 | 0 | } |
305 | 0 | return 0; |
306 | 0 | } |
307 | | |
308 | | /** |
309 | | * Make the to-be-signed certificate object a CA certificate. If the |
310 | | * pathLenConstraint is negative path length constraint is used. |
311 | | * |
312 | | * @param context A hx509 context. |
313 | | * @param tbs object to be signed. |
314 | | * @param pathLenConstraint path length constraint, negative, no |
315 | | * constraint. |
316 | | * |
317 | | * @return An hx509 error code, see hx509_get_error_string(). |
318 | | * |
319 | | * @ingroup hx509_ca |
320 | | */ |
321 | | |
322 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
323 | | hx509_ca_tbs_set_ca(hx509_context context, |
324 | | hx509_ca_tbs tbs, |
325 | | int pathLenConstraint) |
326 | 0 | { |
327 | 0 | tbs->flags.ca = 1; |
328 | 0 | tbs->pathLenConstraint = pathLenConstraint; |
329 | 0 | return 0; |
330 | 0 | } |
331 | | |
332 | | /** |
333 | | * Make the to-be-signed certificate object a proxy certificate. If the |
334 | | * pathLenConstraint is negative path length constraint is used. |
335 | | * |
336 | | * @param context A hx509 context. |
337 | | * @param tbs object to be signed. |
338 | | * @param pathLenConstraint path length constraint, negative, no |
339 | | * constraint. |
340 | | * |
341 | | * @return An hx509 error code, see hx509_get_error_string(). |
342 | | * |
343 | | * @ingroup hx509_ca |
344 | | */ |
345 | | |
346 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
347 | | hx509_ca_tbs_set_proxy(hx509_context context, |
348 | | hx509_ca_tbs tbs, |
349 | | int pathLenConstraint) |
350 | 0 | { |
351 | 0 | tbs->flags.proxy = 1; |
352 | 0 | tbs->pathLenConstraint = pathLenConstraint; |
353 | 0 | return 0; |
354 | 0 | } |
355 | | |
356 | | |
357 | | /** |
358 | | * Make the to-be-signed certificate object a windows domain controller certificate. |
359 | | * |
360 | | * @param context A hx509 context. |
361 | | * @param tbs object to be signed. |
362 | | * |
363 | | * @return An hx509 error code, see hx509_get_error_string(). |
364 | | * |
365 | | * @ingroup hx509_ca |
366 | | */ |
367 | | |
368 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
369 | | hx509_ca_tbs_set_domaincontroller(hx509_context context, |
370 | | hx509_ca_tbs tbs) |
371 | 0 | { |
372 | 0 | tbs->flags.domaincontroller = 1; |
373 | 0 | return 0; |
374 | 0 | } |
375 | | |
376 | | /** |
377 | | * Set the subject public key info (SPKI) in the to-be-signed certificate |
378 | | * object. SPKI is the public key and key related parameters in the |
379 | | * certificate. |
380 | | * |
381 | | * @param context A hx509 context. |
382 | | * @param tbs object to be signed. |
383 | | * @param spki subject public key info to use for the to-be-signed certificate object. |
384 | | * |
385 | | * @return An hx509 error code, see hx509_get_error_string(). |
386 | | * |
387 | | * @ingroup hx509_ca |
388 | | */ |
389 | | |
390 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
391 | | hx509_ca_tbs_set_spki(hx509_context context, |
392 | | hx509_ca_tbs tbs, |
393 | | const SubjectPublicKeyInfo *spki) |
394 | 0 | { |
395 | 0 | int ret; |
396 | 0 | free_SubjectPublicKeyInfo(&tbs->spki); |
397 | 0 | ret = copy_SubjectPublicKeyInfo(spki, &tbs->spki); |
398 | 0 | tbs->flags.key = !ret; |
399 | 0 | return ret; |
400 | 0 | } |
401 | | |
402 | | /** |
403 | | * Set the serial number to use for to-be-signed certificate object. |
404 | | * |
405 | | * @param context A hx509 context. |
406 | | * @param tbs object to be signed. |
407 | | * @param serialNumber serial number to use for the to-be-signed |
408 | | * certificate object. |
409 | | * |
410 | | * @return An hx509 error code, see hx509_get_error_string(). |
411 | | * |
412 | | * @ingroup hx509_ca |
413 | | */ |
414 | | |
415 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
416 | | hx509_ca_tbs_set_serialnumber(hx509_context context, |
417 | | hx509_ca_tbs tbs, |
418 | | const heim_integer *serialNumber) |
419 | 0 | { |
420 | 0 | int ret; |
421 | 0 | der_free_heim_integer(&tbs->serial); |
422 | 0 | ret = der_copy_heim_integer(serialNumber, &tbs->serial); |
423 | 0 | tbs->flags.serial = !ret; |
424 | 0 | return ret; |
425 | 0 | } |
426 | | |
427 | | /** |
428 | | * Copy elements of a CSR into a TBS, but only if all of them are authorized. |
429 | | * |
430 | | * @param context A hx509 context. |
431 | | * @param tbs object to be signed. |
432 | | * @param req CSR |
433 | | * |
434 | | * @return An hx509 error code, see hx509_get_error_string(). |
435 | | * |
436 | | * @ingroup hx509_ca |
437 | | */ |
438 | | |
439 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
440 | | hx509_ca_tbs_set_from_csr(hx509_context context, |
441 | | hx509_ca_tbs tbs, |
442 | | hx509_request req) |
443 | 0 | { |
444 | 0 | hx509_san_type san_type; |
445 | 0 | heim_oid oid = { 0, NULL }; |
446 | 0 | KeyUsage ku; |
447 | 0 | size_t i; |
448 | 0 | char *s = NULL; |
449 | 0 | int ret; |
450 | |
|
451 | 0 | if (hx509_request_count_unauthorized(req)) { |
452 | 0 | hx509_set_error_string(context, 0, EACCES, |
453 | 0 | "Some certificate features requested in the CSR were not authorized"); |
454 | 0 | return EACCES; |
455 | 0 | } |
456 | | |
457 | 0 | ret = hx509_request_get_ku(context, req, &ku); |
458 | 0 | if (ret == 0 && KeyUsage2int(ku)) |
459 | 0 | ret = hx509_ca_tbs_add_ku(context, tbs, ku); |
460 | |
|
461 | 0 | for (i = 0; ret == 0; i++) { |
462 | 0 | free(s); s = NULL; |
463 | 0 | der_free_oid(&oid); |
464 | 0 | ret = hx509_request_get_eku(req, i, &s); |
465 | 0 | if (ret == 0) |
466 | 0 | ret = der_parse_heim_oid(s, ".", &oid); |
467 | 0 | if (ret == 0) |
468 | 0 | ret = hx509_ca_tbs_add_eku(context, tbs, &oid); |
469 | 0 | } |
470 | 0 | if (ret == HX509_NO_ITEM) |
471 | 0 | ret = 0; |
472 | |
|
473 | 0 | for (i = 0; ret == 0; i++) { |
474 | 0 | free(s); s = NULL; |
475 | 0 | ret = hx509_request_get_san(req, i, &san_type, &s); |
476 | 0 | if (ret == 0) |
477 | 0 | ret = hx509_ca_tbs_add_san(context, tbs, san_type, s); |
478 | 0 | } |
479 | 0 | if (ret == HX509_NO_ITEM) |
480 | 0 | ret = 0; |
481 | |
|
482 | 0 | der_free_oid(&oid); |
483 | 0 | free(s); |
484 | 0 | return ret; |
485 | 0 | } |
486 | | |
487 | | /** |
488 | | * An an extended key usage to the to-be-signed certificate object. |
489 | | * Duplicates will detected and not added. |
490 | | * |
491 | | * @param context A hx509 context. |
492 | | * @param tbs object to be signed. |
493 | | * @param oid extended key usage to add. |
494 | | * |
495 | | * @return An hx509 error code, see hx509_get_error_string(). |
496 | | * |
497 | | * @ingroup hx509_ca |
498 | | */ |
499 | | |
500 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
501 | | hx509_ca_tbs_add_ku(hx509_context context, |
502 | | hx509_ca_tbs tbs, |
503 | | KeyUsage ku) |
504 | 0 | { |
505 | 0 | tbs->ku = ku; |
506 | 0 | return 0; |
507 | 0 | } |
508 | | |
509 | | /** |
510 | | * An an extended key usage to the to-be-signed certificate object. |
511 | | * Duplicates will detected and not added. |
512 | | * |
513 | | * @param context A hx509 context. |
514 | | * @param tbs object to be signed. |
515 | | * @param oid extended key usage to add. |
516 | | * |
517 | | * @return An hx509 error code, see hx509_get_error_string(). |
518 | | * |
519 | | * @ingroup hx509_ca |
520 | | */ |
521 | | |
522 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
523 | | hx509_ca_tbs_add_eku(hx509_context context, |
524 | | hx509_ca_tbs tbs, |
525 | | const heim_oid *oid) |
526 | 0 | { |
527 | 0 | void *ptr; |
528 | 0 | int ret; |
529 | 0 | unsigned i; |
530 | | |
531 | | /* search for duplicates */ |
532 | 0 | for (i = 0; i < tbs->eku.len; i++) { |
533 | 0 | if (der_heim_oid_cmp(oid, &tbs->eku.val[i]) == 0) |
534 | 0 | return 0; |
535 | 0 | } |
536 | | |
537 | 0 | ptr = realloc(tbs->eku.val, sizeof(tbs->eku.val[0]) * (tbs->eku.len + 1)); |
538 | 0 | if (ptr == NULL) { |
539 | 0 | hx509_set_error_string(context, 0, ENOMEM, "out of memory"); |
540 | 0 | return ENOMEM; |
541 | 0 | } |
542 | 0 | tbs->eku.val = ptr; |
543 | 0 | ret = der_copy_oid(oid, &tbs->eku.val[tbs->eku.len]); |
544 | 0 | if (ret) { |
545 | 0 | hx509_set_error_string(context, 0, ret, "out of memory"); |
546 | 0 | return ret; |
547 | 0 | } |
548 | 0 | tbs->eku.len += 1; |
549 | 0 | return 0; |
550 | 0 | } |
551 | | |
552 | | /** |
553 | | * Add a certificate policy to the to-be-signed certificate object. Duplicates |
554 | | * will detected and not added. |
555 | | * |
556 | | * @param context A hx509 context. |
557 | | * @param tbs object to be signed. |
558 | | * @param oid policy OID. |
559 | | * @param cps_uri CPS URI to qualify policy with. |
560 | | * @param user_notice user notice display text to qualify policy with. |
561 | | * |
562 | | * @return An hx509 error code, see hx509_get_error_string(). |
563 | | * |
564 | | * @ingroup hx509_ca |
565 | | */ |
566 | | |
567 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
568 | | hx509_ca_tbs_add_pol(hx509_context context, |
569 | | hx509_ca_tbs tbs, |
570 | | const heim_oid *oid, |
571 | | const char *cps_uri, |
572 | | const char *user_notice) |
573 | 0 | { |
574 | 0 | PolicyQualifierInfos pqis; |
575 | 0 | PolicyQualifierInfo pqi; |
576 | 0 | PolicyInformation pi; |
577 | 0 | size_t i, size; |
578 | 0 | int ret = 0; |
579 | | |
580 | | /* search for duplicates */ |
581 | 0 | for (i = 0; i < tbs->cps.len; i++) { |
582 | 0 | if (der_heim_oid_cmp(oid, &tbs->cps.val[i].policyIdentifier) == 0) |
583 | 0 | return 0; |
584 | 0 | } |
585 | | |
586 | 0 | memset(&pi, 0, sizeof(pi)); |
587 | 0 | memset(&pqi, 0, sizeof(pqi)); |
588 | 0 | memset(&pqis, 0, sizeof(pqis)); |
589 | |
|
590 | 0 | pi.policyIdentifier = *oid; |
591 | 0 | if (cps_uri) { |
592 | 0 | CPSuri uri; |
593 | |
|
594 | 0 | uri.length = strlen(cps_uri); |
595 | 0 | uri.data = (void *)(uintptr_t)cps_uri; |
596 | 0 | pqi.policyQualifierId = asn1_oid_id_pkix_qt_cps; |
597 | |
|
598 | 0 | ASN1_MALLOC_ENCODE(CPSuri, |
599 | 0 | pqi.qualifier.data, |
600 | 0 | pqi.qualifier.length, |
601 | 0 | &uri, &size, ret); |
602 | 0 | if (ret == 0) { |
603 | 0 | ret = add_PolicyQualifierInfos(&pqis, &pqi); |
604 | 0 | free_heim_any(&pqi.qualifier); |
605 | 0 | } |
606 | 0 | } |
607 | 0 | if (ret == 0 && user_notice) { |
608 | 0 | DisplayText dt; |
609 | 0 | UserNotice un; |
610 | |
|
611 | 0 | dt.element = choice_DisplayText_utf8String; |
612 | 0 | dt.u.utf8String = (void *)(uintptr_t)user_notice; |
613 | 0 | un.explicitText = &dt; |
614 | 0 | un.noticeRef = 0; |
615 | |
|
616 | 0 | pqi.policyQualifierId = asn1_oid_id_pkix_qt_unotice; |
617 | 0 | ASN1_MALLOC_ENCODE(UserNotice, |
618 | 0 | pqi.qualifier.data, |
619 | 0 | pqi.qualifier.length, |
620 | 0 | &un, &size, ret); |
621 | 0 | if (ret == 0) { |
622 | 0 | ret = add_PolicyQualifierInfos(&pqis, &pqi); |
623 | 0 | free_heim_any(&pqi.qualifier); |
624 | 0 | } |
625 | 0 | } |
626 | |
|
627 | 0 | pi.policyQualifiers = pqis.len ? &pqis : 0; |
628 | |
|
629 | 0 | if (ret == 0) |
630 | 0 | ret = add_CertificatePolicies(&tbs->cps, &pi); |
631 | |
|
632 | 0 | free_PolicyQualifierInfos(&pqis); |
633 | 0 | return ret; |
634 | 0 | } |
635 | | |
636 | | /** |
637 | | * Add a certificate policy mapping to the to-be-signed certificate object. |
638 | | * Duplicates will detected and not added. |
639 | | * |
640 | | * @param context A hx509 context. |
641 | | * @param tbs object to be signed. |
642 | | * @param issuer issuerDomainPolicy policy OID. |
643 | | * @param subject subjectDomainPolicy policy OID. |
644 | | * |
645 | | * @return An hx509 error code, see hx509_get_error_string(). |
646 | | * |
647 | | * @ingroup hx509_ca |
648 | | */ |
649 | | |
650 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
651 | | hx509_ca_tbs_add_pol_mapping(hx509_context context, |
652 | | hx509_ca_tbs tbs, |
653 | | const heim_oid *issuer, |
654 | | const heim_oid *subject) |
655 | 0 | { |
656 | 0 | PolicyMapping pm; |
657 | 0 | size_t i; |
658 | | |
659 | | /* search for duplicates */ |
660 | 0 | for (i = 0; i < tbs->pms.len; i++) { |
661 | 0 | PolicyMapping *pmp = &tbs->pms.val[i]; |
662 | 0 | if (der_heim_oid_cmp(issuer, &pmp->issuerDomainPolicy) == 0 && |
663 | 0 | der_heim_oid_cmp(subject, &pmp->subjectDomainPolicy) == 0) |
664 | 0 | return 0; |
665 | 0 | } |
666 | | |
667 | 0 | memset(&pm, 0, sizeof(pm)); |
668 | 0 | pm.issuerDomainPolicy = *issuer; |
669 | 0 | pm.subjectDomainPolicy = *subject; |
670 | 0 | return add_PolicyMappings(&tbs->pms, &pm); |
671 | 0 | } |
672 | | |
673 | | /** |
674 | | * Add CRL distribution point URI to the to-be-signed certificate |
675 | | * object. |
676 | | * |
677 | | * @param context A hx509 context. |
678 | | * @param tbs object to be signed. |
679 | | * @param uri uri to the CRL. |
680 | | * @param issuername name of the issuer. |
681 | | * |
682 | | * @return An hx509 error code, see hx509_get_error_string(). |
683 | | * |
684 | | * @ingroup hx509_ca |
685 | | */ |
686 | | |
687 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
688 | | hx509_ca_tbs_add_crl_dp_uri(hx509_context context, |
689 | | hx509_ca_tbs tbs, |
690 | | const char *uri, |
691 | | hx509_name issuername) |
692 | 0 | { |
693 | 0 | DistributionPointName dpn; |
694 | 0 | DistributionPoint dp; |
695 | 0 | GeneralNames crlissuer; |
696 | 0 | GeneralName gn, ign; |
697 | 0 | Name in; |
698 | 0 | int ret; |
699 | |
|
700 | 0 | memset(&dp, 0, sizeof(dp)); |
701 | 0 | memset(&gn, 0, sizeof(gn)); |
702 | 0 | memset(&ign, 0, sizeof(ign)); |
703 | 0 | memset(&in, 0, sizeof(in)); |
704 | 0 | gn.element = choice_GeneralName_uniformResourceIdentifier; |
705 | 0 | gn.u.uniformResourceIdentifier.data = rk_UNCONST(uri); |
706 | 0 | gn.u.uniformResourceIdentifier.length = strlen(uri); |
707 | 0 | dpn.element = choice_DistributionPointName_fullName; |
708 | 0 | dpn.u.fullName.len = 1; |
709 | 0 | dpn.u.fullName.val = &gn; |
710 | 0 | dp.distributionPoint = &dpn; |
711 | |
|
712 | 0 | if (issuername) { |
713 | 0 | ign.element = choice_GeneralName_directoryName; |
714 | 0 | ret = hx509_name_to_Name(issuername, &ign.u.directoryName); |
715 | 0 | if (ret) { |
716 | 0 | hx509_set_error_string(context, 0, ret, "out of memory"); |
717 | 0 | return ret; |
718 | 0 | } |
719 | 0 | crlissuer.len = 1; |
720 | 0 | crlissuer.val = &ign; |
721 | 0 | dp.cRLIssuer = &crlissuer; |
722 | 0 | } |
723 | | |
724 | 0 | ret = add_CRLDistributionPoints(&tbs->crldp, &dp); |
725 | 0 | if (issuername) |
726 | 0 | free_Name(&ign.u.directoryName); |
727 | |
|
728 | 0 | if (ret) |
729 | 0 | hx509_set_error_string(context, 0, ret, "out of memory"); |
730 | 0 | return ret; |
731 | 0 | } |
732 | | |
733 | | /** |
734 | | * Add Subject Alternative Name otherName to the to-be-signed |
735 | | * certificate object. |
736 | | * |
737 | | * @param context A hx509 context. |
738 | | * @param tbs object to be signed. |
739 | | * @param oid the oid of the OtherName. |
740 | | * @param os data in the other name. |
741 | | * |
742 | | * @return An hx509 error code, see hx509_get_error_string(). |
743 | | * |
744 | | * @ingroup hx509_ca |
745 | | */ |
746 | | |
747 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
748 | | hx509_ca_tbs_add_san_otherName(hx509_context context, |
749 | | hx509_ca_tbs tbs, |
750 | | const heim_oid *oid, |
751 | | const heim_octet_string *os) |
752 | 0 | { |
753 | 0 | GeneralName gn; |
754 | |
|
755 | 0 | memset(&gn, 0, sizeof(gn)); |
756 | 0 | gn.element = choice_GeneralName_otherName; |
757 | 0 | gn.u.otherName.type_id = *oid; |
758 | 0 | gn.u.otherName.value = *os; |
759 | |
|
760 | 0 | return add_GeneralNames(&tbs->san, &gn); |
761 | 0 | } |
762 | | |
763 | | static |
764 | | int |
765 | | dequote_strndup(hx509_context context, const char *in, size_t len, char **out) |
766 | 0 | { |
767 | 0 | size_t i, k; |
768 | 0 | char *s; |
769 | |
|
770 | 0 | *out = NULL; |
771 | 0 | if ((s = malloc(len + 1)) == NULL) { |
772 | 0 | hx509_set_error_string(context, 0, ENOMEM, "malloc: out of memory"); |
773 | 0 | return ENOMEM; |
774 | 0 | } |
775 | | |
776 | 0 | for (k = i = 0; i < len; i++) { |
777 | 0 | if (in[i] == '\\') { |
778 | 0 | switch (in[++i]) { |
779 | 0 | case 't': s[k++] = '\t'; break; |
780 | 0 | case 'b': s[k++] = '\b'; break; |
781 | 0 | case 'n': s[k++] = '\n'; break; |
782 | 0 | case '0': |
783 | 0 | for (i++; i < len; i++) { |
784 | 0 | if (in[i] == '\0') |
785 | 0 | break; |
786 | 0 | if (in[i++] == '\\' && in[i] == '0') |
787 | 0 | continue; |
788 | 0 | hx509_set_error_string(context, 0, |
789 | 0 | HX509_PARSING_NAME_FAILED, |
790 | 0 | "embedded NULs not supported in " |
791 | 0 | "PKINIT SANs"); |
792 | 0 | free(s); |
793 | 0 | return HX509_PARSING_NAME_FAILED; |
794 | 0 | } |
795 | 0 | break; |
796 | 0 | case '\0': |
797 | 0 | hx509_set_error_string(context, 0, |
798 | 0 | HX509_PARSING_NAME_FAILED, |
799 | 0 | "trailing unquoted backslashes not " |
800 | 0 | "allowed in PKINIT SANs"); |
801 | 0 | free(s); |
802 | 0 | return HX509_PARSING_NAME_FAILED; |
803 | 0 | default: s[k++] = in[i]; break; |
804 | 0 | } |
805 | 0 | } else { |
806 | 0 | s[k++] = in[i]; |
807 | 0 | } |
808 | 0 | } |
809 | 0 | s[k] = '\0'; |
810 | |
|
811 | 0 | *out = s; |
812 | 0 | return 0; |
813 | 0 | } |
814 | | |
815 | | int |
816 | | _hx509_make_pkinit_san(hx509_context context, |
817 | | const char *principal, |
818 | | heim_octet_string *os) |
819 | 0 | { |
820 | 0 | KRB5PrincipalName p; |
821 | 0 | size_t size; |
822 | 0 | int ret; |
823 | |
|
824 | 0 | os->data = NULL; |
825 | 0 | os->length = 0; |
826 | 0 | memset(&p, 0, sizeof(p)); |
827 | | |
828 | | /* Parse principal */ |
829 | 0 | { |
830 | 0 | const char *str, *str_start; |
831 | 0 | size_t n, i; |
832 | | |
833 | | /* Count number of components */ |
834 | 0 | n = 1; |
835 | 0 | for (str = principal; *str != '\0' && *str != '@'; str++) { |
836 | 0 | if (*str == '\\') { |
837 | 0 | if (str[1] == '\0') { |
838 | 0 | ret = HX509_PARSING_NAME_FAILED; |
839 | 0 | hx509_set_error_string(context, 0, ret, |
840 | 0 | "trailing \\ in principal name"); |
841 | 0 | goto out; |
842 | 0 | } |
843 | 0 | str++; |
844 | 0 | } else if(*str == '/') { |
845 | 0 | n++; |
846 | 0 | } else if(*str == '@') { |
847 | 0 | break; |
848 | 0 | } |
849 | 0 | } |
850 | 0 | if (*str != '@') { |
851 | | /* Note that we allow the realm to be empty */ |
852 | 0 | ret = HX509_PARSING_NAME_FAILED; |
853 | 0 | hx509_set_error_string(context, 0, ret, "Missing @ in principal"); |
854 | 0 | goto out; |
855 | 0 | }; |
856 | |
|
857 | 0 | p.principalName.name_string.val = |
858 | 0 | calloc(n, sizeof(*p.principalName.name_string.val)); |
859 | 0 | if (p.principalName.name_string.val == NULL) { |
860 | 0 | ret = ENOMEM; |
861 | 0 | hx509_set_error_string(context, 0, ret, "malloc: out of memory"); |
862 | 0 | goto out; |
863 | 0 | } |
864 | 0 | p.principalName.name_string.len = n; |
865 | 0 | p.principalName.name_type = KRB5_NT_PRINCIPAL; |
866 | |
|
867 | 0 | for (i = 0, str_start = str = principal; *str != '\0'; str++) { |
868 | 0 | if (*str=='\\') { |
869 | 0 | str++; |
870 | 0 | } else if(*str == '/') { |
871 | | /* Note that we allow components to be empty */ |
872 | 0 | ret = dequote_strndup(context, str_start, str - str_start, |
873 | 0 | &p.principalName.name_string.val[i++]); |
874 | 0 | if (ret) |
875 | 0 | goto out; |
876 | 0 | str_start = str + 1; |
877 | 0 | } else if(*str == '@') { |
878 | 0 | ret = dequote_strndup(context, str_start, str - str_start, |
879 | 0 | &p.principalName.name_string.val[i++]); |
880 | 0 | if (ret == 0) |
881 | 0 | ret = dequote_strndup(context, str + 1, strlen(str + 1), &p.realm); |
882 | 0 | if (ret) |
883 | 0 | goto out; |
884 | 0 | break; |
885 | 0 | } |
886 | 0 | } |
887 | 0 | } |
888 | | |
889 | 0 | ASN1_MALLOC_ENCODE(KRB5PrincipalName, os->data, os->length, &p, &size, ret); |
890 | 0 | if (ret) { |
891 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
892 | 0 | goto out; |
893 | 0 | } |
894 | 0 | if (size != os->length) |
895 | 0 | _hx509_abort("internal ASN.1 encoder error"); |
896 | | |
897 | 0 | out: |
898 | 0 | free_KRB5PrincipalName(&p); |
899 | 0 | return ret; |
900 | 0 | } |
901 | | |
902 | | static int |
903 | | add_ia5string_san(hx509_context context, |
904 | | hx509_ca_tbs tbs, |
905 | | const heim_oid *oid, |
906 | | const char *string) |
907 | 0 | { |
908 | 0 | SRVName ustring; |
909 | 0 | heim_octet_string os; |
910 | 0 | size_t size; |
911 | 0 | int ret; |
912 | |
|
913 | 0 | ustring.data = (void *)(uintptr_t)string; |
914 | 0 | ustring.length = strlen(string); |
915 | |
|
916 | 0 | os.length = 0; |
917 | 0 | os.data = NULL; |
918 | |
|
919 | 0 | ASN1_MALLOC_ENCODE(SRVName, os.data, os.length, &ustring, &size, ret); |
920 | 0 | if (ret) { |
921 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
922 | 0 | return ret; |
923 | 0 | } |
924 | 0 | if (size != os.length) |
925 | 0 | _hx509_abort("internal ASN.1 encoder error"); |
926 | | |
927 | 0 | ret = hx509_ca_tbs_add_san_otherName(context, tbs, oid, &os); |
928 | 0 | free(os.data); |
929 | 0 | return ret; |
930 | 0 | } |
931 | | |
932 | | /** |
933 | | * Add DNSSRV Subject Alternative Name to the to-be-signed certificate object. |
934 | | * |
935 | | * @param context A hx509 context. |
936 | | * @param tbs object to be signed. |
937 | | * @param dnssrv An ASCII string of the for _Service.Name. |
938 | | * |
939 | | * @return An hx509 error code, see hx509_get_error_string(). |
940 | | * |
941 | | * @ingroup hx509_ca |
942 | | */ |
943 | | |
944 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
945 | | hx509_ca_tbs_add_san_dnssrv(hx509_context context, |
946 | | hx509_ca_tbs tbs, |
947 | | const char *dnssrv) |
948 | 0 | { |
949 | 0 | size_t i, len; |
950 | | |
951 | | /* Minimal DNSSRV input validation */ |
952 | 0 | if (dnssrv == 0 || dnssrv[0] != '_') { |
953 | 0 | hx509_set_error_string(context, 0, EINVAL, "Invalid DNSSRV name"); |
954 | 0 | return EINVAL; |
955 | 0 | } |
956 | 0 | for (i = 1, len = strlen(dnssrv); i < len; i++) { |
957 | 0 | if (dnssrv[i] == '.' && dnssrv[i + 1] != '\0') |
958 | 0 | break; |
959 | 0 | } |
960 | 0 | if (i == len) { |
961 | 0 | hx509_set_error_string(context, 0, EINVAL, "Invalid DNSSRV name"); |
962 | 0 | return EINVAL; |
963 | 0 | } |
964 | | |
965 | 0 | return add_ia5string_san(context, tbs, |
966 | 0 | &asn1_oid_id_pkix_on_dnsSRV, dnssrv); |
967 | 0 | } |
968 | | |
969 | | /** |
970 | | * Add Kerberos Subject Alternative Name to the to-be-signed |
971 | | * certificate object. The principal string is a UTF8 string. |
972 | | * |
973 | | * @param context A hx509 context. |
974 | | * @param tbs object to be signed. |
975 | | * @param principal Kerberos principal to add to the certificate. |
976 | | * |
977 | | * @return An hx509 error code, see hx509_get_error_string(). |
978 | | * |
979 | | * @ingroup hx509_ca |
980 | | */ |
981 | | |
982 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
983 | | hx509_ca_tbs_add_san_pkinit(hx509_context context, |
984 | | hx509_ca_tbs tbs, |
985 | | const char *principal) |
986 | 0 | { |
987 | 0 | heim_octet_string os; |
988 | 0 | int ret; |
989 | |
|
990 | 0 | ret = _hx509_make_pkinit_san(context, principal, &os); |
991 | 0 | if (ret == 0) |
992 | 0 | ret = hx509_ca_tbs_add_san_otherName(context, tbs, |
993 | 0 | &asn1_oid_id_pkinit_san, &os); |
994 | 0 | free(os.data); |
995 | 0 | return ret; |
996 | 0 | } |
997 | | |
998 | | /* |
999 | | * |
1000 | | */ |
1001 | | |
1002 | | static int |
1003 | | add_utf8_san(hx509_context context, |
1004 | | hx509_ca_tbs tbs, |
1005 | | const heim_oid *oid, |
1006 | | const char *string) |
1007 | 0 | { |
1008 | 0 | const PKIXXmppAddr ustring = (const PKIXXmppAddr)(uintptr_t)string; |
1009 | 0 | heim_octet_string os; |
1010 | 0 | size_t size; |
1011 | 0 | int ret; |
1012 | |
|
1013 | 0 | os.length = 0; |
1014 | 0 | os.data = NULL; |
1015 | |
|
1016 | 0 | ASN1_MALLOC_ENCODE(PKIXXmppAddr, os.data, os.length, &ustring, &size, ret); |
1017 | 0 | if (ret) { |
1018 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1019 | 0 | return ret; |
1020 | 0 | } |
1021 | 0 | if (size != os.length) |
1022 | 0 | _hx509_abort("internal ASN.1 encoder error"); |
1023 | | |
1024 | 0 | ret = hx509_ca_tbs_add_san_otherName(context, tbs, oid, &os); |
1025 | 0 | free(os.data); |
1026 | 0 | return ret; |
1027 | 0 | } |
1028 | | |
1029 | | /** |
1030 | | * Add Microsoft UPN Subject Alternative Name to the to-be-signed |
1031 | | * certificate object. The principal string is a UTF8 string. |
1032 | | * |
1033 | | * @param context A hx509 context. |
1034 | | * @param tbs object to be signed. |
1035 | | * @param principal Microsoft UPN string. |
1036 | | * |
1037 | | * @return An hx509 error code, see hx509_get_error_string(). |
1038 | | * |
1039 | | * @ingroup hx509_ca |
1040 | | */ |
1041 | | |
1042 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
1043 | | hx509_ca_tbs_add_san_ms_upn(hx509_context context, |
1044 | | hx509_ca_tbs tbs, |
1045 | | const char *principal) |
1046 | 0 | { |
1047 | 0 | return add_utf8_san(context, tbs, &asn1_oid_id_pkinit_ms_san, principal); |
1048 | 0 | } |
1049 | | |
1050 | | /** |
1051 | | * Add a Jabber/XMPP jid Subject Alternative Name to the to-be-signed |
1052 | | * certificate object. The jid is an UTF8 string. |
1053 | | * |
1054 | | * @param context A hx509 context. |
1055 | | * @param tbs object to be signed. |
1056 | | * @param jid string of an a jabber id in UTF8. |
1057 | | * |
1058 | | * @return An hx509 error code, see hx509_get_error_string(). |
1059 | | * |
1060 | | * @ingroup hx509_ca |
1061 | | */ |
1062 | | |
1063 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
1064 | | hx509_ca_tbs_add_san_jid(hx509_context context, |
1065 | | hx509_ca_tbs tbs, |
1066 | | const char *jid) |
1067 | 0 | { |
1068 | 0 | return add_utf8_san(context, tbs, &asn1_oid_id_pkix_on_xmppAddr, jid); |
1069 | 0 | } |
1070 | | |
1071 | | |
1072 | | /** |
1073 | | * Add a Subject Alternative Name hostname to to-be-signed certificate |
1074 | | * object. A domain match starts with ., an exact match does not. |
1075 | | * |
1076 | | * Example of a an domain match: .domain.se matches the hostname |
1077 | | * host.domain.se. |
1078 | | * |
1079 | | * @param context A hx509 context. |
1080 | | * @param tbs object to be signed. |
1081 | | * @param dnsname a hostame. |
1082 | | * |
1083 | | * @return An hx509 error code, see hx509_get_error_string(). |
1084 | | * |
1085 | | * @ingroup hx509_ca |
1086 | | */ |
1087 | | |
1088 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
1089 | | hx509_ca_tbs_add_san_hostname(hx509_context context, |
1090 | | hx509_ca_tbs tbs, |
1091 | | const char *dnsname) |
1092 | 0 | { |
1093 | 0 | GeneralName gn; |
1094 | |
|
1095 | 0 | memset(&gn, 0, sizeof(gn)); |
1096 | 0 | gn.element = choice_GeneralName_dNSName; |
1097 | 0 | gn.u.dNSName.data = rk_UNCONST(dnsname); |
1098 | 0 | gn.u.dNSName.length = strlen(dnsname); |
1099 | |
|
1100 | 0 | return add_GeneralNames(&tbs->san, &gn); |
1101 | 0 | } |
1102 | | |
1103 | | /** |
1104 | | * Add a Subject Alternative Name rfc822 (email address) to |
1105 | | * to-be-signed certificate object. |
1106 | | * |
1107 | | * @param context A hx509 context. |
1108 | | * @param tbs object to be signed. |
1109 | | * @param rfc822Name a string to a email address. |
1110 | | * |
1111 | | * @return An hx509 error code, see hx509_get_error_string(). |
1112 | | * |
1113 | | * @ingroup hx509_ca |
1114 | | */ |
1115 | | |
1116 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
1117 | | hx509_ca_tbs_add_san_rfc822name(hx509_context context, |
1118 | | hx509_ca_tbs tbs, |
1119 | | const char *rfc822Name) |
1120 | 0 | { |
1121 | 0 | GeneralName gn; |
1122 | |
|
1123 | 0 | memset(&gn, 0, sizeof(gn)); |
1124 | 0 | gn.element = choice_GeneralName_rfc822Name; |
1125 | 0 | gn.u.rfc822Name.data = rk_UNCONST(rfc822Name); |
1126 | 0 | gn.u.rfc822Name.length = strlen(rfc822Name); |
1127 | |
|
1128 | 0 | return add_GeneralNames(&tbs->san, &gn); |
1129 | 0 | } |
1130 | | |
1131 | | /* |
1132 | | * PermanentIdentifier is one SAN for naming devices with TPMs after their |
1133 | | * endorsement keys or EK certificates. See TPM 2.0 Keys for Device Identity |
1134 | | * and Attestation, Version 1.00, Revision 2, 9/17/2020 (DRAFT). |
1135 | | * |
1136 | | * The text on the form of permanent identifiers for TPM endorsement keys sans |
1137 | | * certificates is clearly problematic, saying: "When the TPM does not have an |
1138 | | * EK certificate, the identifierValue is a digest of a concatenation of the |
1139 | | * UTF8 string “EkPubkey” (terminating NULL not included) with the binary EK |
1140 | | * public key", but since arbitrary binary is not necessarily valid UTF-8... |
1141 | | * and since NULs embedded in UTF-8 might be OK in some contexts but really |
1142 | | * isn't in C (and Heimdal's ASN.1 compiler does not allow NULs in the |
1143 | | * middle of strings)... That just cannot be correct. Since elsewhere the TCG |
1144 | | * specs use the hex encoding of the SHA-256 digest of the DER encoding of |
1145 | | * public keys, that's what we should support in Heimdal, and maybe send in a |
1146 | | * comment. |
1147 | | * |
1148 | | * Also, even where one should use hex encoding of the SHA-256 digest of the |
1149 | | * DER encoding of public keys, how should the public keys be represented? |
1150 | | * Presumably as SPKIs, with all the required parameters and no more. |
1151 | | */ |
1152 | | |
1153 | | /** |
1154 | | * Add a Subject Alternative Name of PermanentIdentifier type to a to-be-signed |
1155 | | * certificate object. The permanent identifier form for TPM endorsement key |
1156 | | * certificates is the hex encoding of the SHA-256 digest of the DER encoding |
1157 | | * of the certificate. The permanent identifier form for TPM endorsement keys |
1158 | | * are of the form "EkPubkey<public-key>", where the form of <public-key> is |
1159 | | * not well specified at this point. It is the caller's responsibility to |
1160 | | * format the identifierValue. |
1161 | | * |
1162 | | * @param context A hx509 context. |
1163 | | * @param tbs object to be signed. |
1164 | | * @param str permanent identifier name in the form "[<assigner-oid>]:[<id>]". |
1165 | | * @param assigner The OID of an assigner. |
1166 | | * |
1167 | | * @return An hx509 error code, see hx509_get_error_string(). |
1168 | | * |
1169 | | * @ingroup hx509_ca |
1170 | | */ |
1171 | | |
1172 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
1173 | | hx509_ca_tbs_add_san_permanentIdentifier_string(hx509_context context, |
1174 | | hx509_ca_tbs tbs, |
1175 | | const char *str) |
1176 | 0 | { |
1177 | 0 | const heim_oid *found = NULL; |
1178 | 0 | heim_oid oid; |
1179 | 0 | const char *oidstr, *id; |
1180 | 0 | char *freeme, *p; |
1181 | 0 | int ret; |
1182 | |
|
1183 | 0 | memset(&oid, 0, sizeof(oid)); |
1184 | 0 | if ((freeme = strdup(str)) == NULL) |
1185 | 0 | return hx509_enomem(context); |
1186 | | |
1187 | 0 | oidstr = freeme; |
1188 | 0 | p = strchr(freeme, ':'); |
1189 | 0 | if (!p) { |
1190 | 0 | hx509_set_error_string(context, 0, EINVAL, |
1191 | 0 | "Invalid PermanentIdentifier string (should be \"[<oid>]:[<id>]\")"); |
1192 | 0 | free(freeme); |
1193 | 0 | return EINVAL; |
1194 | 0 | } |
1195 | 0 | if (p) { |
1196 | 0 | *(p++) = '\0'; |
1197 | 0 | id = p; |
1198 | 0 | } |
1199 | 0 | if (oidstr[0] != '\0') { |
1200 | 0 | ret = der_find_heim_oid_by_name(oidstr, &found); |
1201 | 0 | if (ret) { |
1202 | 0 | ret = der_parse_heim_oid(oidstr, " .", &oid); |
1203 | 0 | if (ret == 0) |
1204 | 0 | found = &oid; |
1205 | 0 | } |
1206 | 0 | } |
1207 | 0 | ret = hx509_ca_tbs_add_san_permanentIdentifier(context, tbs, id, found); |
1208 | 0 | if (found == &oid) |
1209 | 0 | der_free_oid(&oid); |
1210 | 0 | free(freeme); |
1211 | 0 | return ret; |
1212 | 0 | } |
1213 | | |
1214 | | /** |
1215 | | * Add a Subject Alternative Name of PermanentIdentifier type to a to-be-signed |
1216 | | * certificate object. The permanent identifier form for TPM endorsement key |
1217 | | * certificates is the hex encoding of the SHA-256 digest of the DER encoding |
1218 | | * of the certificate. The permanent identifier form for TPM endorsement keys |
1219 | | * are of the form "EkPubkey<public-key>", where the form of <public-key> is |
1220 | | * not well specified at this point. It is the caller's responsibility to |
1221 | | * format the identifierValue. |
1222 | | * |
1223 | | * @param context A hx509 context. |
1224 | | * @param tbs object to be signed. |
1225 | | * @param identifierValue The permanent identifier name. |
1226 | | * @param assigner The OID of an assigner. |
1227 | | * |
1228 | | * @return An hx509 error code, see hx509_get_error_string(). |
1229 | | * |
1230 | | * @ingroup hx509_ca |
1231 | | */ |
1232 | | |
1233 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
1234 | | hx509_ca_tbs_add_san_permanentIdentifier(hx509_context context, |
1235 | | hx509_ca_tbs tbs, |
1236 | | const char *identifierValue, |
1237 | | const heim_oid *assigner) |
1238 | 0 | { |
1239 | 0 | PermanentIdentifier pi; |
1240 | 0 | heim_utf8_string s = (void *)(uintptr_t)identifierValue; |
1241 | 0 | heim_octet_string os; |
1242 | 0 | size_t size; |
1243 | 0 | int ret; |
1244 | |
|
1245 | 0 | pi.identifierValue = &s; |
1246 | 0 | pi.assigner = (heim_oid*)(uintptr_t)assigner; |
1247 | 0 | os.length = 0; |
1248 | 0 | os.data = NULL; |
1249 | |
|
1250 | 0 | ASN1_MALLOC_ENCODE(PermanentIdentifier, os.data, os.length, &pi, &size, |
1251 | 0 | ret); |
1252 | 0 | if (ret) { |
1253 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1254 | 0 | return ret; |
1255 | 0 | } |
1256 | 0 | if (size != os.length) |
1257 | 0 | _hx509_abort("internal ASN.1 encoder error"); |
1258 | | |
1259 | 0 | ret = hx509_ca_tbs_add_san_otherName(context, tbs, |
1260 | 0 | &asn1_oid_id_pkix_on_permanentIdentifier, |
1261 | 0 | &os); |
1262 | 0 | free(os.data); |
1263 | 0 | return ret; |
1264 | 0 | } |
1265 | | |
1266 | | /** |
1267 | | * Add a Subject Alternative Name of HardwareModuleName type to a to-be-signed |
1268 | | * certificate object. |
1269 | | * |
1270 | | * @param context A hx509 context. |
1271 | | * @param tbs object to be signed. |
1272 | | * @param str a string of the form "<oid>:<serial>". |
1273 | | * @param hwserial The serial number. |
1274 | | * |
1275 | | * @return An hx509 error code, see hx509_get_error_string(). |
1276 | | * |
1277 | | * @ingroup hx509_ca |
1278 | | */ |
1279 | | |
1280 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
1281 | | hx509_ca_tbs_add_san_hardwareModuleName_string(hx509_context context, |
1282 | | hx509_ca_tbs tbs, |
1283 | | const char *str) |
1284 | 0 | { |
1285 | 0 | const heim_oid *found = NULL; |
1286 | 0 | heim_oid oid; |
1287 | 0 | const char *oidstr, *sno; |
1288 | 0 | char *freeme, *p; |
1289 | 0 | int ret; |
1290 | |
|
1291 | 0 | memset(&oid, 0, sizeof(oid)); |
1292 | 0 | if ((freeme = strdup(str)) == NULL) |
1293 | 0 | return hx509_enomem(context); |
1294 | | |
1295 | 0 | oidstr = freeme; |
1296 | 0 | p = strchr(freeme, ':'); |
1297 | 0 | if (!p) { |
1298 | 0 | hx509_set_error_string(context, 0, EINVAL, |
1299 | 0 | "Invalid HardwareModuleName string (should be " |
1300 | 0 | "\"<oid>:<serial>\")"); |
1301 | 0 | free(freeme); |
1302 | 0 | return EINVAL; |
1303 | 0 | } |
1304 | 0 | if (p) { |
1305 | 0 | *(p++) = '\0'; |
1306 | 0 | sno = p; |
1307 | 0 | } |
1308 | 0 | if (oidstr[0] == '\0') { |
1309 | 0 | found = &asn1_oid_tcg_tpm20; |
1310 | 0 | } else { |
1311 | 0 | ret = der_find_heim_oid_by_name(oidstr, &found); |
1312 | 0 | if (ret) { |
1313 | 0 | ret = der_parse_heim_oid(oidstr, " .", &oid); |
1314 | 0 | if (ret == 0) |
1315 | 0 | found = &oid; |
1316 | 0 | } |
1317 | 0 | } |
1318 | 0 | if (!found) { |
1319 | 0 | hx509_set_error_string(context, 0, EINVAL, |
1320 | 0 | "Could not resolve or parse OID \"%s\"", |
1321 | 0 | oidstr); |
1322 | 0 | free(freeme); |
1323 | 0 | return EINVAL; |
1324 | 0 | } |
1325 | 0 | ret = hx509_ca_tbs_add_san_hardwareModuleName(context, tbs, found, sno); |
1326 | 0 | if (found == &oid) |
1327 | 0 | der_free_oid(&oid); |
1328 | 0 | free(freeme); |
1329 | 0 | return ret; |
1330 | 0 | } |
1331 | | |
1332 | | /** |
1333 | | * Add a Subject Alternative Name of HardwareModuleName type to a to-be-signed |
1334 | | * certificate object. |
1335 | | * |
1336 | | * @param context A hx509 context. |
1337 | | * @param tbs object to be signed. |
1338 | | * @param hwtype The hardwar module type (e.g., `&asn1_oid_tcg_tpm20'). |
1339 | | * @param hwserial The serial number. |
1340 | | * |
1341 | | * @return An hx509 error code, see hx509_get_error_string(). |
1342 | | * |
1343 | | * @ingroup hx509_ca |
1344 | | */ |
1345 | | |
1346 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
1347 | | hx509_ca_tbs_add_san_hardwareModuleName(hx509_context context, |
1348 | | hx509_ca_tbs tbs, |
1349 | | const heim_oid *hwtype, |
1350 | | const char *hwserial) |
1351 | 0 | { |
1352 | 0 | HardwareModuleName hm; |
1353 | 0 | heim_octet_string os; |
1354 | 0 | size_t size; |
1355 | 0 | int ret; |
1356 | |
|
1357 | 0 | hm.hwType = *hwtype; |
1358 | 0 | hm.hwSerialNum.data = (void *)(uintptr_t)hwserial; |
1359 | 0 | hm.hwSerialNum.length = strlen(hwserial); |
1360 | 0 | os.length = 0; |
1361 | 0 | os.data = NULL; |
1362 | |
|
1363 | 0 | ASN1_MALLOC_ENCODE(HardwareModuleName, os.data, os.length, &hm, &size, |
1364 | 0 | ret); |
1365 | 0 | if (ret) { |
1366 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1367 | 0 | return ret; |
1368 | 0 | } |
1369 | 0 | if (size != os.length) |
1370 | 0 | _hx509_abort("internal ASN.1 encoder error"); |
1371 | | |
1372 | 0 | ret = hx509_ca_tbs_add_san_otherName(context, tbs, |
1373 | 0 | &asn1_oid_id_on_hardwareModuleName, |
1374 | 0 | &os); |
1375 | 0 | free(os.data); |
1376 | 0 | return ret; |
1377 | 0 | } |
1378 | | |
1379 | | /** |
1380 | | * Add a Subject Alternative Name of the given type to the |
1381 | | * to-be-signed certificate object. |
1382 | | * |
1383 | | * @param context A hx509 context. |
1384 | | * @param tbs object to be signed. |
1385 | | * @param rfc822Name a string to a email address. |
1386 | | * |
1387 | | * @return An hx509 error code, see hx509_get_error_string(). |
1388 | | * |
1389 | | * @ingroup hx509_ca |
1390 | | */ |
1391 | | |
1392 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
1393 | | hx509_ca_tbs_add_san(hx509_context context, |
1394 | | hx509_ca_tbs tbs, |
1395 | | hx509_san_type type, |
1396 | | const char *s) |
1397 | 0 | { |
1398 | 0 | switch (type) { |
1399 | 0 | case HX509_SAN_TYPE_EMAIL: |
1400 | 0 | return hx509_ca_tbs_add_san_rfc822name(context, tbs, s); |
1401 | 0 | case HX509_SAN_TYPE_DNSNAME: |
1402 | 0 | return hx509_ca_tbs_add_san_hostname(context, tbs, s); |
1403 | 0 | case HX509_SAN_TYPE_DN: |
1404 | 0 | return ENOTSUP; |
1405 | 0 | case HX509_SAN_TYPE_REGISTERED_ID: |
1406 | 0 | return ENOTSUP; |
1407 | 0 | case HX509_SAN_TYPE_XMPP: |
1408 | 0 | return hx509_ca_tbs_add_san_jid(context, tbs, s); |
1409 | 0 | case HX509_SAN_TYPE_PKINIT: |
1410 | 0 | return hx509_ca_tbs_add_san_pkinit(context, tbs, s); |
1411 | 0 | case HX509_SAN_TYPE_MS_UPN: |
1412 | 0 | return hx509_ca_tbs_add_san_ms_upn(context, tbs, s); |
1413 | 0 | default: |
1414 | 0 | return ENOTSUP; |
1415 | 0 | } |
1416 | 0 | } |
1417 | | |
1418 | | /** |
1419 | | * Set the subject name of a to-be-signed certificate object. |
1420 | | * |
1421 | | * @param context A hx509 context. |
1422 | | * @param tbs object to be signed. |
1423 | | * @param subject the name to set a subject. |
1424 | | * |
1425 | | * @return An hx509 error code, see hx509_get_error_string(). |
1426 | | * |
1427 | | * @ingroup hx509_ca |
1428 | | */ |
1429 | | |
1430 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
1431 | | hx509_ca_tbs_set_subject(hx509_context context, |
1432 | | hx509_ca_tbs tbs, |
1433 | | hx509_name subject) |
1434 | 0 | { |
1435 | 0 | if (tbs->subject) |
1436 | 0 | hx509_name_free(&tbs->subject); |
1437 | 0 | return hx509_name_copy(context, subject, &tbs->subject); |
1438 | 0 | } |
1439 | | |
1440 | | /** |
1441 | | * Set the issuerUniqueID and subjectUniqueID |
1442 | | * |
1443 | | * These are only supposed to be used considered with version 2 |
1444 | | * certificates, replaced by the two extensions SubjectKeyIdentifier |
1445 | | * and IssuerKeyIdentifier. This function is to allow application |
1446 | | * using legacy protocol to issue them. |
1447 | | * |
1448 | | * @param context A hx509 context. |
1449 | | * @param tbs object to be signed. |
1450 | | * @param issuerUniqueID to be set |
1451 | | * @param subjectUniqueID to be set |
1452 | | * |
1453 | | * @return An hx509 error code, see hx509_get_error_string(). |
1454 | | * |
1455 | | * @ingroup hx509_ca |
1456 | | */ |
1457 | | |
1458 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
1459 | | hx509_ca_tbs_set_unique(hx509_context context, |
1460 | | hx509_ca_tbs tbs, |
1461 | | const heim_bit_string *subjectUniqueID, |
1462 | | const heim_bit_string *issuerUniqueID) |
1463 | 0 | { |
1464 | 0 | int ret; |
1465 | |
|
1466 | 0 | der_free_bit_string(&tbs->subjectUniqueID); |
1467 | 0 | der_free_bit_string(&tbs->issuerUniqueID); |
1468 | |
|
1469 | 0 | if (subjectUniqueID) { |
1470 | 0 | ret = der_copy_bit_string(subjectUniqueID, &tbs->subjectUniqueID); |
1471 | 0 | if (ret) |
1472 | 0 | return ret; |
1473 | 0 | } |
1474 | | |
1475 | 0 | if (issuerUniqueID) { |
1476 | 0 | ret = der_copy_bit_string(issuerUniqueID, &tbs->issuerUniqueID); |
1477 | 0 | if (ret) |
1478 | 0 | return ret; |
1479 | 0 | } |
1480 | | |
1481 | 0 | return 0; |
1482 | 0 | } |
1483 | | |
1484 | | /** |
1485 | | * Expand the the subject name in the to-be-signed certificate object |
1486 | | * using hx509_name_expand(). |
1487 | | * |
1488 | | * @param context A hx509 context. |
1489 | | * @param tbs object to be signed. |
1490 | | * @param env environment variable to expand variables in the subject |
1491 | | * name, see hx509_env_init(). |
1492 | | * |
1493 | | * @return An hx509 error code, see hx509_get_error_string(). |
1494 | | * |
1495 | | * @ingroup hx509_ca |
1496 | | */ |
1497 | | |
1498 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
1499 | | hx509_ca_tbs_subject_expand(hx509_context context, |
1500 | | hx509_ca_tbs tbs, |
1501 | | hx509_env env) |
1502 | 0 | { |
1503 | 0 | return hx509_name_expand(context, tbs->subject, env); |
1504 | 0 | } |
1505 | | |
1506 | | /** |
1507 | | * Get the name of a to-be-signed certificate object. |
1508 | | * |
1509 | | * @param context A hx509 context. |
1510 | | * @param tbs object to be signed. |
1511 | | * |
1512 | | * @return An hx509 name. |
1513 | | * |
1514 | | * @ingroup hx509_ca |
1515 | | */ |
1516 | | |
1517 | | HX509_LIB_FUNCTION hx509_name HX509_LIB_CALL |
1518 | | hx509_ca_tbs_get_name(hx509_ca_tbs tbs) |
1519 | 0 | { |
1520 | 0 | return tbs->subject; |
1521 | 0 | } |
1522 | | |
1523 | | /** |
1524 | | * Set signature algorithm on the to be signed certificate |
1525 | | * |
1526 | | * @param context A hx509 context. |
1527 | | * @param tbs object to be signed. |
1528 | | * @param sigalg signature algorithm to use |
1529 | | * |
1530 | | * @return An hx509 error code, see hx509_get_error_string(). |
1531 | | * |
1532 | | * @ingroup hx509_ca |
1533 | | */ |
1534 | | |
1535 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
1536 | | hx509_ca_tbs_set_signature_algorithm(hx509_context context, |
1537 | | hx509_ca_tbs tbs, |
1538 | | const AlgorithmIdentifier *sigalg) |
1539 | 0 | { |
1540 | 0 | int ret; |
1541 | |
|
1542 | 0 | tbs->sigalg = calloc(1, sizeof(*tbs->sigalg)); |
1543 | 0 | if (tbs->sigalg == NULL) { |
1544 | 0 | hx509_set_error_string(context, 0, ENOMEM, "Out of memory"); |
1545 | 0 | return ENOMEM; |
1546 | 0 | } |
1547 | 0 | ret = copy_AlgorithmIdentifier(sigalg, tbs->sigalg); |
1548 | 0 | if (ret) { |
1549 | 0 | free(tbs->sigalg); |
1550 | 0 | tbs->sigalg = NULL; |
1551 | 0 | return ret; |
1552 | 0 | } |
1553 | 0 | return 0; |
1554 | 0 | } |
1555 | | |
1556 | | /* |
1557 | | * |
1558 | | */ |
1559 | | |
1560 | | static int |
1561 | | add_extension(hx509_context context, |
1562 | | TBSCertificate *tbsc, |
1563 | | int critical_flag, |
1564 | | const heim_oid *oid, |
1565 | | const heim_octet_string *data) |
1566 | 0 | { |
1567 | 0 | Extension ext; |
1568 | 0 | int ret; |
1569 | |
|
1570 | 0 | memset(&ext, 0, sizeof(ext)); |
1571 | |
|
1572 | 0 | ext.critical = critical_flag; |
1573 | 0 | ret = der_copy_oid(oid, &ext.extnID); |
1574 | 0 | if (ret) { |
1575 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1576 | 0 | goto out; |
1577 | 0 | } |
1578 | 0 | ret = der_copy_octet_string(data, &ext.extnValue); |
1579 | 0 | if (ret) { |
1580 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1581 | 0 | goto out; |
1582 | 0 | } |
1583 | 0 | ret = add_Extensions(tbsc->extensions, &ext); |
1584 | 0 | if (ret) { |
1585 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1586 | 0 | goto out; |
1587 | 0 | } |
1588 | 0 | out: |
1589 | 0 | free_Extension(&ext); |
1590 | 0 | return ret; |
1591 | 0 | } |
1592 | | |
1593 | | static int |
1594 | | build_proxy_prefix(hx509_context context, const Name *issuer, Name *subject) |
1595 | 0 | { |
1596 | 0 | char *tstr; |
1597 | 0 | time_t t; |
1598 | 0 | int ret; |
1599 | |
|
1600 | 0 | ret = copy_Name(issuer, subject); |
1601 | 0 | if (ret) { |
1602 | 0 | hx509_set_error_string(context, 0, ret, |
1603 | 0 | "Failed to copy subject name"); |
1604 | 0 | return ret; |
1605 | 0 | } |
1606 | | |
1607 | 0 | t = time(NULL); |
1608 | 0 | ret = asprintf(&tstr, "ts-%lu", (unsigned long)t); |
1609 | 0 | if (ret == -1 || tstr == NULL) { |
1610 | 0 | hx509_set_error_string(context, 0, ENOMEM, |
1611 | 0 | "Failed to copy subject name"); |
1612 | 0 | return ENOMEM; |
1613 | 0 | } |
1614 | | /* prefix with CN=<ts>,...*/ |
1615 | 0 | ret = _hx509_name_modify(context, subject, 1, &asn1_oid_id_at_commonName, tstr); |
1616 | 0 | free(tstr); |
1617 | 0 | if (ret) |
1618 | 0 | free_Name(subject); |
1619 | 0 | return ret; |
1620 | 0 | } |
1621 | | |
1622 | | static int |
1623 | | ca_sign(hx509_context context, |
1624 | | hx509_ca_tbs tbs, |
1625 | | hx509_private_key signer, |
1626 | | const AuthorityKeyIdentifier *ai, |
1627 | | const Name *issuername, |
1628 | | hx509_cert *certificate) |
1629 | 0 | { |
1630 | 0 | heim_error_t error = NULL; |
1631 | 0 | heim_octet_string data; |
1632 | 0 | Certificate c; |
1633 | 0 | TBSCertificate *tbsc; |
1634 | 0 | size_t size; |
1635 | 0 | int ret; |
1636 | 0 | const AlgorithmIdentifier *sigalg; |
1637 | 0 | time_t notBefore; |
1638 | 0 | time_t notAfter; |
1639 | |
|
1640 | 0 | sigalg = tbs->sigalg; |
1641 | 0 | if (sigalg == NULL) |
1642 | 0 | sigalg = _hx509_crypto_default_sig_alg; |
1643 | |
|
1644 | 0 | memset(&c, 0, sizeof(c)); |
1645 | | |
1646 | | /* |
1647 | | * Default values are: Valid since 24h ago, valid one year into |
1648 | | * the future, KeyUsage digitalSignature and keyEncipherment set, |
1649 | | * and keyCertSign for CA certificates. |
1650 | | */ |
1651 | 0 | notBefore = tbs->notBefore; |
1652 | 0 | if (notBefore == 0) |
1653 | 0 | notBefore = time(NULL) - 3600 * 24; |
1654 | 0 | notAfter = tbs->notAfter; |
1655 | 0 | if (notAfter == 0) |
1656 | 0 | notAfter = time(NULL) + 3600 * 24 * 365; |
1657 | |
|
1658 | 0 | if (tbs->flags.ca) { |
1659 | 0 | tbs->ku.keyCertSign = 1; |
1660 | 0 | tbs->ku.cRLSign = 1; |
1661 | 0 | } else if (KeyUsage2int(tbs->ku) == 0) { |
1662 | 0 | tbs->ku.digitalSignature = 1; |
1663 | 0 | tbs->ku.keyEncipherment = 1; |
1664 | 0 | } |
1665 | | |
1666 | | /* |
1667 | | * |
1668 | | */ |
1669 | |
|
1670 | 0 | tbsc = &c.tbsCertificate; |
1671 | | |
1672 | | /* Default subject Name to empty */ |
1673 | 0 | if (tbs->subject == NULL && |
1674 | 0 | (ret = hx509_empty_name(context, &tbs->subject))) |
1675 | 0 | return ret; |
1676 | | |
1677 | | /* Sanity checks */ |
1678 | 0 | if (tbs->flags.key == 0) { |
1679 | 0 | ret = EINVAL; |
1680 | 0 | hx509_set_error_string(context, 0, ret, "No public key set"); |
1681 | 0 | return ret; |
1682 | 0 | } |
1683 | | /* |
1684 | | * Don't put restrictions on proxy certificate's subject name, it |
1685 | | * will be generated below. |
1686 | | */ |
1687 | 0 | if (!tbs->flags.proxy) { |
1688 | 0 | if (hx509_name_is_null_p(tbs->subject) && tbs->san.len == 0) { |
1689 | 0 | hx509_set_error_string(context, 0, EINVAL, |
1690 | 0 | "Empty subject and no SubjectAltNames"); |
1691 | 0 | return EINVAL; |
1692 | 0 | } |
1693 | 0 | } |
1694 | 0 | if (tbs->flags.ca && tbs->flags.proxy) { |
1695 | 0 | hx509_set_error_string(context, 0, EINVAL, "Can't be proxy and CA " |
1696 | 0 | "at the same time"); |
1697 | 0 | return EINVAL; |
1698 | 0 | } |
1699 | 0 | if (tbs->flags.proxy) { |
1700 | 0 | if (tbs->san.len > 0) { |
1701 | 0 | hx509_set_error_string(context, 0, EINVAL, |
1702 | 0 | "Proxy certificate is not allowed " |
1703 | 0 | "to have SubjectAltNames"); |
1704 | 0 | return EINVAL; |
1705 | 0 | } |
1706 | 0 | } |
1707 | | |
1708 | | /* version [0] Version OPTIONAL, -- EXPLICIT nnn DEFAULT 1, */ |
1709 | 0 | tbsc->version = calloc(1, sizeof(*tbsc->version)); |
1710 | 0 | if (tbsc->version == NULL) { |
1711 | 0 | ret = ENOMEM; |
1712 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1713 | 0 | goto out; |
1714 | 0 | } |
1715 | 0 | *tbsc->version = rfc3280_version_3; |
1716 | | /* serialNumber CertificateSerialNumber, */ |
1717 | 0 | if (tbs->flags.serial) { |
1718 | 0 | ret = der_copy_heim_integer(&tbs->serial, &tbsc->serialNumber); |
1719 | 0 | if (ret) { |
1720 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1721 | 0 | goto out; |
1722 | 0 | } |
1723 | 0 | } else { |
1724 | | /* |
1725 | | * If no explicit serial number is specified, 20 random bytes should be |
1726 | | * sufficiently collision resistant. Since the serial number must be a |
1727 | | * positive integer, ensure minimal ASN.1 DER form by forcing the high |
1728 | | * bit off and the next bit on (thus avoiding an all zero first octet). |
1729 | | */ |
1730 | 0 | tbsc->serialNumber.length = 20; |
1731 | 0 | tbsc->serialNumber.data = malloc(tbsc->serialNumber.length); |
1732 | 0 | if (tbsc->serialNumber.data == NULL){ |
1733 | 0 | ret = ENOMEM; |
1734 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1735 | 0 | goto out; |
1736 | 0 | } |
1737 | 0 | ret = RAND_bytes(tbsc->serialNumber.data, tbsc->serialNumber.length); |
1738 | 0 | if (ret != 1) { |
1739 | 0 | ret = HX509_CRYPTO_INTERNAL_ERROR; |
1740 | 0 | hx509_set_error_string(context, 0, ret, "Failed to generate random bytes"); |
1741 | 0 | goto out; |
1742 | 0 | } |
1743 | 0 | ((unsigned char *)tbsc->serialNumber.data)[0] &= 0x7f; |
1744 | 0 | ((unsigned char *)tbsc->serialNumber.data)[0] |= 0x40; |
1745 | 0 | } |
1746 | | /* signature AlgorithmIdentifier, */ |
1747 | 0 | ret = copy_AlgorithmIdentifier(sigalg, &tbsc->signature); |
1748 | 0 | if (ret) { |
1749 | 0 | hx509_set_error_string(context, 0, ret, "Failed to copy signature alg"); |
1750 | 0 | goto out; |
1751 | 0 | } |
1752 | | /* issuer Name, */ |
1753 | 0 | if (issuername) |
1754 | 0 | ret = copy_Name(issuername, &tbsc->issuer); |
1755 | 0 | else |
1756 | 0 | ret = hx509_name_to_Name(tbs->subject, &tbsc->issuer); |
1757 | 0 | if (ret) { |
1758 | 0 | hx509_set_error_string(context, 0, ret, "Failed to copy issuer name"); |
1759 | 0 | goto out; |
1760 | 0 | } |
1761 | | /* validity Validity, */ |
1762 | 0 | { |
1763 | | /* |
1764 | | * From RFC 5280, section 4.1.2.5: |
1765 | | * |
1766 | | * CAs conforming to this profile MUST always encode certificate |
1767 | | * validity dates through the year 2049 as UTCTime; certificate validity |
1768 | | * dates in 2050 or later MUST be encoded as GeneralizedTime. |
1769 | | * Conforming applications MUST be able to process validity dates that |
1770 | | * are encoded in either UTCTime or GeneralizedTime. |
1771 | | * |
1772 | | * 2524608000 is seconds since the epoch for 2050-01-01T00:00:00Z. |
1773 | | * |
1774 | | * Both, ...u.generalTime and ...u..utcTime are time_t. |
1775 | | */ |
1776 | 0 | if (notBefore < 1 || (int64_t)notBefore < 2524608000) |
1777 | 0 | tbsc->validity.notBefore.element = choice_Time_utcTime; |
1778 | 0 | else |
1779 | 0 | tbsc->validity.notBefore.element = choice_Time_generalTime; |
1780 | 0 | tbsc->validity.notBefore.u.generalTime = notBefore; |
1781 | |
|
1782 | 0 | if (notAfter < 1 || (int64_t)notAfter < 2524608000) |
1783 | 0 | tbsc->validity.notAfter.element = choice_Time_utcTime; |
1784 | 0 | else |
1785 | 0 | tbsc->validity.notAfter.element = choice_Time_generalTime; |
1786 | 0 | tbsc->validity.notAfter.u.generalTime = notAfter; |
1787 | 0 | } |
1788 | | /* subject Name, */ |
1789 | 0 | if (tbs->flags.proxy) { |
1790 | 0 | ret = build_proxy_prefix(context, &tbsc->issuer, &tbsc->subject); |
1791 | 0 | if (ret) |
1792 | 0 | goto out; |
1793 | 0 | } else { |
1794 | 0 | ret = hx509_name_to_Name(tbs->subject, &tbsc->subject); |
1795 | 0 | if (ret) { |
1796 | 0 | hx509_set_error_string(context, 0, ret, |
1797 | 0 | "Failed to copy subject name"); |
1798 | 0 | goto out; |
1799 | 0 | } |
1800 | 0 | } |
1801 | | /* subjectPublicKeyInfo SubjectPublicKeyInfo, */ |
1802 | 0 | ret = copy_SubjectPublicKeyInfo(&tbs->spki, &tbsc->subjectPublicKeyInfo); |
1803 | 0 | if (ret) { |
1804 | 0 | hx509_set_error_string(context, 0, ret, "Failed to copy spki"); |
1805 | 0 | goto out; |
1806 | 0 | } |
1807 | | /* issuerUniqueID [1] IMPLICIT BIT STRING OPTIONAL */ |
1808 | 0 | if (tbs->issuerUniqueID.length) { |
1809 | 0 | tbsc->issuerUniqueID = calloc(1, sizeof(*tbsc->issuerUniqueID)); |
1810 | 0 | if (tbsc->issuerUniqueID == NULL) { |
1811 | 0 | ret = ENOMEM; |
1812 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1813 | 0 | goto out; |
1814 | 0 | } |
1815 | 0 | ret = der_copy_bit_string(&tbs->issuerUniqueID, tbsc->issuerUniqueID); |
1816 | 0 | if (ret) { |
1817 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1818 | 0 | goto out; |
1819 | 0 | } |
1820 | 0 | } |
1821 | | /* subjectUniqueID [2] IMPLICIT BIT STRING OPTIONAL */ |
1822 | 0 | if (tbs->subjectUniqueID.length) { |
1823 | 0 | tbsc->subjectUniqueID = calloc(1, sizeof(*tbsc->subjectUniqueID)); |
1824 | 0 | if (tbsc->subjectUniqueID == NULL) { |
1825 | 0 | ret = ENOMEM; |
1826 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1827 | 0 | goto out; |
1828 | 0 | } |
1829 | | |
1830 | 0 | ret = der_copy_bit_string(&tbs->subjectUniqueID, tbsc->subjectUniqueID); |
1831 | 0 | if (ret) { |
1832 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1833 | 0 | goto out; |
1834 | 0 | } |
1835 | 0 | } |
1836 | | |
1837 | | /* extensions [3] EXPLICIT Extensions OPTIONAL */ |
1838 | 0 | tbsc->extensions = calloc(1, sizeof(*tbsc->extensions)); |
1839 | 0 | if (tbsc->extensions == NULL) { |
1840 | 0 | ret = ENOMEM; |
1841 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1842 | 0 | goto out; |
1843 | 0 | } |
1844 | | |
1845 | | /* Add the text BMP string Domaincontroller to the cert */ |
1846 | 0 | if (tbs->flags.domaincontroller) { |
1847 | 0 | data.data = rk_UNCONST("\x1e\x20\x00\x44\x00\x6f\x00\x6d" |
1848 | 0 | "\x00\x61\x00\x69\x00\x6e\x00\x43" |
1849 | 0 | "\x00\x6f\x00\x6e\x00\x74\x00\x72" |
1850 | 0 | "\x00\x6f\x00\x6c\x00\x6c\x00\x65" |
1851 | 0 | "\x00\x72"); |
1852 | 0 | data.length = 34; |
1853 | |
|
1854 | 0 | ret = add_extension(context, tbsc, 0, |
1855 | 0 | &asn1_oid_id_ms_cert_enroll_domaincontroller, |
1856 | 0 | &data); |
1857 | 0 | if (ret) |
1858 | 0 | goto out; |
1859 | 0 | } |
1860 | | |
1861 | | /* Add KeyUsage */ |
1862 | 0 | if (KeyUsage2int(tbs->ku) > 0) { |
1863 | 0 | ASN1_MALLOC_ENCODE(KeyUsage, data.data, data.length, |
1864 | 0 | &tbs->ku, &size, ret); |
1865 | 0 | if (ret) { |
1866 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1867 | 0 | goto out; |
1868 | 0 | } |
1869 | 0 | if (size != data.length) |
1870 | 0 | _hx509_abort("internal ASN.1 encoder error"); |
1871 | 0 | ret = add_extension(context, tbsc, 1, |
1872 | 0 | &asn1_oid_id_x509_ce_keyUsage, &data); |
1873 | 0 | free(data.data); |
1874 | 0 | if (ret) |
1875 | 0 | goto out; |
1876 | 0 | } |
1877 | | |
1878 | | /* Add ExtendedKeyUsage */ |
1879 | 0 | if (tbs->eku.len > 0) { |
1880 | 0 | ASN1_MALLOC_ENCODE(ExtKeyUsage, data.data, data.length, |
1881 | 0 | &tbs->eku, &size, ret); |
1882 | 0 | if (ret) { |
1883 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1884 | 0 | goto out; |
1885 | 0 | } |
1886 | 0 | if (size != data.length) |
1887 | 0 | _hx509_abort("internal ASN.1 encoder error"); |
1888 | 0 | ret = add_extension(context, tbsc, 1, |
1889 | 0 | &asn1_oid_id_x509_ce_extKeyUsage, &data); |
1890 | 0 | free(data.data); |
1891 | 0 | if (ret) |
1892 | 0 | goto out; |
1893 | 0 | } |
1894 | | |
1895 | | /* Add Subject Alternative Name */ |
1896 | 0 | if (tbs->san.len > 0) { |
1897 | 0 | ASN1_MALLOC_ENCODE(GeneralNames, data.data, data.length, |
1898 | 0 | &tbs->san, &size, ret); |
1899 | 0 | if (ret) { |
1900 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1901 | 0 | goto out; |
1902 | 0 | } |
1903 | 0 | if (size != data.length) |
1904 | 0 | _hx509_abort("internal ASN.1 encoder error"); |
1905 | | |
1906 | | /* The SAN extension is critical if the subject Name is empty */ |
1907 | 0 | ret = add_extension(context, tbsc, hx509_name_is_null_p(tbs->subject), |
1908 | 0 | &asn1_oid_id_x509_ce_subjectAltName, &data); |
1909 | 0 | free(data.data); |
1910 | 0 | if (ret) |
1911 | 0 | goto out; |
1912 | 0 | } |
1913 | | |
1914 | | /* Add Authority Key Identifier */ |
1915 | 0 | if (ai) { |
1916 | 0 | ASN1_MALLOC_ENCODE(AuthorityKeyIdentifier, data.data, data.length, |
1917 | 0 | ai, &size, ret); |
1918 | 0 | if (ret) { |
1919 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1920 | 0 | goto out; |
1921 | 0 | } |
1922 | 0 | if (size != data.length) |
1923 | 0 | _hx509_abort("internal ASN.1 encoder error"); |
1924 | 0 | ret = add_extension(context, tbsc, 0, |
1925 | 0 | &asn1_oid_id_x509_ce_authorityKeyIdentifier, |
1926 | 0 | &data); |
1927 | 0 | free(data.data); |
1928 | 0 | if (ret) |
1929 | 0 | goto out; |
1930 | 0 | } |
1931 | | |
1932 | | /* Add Subject Key Identifier */ |
1933 | 0 | { |
1934 | 0 | SubjectKeyIdentifier si; |
1935 | 0 | unsigned char hash[SHA_DIGEST_LENGTH]; |
1936 | |
|
1937 | 0 | { |
1938 | 0 | EVP_MD_CTX *ctx; |
1939 | |
|
1940 | 0 | ctx = EVP_MD_CTX_create(); |
1941 | 0 | EVP_DigestInit_ex(ctx, EVP_sha1(), NULL); |
1942 | 0 | EVP_DigestUpdate(ctx, tbs->spki.subjectPublicKey.data, |
1943 | 0 | tbs->spki.subjectPublicKey.length / 8); |
1944 | 0 | EVP_DigestFinal_ex(ctx, hash, NULL); |
1945 | 0 | EVP_MD_CTX_destroy(ctx); |
1946 | 0 | } |
1947 | |
|
1948 | 0 | si.data = hash; |
1949 | 0 | si.length = sizeof(hash); |
1950 | |
|
1951 | 0 | ASN1_MALLOC_ENCODE(SubjectKeyIdentifier, data.data, data.length, |
1952 | 0 | &si, &size, ret); |
1953 | 0 | if (ret) { |
1954 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1955 | 0 | goto out; |
1956 | 0 | } |
1957 | 0 | if (size != data.length) |
1958 | 0 | _hx509_abort("internal ASN.1 encoder error"); |
1959 | 0 | ret = add_extension(context, tbsc, 0, |
1960 | 0 | &asn1_oid_id_x509_ce_subjectKeyIdentifier, |
1961 | 0 | &data); |
1962 | 0 | free(data.data); |
1963 | 0 | if (ret) |
1964 | 0 | goto out; |
1965 | 0 | } |
1966 | | |
1967 | | /* Add BasicConstraints */ |
1968 | 0 | { |
1969 | 0 | BasicConstraints bc; |
1970 | 0 | unsigned int path; |
1971 | |
|
1972 | 0 | memset(&bc, 0, sizeof(bc)); |
1973 | |
|
1974 | 0 | if (tbs->flags.ca) { |
1975 | 0 | bc.cA = 1; |
1976 | 0 | if (tbs->pathLenConstraint >= 0) { |
1977 | 0 | path = tbs->pathLenConstraint; |
1978 | 0 | bc.pathLenConstraint = &path; |
1979 | 0 | } |
1980 | 0 | } |
1981 | |
|
1982 | 0 | ASN1_MALLOC_ENCODE(BasicConstraints, data.data, data.length, |
1983 | 0 | &bc, &size, ret); |
1984 | 0 | if (ret) { |
1985 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
1986 | 0 | goto out; |
1987 | 0 | } |
1988 | 0 | if (size != data.length) |
1989 | 0 | _hx509_abort("internal ASN.1 encoder error"); |
1990 | | /* Critical if this is a CA */ |
1991 | 0 | ret = add_extension(context, tbsc, tbs->flags.ca, |
1992 | 0 | &asn1_oid_id_x509_ce_basicConstraints, |
1993 | 0 | &data); |
1994 | 0 | free(data.data); |
1995 | 0 | if (ret) |
1996 | 0 | goto out; |
1997 | 0 | } |
1998 | | |
1999 | | /* Add Proxy */ |
2000 | 0 | if (tbs->flags.proxy) { |
2001 | 0 | ProxyCertInfo info; |
2002 | |
|
2003 | 0 | memset(&info, 0, sizeof(info)); |
2004 | |
|
2005 | 0 | if (tbs->pathLenConstraint >= 0) { |
2006 | 0 | info.pCPathLenConstraint = |
2007 | 0 | malloc(sizeof(*info.pCPathLenConstraint)); |
2008 | 0 | if (info.pCPathLenConstraint == NULL) { |
2009 | 0 | ret = ENOMEM; |
2010 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
2011 | 0 | goto out; |
2012 | 0 | } |
2013 | 0 | *info.pCPathLenConstraint = tbs->pathLenConstraint; |
2014 | 0 | } |
2015 | | |
2016 | 0 | ret = der_copy_oid(&asn1_oid_id_pkix_ppl_inheritAll, |
2017 | 0 | &info.proxyPolicy.policyLanguage); |
2018 | 0 | if (ret) { |
2019 | 0 | free_ProxyCertInfo(&info); |
2020 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
2021 | 0 | goto out; |
2022 | 0 | } |
2023 | | |
2024 | 0 | ASN1_MALLOC_ENCODE(ProxyCertInfo, data.data, data.length, |
2025 | 0 | &info, &size, ret); |
2026 | 0 | free_ProxyCertInfo(&info); |
2027 | 0 | if (ret) { |
2028 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
2029 | 0 | goto out; |
2030 | 0 | } |
2031 | 0 | if (size != data.length) |
2032 | 0 | _hx509_abort("internal ASN.1 encoder error"); |
2033 | 0 | ret = add_extension(context, tbsc, 0, |
2034 | 0 | &asn1_oid_id_pkix_pe_proxyCertInfo, |
2035 | 0 | &data); |
2036 | 0 | free(data.data); |
2037 | 0 | if (ret) |
2038 | 0 | goto out; |
2039 | 0 | } |
2040 | | |
2041 | | /* Add CRL distribution point */ |
2042 | 0 | if (tbs->crldp.len) { |
2043 | 0 | ASN1_MALLOC_ENCODE(CRLDistributionPoints, data.data, data.length, |
2044 | 0 | &tbs->crldp, &size, ret); |
2045 | 0 | if (ret) { |
2046 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
2047 | 0 | goto out; |
2048 | 0 | } |
2049 | 0 | if (size != data.length) |
2050 | 0 | _hx509_abort("internal ASN.1 encoder error"); |
2051 | 0 | ret = add_extension(context, tbsc, FALSE, |
2052 | 0 | &asn1_oid_id_x509_ce_cRLDistributionPoints, |
2053 | 0 | &data); |
2054 | 0 | free(data.data); |
2055 | 0 | if (ret) |
2056 | 0 | goto out; |
2057 | 0 | } |
2058 | | |
2059 | | /* Add CertificatePolicies */ |
2060 | 0 | if (tbs->cps.len) { |
2061 | 0 | ASN1_MALLOC_ENCODE(CertificatePolicies, data.data, data.length, |
2062 | 0 | &tbs->cps, &size, ret); |
2063 | 0 | if (ret) { |
2064 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
2065 | 0 | goto out; |
2066 | 0 | } |
2067 | 0 | if (size != data.length) |
2068 | 0 | _hx509_abort("internal ASN.1 encoder error"); |
2069 | 0 | ret = add_extension(context, tbsc, FALSE, |
2070 | 0 | &asn1_oid_id_x509_ce_certificatePolicies, &data); |
2071 | 0 | free(data.data); |
2072 | 0 | if (ret) |
2073 | 0 | goto out; |
2074 | 0 | } |
2075 | | |
2076 | | /* Add PolicyMappings */ |
2077 | 0 | if (tbs->cps.len) { |
2078 | 0 | ASN1_MALLOC_ENCODE(PolicyMappings, data.data, data.length, |
2079 | 0 | &tbs->pms, &size, ret); |
2080 | 0 | if (ret) { |
2081 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
2082 | 0 | goto out; |
2083 | 0 | } |
2084 | 0 | if (size != data.length) |
2085 | 0 | _hx509_abort("internal ASN.1 encoder error"); |
2086 | 0 | ret = add_extension(context, tbsc, FALSE, |
2087 | 0 | &asn1_oid_id_x509_ce_policyMappings, &data); |
2088 | 0 | free(data.data); |
2089 | 0 | if (ret) |
2090 | 0 | goto out; |
2091 | 0 | } |
2092 | | |
2093 | | /* Add Heimdal PKINIT ticket max life extension */ |
2094 | 0 | if (tbs->pkinitTicketMaxLife > 0) { |
2095 | 0 | ASN1_MALLOC_ENCODE(HeimPkinitPrincMaxLifeSecs, data.data, data.length, |
2096 | 0 | &tbs->pkinitTicketMaxLife, &size, ret); |
2097 | 0 | if (ret) { |
2098 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
2099 | 0 | goto out; |
2100 | 0 | } |
2101 | 0 | if (size != data.length) |
2102 | 0 | _hx509_abort("internal ASN.1 encoder error"); |
2103 | 0 | ret = add_extension(context, tbsc, FALSE, |
2104 | 0 | &asn1_oid_id_heim_ce_pkinit_princ_max_life, &data); |
2105 | 0 | free(data.data); |
2106 | 0 | if (ret) |
2107 | 0 | goto out; |
2108 | 0 | } |
2109 | | |
2110 | 0 | ASN1_MALLOC_ENCODE(TBSCertificate, data.data, data.length,tbsc, &size, ret); |
2111 | 0 | if (ret) { |
2112 | 0 | hx509_set_error_string(context, 0, ret, "malloc out of memory"); |
2113 | 0 | goto out; |
2114 | 0 | } |
2115 | 0 | if (data.length != size) |
2116 | 0 | _hx509_abort("internal ASN.1 encoder error"); |
2117 | | |
2118 | 0 | ret = _hx509_create_signature_bitstring(context, |
2119 | 0 | signer, |
2120 | 0 | sigalg, |
2121 | 0 | &data, |
2122 | 0 | &c.signatureAlgorithm, |
2123 | 0 | &c.signatureValue); |
2124 | 0 | free(data.data); |
2125 | 0 | if (ret) |
2126 | 0 | goto out; |
2127 | | |
2128 | 0 | *certificate = hx509_cert_init(context, &c, &error); |
2129 | 0 | if (*certificate == NULL) { |
2130 | 0 | ret = heim_error_get_code(error); |
2131 | 0 | heim_release(error); |
2132 | 0 | goto out; |
2133 | 0 | } |
2134 | | |
2135 | 0 | free_Certificate(&c); |
2136 | |
|
2137 | 0 | return 0; |
2138 | | |
2139 | 0 | out: |
2140 | 0 | free_Certificate(&c); |
2141 | 0 | return ret; |
2142 | 0 | } |
2143 | | |
2144 | | static int |
2145 | | get_AuthorityKeyIdentifier(hx509_context context, |
2146 | | const Certificate *certificate, |
2147 | | AuthorityKeyIdentifier *ai) |
2148 | 0 | { |
2149 | 0 | SubjectKeyIdentifier si; |
2150 | 0 | int ret; |
2151 | |
|
2152 | 0 | ret = _hx509_find_extension_subject_key_id(certificate, &si); |
2153 | 0 | if (ret == 0) { |
2154 | 0 | ai->keyIdentifier = calloc(1, sizeof(*ai->keyIdentifier)); |
2155 | 0 | if (ai->keyIdentifier == NULL) { |
2156 | 0 | free_SubjectKeyIdentifier(&si); |
2157 | 0 | ret = ENOMEM; |
2158 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
2159 | 0 | goto out; |
2160 | 0 | } |
2161 | 0 | ret = der_copy_octet_string(&si, ai->keyIdentifier); |
2162 | 0 | free_SubjectKeyIdentifier(&si); |
2163 | 0 | if (ret) { |
2164 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
2165 | 0 | goto out; |
2166 | 0 | } |
2167 | 0 | } else { |
2168 | 0 | GeneralNames gns; |
2169 | 0 | GeneralName gn; |
2170 | 0 | Name name; |
2171 | |
|
2172 | 0 | memset(&gn, 0, sizeof(gn)); |
2173 | 0 | memset(&gns, 0, sizeof(gns)); |
2174 | 0 | memset(&name, 0, sizeof(name)); |
2175 | |
|
2176 | 0 | ai->authorityCertIssuer = |
2177 | 0 | calloc(1, sizeof(*ai->authorityCertIssuer)); |
2178 | 0 | if (ai->authorityCertIssuer == NULL) { |
2179 | 0 | ret = ENOMEM; |
2180 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
2181 | 0 | goto out; |
2182 | 0 | } |
2183 | 0 | ai->authorityCertSerialNumber = |
2184 | 0 | calloc(1, sizeof(*ai->authorityCertSerialNumber)); |
2185 | 0 | if (ai->authorityCertSerialNumber == NULL) { |
2186 | 0 | ret = ENOMEM; |
2187 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
2188 | 0 | goto out; |
2189 | 0 | } |
2190 | | |
2191 | | /* |
2192 | | * XXX unbreak when asn1 compiler handle IMPLICIT |
2193 | | * |
2194 | | * This is so horrible. |
2195 | | */ |
2196 | | |
2197 | 0 | ret = copy_Name(&certificate->tbsCertificate.subject, &name); |
2198 | 0 | if (ret) { |
2199 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
2200 | 0 | goto out; |
2201 | 0 | } |
2202 | | |
2203 | 0 | memset(&gn, 0, sizeof(gn)); |
2204 | 0 | gn.element = choice_GeneralName_directoryName; |
2205 | 0 | gn.u.directoryName.element = choice_Name_rdnSequence; |
2206 | 0 | gn.u.directoryName.u.rdnSequence = name.u.rdnSequence; |
2207 | |
|
2208 | 0 | ret = add_GeneralNames(&gns, &gn); |
2209 | 0 | if (ret) { |
2210 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
2211 | 0 | goto out; |
2212 | 0 | } |
2213 | | |
2214 | 0 | ai->authorityCertIssuer->val = gns.val; |
2215 | 0 | ai->authorityCertIssuer->len = gns.len; |
2216 | |
|
2217 | 0 | ret = der_copy_heim_integer(&certificate->tbsCertificate.serialNumber, |
2218 | 0 | ai->authorityCertSerialNumber); |
2219 | 0 | if (ai->authorityCertSerialNumber == NULL) { |
2220 | 0 | ret = ENOMEM; |
2221 | 0 | hx509_set_error_string(context, 0, ret, "Out of memory"); |
2222 | 0 | goto out; |
2223 | 0 | } |
2224 | 0 | } |
2225 | 0 | out: |
2226 | 0 | if (ret) |
2227 | 0 | free_AuthorityKeyIdentifier(ai); |
2228 | 0 | return ret; |
2229 | 0 | } |
2230 | | |
2231 | | |
2232 | | /** |
2233 | | * Sign a to-be-signed certificate object with a issuer certificate. |
2234 | | * |
2235 | | * The caller needs to at least have called the following functions on the |
2236 | | * to-be-signed certificate object: |
2237 | | * - hx509_ca_tbs_init() |
2238 | | * - hx509_ca_tbs_set_subject() |
2239 | | * - hx509_ca_tbs_set_spki() |
2240 | | * |
2241 | | * When done the to-be-signed certificate object should be freed with |
2242 | | * hx509_ca_tbs_free(). |
2243 | | * |
2244 | | * When creating self-signed certificate use hx509_ca_sign_self() instead. |
2245 | | * |
2246 | | * @param context A hx509 context. |
2247 | | * @param tbs object to be signed. |
2248 | | * @param signer the CA certificate object to sign with (need private key). |
2249 | | * @param certificate return cerificate, free with hx509_cert_free(). |
2250 | | * |
2251 | | * @return An hx509 error code, see hx509_get_error_string(). |
2252 | | * |
2253 | | * @ingroup hx509_ca |
2254 | | */ |
2255 | | |
2256 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
2257 | | hx509_ca_sign(hx509_context context, |
2258 | | hx509_ca_tbs tbs, |
2259 | | hx509_cert signer, |
2260 | | hx509_cert *certificate) |
2261 | 0 | { |
2262 | 0 | const Certificate *signer_cert; |
2263 | 0 | AuthorityKeyIdentifier ai; |
2264 | 0 | int ret; |
2265 | |
|
2266 | 0 | memset(&ai, 0, sizeof(ai)); |
2267 | |
|
2268 | 0 | signer_cert = _hx509_get_cert(signer); |
2269 | |
|
2270 | 0 | ret = get_AuthorityKeyIdentifier(context, signer_cert, &ai); |
2271 | 0 | if (ret) |
2272 | 0 | goto out; |
2273 | | |
2274 | 0 | ret = ca_sign(context, |
2275 | 0 | tbs, |
2276 | 0 | _hx509_cert_private_key(signer), |
2277 | 0 | &ai, |
2278 | 0 | &signer_cert->tbsCertificate.subject, |
2279 | 0 | certificate); |
2280 | |
|
2281 | 0 | out: |
2282 | 0 | free_AuthorityKeyIdentifier(&ai); |
2283 | |
|
2284 | 0 | return ret; |
2285 | 0 | } |
2286 | | |
2287 | | /** |
2288 | | * Work just like hx509_ca_sign() but signs it-self. |
2289 | | * |
2290 | | * @param context A hx509 context. |
2291 | | * @param tbs object to be signed. |
2292 | | * @param signer private key to sign with. |
2293 | | * @param certificate return cerificate, free with hx509_cert_free(). |
2294 | | * |
2295 | | * @return An hx509 error code, see hx509_get_error_string(). |
2296 | | * |
2297 | | * @ingroup hx509_ca |
2298 | | */ |
2299 | | |
2300 | | HX509_LIB_FUNCTION int HX509_LIB_CALL |
2301 | | hx509_ca_sign_self(hx509_context context, |
2302 | | hx509_ca_tbs tbs, |
2303 | | hx509_private_key signer, |
2304 | | hx509_cert *certificate) |
2305 | 0 | { |
2306 | 0 | return ca_sign(context, |
2307 | 0 | tbs, |
2308 | 0 | signer, |
2309 | 0 | NULL, |
2310 | 0 | NULL, |
2311 | 0 | certificate); |
2312 | 0 | } |
2313 | | |
2314 | | /* |
2315 | | * The following used to be `kdc_issue_certificate()', which was added for |
2316 | | * kx509 support in the kdc, then adapted for bx509d. It now has no |
2317 | | * kdc-specific code and very little krb5-specific code, and is named |
2318 | | * `hx509_ca_issue_certificate()'. |
2319 | | */ |
2320 | | |
2321 | | /* From lib/krb5/principal.c */ |
2322 | 0 | #define princ_num_comp(P) ((P)->principalName.name_string.len) |
2323 | | #define princ_type(P) ((P)->principalName.name_type) |
2324 | | #define princ_comp(P) ((P)->principalName.name_string.val) |
2325 | 0 | #define princ_ncomp(P, N) ((P)->principalName.name_string.val[(N)]) |
2326 | 0 | #define princ_realm(P) ((P)->realm) |
2327 | | |
2328 | | static const char * |
2329 | | princ_get_comp_string(KRB5PrincipalName *principal, unsigned int component) |
2330 | 0 | { |
2331 | 0 | if (component >= princ_num_comp(principal)) |
2332 | 0 | return NULL; |
2333 | 0 | return princ_ncomp(principal, component); |
2334 | 0 | } |
2335 | | /* XXX Add unparse_name() */ |
2336 | | |
2337 | | typedef enum { |
2338 | | CERT_NOTSUP = 0, |
2339 | | CERT_CLIENT = 1, |
2340 | | CERT_SERVER = 2, |
2341 | | CERT_MIXED = 3 |
2342 | | } cert_type; |
2343 | | |
2344 | | static void |
2345 | | frees(char **s) |
2346 | 0 | { |
2347 | 0 | free(*s); |
2348 | 0 | *s = NULL; |
2349 | 0 | } |
2350 | | |
2351 | | static heim_error_code |
2352 | | count_sans(hx509_request req, size_t *n) |
2353 | 0 | { |
2354 | 0 | size_t i; |
2355 | 0 | char *s = NULL; |
2356 | 0 | int ret = 0; |
2357 | |
|
2358 | 0 | *n = 0; |
2359 | 0 | for (i = 0; ret == 0; i++) { |
2360 | 0 | hx509_san_type san_type; |
2361 | |
|
2362 | 0 | ret = hx509_request_get_san(req, i, &san_type, &s); |
2363 | 0 | if (ret) |
2364 | 0 | break; |
2365 | 0 | switch (san_type) { |
2366 | 0 | case HX509_SAN_TYPE_DNSNAME: |
2367 | 0 | case HX509_SAN_TYPE_EMAIL: |
2368 | 0 | case HX509_SAN_TYPE_XMPP: |
2369 | 0 | case HX509_SAN_TYPE_PKINIT: |
2370 | 0 | case HX509_SAN_TYPE_MS_UPN: |
2371 | 0 | (*n)++; |
2372 | 0 | break; |
2373 | 0 | default: |
2374 | 0 | ret = ENOTSUP; |
2375 | 0 | } |
2376 | 0 | frees(&s); |
2377 | 0 | } |
2378 | 0 | free(s); |
2379 | 0 | return ret == HX509_NO_ITEM ? 0 : ret; |
2380 | 0 | } |
2381 | | |
2382 | | static int |
2383 | | has_sans(hx509_request req) |
2384 | 0 | { |
2385 | 0 | hx509_san_type san_type; |
2386 | 0 | char *s = NULL; |
2387 | 0 | int ret = hx509_request_get_san(req, 0, &san_type, &s); |
2388 | |
|
2389 | 0 | frees(&s); |
2390 | 0 | return ret == HX509_NO_ITEM ? 0 : 1; |
2391 | 0 | } |
2392 | | |
2393 | | static cert_type |
2394 | | characterize_cprinc(hx509_context context, |
2395 | | KRB5PrincipalName *cprinc) |
2396 | 0 | { |
2397 | 0 | unsigned int ncomp = princ_num_comp(cprinc); |
2398 | 0 | const char *comp1 = princ_get_comp_string(cprinc, 1); |
2399 | |
|
2400 | 0 | switch (ncomp) { |
2401 | 0 | case 1: |
2402 | 0 | return CERT_CLIENT; |
2403 | 0 | case 2: |
2404 | 0 | if (strchr(comp1, '.') == NULL) |
2405 | 0 | return CERT_CLIENT; |
2406 | 0 | return CERT_SERVER; |
2407 | 0 | case 3: |
2408 | 0 | if (strchr(comp1, '.')) |
2409 | 0 | return CERT_SERVER; |
2410 | 0 | return CERT_NOTSUP; |
2411 | 0 | default: |
2412 | 0 | return CERT_NOTSUP; |
2413 | 0 | } |
2414 | 0 | } |
2415 | | |
2416 | | /* Characterize request as client or server cert req */ |
2417 | | static cert_type |
2418 | | characterize(hx509_context context, |
2419 | | KRB5PrincipalName *cprinc, |
2420 | | hx509_request req) |
2421 | 0 | { |
2422 | 0 | heim_error_code ret = 0; |
2423 | 0 | cert_type res = CERT_NOTSUP; |
2424 | 0 | size_t i; |
2425 | 0 | char *s = NULL; |
2426 | 0 | int want_ekus = 0; |
2427 | |
|
2428 | 0 | if (!has_sans(req)) |
2429 | 0 | return characterize_cprinc(context, cprinc); |
2430 | | |
2431 | 0 | for (i = 0; ret == 0; i++) { |
2432 | 0 | heim_oid oid; |
2433 | |
|
2434 | 0 | frees(&s); |
2435 | 0 | ret = hx509_request_get_eku(req, i, &s); |
2436 | 0 | if (ret) |
2437 | 0 | break; |
2438 | | |
2439 | 0 | want_ekus = 1; |
2440 | 0 | ret = der_parse_heim_oid(s, ".", &oid); |
2441 | 0 | if (ret) |
2442 | 0 | break; |
2443 | | /* |
2444 | | * If the client wants only a server certificate, then we'll be |
2445 | | * willing to issue one that may be longer-lived than the client's |
2446 | | * ticket/token. |
2447 | | * |
2448 | | * There may be other server EKUs, but these are the ones we know |
2449 | | * of. |
2450 | | */ |
2451 | 0 | if (der_heim_oid_cmp(&asn1_oid_id_pkix_kp_serverAuth, &oid) && |
2452 | 0 | der_heim_oid_cmp(&asn1_oid_id_pkix_kp_OCSPSigning, &oid) && |
2453 | 0 | der_heim_oid_cmp(&asn1_oid_id_pkix_kp_secureShellServer, &oid)) |
2454 | 0 | res |= CERT_CLIENT; |
2455 | 0 | else |
2456 | 0 | res |= CERT_SERVER; |
2457 | 0 | der_free_oid(&oid); |
2458 | 0 | } |
2459 | 0 | frees(&s); |
2460 | 0 | if (ret == HX509_NO_ITEM) |
2461 | 0 | ret = 0; |
2462 | |
|
2463 | 0 | for (i = 0; ret == 0; i++) { |
2464 | 0 | hx509_san_type san_type; |
2465 | |
|
2466 | 0 | frees(&s); |
2467 | 0 | ret = hx509_request_get_san(req, i, &san_type, &s); |
2468 | 0 | if (ret) |
2469 | 0 | break; |
2470 | 0 | switch (san_type) { |
2471 | 0 | case HX509_SAN_TYPE_DNSNAME: |
2472 | 0 | if (!want_ekus) |
2473 | 0 | res |= CERT_SERVER; |
2474 | 0 | break; |
2475 | 0 | case HX509_SAN_TYPE_EMAIL: |
2476 | 0 | case HX509_SAN_TYPE_XMPP: |
2477 | 0 | case HX509_SAN_TYPE_PKINIT: |
2478 | 0 | case HX509_SAN_TYPE_MS_UPN: |
2479 | 0 | if (!want_ekus) |
2480 | 0 | res |= CERT_CLIENT; |
2481 | 0 | break; |
2482 | 0 | default: |
2483 | 0 | ret = ENOTSUP; |
2484 | 0 | } |
2485 | 0 | if (ret) |
2486 | 0 | break; |
2487 | 0 | } |
2488 | 0 | frees(&s); |
2489 | 0 | if (ret == HX509_NO_ITEM) |
2490 | 0 | ret = 0; |
2491 | 0 | return ret ? CERT_NOTSUP : res; |
2492 | 0 | } |
2493 | | |
2494 | | /* |
2495 | | * Get a configuration sub-tree for kx509 based on what's being requested and |
2496 | | * by whom. |
2497 | | * |
2498 | | * We have a number of cases: |
2499 | | * |
2500 | | * - default certificate (no CSR used, or no certificate extensions requested) |
2501 | | * - for client principals |
2502 | | * - for service principals |
2503 | | * - client certificate requested (CSR used and client-y SANs/EKUs requested) |
2504 | | * - server certificate requested (CSR used and server-y SANs/EKUs requested) |
2505 | | * - mixed client/server certificate requested (...) |
2506 | | */ |
2507 | | static heim_error_code |
2508 | | get_cf(hx509_context context, |
2509 | | const heim_config_binding *cf, |
2510 | | heim_log_facility *logf, |
2511 | | hx509_request req, |
2512 | | KRB5PrincipalName *cprinc, |
2513 | | const heim_config_binding **out) |
2514 | 0 | { |
2515 | 0 | heim_error_code ret; |
2516 | 0 | unsigned int ncomp = princ_num_comp(cprinc); |
2517 | 0 | const char *realm = princ_realm(cprinc); |
2518 | 0 | const char *comp0 = princ_get_comp_string(cprinc, 0); |
2519 | 0 | const char *comp1 = princ_get_comp_string(cprinc, 1); |
2520 | 0 | const char *label = NULL; |
2521 | 0 | const char *svc = NULL; |
2522 | 0 | const char *def = NULL; |
2523 | 0 | cert_type certtype = CERT_NOTSUP; |
2524 | 0 | size_t nsans = 0; |
2525 | |
|
2526 | 0 | *out = NULL; |
2527 | 0 | if (ncomp == 0) { |
2528 | 0 | heim_log_msg(context->hcontext, logf, 5, NULL, |
2529 | 0 | "Client principal has no components!"); |
2530 | 0 | hx509_set_error_string(context, 0, ret = ENOTSUP, |
2531 | 0 | "Client principal has no components!"); |
2532 | 0 | return ret; |
2533 | 0 | } |
2534 | | |
2535 | 0 | if ((ret = count_sans(req, &nsans)) || |
2536 | 0 | (certtype = characterize(context, cprinc, req)) == CERT_NOTSUP) { |
2537 | 0 | heim_log_msg(context->hcontext, logf, 5, NULL, |
2538 | 0 | "Could not characterize CSR"); |
2539 | 0 | hx509_set_error_string(context, 0, ret, "Could not characterize CSR"); |
2540 | 0 | return ret; |
2541 | 0 | } |
2542 | | |
2543 | 0 | if (nsans) { |
2544 | 0 | def = "custom"; |
2545 | | /* Client requested some certificate extension, a SAN or EKU */ |
2546 | 0 | switch (certtype) { |
2547 | 0 | case CERT_MIXED: label = "mixed"; break; |
2548 | 0 | case CERT_CLIENT: label = "client"; break; |
2549 | 0 | case CERT_SERVER: label = "server"; break; |
2550 | 0 | default: |
2551 | 0 | hx509_set_error_string(context, 0, ret = ENOTSUP, |
2552 | 0 | "Requested SAN/EKU combination not " |
2553 | 0 | "supported"); |
2554 | 0 | return ret; |
2555 | 0 | } |
2556 | 0 | } else { |
2557 | 0 | def = "default"; |
2558 | | /* Default certificate desired */ |
2559 | 0 | if (ncomp == 1) { |
2560 | 0 | label = "user"; |
2561 | 0 | } else if (ncomp == 2 && strcmp(comp1, "root") == 0) { |
2562 | 0 | label = "root_user"; |
2563 | 0 | } else if (ncomp == 2 && strcmp(comp1, "admin") == 0) { |
2564 | 0 | label = "admin_user"; |
2565 | 0 | } else if (strchr(comp1, '.')) { |
2566 | 0 | label = "hostbased_service"; |
2567 | 0 | svc = comp0; |
2568 | 0 | } else { |
2569 | 0 | label = "other"; |
2570 | 0 | } |
2571 | 0 | } |
2572 | | |
2573 | 0 | *out = heim_config_get_list(context->hcontext, cf, label, svc, NULL); |
2574 | 0 | if (*out) { |
2575 | 0 | ret = 0; |
2576 | 0 | } else { |
2577 | 0 | heim_log_msg(context->hcontext, logf, 3, NULL, |
2578 | 0 | "No configuration for %s %s certificate's realm " |
2579 | 0 | "-> %s -> kx509 -> %s%s%s", def, label, realm, label, |
2580 | 0 | svc ? " -> " : "", svc ? svc : ""); |
2581 | 0 | hx509_set_error_string(context, 0, EACCES, |
2582 | 0 | "No configuration for %s %s certificate's realm " |
2583 | 0 | "-> %s -> kx509 -> %s%s%s", def, label, realm, label, |
2584 | 0 | svc ? " -> " : "", svc ? svc : ""); |
2585 | 0 | } |
2586 | 0 | return ret; |
2587 | 0 | } |
2588 | | |
2589 | | |
2590 | | /* |
2591 | | * Find and set a certificate template using a configuration sub-tree |
2592 | | * appropriate to the requesting principal. |
2593 | | * |
2594 | | * This allows for the specification of the following in configuration: |
2595 | | * |
2596 | | * - certificates as templates, with ${var} tokens in subjectName attribute |
2597 | | * values that will be expanded later |
2598 | | * - a plain string with ${var} tokens to use as the subjectName |
2599 | | * - EKUs |
2600 | | * - whether to include a PKINIT SAN |
2601 | | */ |
2602 | | static heim_error_code |
2603 | | set_template(hx509_context context, |
2604 | | heim_log_facility *logf, |
2605 | | const heim_config_binding *cf, |
2606 | | hx509_ca_tbs tbs) |
2607 | 0 | { |
2608 | 0 | heim_error_code ret = 0; |
2609 | 0 | const char *cert_template = NULL; |
2610 | 0 | const char *subj_name = NULL; |
2611 | 0 | char **ekus = NULL; |
2612 | |
|
2613 | 0 | if (cf == NULL) |
2614 | 0 | return EACCES; /* Can't happen */ |
2615 | | |
2616 | 0 | cert_template = heim_config_get_string(context->hcontext, cf, |
2617 | 0 | "template_cert", NULL); |
2618 | 0 | subj_name = heim_config_get_string(context->hcontext, cf, "subject_name", |
2619 | 0 | NULL); |
2620 | |
|
2621 | 0 | if (cert_template) { |
2622 | 0 | hx509_certs certs; |
2623 | 0 | hx509_cert template; |
2624 | |
|
2625 | 0 | ret = hx509_certs_init(context, cert_template, 0, NULL, &certs); |
2626 | 0 | if (ret == 0) |
2627 | 0 | ret = hx509_get_one_cert(context, certs, &template); |
2628 | 0 | hx509_certs_free(&certs); |
2629 | 0 | if (ret) { |
2630 | 0 | heim_log_msg(context->hcontext, logf, 1, NULL, |
2631 | 0 | "Failed to load certificate template from %s", |
2632 | 0 | cert_template); |
2633 | 0 | hx509_set_error_string(context, 0, EACCES, |
2634 | 0 | "Failed to load certificate template from " |
2635 | 0 | "%s", cert_template); |
2636 | 0 | return ret; |
2637 | 0 | } |
2638 | | |
2639 | | /* |
2640 | | * Only take the subjectName, the keyUsage, and EKUs from the template |
2641 | | * certificate. |
2642 | | */ |
2643 | 0 | ret = hx509_ca_tbs_set_template(context, tbs, |
2644 | 0 | HX509_CA_TEMPLATE_SUBJECT | |
2645 | 0 | HX509_CA_TEMPLATE_KU | |
2646 | 0 | HX509_CA_TEMPLATE_EKU, |
2647 | 0 | template); |
2648 | 0 | hx509_cert_free(template); |
2649 | 0 | if (ret) |
2650 | 0 | return ret; |
2651 | 0 | } |
2652 | | |
2653 | 0 | if (subj_name) { |
2654 | 0 | hx509_name dn = NULL; |
2655 | |
|
2656 | 0 | ret = hx509_parse_name(context, subj_name, &dn); |
2657 | 0 | if (ret == 0) |
2658 | 0 | ret = hx509_ca_tbs_set_subject(context, tbs, dn); |
2659 | 0 | hx509_name_free(&dn); |
2660 | 0 | if (ret) |
2661 | 0 | return ret; |
2662 | 0 | } |
2663 | | |
2664 | 0 | if (cert_template == NULL && subj_name == NULL) { |
2665 | 0 | hx509_name dn = NULL; |
2666 | |
|
2667 | 0 | ret = hx509_empty_name(context, &dn); |
2668 | 0 | if (ret == 0) |
2669 | 0 | ret = hx509_ca_tbs_set_subject(context, tbs, dn); |
2670 | 0 | hx509_name_free(&dn); |
2671 | 0 | if (ret) |
2672 | 0 | return ret; |
2673 | 0 | } |
2674 | | |
2675 | 0 | ekus = heim_config_get_strings(context->hcontext, cf, "ekus", NULL); |
2676 | 0 | if (ekus) { |
2677 | 0 | size_t i; |
2678 | |
|
2679 | 0 | for (i = 0; ret == 0 && ekus[i]; i++) { |
2680 | 0 | heim_oid oid = { 0, NULL }; |
2681 | |
|
2682 | 0 | if ((ret = der_find_or_parse_heim_oid(ekus[i], ".", &oid)) == 0) |
2683 | 0 | ret = hx509_ca_tbs_add_eku(context, tbs, &oid); |
2684 | 0 | der_free_oid(&oid); |
2685 | 0 | } |
2686 | 0 | heim_config_free_strings(ekus); |
2687 | 0 | } |
2688 | | |
2689 | | /* |
2690 | | * XXX A KeyUsage template would be nice, but it needs some smarts to |
2691 | | * remove, e.g., encipherOnly, decipherOnly, keyEncipherment, if the SPKI |
2692 | | * algorithm does not support encryption. The same logic should be added |
2693 | | * to hx509_ca_tbs_set_template()'s HX509_CA_TEMPLATE_KU functionality. |
2694 | | */ |
2695 | 0 | return ret; |
2696 | 0 | } |
2697 | | |
2698 | | /* |
2699 | | * Find and set a certificate template, set "variables" in `env', and add add |
2700 | | * default SANs/EKUs as appropriate. |
2701 | | * |
2702 | | * TODO: |
2703 | | * - lookup a template for the client principal in its HDB entry |
2704 | | * - lookup subjectName, SANs for a principal in its HDB entry |
2705 | | * - lookup a host-based client principal's HDB entry and add its canonical |
2706 | | * name / aliases as dNSName SANs |
2707 | | * (this would have to be if requested by the client, perhaps) |
2708 | | */ |
2709 | | static heim_error_code |
2710 | | set_tbs(hx509_context context, |
2711 | | heim_log_facility *logf, |
2712 | | const heim_config_binding *cf, |
2713 | | hx509_request req, |
2714 | | KRB5PrincipalName *cprinc, |
2715 | | hx509_env *env, |
2716 | | hx509_ca_tbs tbs) |
2717 | 0 | { |
2718 | 0 | KRB5PrincipalName cprinc_no_realm = *cprinc; |
2719 | 0 | heim_error_code ret; |
2720 | 0 | unsigned int ncomp = princ_num_comp(cprinc); |
2721 | 0 | const char *realm = princ_realm(cprinc); |
2722 | 0 | const char *comp0 = princ_get_comp_string(cprinc, 0); |
2723 | 0 | const char *comp1 = princ_get_comp_string(cprinc, 1); |
2724 | 0 | const char *comp2 = princ_get_comp_string(cprinc, 2); |
2725 | 0 | struct rk_strpool *strpool; |
2726 | 0 | char *princ_no_realm = NULL; |
2727 | 0 | char *princ = NULL; |
2728 | |
|
2729 | 0 | strpool = _hx509_unparse_kerberos_name(NULL, cprinc); |
2730 | 0 | if (strpool) |
2731 | 0 | princ = rk_strpoolcollect(strpool); |
2732 | 0 | cprinc_no_realm.realm = NULL; |
2733 | 0 | strpool = _hx509_unparse_kerberos_name(NULL, &cprinc_no_realm); |
2734 | 0 | if (strpool) |
2735 | 0 | princ_no_realm = rk_strpoolcollect(strpool); |
2736 | 0 | if (princ == NULL || princ_no_realm == NULL) { |
2737 | 0 | free(princ); |
2738 | 0 | return hx509_enomem(context); |
2739 | 0 | } |
2740 | 0 | strpool = NULL; |
2741 | 0 | ret = hx509_env_add(context, env, "principal-name-without-realm", |
2742 | 0 | princ_no_realm); |
2743 | 0 | if (ret == 0) |
2744 | 0 | ret = hx509_env_add(context, env, "principal-name", princ); |
2745 | 0 | if (ret == 0) |
2746 | 0 | ret = hx509_env_add(context, env, "principal-name-realm", |
2747 | 0 | realm); |
2748 | | |
2749 | | /* Populate requested certificate extensions from CSR/CSRPlus if allowed */ |
2750 | 0 | if (ret == 0) |
2751 | 0 | ret = hx509_ca_tbs_set_from_csr(context, tbs, req); |
2752 | 0 | if (ret == 0) |
2753 | 0 | ret = set_template(context, logf, cf, tbs); |
2754 | | |
2755 | | /* |
2756 | | * Optionally add PKINIT SAN. |
2757 | | * |
2758 | | * Adding an id-pkinit-san means the client can use the certificate to |
2759 | | * initiate PKINIT. That might seem odd, but it enables a sort of PKIX |
2760 | | * credential delegation by allowing forwarded Kerberos tickets to be |
2761 | | * used to acquire PKIX credentials. Thus this can work: |
2762 | | * |
2763 | | * PKIX (w/ HW token) -> Kerberos -> |
2764 | | * PKIX (w/ softtoken) -> Kerberos -> |
2765 | | * PKIX (w/ softtoken) -> Kerberos -> |
2766 | | * ... |
2767 | | * |
2768 | | * Note that we may not have added the PKINIT EKU -- that depends on the |
2769 | | * template, and host-based service templates might well not include it. |
2770 | | */ |
2771 | 0 | if (ret == 0 && !has_sans(req) && |
2772 | 0 | heim_config_get_bool_default(context->hcontext, cf, FALSE, |
2773 | 0 | "include_pkinit_san", NULL)) { |
2774 | 0 | ret = hx509_ca_tbs_add_san_pkinit(context, tbs, princ); |
2775 | 0 | } |
2776 | |
|
2777 | 0 | if (ret) |
2778 | 0 | goto out; |
2779 | | |
2780 | 0 | if (ncomp == 1) { |
2781 | 0 | const char *email_domain; |
2782 | |
|
2783 | 0 | ret = hx509_env_add(context, env, "principal-component0", |
2784 | 0 | princ_no_realm); |
2785 | | |
2786 | | /* |
2787 | | * If configured, include an rfc822Name that's just the client's |
2788 | | * principal name sans realm @ configured email domain. |
2789 | | */ |
2790 | 0 | if (ret == 0 && !has_sans(req) && |
2791 | 0 | (email_domain = heim_config_get_string(context->hcontext, cf, |
2792 | 0 | "email_domain", NULL))) { |
2793 | 0 | char *email; |
2794 | |
|
2795 | 0 | if (asprintf(&email, "%s@%s", princ_no_realm, email_domain) == -1 || |
2796 | 0 | email == NULL) |
2797 | 0 | goto enomem; |
2798 | 0 | ret = hx509_ca_tbs_add_san_rfc822name(context, tbs, email); |
2799 | 0 | free(email); |
2800 | 0 | } |
2801 | 0 | } else if (ncomp == 2 || ncomp == 3) { |
2802 | | /* |
2803 | | * 2- and 3-component principal name. |
2804 | | * |
2805 | | * We do not have a reliable name-type indicator. If the second |
2806 | | * component has a '.' in it then we'll assume that the name is a |
2807 | | * host-based (2-component) or domain-based (3-component) service |
2808 | | * principal name. Else we'll assume it's a two-component admin-style |
2809 | | * username. |
2810 | | */ |
2811 | |
|
2812 | 0 | ret = hx509_env_add(context, env, "principal-component0", comp0); |
2813 | 0 | if (ret == 0) |
2814 | 0 | ret = hx509_env_add(context, env, "principal-component1", comp1); |
2815 | 0 | if (ret == 0 && ncomp == 3) |
2816 | 0 | ret = hx509_env_add(context, env, "principal-component2", comp2); |
2817 | 0 | if (ret == 0 && strchr(comp1, '.')) { |
2818 | | /* Looks like host-based or domain-based service */ |
2819 | 0 | ret = hx509_env_add(context, env, "principal-service-name", comp0); |
2820 | 0 | if (ret == 0) |
2821 | 0 | ret = hx509_env_add(context, env, "principal-host-name", |
2822 | 0 | comp1); |
2823 | 0 | if (ret == 0 && ncomp == 3) |
2824 | 0 | ret = hx509_env_add(context, env, "principal-domain-name", |
2825 | 0 | comp2); |
2826 | 0 | if (ret == 0 && !has_sans(req) && |
2827 | 0 | heim_config_get_bool_default(context->hcontext, cf, FALSE, |
2828 | 0 | "include_dnsname_san", NULL)) { |
2829 | 0 | ret = hx509_ca_tbs_add_san_hostname(context, tbs, comp1); |
2830 | 0 | } |
2831 | 0 | } |
2832 | 0 | } else { |
2833 | 0 | heim_log_msg(context->hcontext, logf, 5, NULL, |
2834 | 0 | "kx509/bx509 client %s has too many components!", princ); |
2835 | 0 | hx509_set_error_string(context, 0, ret = EACCES, |
2836 | 0 | "kx509/bx509 client %s has too many " |
2837 | 0 | "components!", princ); |
2838 | 0 | } |
2839 | | |
2840 | 0 | out: |
2841 | 0 | if (ret == ENOMEM) |
2842 | 0 | goto enomem; |
2843 | 0 | free(princ_no_realm); |
2844 | 0 | free(princ); |
2845 | 0 | return ret; |
2846 | | |
2847 | 0 | enomem: |
2848 | 0 | heim_log_msg(context->hcontext, logf, 0, NULL, |
2849 | 0 | "Could not set up TBSCertificate: Out of memory"); |
2850 | 0 | ret = hx509_enomem(context); |
2851 | 0 | goto out; |
2852 | 0 | } |
2853 | | |
2854 | | /* |
2855 | | * Set the notBefore/notAfter for the certificate to be issued. |
2856 | | * |
2857 | | * Here `starttime' is the supplicant's credentials' notBefore equivalent, |
2858 | | * while `endtime' is the supplicant's credentials' notAfter equivalent. |
2859 | | * |
2860 | | * `req_life' is the lifetime requested by the supplicant. |
2861 | | * |
2862 | | * `endtime' must be larger than the current time. |
2863 | | * |
2864 | | * `starttime' can be zero or negative, in which case the notBefore will be the |
2865 | | * current time minus five minutes. |
2866 | | * |
2867 | | * `endtime', `req_life' and configuration parameters will be used to compute |
2868 | | * the actual notAfter. |
2869 | | */ |
2870 | | static heim_error_code |
2871 | | tbs_set_times(hx509_context context, |
2872 | | const heim_config_binding *cf, |
2873 | | heim_log_facility *logf, |
2874 | | time_t starttime, |
2875 | | time_t endtime, |
2876 | | time_t req_life, |
2877 | | hx509_ca_tbs tbs) |
2878 | 0 | { |
2879 | 0 | time_t now = time(NULL); |
2880 | 0 | time_t force = heim_config_get_time_default(context->hcontext, |
2881 | 0 | cf, 5 * 24 * 3600, |
2882 | 0 | "force_cert_lifetime", NULL); |
2883 | 0 | time_t clamp = heim_config_get_time_default(context->hcontext, cf, 0, |
2884 | 0 | "max_cert_lifetime", NULL); |
2885 | 0 | int allow_more = heim_config_get_bool_default(context->hcontext, cf, FALSE, |
2886 | 0 | "allow_extra_lifetime", |
2887 | 0 | NULL); |
2888 | 0 | starttime = starttime > 0 ? starttime : now - 5 * 60; |
2889 | |
|
2890 | 0 | if (endtime < now) { |
2891 | 0 | heim_log_msg(context->hcontext, logf, 3, NULL, |
2892 | 0 | "Endtime is in the past"); |
2893 | 0 | hx509_set_error_string(context, 0, ERANGE, "Endtime is in the past"); |
2894 | 0 | return ERANGE; |
2895 | 0 | } |
2896 | | |
2897 | | /* Apply requested lifetime if shorter or if allowed more */ |
2898 | 0 | if (req_life > 0 && req_life <= endtime - now) |
2899 | 0 | endtime = now + req_life; |
2900 | 0 | else if (req_life > 0 && allow_more) |
2901 | 0 | endtime = now + req_life; |
2902 | | |
2903 | | /* Apply floor */ |
2904 | 0 | if (force > 0 && force > endtime - now) |
2905 | 0 | endtime = now + force; |
2906 | | |
2907 | | /* Apply ceiling */ |
2908 | 0 | if (clamp > 0 && clamp < endtime - now) |
2909 | 0 | endtime = now + clamp; |
2910 | |
|
2911 | 0 | hx509_ca_tbs_set_notAfter(context, tbs, endtime); |
2912 | 0 | hx509_ca_tbs_set_notBefore(context, tbs, starttime); |
2913 | 0 | return 0; |
2914 | 0 | } |
2915 | | |
2916 | | /* |
2917 | | * Build a certifate for `principal' and its CSR. |
2918 | | * |
2919 | | * XXX Make `cprinc' a GeneralName! That's why this is private for now. |
2920 | | */ |
2921 | | heim_error_code |
2922 | | _hx509_ca_issue_certificate(hx509_context context, |
2923 | | const heim_config_binding *cf, |
2924 | | heim_log_facility *logf, |
2925 | | hx509_request req, |
2926 | | KRB5PrincipalName *cprinc, |
2927 | | time_t starttime, |
2928 | | time_t endtime, |
2929 | | time_t req_life, |
2930 | | int send_chain, |
2931 | | hx509_certs *out) |
2932 | 0 | { |
2933 | 0 | heim_error_code ret; |
2934 | 0 | const char *ca; |
2935 | 0 | hx509_ca_tbs tbs = NULL; |
2936 | 0 | hx509_certs chain = NULL; |
2937 | 0 | hx509_cert signer = NULL; |
2938 | 0 | hx509_cert cert = NULL; |
2939 | 0 | hx509_env env = NULL; |
2940 | 0 | KeyUsage ku; |
2941 | |
|
2942 | 0 | *out = NULL; |
2943 | | /* Force KU */ |
2944 | 0 | ku = int2KeyUsage(0); |
2945 | 0 | ku.digitalSignature = 1; |
2946 | 0 | hx509_request_authorize_ku(req, ku); |
2947 | |
|
2948 | 0 | ret = get_cf(context, cf, logf, req, cprinc, &cf); |
2949 | 0 | if (ret) |
2950 | 0 | return ret; |
2951 | | |
2952 | 0 | if ((ca = heim_config_get_string(context->hcontext, cf, |
2953 | 0 | "ca", NULL)) == NULL) { |
2954 | 0 | heim_log_msg(context->hcontext, logf, 3, NULL, |
2955 | 0 | "No kx509 CA issuer credential specified"); |
2956 | 0 | hx509_set_error_string(context, 0, ret = EACCES, |
2957 | 0 | "No kx509 CA issuer credential specified"); |
2958 | 0 | return ret; |
2959 | 0 | } |
2960 | | |
2961 | 0 | ret = hx509_ca_tbs_init(context, &tbs); |
2962 | 0 | if (ret) { |
2963 | 0 | heim_log_msg(context->hcontext, logf, 0, NULL, |
2964 | 0 | "Failed to create certificate: Out of memory"); |
2965 | 0 | return ret; |
2966 | 0 | } |
2967 | | |
2968 | | /* Lookup a template and set things in `env' and `tbs' as appropriate */ |
2969 | 0 | if (ret == 0) |
2970 | 0 | ret = set_tbs(context, logf, cf, req, cprinc, &env, tbs); |
2971 | | |
2972 | | /* Populate generic template "env" variables */ |
2973 | | |
2974 | | /* |
2975 | | * The `tbs' and `env' are now complete as to naming and EKUs. |
2976 | | * |
2977 | | * We check that the `tbs' is not name-less, after which all remaining |
2978 | | * failures here will not be policy failures. So we also log the intent to |
2979 | | * issue a certificate now. |
2980 | | */ |
2981 | 0 | if (ret == 0 && hx509_name_is_null_p(hx509_ca_tbs_get_name(tbs)) && |
2982 | 0 | !has_sans(req)) { |
2983 | 0 | heim_log_msg(context->hcontext, logf, 3, NULL, |
2984 | 0 | "Not issuing certificate because it would have no names"); |
2985 | 0 | hx509_set_error_string(context, 0, ret = EACCES, |
2986 | 0 | "Not issuing certificate because it " |
2987 | 0 | "would have no names"); |
2988 | 0 | } |
2989 | 0 | if (ret) |
2990 | 0 | goto out; |
2991 | | |
2992 | | /* |
2993 | | * Still to be done below: |
2994 | | * |
2995 | | * - set certificate spki |
2996 | | * - set certificate validity |
2997 | | * - expand variables in certificate subject name template |
2998 | | * - sign certificate |
2999 | | * - encode certificate and chain |
3000 | | */ |
3001 | | |
3002 | | /* Load the issuer certificate and private key */ |
3003 | 0 | { |
3004 | 0 | hx509_certs certs; |
3005 | 0 | hx509_query *q; |
3006 | |
|
3007 | 0 | ret = hx509_certs_init(context, ca, 0, NULL, &certs); |
3008 | 0 | if (ret) { |
3009 | 0 | heim_log_msg(context->hcontext, logf, 1, NULL, |
3010 | 0 | "Failed to load CA certificate and private key %s", |
3011 | 0 | ca); |
3012 | 0 | hx509_set_error_string(context, 0, ret, "Failed to load " |
3013 | 0 | "CA certificate and private key %s", ca); |
3014 | 0 | goto out; |
3015 | 0 | } |
3016 | 0 | ret = hx509_query_alloc(context, &q); |
3017 | 0 | if (ret) { |
3018 | 0 | hx509_certs_free(&certs); |
3019 | 0 | goto out; |
3020 | 0 | } |
3021 | | |
3022 | 0 | hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); |
3023 | 0 | hx509_query_match_option(q, HX509_QUERY_OPTION_KU_KEYCERTSIGN); |
3024 | |
|
3025 | 0 | ret = hx509_certs_find(context, certs, q, &signer); |
3026 | 0 | hx509_query_free(context, q); |
3027 | 0 | hx509_certs_free(&certs); |
3028 | 0 | if (ret) { |
3029 | 0 | heim_log_msg(context->hcontext, logf, 1, NULL, |
3030 | 0 | "Failed to find a CA certificate in %s", ca); |
3031 | 0 | hx509_set_error_string(context, 0, ret, |
3032 | 0 | "Failed to find a CA certificate in %s", |
3033 | 0 | ca); |
3034 | 0 | goto out; |
3035 | 0 | } |
3036 | 0 | } |
3037 | | |
3038 | | /* Populate the subject public key in the TBS context */ |
3039 | 0 | { |
3040 | 0 | SubjectPublicKeyInfo spki; |
3041 | |
|
3042 | 0 | ret = hx509_request_get_SubjectPublicKeyInfo(context, |
3043 | 0 | req, &spki); |
3044 | 0 | if (ret == 0) |
3045 | 0 | ret = hx509_ca_tbs_set_spki(context, tbs, &spki); |
3046 | 0 | free_SubjectPublicKeyInfo(&spki); |
3047 | 0 | if (ret) |
3048 | 0 | goto out; |
3049 | 0 | } |
3050 | | |
3051 | | /* Work out cert expiration */ |
3052 | 0 | if (ret == 0) |
3053 | 0 | ret = tbs_set_times(context, cf, logf, starttime, endtime, req_life, |
3054 | 0 | tbs); |
3055 | | |
3056 | | /* Expand the subjectName template in the TBS using the env */ |
3057 | 0 | if (ret == 0) |
3058 | 0 | ret = hx509_ca_tbs_subject_expand(context, tbs, env); |
3059 | 0 | hx509_env_free(&env); |
3060 | | |
3061 | | /* All done with the TBS, sign/issue the certificate */ |
3062 | 0 | if (ret == 0) |
3063 | 0 | ret = hx509_ca_sign(context, tbs, signer, &cert); |
3064 | | |
3065 | | /* |
3066 | | * Gather the certificate and chain into a MEMORY store, being careful not |
3067 | | * to include private keys in the chain. |
3068 | | * |
3069 | | * We could have specified a separate configuration parameter for an hx509 |
3070 | | * store meant to have only the chain and no private keys, but expecting |
3071 | | * the full chain in the issuer credential store and copying only the certs |
3072 | | * (but not the private keys) is safer and easier to configure. |
3073 | | */ |
3074 | 0 | if (ret == 0) |
3075 | 0 | ret = hx509_certs_init(context, "MEMORY:certs", |
3076 | 0 | HX509_CERTS_NO_PRIVATE_KEYS, NULL, out); |
3077 | 0 | if (ret == 0) |
3078 | 0 | ret = hx509_certs_add(context, *out, cert); |
3079 | 0 | if (ret == 0 && send_chain) { |
3080 | 0 | ret = hx509_certs_init(context, ca, |
3081 | 0 | HX509_CERTS_NO_PRIVATE_KEYS, NULL, &chain); |
3082 | 0 | if (ret == 0) |
3083 | 0 | ret = hx509_certs_merge(context, *out, chain); |
3084 | 0 | } |
3085 | |
|
3086 | 0 | out: |
3087 | 0 | hx509_certs_free(&chain); |
3088 | 0 | if (env) |
3089 | 0 | hx509_env_free(&env); |
3090 | 0 | if (tbs) |
3091 | 0 | hx509_ca_tbs_free(&tbs); |
3092 | 0 | if (cert) |
3093 | 0 | hx509_cert_free(cert); |
3094 | 0 | if (signer) |
3095 | 0 | hx509_cert_free(signer); |
3096 | 0 | if (ret) |
3097 | 0 | hx509_certs_free(out); |
3098 | 0 | return ret; |
3099 | 0 | } |