// returns TRUE if SAN was used to produce names // return FALSE if nothing was produced // names => a single name or a list of names // multipleNames => whether multiple names were delivered static bool GetSubjectAltNames(CERTCertificate *nssCert, nsINSSComponent *component, nsString &allNames, uint32_t &nameCount) { allNames.Truncate(); nameCount = 0; SECItem altNameExtension = {siBuffer, nullptr, 0 }; CERTGeneralName *sanNameList = nullptr; SECStatus rv = CERT_FindCertExtension(nssCert, SEC_OID_X509_SUBJECT_ALT_NAME, &altNameExtension); if (rv != SECSuccess) { return false; } ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return false; } sanNameList = CERT_DecodeAltNameExtension(arena.get(), &altNameExtension); if (!sanNameList) { return false; } SECITEM_FreeItem(&altNameExtension, false); CERTGeneralName *current = sanNameList; do { nsAutoString name; switch (current->type) { case certDNSName: { nsDependentCSubstring nameFromCert(reinterpret_cast<char*> (current->name.other.data), current->name.other.len); // dNSName fields are defined as type IA5String and thus should // be limited to ASCII characters. if (IsASCII(nameFromCert)) { name.Assign(NS_ConvertASCIItoUTF16(nameFromCert)); if (!allNames.IsEmpty()) { allNames.AppendLiteral(", "); } ++nameCount; allNames.Append(name); } } break; case certIPAddress: { char buf[INET6_ADDRSTRLEN]; PRNetAddr addr; if (current->name.other.len == 4) { addr.inet.family = PR_AF_INET; memcpy(&addr.inet.ip, current->name.other.data, current->name.other.len); PR_NetAddrToString(&addr, buf, sizeof(buf)); name.AssignASCII(buf); } else if (current->name.other.len == 16) { addr.ipv6.family = PR_AF_INET6; memcpy(&addr.ipv6.ip, current->name.other.data, current->name.other.len); PR_NetAddrToString(&addr, buf, sizeof(buf)); name.AssignASCII(buf); } else { /* invalid IP address */ } if (!name.IsEmpty()) { if (!allNames.IsEmpty()) { allNames.AppendLiteral(", "); } ++nameCount; allNames.Append(name); } break; } default: // all other types of names are ignored break; } current = CERT_GetNextGeneralName(current); } while (current != sanNameList); // double linked return true; }
bool cert_VerifySubjectAltName(const CERTCertificate *cert, const char *name) { SECStatus rv; SECItem subAltName; PLArenaPool *arena = NULL; CERTGeneralName *nameList = NULL; CERTGeneralName *current = NULL; bool san_ip = FALSE; unsigned int len = strlen(name); ip_address myip; rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, &subAltName); if (rv != SECSuccess) { DBG(DBG_X509, DBG_log("certificate contains no subjectAltName extension")); return FALSE; } if (tnatoaddr(name, 0, AF_UNSPEC, &myip) == NULL) san_ip = TRUE; arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); passert(arena != NULL); nameList = current = CERT_DecodeAltNameExtension(arena, &subAltName); passert(current != NULL); do { switch (current->type) { case certDNSName: case certRFC822Name: if (san_ip) break; if (current->name.other.len == len) { if (memcmp(current->name.other.data, name, len) == 0) { DBG(DBG_X509, DBG_log("subjectAltname %s found in certificate", name)); PORT_FreeArena(arena, PR_FALSE); return TRUE; } } if (current->name.other.len != 0 && current->name.other.len < IDTOA_BUF) { char osan[IDTOA_BUF]; memcpy(osan,current->name.other.data, current->name.other.len); osan[current->name.other.len] = '\0'; DBG(DBG_X509, DBG_log("subjectAltname (len=%d) %s not match %s", current->name.other.len, osan, name)); } else { DBG(DBG_X509, DBG_log("subjectAltname <TOO BIG TO PRINT> does not match %s", name)); } break; case certIPAddress: if (!san_ip) break; if ((current->name.other.len == 4) && (addrtypeof(&myip) == AF_INET)) { if (memcmp(current->name.other.data, &myip.u.v4.sin_addr.s_addr, 4) == 0) { DBG(DBG_X509, DBG_log("subjectAltname IPv4 matches %s", name)); PORT_FreeArena(arena, PR_FALSE); return TRUE; } else { DBG(DBG_X509, DBG_log("subjectAltname IPv4 does not match %s", name)); break; } } if ((current->name.other.len == 16) && (addrtypeof(&myip) == AF_INET6)) { if (memcmp(current->name.other.data, &myip.u.v6.sin6_addr.s6_addr, 16) == 0) { DBG(DBG_X509, DBG_log("subjectAltname IPv6 matches %s", name)); PORT_FreeArena(arena, PR_FALSE); return TRUE; } else { DBG(DBG_X509, DBG_log("subjectAltname IPv6 does not match %s", name)); break; } } DBG(DBG_X509, DBG_log("subjectAltnamea IP address family mismatch for %s", name)); break; default: break; } current = CERT_GetNextGeneralName(current); } while (current != nameList); loglog(RC_LOG_SERIOUS, "No matching subjectAltName found"); /* Don't free nameList, it's part of the arena. */ PORT_FreeArena(arena, PR_FALSE); return FALSE; }