/* * Verify the certificate validity, with profile */ int ttls_x509_crt_verify_with_profile(ttls_x509_crt *crt, ttls_x509_crt *trust_ca, ttls_x509_crl *ca_crl, const ttls_x509_crt_profile *profile, const char *cn, uint32_t *flags) { size_t cn_len; int ret; int pathlen = 0, selfsigned = 0; ttls_x509_crt *parent; ttls_x509_name *name; ttls_x509_sequence *cur = NULL; ttls_pk_type_t pk_type; *flags = 0; if (profile == NULL) { ret = TTLS_ERR_X509_BAD_INPUT_DATA; goto exit; } if (cn != NULL) { name = &crt->subject; cn_len = strlen(cn); if (crt->ext_types & TTLS_X509_EXT_SUBJECT_ALT_NAME) { cur = &crt->subject_alt_names; while (cur != NULL) { if (cur->buf.len == cn_len && !x509_memcasecmp(cn, cur->buf.p, cn_len)) break; if (cur->buf.len > 2 && !memcmp(cur->buf.p, "*.", 2) && !x509_check_wildcard(cn, &cur->buf)) break; cur = cur->next; } if (cur == NULL) *flags |= TTLS_X509_BADCERT_CN_MISMATCH; } else { while (name != NULL) { if (!TTLS_OID_CMP(TTLS_OID_AT_CN, &name->oid)) { if (name->val.len == cn_len && !x509_memcasecmp(name->val.p, cn, cn_len)) break; if (name->val.len > 2 && !memcmp(name->val.p, "*.", 2) && !x509_check_wildcard(cn, &name->val)) break; } name = name->next; } if (name == NULL) *flags |= TTLS_X509_BADCERT_CN_MISMATCH; } } /* Check the type and size of the key */ pk_type = ttls_pk_get_type(&crt->pk); if (x509_profile_check_pk_alg(profile, pk_type) != 0) *flags |= TTLS_X509_BADCERT_BAD_PK; if (x509_profile_check_key(profile, pk_type, &crt->pk) != 0) *flags |= TTLS_X509_BADCERT_BAD_KEY; /* Look for a parent in trusted CAs */ for (parent = trust_ca; parent != NULL; parent = parent->next) { if (x509_crt_check_parent(crt, parent, 0, pathlen == 0) == 0) break; } if (parent != NULL) { ret = x509_crt_verify_top(crt, parent, ca_crl, profile, pathlen, selfsigned, flags); if (ret != 0) goto exit; } else { /* Look for a parent upwards the chain */ for (parent = crt->next; parent != NULL; parent = parent->next) if (x509_crt_check_parent(crt, parent, 0, pathlen == 0) == 0) break; /* Are we part of the chain or at the top? */ if (parent != NULL) { ret = x509_crt_verify_child(crt, parent, trust_ca, ca_crl, profile, pathlen, selfsigned, flags); if (ret != 0) goto exit; } else { ret = x509_crt_verify_top(crt, trust_ca, ca_crl, profile, pathlen, selfsigned, flags); if (ret != 0) goto exit; } } exit: /* prevent misuse of the vrfy callback - VERIFY_FAILED would be ignored by * the SSL module for authmode optional, but non-zero return from the * callback means a fatal error so it shouldn't be ignored */ if (ret == TTLS_ERR_X509_CERT_VERIFY_FAILED) ret = TTLS_ERR_X509_FATAL_ERROR; if (ret != 0) { *flags = (uint32_t) -1; return ret; } if (*flags != 0) return TTLS_ERR_X509_CERT_VERIFY_FAILED; return 0; }
/* * Verify the certificate validity */ int x509_crt_verify( x509_crt *crt, x509_crt *trust_ca, x509_crl *ca_crl, const char *cn, int *flags, int (*f_vrfy)(void *, x509_crt *, int, int *), void *p_vrfy ) { size_t cn_len; int ret; int pathlen = 0; x509_crt *parent; x509_name *name; x509_sequence *cur = NULL; *flags = 0; if( cn != NULL ) { name = &crt->subject; cn_len = strlen( cn ); if( crt->ext_types & EXT_SUBJECT_ALT_NAME ) { cur = &crt->subject_alt_names; while( cur != NULL ) { if( cur->buf.len == cn_len && x509_name_cmp( cn, cur->buf.p, cn_len ) == 0 ) break; if( cur->buf.len > 2 && memcmp( cur->buf.p, "*.", 2 ) == 0 && x509_wildcard_verify( cn, &cur->buf ) ) break; cur = cur->next; } if( cur == NULL ) *flags |= BADCERT_CN_MISMATCH; } else { while( name != NULL ) { if( OID_CMP( OID_AT_CN, &name->oid ) ) { if( name->val.len == cn_len && x509_name_cmp( name->val.p, cn, cn_len ) == 0 ) break; if( name->val.len > 2 && memcmp( name->val.p, "*.", 2 ) == 0 && x509_wildcard_verify( cn, &name->val ) ) break; } name = name->next; } if( name == NULL ) *flags |= BADCERT_CN_MISMATCH; } } /* * Iterate upwards in the given cert chain, to find our crt parent. * Ignore any upper cert with CA != TRUE. */ parent = crt->next; while( parent != NULL && parent->version != 0 ) { if( parent->ca_istrue == 0 || crt->issuer_raw.len != parent->subject_raw.len || memcmp( crt->issuer_raw.p, parent->subject_raw.p, crt->issuer_raw.len ) != 0 ) { parent = parent->next; continue; } break; } if( parent != NULL ) { /* * Part of the chain */ ret = x509_crt_verify_child( crt, parent, trust_ca, ca_crl, pathlen, flags, f_vrfy, p_vrfy ); if( ret != 0 ) return( ret ); } else { ret = x509_crt_verify_top( crt, trust_ca, ca_crl, pathlen, flags, f_vrfy, p_vrfy ); if( ret != 0 ) return( ret ); } if( *flags != 0 ) return( POLARSSL_ERR_X509_CERT_VERIFY_FAILED ); return( 0 ); }
static int x509_crt_verify_child(ttls_x509_crt *child, ttls_x509_crt *parent, ttls_x509_crt *trust_ca, ttls_x509_crl *ca_crl, const ttls_x509_crt_profile *profile, int path_cnt, int self_cnt, uint32_t *flags) { int ret; uint32_t parent_flags = 0; unsigned char hash[TTLS_MD_MAX_SIZE]; ttls_x509_crt *grandparent; const TlsMdInfo *md_info; /* Counting intermediate self signed certificates */ if ((path_cnt != 0) && x509_name_cmp(&child->issuer, &child->subject) == 0) self_cnt++; /* path_cnt is 0 for the first intermediate CA */ if (1 + path_cnt > TTLS_X509_MAX_INTERMEDIATE_CA) { /* return immediately as the goal is to avoid unbounded recursion */ return(TTLS_ERR_X509_FATAL_ERROR); } if (ttls_x509_time_is_past(&child->valid_to)) *flags |= TTLS_X509_BADCERT_EXPIRED; if (ttls_x509_time_is_future(&child->valid_from)) *flags |= TTLS_X509_BADCERT_FUTURE; if (x509_profile_check_md_alg(profile, child->sig_md) != 0) *flags |= TTLS_X509_BADCERT_BAD_MD; if (x509_profile_check_pk_alg(profile, child->sig_pk) != 0) *flags |= TTLS_X509_BADCERT_BAD_PK; md_info = ttls_md_info_from_type(child->sig_md); if (md_info == NULL) { /* * Cannot check 'unknown' hash */ T_WARN("certificate uses unsupported hash %d\n", child->sig_md); *flags |= TTLS_X509_BADCERT_NOT_TRUSTED; } else { ttls_md(md_info, child->tbs.p, child->tbs.len, hash); if (x509_profile_check_key(profile, child->sig_pk, &parent->pk) != 0) *flags |= TTLS_X509_BADCERT_BAD_KEY; if (ttls_pk_verify_ext(child->sig_pk, child->sig_opts, &parent->pk, child->sig_md, hash, ttls_md_get_size(md_info), child->sig.p, child->sig.len) != 0) { *flags |= TTLS_X509_BADCERT_NOT_TRUSTED; } } #if defined(TTLS_X509_CRL_PARSE_C) /* Check trusted CA's CRL for the given crt */ *flags |= x509_crt_verifycrl(child, parent, ca_crl, profile); #endif /* Look for a grandparent in trusted CAs */ for (grandparent = trust_ca; grandparent != NULL; grandparent = grandparent->next) { if (x509_crt_check_parent(parent, grandparent, 0, path_cnt == 0) == 0) break; } if (grandparent != NULL) { ret = x509_crt_verify_top(parent, grandparent, ca_crl, profile, path_cnt + 1, self_cnt, &parent_flags); if (ret != 0) return ret; } else { /* Look for a grandparent upwards the chain */ for (grandparent = parent->next; grandparent != NULL; grandparent = grandparent->next) { /* +2 because the current step is not yet accounted for * and because max_pathlen is one higher than it should be. * Also self signed certificates do not count to the limit. */ if (grandparent->max_pathlen > 0 && grandparent->max_pathlen < 2 + path_cnt - self_cnt) { continue; } if (x509_crt_check_parent(parent, grandparent, 0, path_cnt == 0) == 0) break; } /* Is our parent part of the chain or at the top? */ if (grandparent != NULL) { ret = x509_crt_verify_child(parent, grandparent, trust_ca, ca_crl, profile, path_cnt + 1, self_cnt, &parent_flags); if (ret != 0) return ret; } else { ret = x509_crt_verify_top(parent, trust_ca, ca_crl, profile, path_cnt + 1, self_cnt, &parent_flags); if (ret != 0) return ret; } } *flags |= parent_flags; return 0; }
static int x509_crt_verify_child( x509_crt *child, x509_crt *parent, x509_crt *trust_ca, x509_crl *ca_crl, int path_cnt, int *flags, int (*f_vrfy)(void *, x509_crt *, int, int *), void *p_vrfy ) { int ret; int parent_flags = 0; unsigned char hash[POLARSSL_MD_MAX_SIZE]; x509_crt *grandparent; const md_info_t *md_info; if( x509_time_expired( &child->valid_to ) ) *flags |= BADCERT_EXPIRED; md_info = md_info_from_type( child->sig_md ); if( md_info == NULL ) { /* * Cannot check 'unknown' hash */ *flags |= BADCERT_NOT_TRUSTED; } else { md( md_info, child->tbs.p, child->tbs.len, hash ); if( pk_can_do( &parent->pk, child->sig_pk ) == 0 || pk_verify( &parent->pk, child->sig_md, hash, md_info->size, child->sig.p, child->sig.len ) != 0 ) { *flags |= BADCERT_NOT_TRUSTED; } } #if defined(POLARSSL_X509_CRL_PARSE_C) /* Check trusted CA's CRL for the given crt */ *flags |= x509_crt_verifycrl(child, parent, ca_crl); #endif grandparent = parent->next; while( grandparent != NULL ) { if( grandparent->version == 0 || grandparent->ca_istrue == 0 || parent->issuer_raw.len != grandparent->subject_raw.len || memcmp( parent->issuer_raw.p, grandparent->subject_raw.p, parent->issuer_raw.len ) != 0 ) { grandparent = grandparent->next; continue; } break; } if( grandparent != NULL ) { /* * Part of the chain */ ret = x509_crt_verify_child( parent, grandparent, trust_ca, ca_crl, path_cnt + 1, &parent_flags, f_vrfy, p_vrfy ); if( ret != 0 ) return( ret ); } else { ret = x509_crt_verify_top( parent, trust_ca, ca_crl, path_cnt + 1, &parent_flags, f_vrfy, p_vrfy ); if( ret != 0 ) return( ret ); } /* child is verified to be a child of the parent, call verify callback */ if( NULL != f_vrfy ) if( ( ret = f_vrfy( p_vrfy, child, path_cnt, flags ) ) != 0 ) return( ret ); *flags |= parent_flags; return( 0 ); }