static int x509_wildcard_verify( const char *cn, x509_buf *name ) { size_t i; size_t cn_idx = 0; if( name->len < 3 || name->p[0] != '*' || name->p[1] != '.' ) return( 0 ); for( i = 0; i < strlen( cn ); ++i ) { if( cn[i] == '.' ) { cn_idx = i; break; } } if( cn_idx == 0 ) return( 0 ); if( strlen( cn ) - cn_idx == name->len - 1 && x509_name_cmp( name->p + 1, cn + cn_idx, name->len - 1 ) == 0 ) { return( 1 ); } return( 0 ); }
/* * Check if 'parent' is a suitable parent (signing CA) for 'child'. * Return 0 if yes, -1 if not. * * top means parent is a locally-trusted certificate * bottom means child is the end entity cert */ static int x509_crt_check_parent(const ttls_x509_crt *child, const ttls_x509_crt *parent, int top, int bottom) { int need_ca_bit; /* Parent must be the issuer */ if (x509_name_cmp(&child->issuer, &parent->subject) != 0) return(-1); /* Parent must have the basicConstraints CA bit set as a general rule */ need_ca_bit = 1; /* Exception: v1/v2 certificates that are locally trusted. */ if (top && parent->version < 3) need_ca_bit = 0; /* Exception: self-signed end-entity certs that are locally trusted. */ if (top && bottom && child->raw.len == parent->raw.len && memcmp(child->raw.p, parent->raw.p, child->raw.len) == 0) { need_ca_bit = 0; } if (need_ca_bit && ! parent->ca_istrue) return(-1); #if defined(TTLS_X509_CHECK_KEY_USAGE) if (need_ca_bit && ttls_x509_crt_check_key_usage(parent, TTLS_X509_KU_KEY_CERT_SIGN) != 0) { return(-1); } #endif 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; }
/* * 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 ); }