diff options
author | beck <> | 2016-11-06 10:37:38 +0000 |
---|---|---|
committer | beck <> | 2016-11-06 10:37:38 +0000 |
commit | 6732c1155155b36860bbe6f86c2525400918a716 (patch) | |
tree | fba04b377288b64ebf38d04431c7350aa560e660 /src | |
parent | d53b4cfb1657b03af7f1654f76351dbb75a44d2f (diff) | |
download | openbsd-6732c1155155b36860bbe6f86c2525400918a716.tar.gz openbsd-6732c1155155b36860bbe6f86c2525400918a716.tar.bz2 openbsd-6732c1155155b36860bbe6f86c2525400918a716.zip |
Rework X509_verify_cert to support alt chains on certificate verification,
via boringssl.
ok jsing@ miod@
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/libcrypto/x509/x509_vfy.c | 382 |
1 files changed, 265 insertions, 117 deletions
diff --git a/src/lib/libcrypto/x509/x509_vfy.c b/src/lib/libcrypto/x509/x509_vfy.c index 7a6d272023..abd5c65e31 100644 --- a/src/lib/libcrypto/x509/x509_vfy.c +++ b/src/lib/libcrypto/x509/x509_vfy.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: x509_vfy.c,v 1.51 2016/11/04 18:07:23 beck Exp $ */ | 1 | /* $OpenBSD: x509_vfy.c,v 1.52 2016/11/06 10:37:38 beck Exp $ */ |
2 | /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) | 2 | /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) |
3 | * All rights reserved. | 3 | * All rights reserved. |
4 | * | 4 | * |
@@ -74,6 +74,7 @@ | |||
74 | #include <openssl/x509.h> | 74 | #include <openssl/x509.h> |
75 | #include <openssl/x509v3.h> | 75 | #include <openssl/x509v3.h> |
76 | #include "x509_lcl.h" | 76 | #include "x509_lcl.h" |
77 | #include "vpm_int.h" | ||
77 | 78 | ||
78 | /* CRL score values */ | 79 | /* CRL score values */ |
79 | 80 | ||
@@ -153,41 +154,115 @@ x509_subject_cmp(X509 **a, X509 **b) | |||
153 | } | 154 | } |
154 | #endif | 155 | #endif |
155 | 156 | ||
157 | /* Return 1 is a certificate is self signed */ | ||
158 | static int | ||
159 | cert_self_signed(X509 *x) | ||
160 | { | ||
161 | X509_check_purpose(x, -1, 0); | ||
162 | if (x->ex_flags & EXFLAG_SS) | ||
163 | return 1; | ||
164 | else | ||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static int | ||
169 | check_id_error(X509_STORE_CTX *ctx, int errcode) | ||
170 | { | ||
171 | ctx->error = errcode; | ||
172 | ctx->current_cert = ctx->cert; | ||
173 | ctx->error_depth = 0; | ||
174 | return ctx->verify_cb(0, ctx); | ||
175 | } | ||
176 | |||
177 | static int | ||
178 | check_hosts(X509 *x, X509_VERIFY_PARAM_ID *id) | ||
179 | { | ||
180 | size_t i; | ||
181 | size_t n = sk_OPENSSL_STRING_num(id->hosts); | ||
182 | char *name; | ||
183 | |||
184 | free(id->peername); | ||
185 | id->peername = NULL; | ||
186 | |||
187 | for (i = 0; i < n; ++i) { | ||
188 | name = sk_OPENSSL_STRING_value(id->hosts, i); | ||
189 | if (X509_check_host(x, name, strlen(name), id->hostflags, | ||
190 | &id->peername) > 0) | ||
191 | return 1; | ||
192 | } | ||
193 | return n == 0; | ||
194 | } | ||
195 | |||
196 | static int | ||
197 | check_id(X509_STORE_CTX *ctx) | ||
198 | { | ||
199 | X509_VERIFY_PARAM *vpm = ctx->param; | ||
200 | X509_VERIFY_PARAM_ID *id = vpm->id; | ||
201 | X509 *x = ctx->cert; | ||
202 | |||
203 | if (id->hosts && check_hosts(x, id) <= 0) { | ||
204 | if (!check_id_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH)) | ||
205 | return 0; | ||
206 | } | ||
207 | if (id->email != NULL && X509_check_email(x, id->email, id->emaillen, 0) | ||
208 | <= 0) { | ||
209 | if (!check_id_error(ctx, X509_V_ERR_EMAIL_MISMATCH)) | ||
210 | return 0; | ||
211 | } | ||
212 | if (id->ip != NULL && X509_check_ip(x, id->ip, id->iplen, 0) <= 0) { | ||
213 | if (!check_id_error(ctx, X509_V_ERR_IP_ADDRESS_MISMATCH)) | ||
214 | return 0; | ||
215 | } | ||
216 | return 1; | ||
217 | } | ||
218 | |||
156 | int | 219 | int |
157 | X509_verify_cert(X509_STORE_CTX *ctx) | 220 | X509_verify_cert(X509_STORE_CTX *ctx) |
158 | { | 221 | { |
159 | X509 *x, *xtmp, *chain_ss = NULL; | 222 | X509 *x, *xtmp, *xtmp2, *chain_ss = NULL; |
160 | int bad_chain = 0; | 223 | int bad_chain = 0; |
161 | X509_VERIFY_PARAM *param = ctx->param; | 224 | X509_VERIFY_PARAM *param = ctx->param; |
162 | int depth, i, ok = 0; | 225 | int depth, i, ok = 0; |
163 | int num; | 226 | int num, j, retry, trust; |
164 | int (*cb)(int xok, X509_STORE_CTX *xctx); | 227 | int (*cb) (int xok, X509_STORE_CTX *xctx); |
165 | STACK_OF(X509) *sktmp = NULL; | 228 | STACK_OF(X509) *sktmp = NULL; |
166 | |||
167 | if (ctx->cert == NULL) { | 229 | if (ctx->cert == NULL) { |
168 | X509err(X509_F_X509_VERIFY_CERT, | 230 | X509err(X509_F_X509_VERIFY_CERT, |
169 | X509_R_NO_CERT_SET_FOR_US_TO_VERIFY); | 231 | X509_R_NO_CERT_SET_FOR_US_TO_VERIFY); |
232 | ctx->error = X509_V_ERR_INVALID_CALL; | ||
233 | return -1; | ||
234 | } | ||
235 | if (ctx->chain != NULL) { | ||
236 | /* | ||
237 | * This X509_STORE_CTX has already been used to verify | ||
238 | * a cert. We cannot do another one. | ||
239 | */ | ||
240 | X509err(X509_F_X509_VERIFY_CERT, | ||
241 | ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); | ||
242 | ctx->error = X509_V_ERR_INVALID_CALL; | ||
170 | return -1; | 243 | return -1; |
171 | } | 244 | } |
172 | 245 | ||
173 | cb = ctx->verify_cb; | 246 | cb = ctx->verify_cb; |
174 | 247 | ||
175 | /* first we make sure the chain we are going to build is | 248 | /* |
176 | * present and that the first entry is in place */ | 249 | * First we make sure the chain we are going to build is |
177 | if (ctx->chain == NULL) { | 250 | * present and that the first entry is in place. |
178 | if (((ctx->chain = sk_X509_new_null()) == NULL) || | 251 | */ |
179 | (!sk_X509_push(ctx->chain, ctx->cert))) { | 252 | ctx->chain = sk_X509_new_null(); |
180 | X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE); | 253 | if (ctx->chain == NULL || !sk_X509_push(ctx->chain, ctx->cert)) { |
181 | goto end; | 254 | X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE); |
182 | } | 255 | ctx->error = X509_V_ERR_OUT_OF_MEM; |
183 | CRYPTO_add(&ctx->cert->references, 1, CRYPTO_LOCK_X509); | 256 | goto end; |
184 | ctx->last_untrusted = 1; | ||
185 | } | 257 | } |
258 | X509_up_ref(ctx->cert); | ||
259 | ctx->last_untrusted = 1; | ||
186 | 260 | ||
187 | /* We use a temporary STACK so we can chop and hack at it */ | 261 | /* We use a temporary STACK so we can chop and hack at it */ |
188 | if (ctx->untrusted != NULL && | 262 | if (ctx->untrusted != NULL && |
189 | (sktmp = sk_X509_dup(ctx->untrusted)) == NULL) { | 263 | (sktmp = sk_X509_dup(ctx->untrusted)) == NULL) { |
190 | X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE); | 264 | X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE); |
265 | ctx->error = X509_V_ERR_OUT_OF_MEM; | ||
191 | goto end; | 266 | goto end; |
192 | } | 267 | } |
193 | 268 | ||
@@ -197,17 +272,34 @@ X509_verify_cert(X509_STORE_CTX *ctx) | |||
197 | 272 | ||
198 | for (;;) { | 273 | for (;;) { |
199 | /* If we have enough, we break */ | 274 | /* If we have enough, we break */ |
275 | /* FIXME: If this happens, we should take | ||
276 | * note of it and, if appropriate, use the | ||
277 | * X509_V_ERR_CERT_CHAIN_TOO_LONG error code | ||
278 | * later. | ||
279 | */ | ||
200 | if (depth < num) | 280 | if (depth < num) |
201 | break; /* FIXME: If this happens, we should take | 281 | break; |
202 | * note of it and, if appropriate, use the | ||
203 | * X509_V_ERR_CERT_CHAIN_TOO_LONG error | ||
204 | * code later. | ||
205 | */ | ||
206 | |||
207 | /* If we are self signed, we break */ | 282 | /* If we are self signed, we break */ |
208 | if (ctx->check_issued(ctx, x, x)) | 283 | if (cert_self_signed(x)) |
209 | break; | 284 | break; |
210 | 285 | /* | |
286 | * If asked see if we can find issuer in trusted store first | ||
287 | */ | ||
288 | if (ctx->param->flags & X509_V_FLAG_TRUSTED_FIRST) { | ||
289 | ok = ctx->get_issuer(&xtmp, ctx, x); | ||
290 | if (ok < 0) { | ||
291 | ctx->error = X509_V_ERR_STORE_LOOKUP; | ||
292 | goto end; | ||
293 | } | ||
294 | /* | ||
295 | * If successful for now free up cert so it | ||
296 | * will be picked up again later. | ||
297 | */ | ||
298 | if (ok > 0) { | ||
299 | X509_free(xtmp); | ||
300 | break; | ||
301 | } | ||
302 | } | ||
211 | /* If we were passed a cert chain, use it first */ | 303 | /* If we were passed a cert chain, use it first */ |
212 | if (ctx->untrusted != NULL) { | 304 | if (ctx->untrusted != NULL) { |
213 | xtmp = find_issuer(ctx, sktmp, x); | 305 | xtmp = find_issuer(ctx, sktmp, x); |
@@ -215,109 +307,174 @@ X509_verify_cert(X509_STORE_CTX *ctx) | |||
215 | if (!sk_X509_push(ctx->chain, xtmp)) { | 307 | if (!sk_X509_push(ctx->chain, xtmp)) { |
216 | X509err(X509_F_X509_VERIFY_CERT, | 308 | X509err(X509_F_X509_VERIFY_CERT, |
217 | ERR_R_MALLOC_FAILURE); | 309 | ERR_R_MALLOC_FAILURE); |
310 | ctx->error = X509_V_ERR_OUT_OF_MEM; | ||
311 | ok = 0; | ||
218 | goto end; | 312 | goto end; |
219 | } | 313 | } |
220 | CRYPTO_add(&xtmp->references, 1, | 314 | X509_up_ref(xtmp); |
221 | CRYPTO_LOCK_X509); | ||
222 | (void)sk_X509_delete_ptr(sktmp, xtmp); | 315 | (void)sk_X509_delete_ptr(sktmp, xtmp); |
223 | ctx->last_untrusted++; | 316 | ctx->last_untrusted++; |
224 | x = xtmp; | 317 | x = xtmp; |
225 | num++; | 318 | num++; |
226 | /* reparse the full chain for | 319 | /* |
227 | * the next one */ | 320 | * reparse the full chain for the next one |
321 | */ | ||
228 | continue; | 322 | continue; |
229 | } | 323 | } |
230 | } | 324 | } |
231 | break; | 325 | break; |
232 | } | 326 | } |
233 | sk_X509_free(sktmp); | 327 | /* Remember how many untrusted certs we have */ |
234 | sktmp = NULL; | 328 | j = num; |
235 | 329 | ||
236 | /* at this point, chain should contain a list of untrusted | 330 | /* |
331 | * At this point, chain should contain a list of untrusted | ||
237 | * certificates. We now need to add at least one trusted one, | 332 | * certificates. We now need to add at least one trusted one, |
238 | * if possible, otherwise we complain. */ | 333 | * if possible, otherwise we complain. |
239 | 334 | */ | |
240 | /* Examine last certificate in chain and see if it | 335 | |
241 | * is self signed. | 336 | do { |
242 | */ | 337 | /* |
243 | 338 | * Examine last certificate in chain and see if it is | |
244 | i = sk_X509_num(ctx->chain); | 339 | * self signed. |
245 | x = sk_X509_value(ctx->chain, i - 1); | 340 | */ |
246 | if (ctx->check_issued(ctx, x, x)) { | 341 | i = sk_X509_num(ctx->chain); |
247 | /* we have a self signed certificate */ | 342 | x = sk_X509_value(ctx->chain, i - 1); |
248 | if (sk_X509_num(ctx->chain) == 1) { | 343 | if (cert_self_signed(x)) { |
249 | /* We have a single self signed certificate: see if | 344 | /* we have a self signed certificate */ |
250 | * we can find it in the store. We must have an exact | 345 | if (i == 1) { |
251 | * match to avoid possible impersonation. | 346 | /* |
252 | */ | 347 | * We have a single self signed |
253 | ok = ctx->get_issuer(&xtmp, ctx, x); | 348 | * certificate: see if we can find it |
254 | if ((ok <= 0) || X509_cmp(x, xtmp)) { | 349 | * in the store. We must have an exact |
255 | ctx->error = | 350 | * match to avoid possible |
256 | X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT; | 351 | * impersonation. |
257 | ctx->current_cert = x; | 352 | */ |
258 | ctx->error_depth = i - 1; | 353 | ok = ctx->get_issuer(&xtmp, ctx, x); |
259 | if (ok == 1) | 354 | if ((ok <= 0) || X509_cmp(x, xtmp)) { |
260 | X509_free(xtmp); | 355 | ctx->error = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT; |
261 | bad_chain = 1; | 356 | ctx->current_cert = x; |
262 | ok = cb(0, ctx); | 357 | ctx->error_depth = i - 1; |
263 | if (!ok) | 358 | if (ok == 1) |
264 | goto end; | 359 | X509_free(xtmp); |
360 | bad_chain = 1; | ||
361 | ok = cb(0, ctx); | ||
362 | if (!ok) | ||
363 | goto end; | ||
364 | } else { | ||
365 | /* | ||
366 | * We have a match: replace | ||
367 | * certificate with store | ||
368 | * version so we get any trust | ||
369 | * settings. | ||
370 | */ | ||
371 | X509_free(x); | ||
372 | x = xtmp; | ||
373 | (void)sk_X509_set(ctx->chain, i - 1, x); | ||
374 | ctx->last_untrusted = 0; | ||
375 | } | ||
265 | } else { | 376 | } else { |
266 | /* We have a match: replace certificate with store version | 377 | /* |
267 | * so we get any trust settings. | 378 | * extract and save self signed |
379 | * certificate for later use | ||
268 | */ | 380 | */ |
269 | X509_free(x); | 381 | chain_ss = sk_X509_pop(ctx->chain); |
270 | x = xtmp; | 382 | ctx->last_untrusted--; |
271 | (void)sk_X509_set(ctx->chain, i - 1, x); | 383 | num--; |
272 | ctx->last_untrusted = 0; | 384 | j--; |
385 | x = sk_X509_value(ctx->chain, num - 1); | ||
273 | } | 386 | } |
274 | } else { | ||
275 | /* extract and save self signed certificate for later use */ | ||
276 | chain_ss = sk_X509_pop(ctx->chain); | ||
277 | ctx->last_untrusted--; | ||
278 | num--; | ||
279 | x = sk_X509_value(ctx->chain, num - 1); | ||
280 | } | 387 | } |
281 | } | 388 | /* We now lookup certs from the certificate store */ |
282 | 389 | for (;;) { | |
283 | /* We now lookup certs from the certificate store */ | 390 | /* If we have enough, we break */ |
284 | for (;;) { | 391 | if (depth < num) |
285 | /* If we have enough, we break */ | 392 | break; |
286 | if (depth < num) | 393 | /* If we are self signed, we break */ |
287 | break; | 394 | if (cert_self_signed(x)) |
395 | break; | ||
396 | ok = ctx->get_issuer(&xtmp, ctx, x); | ||
288 | 397 | ||
289 | /* If we are self signed, we break */ | 398 | if (ok < 0) { |
290 | if (ctx->check_issued(ctx, x, x)) | 399 | ctx->error = X509_V_ERR_STORE_LOOKUP; |
291 | break; | 400 | goto end; |
401 | } | ||
402 | if (ok == 0) | ||
403 | break; | ||
404 | x = xtmp; | ||
405 | if (!sk_X509_push(ctx->chain, x)) { | ||
406 | X509_free(xtmp); | ||
407 | X509err(X509_F_X509_VERIFY_CERT, | ||
408 | ERR_R_MALLOC_FAILURE); | ||
409 | ctx->error = X509_V_ERR_OUT_OF_MEM; | ||
410 | ok = 0; | ||
411 | goto end; | ||
412 | } | ||
413 | num++; | ||
414 | } | ||
292 | 415 | ||
293 | ok = ctx->get_issuer(&xtmp, ctx, x); | 416 | /* we now have our chain, lets check it... */ |
294 | if (ok < 0) | 417 | trust = check_trust(ctx); |
295 | return ok; | ||
296 | if (ok == 0) | ||
297 | break; | ||
298 | 418 | ||
299 | x = xtmp; | 419 | /* If explicitly rejected error */ |
300 | if (!sk_X509_push(ctx->chain, x)) { | 420 | if (trust == X509_TRUST_REJECTED) { |
301 | X509_free(xtmp); | 421 | ok = 0; |
302 | X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE); | 422 | goto end; |
303 | return 0; | ||
304 | } | 423 | } |
305 | num++; | 424 | /* |
306 | } | 425 | * If it's not explicitly trusted then check if there |
307 | 426 | * is an alternative chain that could be used. We only | |
308 | /* we now have our chain, lets check it... */ | 427 | * do this if we haven't already checked via |
428 | * TRUSTED_FIRST and the user hasn't switched off | ||
429 | * alternate chain checking | ||
430 | */ | ||
431 | retry = 0; | ||
432 | if (trust != X509_TRUST_TRUSTED && | ||
433 | !(ctx->param->flags & X509_V_FLAG_TRUSTED_FIRST) && | ||
434 | !(ctx->param->flags & X509_V_FLAG_NO_ALT_CHAINS)) { | ||
435 | while (j-- > 1) { | ||
436 | xtmp2 = sk_X509_value(ctx->chain, j - 1); | ||
437 | ok = ctx->get_issuer(&xtmp, ctx, xtmp2); | ||
438 | if (ok < 0) | ||
439 | goto end; | ||
440 | /* Check if we found an alternate chain */ | ||
441 | if (ok > 0) { | ||
442 | /* | ||
443 | * Free up the found cert | ||
444 | * we'll add it again later | ||
445 | */ | ||
446 | X509_free(xtmp); | ||
447 | /* | ||
448 | * Dump all the certs above | ||
449 | * this point - we've found an | ||
450 | * alternate chain | ||
451 | */ | ||
452 | while (num > j) { | ||
453 | xtmp = sk_X509_pop(ctx->chain); | ||
454 | X509_free(xtmp); | ||
455 | num--; | ||
456 | } | ||
457 | ctx->last_untrusted = sk_X509_num(ctx->chain); | ||
458 | retry = 1; | ||
459 | break; | ||
460 | } | ||
461 | } | ||
462 | } | ||
463 | } while (retry); | ||
309 | 464 | ||
310 | /* Is last certificate looked up self signed? */ | 465 | /* |
311 | if (!ctx->check_issued(ctx, x, x)) { | 466 | * If not explicitly trusted then indicate error unless it's a single |
312 | if ((chain_ss == NULL) || | 467 | * self signed certificate in which case we've indicated an error already |
313 | !ctx->check_issued(ctx, x, chain_ss)) { | 468 | * and set bad_chain == 1 |
469 | */ | ||
470 | if (trust != X509_TRUST_TRUSTED && !bad_chain) { | ||
471 | if ((chain_ss == NULL) || !ctx->check_issued(ctx, x, chain_ss)) { | ||
314 | if (ctx->last_untrusted >= num) | 472 | if (ctx->last_untrusted >= num) |
315 | ctx->error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY; | 473 | ctx->error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY; |
316 | else | 474 | else |
317 | ctx->error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; | 475 | ctx->error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; |
318 | ctx->current_cert = x; | 476 | ctx->current_cert = x; |
319 | } else { | 477 | } else { |
320 | |||
321 | if (!sk_X509_push(ctx->chain, chain_ss)) { | 478 | if (!sk_X509_push(ctx->chain, chain_ss)) { |
322 | X509_free(chain_ss); | 479 | X509_free(chain_ss); |
323 | X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE); | 480 | X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE); |
@@ -350,19 +507,13 @@ X509_verify_cert(X509_STORE_CTX *ctx) | |||
350 | if (!ok) | 507 | if (!ok) |
351 | goto end; | 508 | goto end; |
352 | 509 | ||
353 | /* The chain extensions are OK: check trust */ | 510 | ok = check_id(ctx); |
354 | |||
355 | if (param->trust > 0) | ||
356 | ok = check_trust(ctx); | ||
357 | 511 | ||
358 | if (!ok) | 512 | if (!ok) |
359 | goto end; | 513 | goto end; |
360 | 514 | /* | |
361 | /* We may as well copy down any DSA parameters that are required */ | 515 | * Check revocation status: we do this after copying parameters because |
362 | X509_get_pubkey_parameters(NULL, ctx->chain); | 516 | * they may be needed for CRL signature verification. |
363 | |||
364 | /* Check revocation status: we do this after copying parameters | ||
365 | * because they may be needed for CRL signature verification. | ||
366 | */ | 517 | */ |
367 | 518 | ||
368 | ok = ctx->check_revocation(ctx); | 519 | ok = ctx->check_revocation(ctx); |
@@ -376,23 +527,20 @@ X509_verify_cert(X509_STORE_CTX *ctx) | |||
376 | ok = internal_verify(ctx); | 527 | ok = internal_verify(ctx); |
377 | if (!ok) | 528 | if (!ok) |
378 | goto end; | 529 | goto end; |
379 | |||
380 | /* If we get this far evaluate policies */ | 530 | /* If we get this far evaluate policies */ |
381 | if (!bad_chain && (ctx->param->flags & X509_V_FLAG_POLICY_CHECK)) | 531 | if (!bad_chain && (ctx->param->flags & X509_V_FLAG_POLICY_CHECK)) |
382 | ok = ctx->check_policy(ctx); | 532 | ok = ctx->check_policy(ctx); |
383 | if (!ok) | 533 | end: |
384 | goto end; | ||
385 | if (0) { | ||
386 | end: | ||
387 | X509_get_pubkey_parameters(NULL, ctx->chain); | ||
388 | } | ||
389 | if (sktmp != NULL) | 534 | if (sktmp != NULL) |
390 | sk_X509_free(sktmp); | 535 | sk_X509_free(sktmp); |
391 | X509_free(chain_ss); | 536 | X509_free(chain_ss); |
537 | |||
538 | /* Safety net, error returns must set ctx->error */ | ||
539 | if (ok <= 0 && ctx->error == X509_V_OK) | ||
540 | ctx->error = X509_V_ERR_UNSPECIFIED; | ||
392 | return ok; | 541 | return ok; |
393 | } | 542 | } |
394 | 543 | ||
395 | |||
396 | /* Given a STACK_OF(X509) find the issuer of cert (if any) | 544 | /* Given a STACK_OF(X509) find the issuer of cert (if any) |
397 | */ | 545 | */ |
398 | 546 | ||