#include "base_object-inl.h" #include "crypto_x509.h" #include "crypto_common.h" #include "crypto_context.h" #include "crypto_keys.h" #include "crypto_bio.h" #include "env-inl.h" #include "memory_tracker-inl.h" #include "node_errors.h" #include "util-inl.h" #include "v8.h" #include #include namespace node { using v8::ArrayBufferView; using v8::Context; using v8::EscapableHandleScope; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::Local; using v8::MaybeLocal; using v8::Object; using v8::Uint32; using v8::Value; namespace crypto { ManagedX509::ManagedX509(X509Pointer&& cert) : cert_(std::move(cert)) {} ManagedX509::ManagedX509(const ManagedX509& that) { *this = that; } ManagedX509& ManagedX509::operator=(const ManagedX509& that) { cert_.reset(that.get()); if (cert_) X509_up_ref(cert_.get()); return *this; } void ManagedX509::MemoryInfo(MemoryTracker* tracker) const { // This is an approximation based on the der encoding size. int size = i2d_X509(cert_.get(), nullptr); tracker->TrackFieldWithSize("cert", size); } Local X509Certificate::GetConstructorTemplate( Environment* env) { Local tmpl = env->x509_constructor_template(); if (tmpl.IsEmpty()) { tmpl = FunctionTemplate::New(env->isolate()); tmpl->InstanceTemplate()->SetInternalFieldCount( BaseObject::kInternalFieldCount); tmpl->Inherit(BaseObject::GetConstructorTemplate(env)); tmpl->SetClassName( FIXED_ONE_BYTE_STRING(env->isolate(), "X509Certificate")); env->SetProtoMethod(tmpl, "subject", Subject); env->SetProtoMethod(tmpl, "subjectAltName", SubjectAltName); env->SetProtoMethod(tmpl, "infoAccess", InfoAccess); env->SetProtoMethod(tmpl, "issuer", Issuer); env->SetProtoMethod(tmpl, "validTo", ValidTo); env->SetProtoMethod(tmpl, "validFrom", ValidFrom); env->SetProtoMethod(tmpl, "fingerprint", Fingerprint); env->SetProtoMethod(tmpl, "fingerprint256", Fingerprint256); env->SetProtoMethod(tmpl, "fingerprint512", Fingerprint512); env->SetProtoMethod(tmpl, "keyUsage", KeyUsage); env->SetProtoMethod(tmpl, "serialNumber", SerialNumber); env->SetProtoMethod(tmpl, "pem", Pem); env->SetProtoMethod(tmpl, "raw", Raw); env->SetProtoMethod(tmpl, "publicKey", PublicKey); env->SetProtoMethod(tmpl, "checkCA", CheckCA); env->SetProtoMethod(tmpl, "checkHost", CheckHost); env->SetProtoMethod(tmpl, "checkEmail", CheckEmail); env->SetProtoMethod(tmpl, "checkIP", CheckIP); env->SetProtoMethod(tmpl, "checkIssued", CheckIssued); env->SetProtoMethod(tmpl, "checkPrivateKey", CheckPrivateKey); env->SetProtoMethod(tmpl, "verify", Verify); env->SetProtoMethod(tmpl, "toLegacy", ToLegacy); env->SetProtoMethod(tmpl, "getIssuerCert", GetIssuerCert); env->set_x509_constructor_template(tmpl); } return tmpl; } bool X509Certificate::HasInstance(Environment* env, Local object) { return GetConstructorTemplate(env)->HasInstance(object); } MaybeLocal X509Certificate::New( Environment* env, X509Pointer cert, STACK_OF(X509)* issuer_chain) { std::shared_ptr mcert(new ManagedX509(std::move(cert))); return New(env, std::move(mcert), issuer_chain); } MaybeLocal X509Certificate::New( Environment* env, std::shared_ptr cert, STACK_OF(X509)* issuer_chain) { EscapableHandleScope scope(env->isolate()); Local ctor; if (!GetConstructorTemplate(env)->GetFunction(env->context()).ToLocal(&ctor)) return MaybeLocal(); Local obj; if (!ctor->NewInstance(env->context()).ToLocal(&obj)) return MaybeLocal(); new X509Certificate(env, obj, std::move(cert), issuer_chain); return scope.Escape(obj); } MaybeLocal X509Certificate::GetCert( Environment* env, const SSLPointer& ssl) { ClearErrorOnReturn clear_error_on_return; X509* cert = SSL_get_certificate(ssl.get()); if (cert == nullptr) return MaybeLocal(); X509Pointer ptr(X509_dup(cert)); return New(env, std::move(ptr)); } MaybeLocal X509Certificate::GetPeerCert( Environment* env, const SSLPointer& ssl, GetPeerCertificateFlag flag) { ClearErrorOnReturn clear_error_on_return; Local obj; MaybeLocal maybe_cert; bool is_server = static_cast(flag) & static_cast(GetPeerCertificateFlag::SERVER); X509Pointer cert(is_server ? SSL_get_peer_certificate(ssl.get()) : nullptr); STACK_OF(X509)* ssl_certs = SSL_get_peer_cert_chain(ssl.get()); if (!cert && (ssl_certs == nullptr || sk_X509_num(ssl_certs) == 0)) return MaybeLocal(); std::vector> certs; if (!cert) { cert.reset(sk_X509_value(ssl_certs, 0)); sk_X509_delete(ssl_certs, 0); } return sk_X509_num(ssl_certs) ? New(env, std::move(cert), ssl_certs) : New(env, std::move(cert)); } void X509Certificate::Parse(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsArrayBufferView()); ArrayBufferViewContents buf(args[0].As()); const unsigned char* data = buf.data(); unsigned data_len = buf.length(); ClearErrorOnReturn clear_error_on_return; BIOPointer bio(LoadBIO(env, args[0])); if (!bio) return ThrowCryptoError(env, ERR_get_error()); Local cert; X509Pointer pem(PEM_read_bio_X509_AUX( bio.get(), nullptr, NoPasswordCallback, nullptr)); if (!pem) { // Try as DER, but return the original PEM failure if it isn't DER. MarkPopErrorOnReturn mark_here; X509Pointer der(d2i_X509(nullptr, &data, data_len)); if (!der) return ThrowCryptoError(env, ERR_get_error()); if (!X509Certificate::New(env, std::move(der)).ToLocal(&cert)) return; } else if (!X509Certificate::New(env, std::move(pem)).ToLocal(&cert)) { return; } args.GetReturnValue().Set(cert); } void X509Certificate::Subject(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); BIOPointer bio(BIO_new(BIO_s_mem())); CHECK(bio); Local ret; if (GetSubject(env, bio, cert->get()).ToLocal(&ret)) args.GetReturnValue().Set(ret); } void X509Certificate::Issuer(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); BIOPointer bio(BIO_new(BIO_s_mem())); CHECK(bio); Local ret; if (GetIssuerString(env, bio, cert->get()).ToLocal(&ret)) args.GetReturnValue().Set(ret); } void X509Certificate::SubjectAltName(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); BIOPointer bio(BIO_new(BIO_s_mem())); CHECK(bio); Local ret; if (GetSubjectAltNameString(env, bio, cert->get()).ToLocal(&ret)) args.GetReturnValue().Set(ret); } void X509Certificate::InfoAccess(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); BIOPointer bio(BIO_new(BIO_s_mem())); CHECK(bio); Local ret; if (GetInfoAccessString(env, bio, cert->get()).ToLocal(&ret)) args.GetReturnValue().Set(ret); } void X509Certificate::ValidFrom(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); BIOPointer bio(BIO_new(BIO_s_mem())); CHECK(bio); Local ret; if (GetValidFrom(env, cert->get(), bio).ToLocal(&ret)) args.GetReturnValue().Set(ret); } void X509Certificate::ValidTo(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); BIOPointer bio(BIO_new(BIO_s_mem())); CHECK(bio); Local ret; if (GetValidTo(env, cert->get(), bio).ToLocal(&ret)) args.GetReturnValue().Set(ret); } void X509Certificate::Fingerprint(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); Local ret; if (GetFingerprintDigest(env, EVP_sha1(), cert->get()).ToLocal(&ret)) args.GetReturnValue().Set(ret); } void X509Certificate::Fingerprint256(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); Local ret; if (GetFingerprintDigest(env, EVP_sha256(), cert->get()).ToLocal(&ret)) args.GetReturnValue().Set(ret); } void X509Certificate::Fingerprint512(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); Local ret; if (GetFingerprintDigest(env, EVP_sha512(), cert->get()).ToLocal(&ret)) args.GetReturnValue().Set(ret); } void X509Certificate::KeyUsage(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); Local ret; if (GetKeyUsage(env, cert->get()).ToLocal(&ret)) args.GetReturnValue().Set(ret); } void X509Certificate::SerialNumber(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); Local ret; if (GetSerialNumber(env, cert->get()).ToLocal(&ret)) args.GetReturnValue().Set(ret); } void X509Certificate::Raw(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); Local ret; if (GetRawDERCertificate(env, cert->get()).ToLocal(&ret)) args.GetReturnValue().Set(ret); } void X509Certificate::PublicKey(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); EVPKeyPointer pkey(X509_get_pubkey(cert->get())); ManagedEVPPKey epkey(std::move(pkey)); std::shared_ptr key_data = KeyObjectData::CreateAsymmetric(kKeyTypePublic, epkey); Local ret; if (KeyObjectHandle::Create(env, key_data).ToLocal(&ret)) args.GetReturnValue().Set(ret); } void X509Certificate::Pem(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); BIOPointer bio(BIO_new(BIO_s_mem())); CHECK(bio); if (PEM_write_bio_X509(bio.get(), cert->get())) args.GetReturnValue().Set(ToV8Value(env, bio)); } void X509Certificate::CheckCA(const FunctionCallbackInfo& args) { X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); args.GetReturnValue().Set(X509_check_ca(cert->get()) == 1); } void X509Certificate::CheckHost(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); CHECK(args[0]->IsString()); // name CHECK(args[1]->IsUint32()); // flags Utf8Value name(env->isolate(), args[0]); uint32_t flags = args[1].As()->Value(); char* peername; switch (X509_check_host( cert->get(), *name, name.length(), flags, &peername)) { case 1: { // Match! Local ret = args[0]; if (peername != nullptr) { ret = OneByteString(env->isolate(), peername); OPENSSL_free(peername); } return args.GetReturnValue().Set(ret); } case 0: // No Match! return; // No return value is set case -2: // Error! return THROW_ERR_INVALID_ARG_VALUE(env, "Invalid name"); default: // Error! return THROW_ERR_CRYPTO_OPERATION_FAILED(env); } } void X509Certificate::CheckEmail(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); CHECK(args[0]->IsString()); // name CHECK(args[1]->IsUint32()); // flags Utf8Value name(env->isolate(), args[0]); uint32_t flags = args[1].As()->Value(); switch (X509_check_email( cert->get(), *name, name.length(), flags)) { case 1: // Match! return args.GetReturnValue().Set(args[0]); case 0: // No Match! return; // No return value is set case -2: // Error! return THROW_ERR_INVALID_ARG_VALUE(env, "Invalid name"); default: // Error! return THROW_ERR_CRYPTO_OPERATION_FAILED(env); } } void X509Certificate::CheckIP(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); CHECK(args[0]->IsString()); // IP CHECK(args[1]->IsUint32()); // flags Utf8Value name(env->isolate(), args[0]); uint32_t flags = args[1].As()->Value(); switch (X509_check_ip_asc(cert->get(), *name, flags)) { case 1: // Match! return args.GetReturnValue().Set(args[0]); case 0: // No Match! return; // No return value is set case -2: // Error! return THROW_ERR_INVALID_ARG_VALUE(env, "Invalid IP"); default: // Error! return THROW_ERR_CRYPTO_OPERATION_FAILED(env); } } void X509Certificate::CheckIssued(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); CHECK(args[0]->IsObject()); CHECK(X509Certificate::HasInstance(env, args[0].As())); X509Certificate* issuer; ASSIGN_OR_RETURN_UNWRAP(&issuer, args[0]); args.GetReturnValue().Set( X509_check_issued(issuer->get(), cert->get()) == X509_V_OK); } void X509Certificate::CheckPrivateKey(const FunctionCallbackInfo& args) { X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); CHECK(args[0]->IsObject()); KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args[0]); CHECK_EQ(key->Data()->GetKeyType(), kKeyTypePrivate); args.GetReturnValue().Set( X509_check_private_key( cert->get(), key->Data()->GetAsymmetricKey().get()) == 1); } void X509Certificate::Verify(const FunctionCallbackInfo& args) { X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); CHECK(args[0]->IsObject()); KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args[0]); CHECK_EQ(key->Data()->GetKeyType(), kKeyTypePublic); args.GetReturnValue().Set( X509_verify( cert->get(), key->Data()->GetAsymmetricKey().get()) > 0); } void X509Certificate::ToLegacy(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); Local ret; if (X509ToObject(env, cert->get()).ToLocal(&ret)) args.GetReturnValue().Set(ret); } void X509Certificate::GetIssuerCert(const FunctionCallbackInfo& args) { X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); if (cert->issuer_cert_) args.GetReturnValue().Set(cert->issuer_cert_->object()); } X509Certificate::X509Certificate( Environment* env, Local object, std::shared_ptr cert, STACK_OF(X509)* issuer_chain) : BaseObject(env, object), cert_(std::move(cert)) { MakeWeak(); if (issuer_chain != nullptr && sk_X509_num(issuer_chain)) { X509Pointer cert(X509_dup(sk_X509_value(issuer_chain, 0))); sk_X509_delete(issuer_chain, 0); Local obj = sk_X509_num(issuer_chain) ? X509Certificate::New(env, std::move(cert), issuer_chain) .ToLocalChecked() : X509Certificate::New(env, std::move(cert)) .ToLocalChecked(); issuer_cert_.reset(Unwrap(obj)); } } void X509Certificate::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("cert", cert_); } BaseObjectPtr X509Certificate::X509CertificateTransferData::Deserialize( Environment* env, Local context, std::unique_ptr self) { if (context != env->context()) { THROW_ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE(env); return {}; } Local handle; if (!X509Certificate::New(env, data_).ToLocal(&handle)) return {}; return BaseObjectPtr( Unwrap(handle.As())); } BaseObject::TransferMode X509Certificate::GetTransferMode() const { return BaseObject::TransferMode::kCloneable; } std::unique_ptr X509Certificate::CloneForMessaging() const { return std::make_unique(cert_); } void X509Certificate::Initialize(Environment* env, Local target) { env->SetMethod(target, "parseX509", X509Certificate::Parse); NODE_DEFINE_CONSTANT(target, X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT); NODE_DEFINE_CONSTANT(target, X509_CHECK_FLAG_NEVER_CHECK_SUBJECT); NODE_DEFINE_CONSTANT(target, X509_CHECK_FLAG_NO_WILDCARDS); NODE_DEFINE_CONSTANT(target, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); NODE_DEFINE_CONSTANT(target, X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS); NODE_DEFINE_CONSTANT(target, X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS); } void X509Certificate::RegisterExternalReferences( ExternalReferenceRegistry* registry) { registry->Register(X509Certificate::Parse); registry->Register(Subject); registry->Register(SubjectAltName); registry->Register(InfoAccess); registry->Register(Issuer); registry->Register(ValidTo); registry->Register(ValidFrom); registry->Register(Fingerprint); registry->Register(Fingerprint256); registry->Register(KeyUsage); registry->Register(SerialNumber); registry->Register(Pem); registry->Register(Raw); registry->Register(PublicKey); registry->Register(CheckCA); registry->Register(CheckHost); registry->Register(CheckEmail); registry->Register(CheckIP); registry->Register(CheckIssued); registry->Register(CheckPrivateKey); registry->Register(Verify); registry->Register(ToLegacy); registry->Register(GetIssuerCert); } } // namespace crypto } // namespace node