Example #1
0
// TODO: Remove #include "pkix/pkixnss.h", #include "cert.h",
// #include "ScopedPtr.h", etc. when this is rewritten to be independent of
// NSS.
Result
CheckNameConstraints(Input encodedNameConstraints,
                     const BackCert& firstChild,
                     KeyPurposeId requiredEKUIfPresent)
{
  ScopedPtr<PLArenaPool, PORT_FreeArena_false>
    arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
  if (!arena) {
    return Result::FATAL_ERROR_NO_MEMORY;
  }

  SECItem encodedNameConstraintsSECItem =
    UnsafeMapInputToSECItem(encodedNameConstraints);

  // Owned by arena
  const CERTNameConstraints* constraints =
    CERT_DecodeNameConstraintsExtension(arena.get(),
                                        &encodedNameConstraintsSECItem);
  if (!constraints) {
    return MapPRErrorCodeToResult(PR_GetError());
  }

  for (const BackCert* child = &firstChild; child; child = child->childCert) {
    SECItem childCertDER = UnsafeMapInputToSECItem(child->GetDER());
    ScopedPtr<CERTCertificate, CERT_DestroyCertificate>
      nssCert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &childCertDER,
                                      nullptr, false, true));
    if (!nssCert) {
      return MapPRErrorCodeToResult(PR_GetError());
    }

    bool includeCN = child->endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
                     requiredEKUIfPresent == KeyPurposeId::id_kp_serverAuth;
    // owned by arena
    const CERTGeneralName*
      names(CERT_GetConstrainedCertificateNames(nssCert.get(), arena.get(),
                                                includeCN));
    if (!names) {
      return MapPRErrorCodeToResult(PR_GetError());
    }

    CERTGeneralName* currentName = const_cast<CERTGeneralName*>(names);
    do {
      if (CERT_CheckNameSpace(arena.get(), constraints, currentName)
            != SECSuccess) {
        // XXX: It seems like CERT_CheckNameSpace doesn't always call
        // PR_SetError when it fails, so we ignore what PR_GetError would
        // return. NSS's cert_VerifyCertChainOld does something similar.
        return Result::ERROR_CERT_NOT_IN_NAME_SPACE;
      }
      currentName = CERT_GetNextGeneralName(currentName);
    } while (currentName != names);
  }

  return Success;
}
Example #2
0
// 4.2.1.10. Name Constraints
Result
CheckNameConstraints(BackCert& cert)
{
  if (!cert.encodedNameConstraints) {
    return Success;
  }

  PLArenaPool* arena = cert.GetArena();
  if (!arena) {
    return FatalError;
  }

  // Owned by arena
  const CERTNameConstraints* constraints =
    CERT_DecodeNameConstraintsExtension(arena, cert.encodedNameConstraints);
  if (!constraints) {
    return MapSECStatus(SECFailure);
  }

  for (BackCert* prev = cert.childCert; prev; prev = prev->childCert) {
    const CERTGeneralName* names = nullptr;
    Result rv = prev->GetConstrainedNames(&names);
    if (rv != Success) {
      return rv;
    }
    PORT_Assert(names);
    CERTGeneralName* currentName = const_cast<CERTGeneralName*>(names);
    do {
      if (CERT_CheckNameSpace(arena, constraints, currentName) != SECSuccess) {
        // XXX: It seems like CERT_CheckNameSpace doesn't always call
        // PR_SetError when it fails. We set the error code here, though this
        // may be papering over some fatal errors. NSS's
        // cert_VerifyCertChainOld does something similar.
        PR_SetError(SEC_ERROR_CERT_NOT_IN_NAME_SPACE, 0);
        return RecoverableError;
      }
      currentName = CERT_GetNextGeneralName(currentName);
    } while (currentName != names);
  }

  return Success;
}
// 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;
}
Example #4
0
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;
}