mirror of https://github.com/ClassiCube/ClassiCube
366 lines
11 KiB
C
366 lines
11 KiB
C
#include "Certs.h"
|
|
#include "Errors.h"
|
|
|
|
#ifndef CC_CRT_BACKEND
|
|
void CertsBackend_Init(void) { }
|
|
|
|
void Certs_BeginChain(struct X509CertContext* ctx) { }
|
|
void Certs_FreeChain( struct X509CertContext* ctx) { }
|
|
int Certs_VerifyChain(struct X509CertContext* ctx) { return ERR_NOT_SUPPORTED; }
|
|
|
|
void Certs_BeginCert( struct X509CertContext* ctx, int size) { }
|
|
void Certs_AppendCert(struct X509CertContext* ctx, const void* data, int len) { }
|
|
void Certs_FinishCert(struct X509CertContext* ctx) { }
|
|
#else
|
|
#include "Platform.h"
|
|
#include "String.h"
|
|
#include "Stream.h"
|
|
|
|
void Certs_BeginCert( struct X509CertContext* ctx, int size) {
|
|
void* data;
|
|
ctx->cert = NULL;
|
|
|
|
/* Should never happen, but never know */
|
|
if (ctx->numCerts >= X509_MAX_CERTS) return;
|
|
|
|
data = Mem_TryAllocCleared(1, size);
|
|
if (!data) return;
|
|
|
|
ctx->cert = &ctx->certs[ctx->numCerts++];
|
|
ctx->cert->data = data;
|
|
ctx->cert->offset = 0;
|
|
}
|
|
|
|
void Certs_AppendCert(struct X509CertContext* ctx, const void* data, int len) {
|
|
if (!ctx->cert) return;
|
|
|
|
Mem_Copy((char*)ctx->cert->data + ctx->cert->offset, data, len);
|
|
ctx->cert->offset += len;
|
|
}
|
|
|
|
void Certs_FinishCert(struct X509CertContext* ctx) {
|
|
}
|
|
|
|
void Certs_BeginChain(struct X509CertContext* ctx) {
|
|
ctx->cert = NULL;
|
|
ctx->numCerts = 0;
|
|
}
|
|
|
|
void Certs_FreeChain( struct X509CertContext* ctx) {
|
|
int i;
|
|
for (i = 0; i < ctx->numCerts; i++)
|
|
{
|
|
Mem_Free(ctx->certs[i].data);
|
|
}
|
|
ctx->numCerts = 0;
|
|
}
|
|
|
|
#if CC_CRT_BACKEND == CC_CRT_BACKEND_OPENSSL
|
|
#include "Funcs.h"
|
|
/* === BEGIN OPENSSL HEADERS === */
|
|
typedef struct X509_ X509;
|
|
typedef struct X509_STORE_ X509_STORE;
|
|
typedef struct X509_STORE_CTX_ X509_STORE_CTX;
|
|
typedef struct OPENSSL_STACK_ OPENSSL_STACK;
|
|
typedef void (*OPENSSL_PopFunc)(void* data);
|
|
|
|
static OPENSSL_STACK* (*_OPENSSL_sk_new_null)(void);
|
|
int (*_OPENSSL_sk_push)(OPENSSL_STACK* st, const void* data);
|
|
void (*_OPENSSL_sk_pop_free)(OPENSSL_STACK* st, OPENSSL_PopFunc func);
|
|
|
|
static X509* (*_d2i_X509)(X509** px, const unsigned char** in, int len);
|
|
|
|
static X509* (*_X509_new)(void);
|
|
static void (*_X509_free)(X509* a);
|
|
|
|
static X509_STORE* (*_X509_STORE_new)(void);
|
|
static int (*_X509_STORE_set_default_paths)(X509_STORE* ctx);
|
|
|
|
static int (*_X509_verify_cert)(X509_STORE_CTX* ctx);
|
|
static const char* (*_X509_verify_cert_error_string)(long n);
|
|
|
|
static X509_STORE_CTX* (*_X509_STORE_CTX_new)(void);
|
|
static void (*_X509_STORE_CTX_free)(X509_STORE_CTX* ctx);
|
|
|
|
static int (*_X509_STORE_CTX_get_error)(X509_STORE_CTX* ctx);
|
|
static int (*_X509_STORE_CTX_init)(X509_STORE_CTX* ctx, X509_STORE* store,
|
|
X509* x509, OPENSSL_STACK* chain);
|
|
/* === END OPENSSL HEADERS === */
|
|
|
|
#if defined CC_BUILD_WIN
|
|
static const cc_string cryptoLib = String_FromConst("libcrypto.dll");
|
|
#elif defined CC_BUILD_HAIKU || defined CC_BUILD_LINUX
|
|
static const cc_string cryptoLib = String_FromConst("libcrypto.so.3");
|
|
static const cc_string cryptoAlt = String_FromConst("libcrypto.so");
|
|
#else
|
|
static const cc_string cryptoLib = String_FromConst("libcrypto.so");
|
|
static const cc_string cryptoAlt = String_FromConst("libcrypto.so.3");
|
|
#endif
|
|
|
|
static X509_STORE* store;
|
|
static cc_bool ossl_loaded;
|
|
|
|
void CertsBackend_Init(void) {
|
|
static const struct DynamicLibSym funcs[] = {
|
|
DynamicLib_ReqSym(d2i_X509), DynamicLib_ReqSym(OPENSSL_sk_new_null),
|
|
DynamicLib_ReqSym(OPENSSL_sk_push), DynamicLib_ReqSym(OPENSSL_sk_pop_free),
|
|
DynamicLib_ReqSym(X509_new), DynamicLib_ReqSym(X509_free),
|
|
DynamicLib_ReqSym(X509_STORE_new), DynamicLib_ReqSym(X509_STORE_set_default_paths),
|
|
DynamicLib_ReqSym(X509_STORE_CTX_new), DynamicLib_ReqSym(X509_STORE_CTX_free),
|
|
DynamicLib_ReqSym(X509_STORE_CTX_init), DynamicLib_ReqSym(X509_STORE_CTX_get_error),
|
|
DynamicLib_ReqSym(X509_verify_cert), DynamicLib_ReqSym(X509_verify_cert_error_string),
|
|
};
|
|
void* lib;
|
|
|
|
ossl_loaded = DynamicLib_LoadAll(&cryptoLib, funcs, Array_Elems(funcs), &lib);
|
|
if (!lib) {
|
|
ossl_loaded = DynamicLib_LoadAll(&cryptoAlt, funcs, Array_Elems(funcs), &lib);
|
|
}
|
|
}
|
|
|
|
static X509* ToOpenSSLCert(struct X509Cert* cert) {
|
|
const unsigned char* data = cert->data;
|
|
return _d2i_X509(NULL, &data, cert->offset);
|
|
}
|
|
|
|
int Certs_VerifyChain(struct X509CertContext* chain) {
|
|
OPENSSL_STACK* inter;
|
|
X509_STORE_CTX* ctx;
|
|
int err, result;
|
|
int i, status;
|
|
X509* cur;
|
|
X509* cert;
|
|
if (!ossl_loaded) return ERR_NOT_SUPPORTED;
|
|
|
|
/* Delay creating X509 store until necessary */
|
|
if (!store) {
|
|
store = _X509_STORE_new();
|
|
if (!store) return ERR_OUT_OF_MEMORY;
|
|
|
|
_X509_STORE_set_default_paths(store);
|
|
}
|
|
|
|
/* End/Leaf certificate */
|
|
cert = ToOpenSSLCert(&chain->certs[0]);
|
|
if (!cert) return ERR_OUT_OF_MEMORY;
|
|
|
|
inter = _OPENSSL_sk_new_null();
|
|
if (!inter) return ERR_OUT_OF_MEMORY;
|
|
|
|
/* Intermediate certificates */
|
|
for (i = 1; i < chain->numCerts; i++)
|
|
{
|
|
cur = ToOpenSSLCert(&chain->certs[i]);
|
|
if (cur) _OPENSSL_sk_push(inter, cur);
|
|
}
|
|
|
|
ctx = _X509_STORE_CTX_new();
|
|
_X509_STORE_CTX_init(ctx, store, cert, inter);
|
|
|
|
status = _X509_verify_cert(ctx);
|
|
result = 0;
|
|
|
|
if (status != 1) {
|
|
err = _X509_STORE_CTX_get_error(ctx);
|
|
Platform_LogConst(_X509_verify_cert_error_string(err));
|
|
result = -1;
|
|
}
|
|
|
|
_X509_STORE_CTX_free(ctx);
|
|
_OPENSSL_sk_pop_free(inter, (OPENSSL_PopFunc)_X509_free);
|
|
_X509_free(cert);
|
|
|
|
return result;
|
|
}
|
|
#elif CC_CRT_BACKEND == CC_CRT_BACKEND_APPLESEC
|
|
#include <Security/SecPolicy.h>
|
|
#include <Security/SecTrust.h>
|
|
#include <Security/SecCertificate.h>
|
|
#ifdef CC_BUILD_MACOS
|
|
#include <Security/SecPolicySearch.h>
|
|
#include <Security/oidsalg.h>
|
|
#endif
|
|
|
|
void CertsBackend_Init(void) {
|
|
|
|
}
|
|
|
|
static SecPolicyRef policy;
|
|
|
|
static void CreateChain(struct X509CertContext* x509, CFMutableArrayRef chain) {
|
|
struct X509Cert* certs = x509->certs;
|
|
int i;
|
|
|
|
#ifdef CC_BUILD_MACOS
|
|
/* Use older APIs that work on macOS earlier than 10.6 */
|
|
for (i = 0; i < x509->numCerts; i++)
|
|
{
|
|
CSSM_DATA data;
|
|
data.Data = certs[i].data;
|
|
data.Length = certs[i].offset;
|
|
|
|
SecCertificateRef cert = NULL;
|
|
int res = SecCertificateCreateFromData(&data, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &cert);
|
|
if (!res && cert) CFArrayAppendValue(chain, cert);
|
|
}
|
|
#else
|
|
for (i = 0; i < x509->numCerts; i++)
|
|
{
|
|
CFDataRef data = CFDataCreateWithBytesNoCopy(NULL, certs[i].data, certs[i].offset, kCFAllocatorNull);
|
|
|
|
SecCertificateRef cert = SecCertificateCreateWithData(NULL, data);
|
|
if (cert) CFArrayAppendValue(chain, cert);
|
|
CFRelease(data);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void CreateX509Policy(void) {
|
|
#ifdef CC_BUILD_MACOS
|
|
SecPolicySearchRef search;
|
|
int err = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, &search);
|
|
if (err) return;
|
|
|
|
err = SecPolicySearchCopyNext(search, &policy);
|
|
CFRelease(search);
|
|
#else
|
|
/* Use older APIs that work on macOS earlier than 10.6 */
|
|
policy = SecPolicyCreateBasicX509();
|
|
#endif
|
|
}
|
|
|
|
int Certs_VerifyChain(struct X509CertContext* x509) {
|
|
CFMutableArrayRef chain;
|
|
SecTrustRef trust;
|
|
int res;
|
|
|
|
if (!policy) CreateX509Policy();
|
|
if (!policy) return ERR_OUT_OF_MEMORY;
|
|
|
|
chain = CFArrayCreateMutable(NULL, x509->numCerts, &kCFTypeArrayCallBacks);
|
|
if (!chain) return ERR_OUT_OF_MEMORY;
|
|
|
|
CreateChain(x509, chain);
|
|
res = SecTrustCreateWithCertificates(chain, policy, &trust);
|
|
|
|
if (!res) {
|
|
SecTrustResultType result;
|
|
res = SecTrustEvaluate(trust, &result);
|
|
|
|
int is_trusted = result == kSecTrustResultUnspecified || result == kSecTrustResultProceed;
|
|
if (!res && !is_trusted) res = -1;
|
|
if (res) Platform_Log1("Cert validation failed: %i", &result);
|
|
}
|
|
|
|
CFRelease(trust);
|
|
CFRelease(chain);
|
|
return res;
|
|
}
|
|
#elif CC_CRT_BACKEND == CC_CRT_BACKEND_ANDROID
|
|
static jmethodID JAVA_sslCreateTrust, JAVA_sslAddCert, JAVA_sslVerifyChain;
|
|
static int created_trust;
|
|
|
|
void CertsBackend_Init(void) {
|
|
JNIEnv* env;
|
|
JavaGetCurrentEnv(env);
|
|
|
|
JAVA_sslCreateTrust = JavaGetSMethod(env, "sslCreateTrust", "()I");
|
|
JAVA_sslAddCert = JavaGetSMethod(env, "sslAddCert", "([B)V");
|
|
JAVA_sslVerifyChain = JavaGetSMethod(env, "sslVerifyChain", "()I");
|
|
}
|
|
|
|
int Certs_VerifyChain(struct X509CertContext* x509) {
|
|
JNIEnv* env;
|
|
jvalue args[1];
|
|
int i;
|
|
JavaGetCurrentEnv(env);
|
|
|
|
if (!created_trust) created_trust = JavaSCall_Int(env, JAVA_sslCreateTrust, NULL);
|
|
if (!created_trust) return ERR_NOT_SUPPORTED;
|
|
|
|
for (i = 0; i < x509->numCerts; i++)
|
|
{
|
|
struct X509Cert* cert = &x509->certs[i];
|
|
args[0].l = JavaMakeBytes(env, cert->data, cert->offset);
|
|
JavaSCall_Void(env, JAVA_sslAddCert, args);
|
|
(*env)->DeleteLocalRef(env, args[0].l);
|
|
}
|
|
|
|
return JavaSCall_Int(env, JAVA_sslVerifyChain, NULL);
|
|
}
|
|
#elif CC_CRT_BACKEND == CC_CRT_BACKEND_WINCRYPTO
|
|
#define CC_CRYPT32_FUNC extern
|
|
#include "Funcs.h"
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#define NOSERVICE
|
|
#define NOMCX
|
|
#define NOIME
|
|
#ifndef UNICODE
|
|
#define UNICODE
|
|
#define _UNICODE
|
|
#endif
|
|
|
|
#include <windows.h>
|
|
/* Compatibility versions so compiling works on older Windows SDKs */
|
|
#include "../misc/windows/min-wincrypt.h" /* #include <wincrypt.h> */
|
|
|
|
void CertsBackend_Init(void) {
|
|
Crypt32_LoadDynamicFuncs();
|
|
}
|
|
|
|
static const LPCSTR const usage[] = {
|
|
szOID_PKIX_KP_SERVER_AUTH,
|
|
szOID_SERVER_GATED_CRYPTO,
|
|
szOID_SGC_NETSCAPE
|
|
};
|
|
|
|
static BOOL BuildChain(struct X509CertContext* x509, HCERTSTORE store, PCCERT_CONTEXT* end_cert, PCCERT_CHAIN_CONTEXT* chain) {
|
|
struct X509Cert* cert = &x509->certs[0];
|
|
CERT_CHAIN_PARA para = { 0 };
|
|
int i;
|
|
|
|
BOOL ok = _CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, cert->data, cert->offset,
|
|
CERT_STORE_ADD_ALWAYS, end_cert);
|
|
if (!ok || !(*end_cert)) return FALSE;
|
|
|
|
for (i = 1; i < x509->numCerts; i++)
|
|
{
|
|
cert = &x509->certs[i];
|
|
ok = _CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, cert->data, cert->offset,
|
|
CERT_STORE_ADD_ALWAYS, NULL);
|
|
|
|
}
|
|
|
|
para.cbSize = sizeof(para);
|
|
para.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
|
|
para.RequestedUsage.Usage.cUsageIdentifier = Array_Elems(usage);
|
|
para.RequestedUsage.Usage.rgpszUsageIdentifier = (LPSTR*)usage;
|
|
|
|
return _CertGetCertificateChain(NULL, *end_cert, NULL, NULL, ¶, 0, NULL, chain);
|
|
}
|
|
|
|
int Certs_VerifyChain(struct X509CertContext* x509) {
|
|
PCCERT_CHAIN_CONTEXT chain = NULL;
|
|
PCCERT_CONTEXT end_cert = NULL;
|
|
HCERTSTORE store;
|
|
DWORD res = 200;
|
|
|
|
if (!_CertOpenStore) return ERR_NOT_SUPPORTED;
|
|
store = _CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL);
|
|
if (!store) return ERR_NOT_SUPPORTED;
|
|
|
|
if (BuildChain(x509, store, &end_cert, &chain)) {
|
|
res = chain->TrustStatus.dwErrorStatus;
|
|
if (res) Platform_Log1("Cert validation failed: %h", &res);
|
|
}
|
|
|
|
_CertFreeCertificateChain(chain);
|
|
_CertFreeCertificateContext(end_cert);
|
|
_CertCloseStore(store, 0);
|
|
return res;
|
|
}
|
|
#endif
|
|
|
|
#endif
|