diff --git a/src/Cedar/Admin.c b/src/Cedar/Admin.c index d2e8b184..4ce38745 100644 --- a/src/Cedar/Admin.c +++ b/src/Cedar/Admin.c @@ -9455,7 +9455,7 @@ UINT StSetServerCert(ADMIN *a, RPC_KEY_PAIR *t) } } - SetCedarCert(c, t->Cert, t->Key); + SetCedarCertAndChain(c, t->Cert, t->Key, t->Chain); ALog(a, NULL, "LA_SET_SERVER_CERT"); @@ -14555,6 +14555,7 @@ void InRpcKeyPair(RPC_KEY_PAIR *t, PACK *p) } t->Cert = PackGetX(p, "Cert"); + t->Chain = PackGetXList(p, "Chain"); t->Key = PackGetK(p, "Key"); t->Flag1 = PackGetInt(p, "Flag1"); } @@ -14567,12 +14568,14 @@ void OutRpcKeyPair(PACK *p, RPC_KEY_PAIR *t) } PackAddX(p, "Cert", t->Cert); + PackAddXList(p, "Chain", t->Chain); PackAddK(p, "Key", t->Key); PackAddInt(p, "Flag1", t->Flag1); } void FreeRpcKeyPair(RPC_KEY_PAIR *t) { FreeX(t->Cert); + FreeXList(t->Chain); FreeK(t->Key); } diff --git a/src/Cedar/Admin.h b/src/Cedar/Admin.h index b176a6f0..89f97e39 100644 --- a/src/Cedar/Admin.h +++ b/src/Cedar/Admin.h @@ -230,6 +230,7 @@ struct RPC_FARM_CONNECTION_STATUS struct RPC_KEY_PAIR { X *Cert; // Certificate + LIST *Chain; // Trust chain K *Key; // Secret key UINT Flag1; // Flag1 }; diff --git a/src/Cedar/CM.c b/src/Cedar/CM.c index 2c04bc39..02df5267 100644 --- a/src/Cedar/CM.c +++ b/src/Cedar/CM.c @@ -8453,6 +8453,11 @@ bool CmLoadKExW(HWND hWnd, K **k, wchar_t *filename, UINT size) // Read a set of certificate and private key bool CmLoadXAndK(HWND hWnd, X **x, K **k) +{ + return CmLoadXListAndK(hWnd, x, k, NULL); +} +// Read a set of certificate and private key and trust chain +bool CmLoadXListAndK(HWND hWnd, X **x, K **k, LIST **cc) { wchar_t *s; bool is_p12; @@ -8500,7 +8505,7 @@ START_FIRST: } if (IsEncryptedP12(p12) == false) { - if (ParseP12(p12, x, k, NULL) == false) + if (ParseP12Ex(p12, x, k, cc, NULL) == false) { MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_BAD_P12_W"), tmp); FreeP12(p12); @@ -8519,7 +8524,7 @@ START_FIRST: } else { - if (ParseP12(p12, x, k, password) == false) + if (ParseP12Ex(p12, x, k, cc, password) == false) { MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_BAD_P12_W"), tmp); FreeP12(p12); @@ -8532,6 +8537,10 @@ START_FIRST: { FreeX(*x); FreeK(*k); + if (cc != NULL) + { + FreeXList(*cc); + } FreeP12(p12); FreeBuf(b); if (MsgBox(hWnd, MB_ICONEXCLAMATION | MB_RETRYCANCEL, _UU("DLG_BAD_SIGNATURE")) == IDRETRY) @@ -8540,6 +8549,11 @@ START_FIRST: } return false; } + if (cc != NULL && LIST_NUM(*cc) == 0) + { + ReleaseList(*cc); + *cc = NULL; + } FreeP12(p12); FreeBuf(b); return true; @@ -8548,19 +8562,40 @@ START_FIRST: { // Processing of X509 BUF *b = ReadDumpW(tmp); - X *x509; + X *x509 = NULL; K *key; + LIST *chain = NULL; if (b == NULL) { MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_OPEN_FILE_ERROR_W"), tmp); return false; } - x509 = BufToX(b, IsBase64(b)); + // DER-encoded X509 files can't hold multiple certificates + if (cc == NULL || IsBase64(b) == false) + { + x509 = BufToX(b, IsBase64(b)); + } + else + { + chain = BufToXList(b, true); + if (LIST_NUM(chain) > 0) + { + x509 = LIST_DATA(chain, 0); + Delete(chain, x509); + + if (LIST_NUM(chain) == 0) + { + ReleaseList(chain); + chain = NULL; + } + } + } FreeBuf(b); if (x509 == NULL) { MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_BAD_X509_W"), tmp); + FreeXList(chain); return false; } @@ -8569,6 +8604,7 @@ START_FIRST: if (s == NULL) { FreeX(x509); + FreeXList(chain); return false; } UniStrCpy(tmp, sizeof(tmp), s); @@ -8579,6 +8615,7 @@ START_FIRST: { MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_OPEN_FILE_ERROR_W"), tmp); FreeX(x509); + FreeXList(chain); return false; } @@ -8593,6 +8630,7 @@ START_FIRST: { FreeBuf(b); FreeX(x509); + FreeXList(chain); return false; } key = BufToK(b, true, IsBase64(b), pass); @@ -8602,6 +8640,7 @@ START_FIRST: { FreeBuf(b); FreeX(x509); + FreeXList(chain); MsgBoxEx(hWnd, MB_ICONSTOP, _UU("DLG_BAD_KEY_W"), tmp); return false; } @@ -8611,6 +8650,7 @@ START_FIRST: FreeBuf(b); FreeX(x509); FreeK(key); + FreeXList(chain); if (MsgBox(hWnd, MB_ICONEXCLAMATION | MB_RETRYCANCEL, _UU("DLG_BAD_SIGNATURE")) == IDRETRY) { goto START_FIRST; @@ -8621,6 +8661,10 @@ START_FIRST: FreeBuf(b); *x = x509; *k = key; + if (cc != NULL) + { + *cc = chain; + } return true; } } diff --git a/src/Cedar/CMInner.h b/src/Cedar/CMInner.h index 72ad0768..e9122b39 100644 --- a/src/Cedar/CMInner.h +++ b/src/Cedar/CMInner.h @@ -409,6 +409,7 @@ void CmEditAccountDlgInit(HWND hWnd, CM_ACCOUNT *a); void CmEditAccountDlgOnOk(HWND hWnd, CM_ACCOUNT *a); void CmEditAccountDlgStartEnumHub(HWND hWnd, CM_ACCOUNT *a); bool CmLoadXAndK(HWND hWnd, X **x, K **k); +bool CmLoadXListAndK(HWND hWnd, X **x, K **k, LIST **cc); bool CmLoadKEx(HWND hWnd, K **k, char *filename, UINT size); bool CmLoadKExW(HWND hWnd, K **k, wchar_t *filename, UINT size); bool CmLoadXFromFileOrSecureCard(HWND hWnd, X **x); diff --git a/src/Cedar/Cedar.c b/src/Cedar/Cedar.c index b12fb3ba..fc234217 100644 --- a/src/Cedar/Cedar.c +++ b/src/Cedar/Cedar.c @@ -1157,6 +1157,10 @@ void CleanupCedar(CEDAR *c) { FreeK(c->ServerK); } + if (c->ServerChain) + { + FreeXList(c->ServerChain); + } if (c->CipherList) { @@ -1386,6 +1390,10 @@ void FreeNetSvcList(CEDAR *cedar) // Change certificate of Cedar void SetCedarCert(CEDAR *c, X *server_x, K *server_k) +{ + SetCedarCertAndChain(c, server_x, server_k, NULL); +} +void SetCedarCertAndChain(CEDAR *c, X *server_x, K *server_k, LIST *server_chain) { // Validate arguments if (server_x == NULL || server_k == NULL) @@ -1405,8 +1413,14 @@ void SetCedarCert(CEDAR *c, X *server_x, K *server_k) FreeK(c->ServerK); } + if (c->ServerChain != NULL) + { + FreeXList(c->ServerChain); + } + c->ServerX = CloneX(server_x); c->ServerK = CloneK(server_k); + c->ServerChain = CloneXList(server_chain); } Unlock(c->lock); } diff --git a/src/Cedar/Cedar.h b/src/Cedar/Cedar.h index 88440c09..bfbf5083 100644 --- a/src/Cedar/Cedar.h +++ b/src/Cedar/Cedar.h @@ -930,6 +930,7 @@ struct CEDAR COUNTER *ConnectionIncrement; // Connection increment counter X *ServerX; // Server certificate K *ServerK; // Private key of the server certificate + LIST *ServerChain; // Server trust chain char UsernameHubSeparator; // Character which separates the username from the hub name char *CipherList; // List of encryption algorithms UINT Version; // Version information @@ -1000,6 +1001,7 @@ CEDAR *NewCedar(X *server_x, K *server_k); void CedarForceLink(); void SetCedarVpnBridge(CEDAR *c); void SetCedarCert(CEDAR *c, X *server_x, K *server_k); +void SetCedarCertAndChain(CEDAR *c, X *server_x, K *server_k, LIST *server_chain); void ReleaseCedar(CEDAR *c); void CleanupCedar(CEDAR *c); void StopCedar(CEDAR *c); diff --git a/src/Cedar/Command.c b/src/Cedar/Command.c index 9e972d18..67ebc4b5 100644 --- a/src/Cedar/Command.c +++ b/src/Cedar/Command.c @@ -8638,18 +8638,51 @@ UINT PsServerKeyGet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param) // Read the certificate and the private key bool CmdLoadCertAndKey(CONSOLE *c, X **xx, K **kk, wchar_t *cert_filename, wchar_t *key_filename) { - X *x; + return CmdLoadCertChainAndKey(c, xx, kk, NULL, cert_filename, key_filename); +} +bool CmdLoadCertChainAndKey(CONSOLE *c, X **xx, K **kk, LIST **cc, wchar_t *cert_filename, wchar_t *key_filename) +{ + X *x = NULL; K *k; + LIST *chain = NULL; // Validate arguments if (c == NULL || cert_filename == NULL || key_filename == NULL || xx == NULL || kk == NULL) { return false; } - x = FileToXW(cert_filename); + BUF *b = ReadDumpW(cert_filename); + if (b == NULL) + { + c->Write(c, _UU("CMD_LOADCERT_FAILED")); + return false; + } + + // DER-encoded X509 files can't hold multiple certificates + if (cc == NULL || IsBase64(b) == false) + { + x = BufToX(b, IsBase64(b)); + } + else + { + chain = BufToXList(b, true); + if (LIST_NUM(chain) > 0) + { + x = LIST_DATA(chain, 0); + Delete(chain, x); + + if (LIST_NUM(chain) == 0) + { + ReleaseList(chain); + chain = NULL; + } + } + } + FreeBuf(b); if (x == NULL) { c->Write(c, _UU("CMD_LOADCERT_FAILED")); + FreeXList(chain); return false; } @@ -8658,6 +8691,7 @@ bool CmdLoadCertAndKey(CONSOLE *c, X **xx, K **kk, wchar_t *cert_filename, wchar { c->Write(c, _UU("CMD_LOADKEY_FAILED")); FreeX(x); + FreeXList(chain); return false; } @@ -8666,12 +8700,17 @@ bool CmdLoadCertAndKey(CONSOLE *c, X **xx, K **kk, wchar_t *cert_filename, wchar c->Write(c, _UU("CMD_KEYPAIR_FAILED")); FreeX(x); FreeK(k); + FreeXList(chain); return false; } *xx = x; *kk = k; + if (cc != NULL) + { + *cc = chain; + } return true; } @@ -8754,7 +8793,7 @@ UINT PsServerCertSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param) Zero(&t, sizeof(t)); - if (CmdLoadCertAndKey(c, &t.Cert, &t.Key, + if (CmdLoadCertChainAndKey(c, &t.Cert, &t.Key, &t.Chain, GetParamUniStr(o, "LOADCERT"), GetParamUniStr(o, "LOADKEY"))) { diff --git a/src/Cedar/Command.h b/src/Cedar/Command.h index bbf1dc1e..37846bb7 100644 --- a/src/Cedar/Command.h +++ b/src/Cedar/Command.h @@ -236,6 +236,7 @@ bool CmdEvalPortList(CONSOLE *c, wchar_t *str, void *param); wchar_t *PsClusterSettingMemberPromptPorts(CONSOLE *c, void *param); K *CmdLoadKey(CONSOLE *c, wchar_t *filename); bool CmdLoadCertAndKey(CONSOLE *c, X **xx, K **kk, wchar_t *cert_filename, wchar_t *key_filename); +bool CmdLoadCertChainAndKey(CONSOLE *c, X **xx, K **kk, LIST **cc, wchar_t *cert_filename, wchar_t *key_filename); bool CmdEvalTcpOrUdp(CONSOLE *c, wchar_t *str, void *param); wchar_t *GetConnectionTypeStr(UINT type); bool CmdEvalHostAndSubnetMask4(CONSOLE *c, wchar_t *str, void *param); diff --git a/src/Cedar/Connection.c b/src/Cedar/Connection.c index c7096475..99522447 100644 --- a/src/Cedar/Connection.c +++ b/src/Cedar/Connection.c @@ -2990,6 +2990,7 @@ void ConnectionAccept(CONNECTION *c) SOCK *s; X *x; K *k; + LIST *chain; char tmp[128]; UINT initial_timeout = CONNECTING_TIMEOUT; UCHAR ctoken_hash[SHA1_SIZE]; @@ -3040,24 +3041,27 @@ void ConnectionAccept(CONNECTION *c) x = CloneX(c->Cedar->ServerX); k = CloneK(c->Cedar->ServerK); + chain = CloneXList(c->Cedar->ServerChain); } Unlock(c->Cedar->lock); // Start the SSL communication Copy(&s->SslAcceptSettings, &c->Cedar->SslAcceptSettings, sizeof(SSL_ACCEPT_SETTINGS)); - if (StartSSL(s, x, k) == false) + if (StartSSLEx2(s, x, k, chain, 0, NULL) == false) { // Failed AddNoSsl(c->Cedar, &s->RemoteIP); Debug("ConnectionAccept(): StartSSL() failed\n"); FreeX(x); FreeK(k); + FreeXList(chain); goto FINAL; } FreeX(x); FreeK(k); + FreeXList(chain); SLog(c->Cedar, "LS_SSL_START", c->Name, s->CipherName); diff --git a/src/Cedar/Proto_PPP.c b/src/Cedar/Proto_PPP.c index 86b418f8..96094b70 100644 --- a/src/Cedar/Proto_PPP.c +++ b/src/Cedar/Proto_PPP.c @@ -3605,7 +3605,7 @@ bool PPPProcessEAPTlsResponse(PPP_SESSION *p, PPP_EAP *eap_packet, UINT eapTlsSi if (p->Eap_TlsCtx.SslPipe == NULL) { p->Eap_TlsCtx.Dh = DhNewFromBits(DH_PARAM_BITS_DEFAULT); - p->Eap_TlsCtx.SslPipe = NewSslPipeEx(true, p->Cedar->ServerX, p->Cedar->ServerK, p->Eap_TlsCtx.Dh, true, &(p->Eap_TlsCtx.ClientCert)); + p->Eap_TlsCtx.SslPipe = NewSslPipeEx2(true, p->Cedar->ServerX, p->Cedar->ServerK, p->Cedar->ServerChain, p->Eap_TlsCtx.Dh, true, &(p->Eap_TlsCtx.ClientCert)); } // If the current frame is fragmented, or it is a possible last of a fragmented series, bufferize it diff --git a/src/Cedar/SM.c b/src/Cedar/SM.c index acbbc12e..d8954ffd 100644 --- a/src/Cedar/SM.c +++ b/src/Cedar/SM.c @@ -16809,6 +16809,7 @@ void SmSslDlgOnOk(HWND hWnd, SM_SSL *s) t.Cert = CloneX(s->Cert); t.Key = CloneK(s->Key); + t.Chain = CloneXList(s->Chain); if (CALL(hWnd, ScSetServerCert(s->p->Rpc, &t)) == false) { @@ -16923,6 +16924,7 @@ void SmSslDlgInit(HWND hWnd, SM_SSL *s) // Copy the certificate and key s->Cert = CloneX(t.Cert); s->Key = CloneK(t.Key); + s->Chain = CloneXList(t.Chain); if (t.Key != NULL) { @@ -17174,6 +17176,7 @@ UINT SmSslDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param SM_SSL *s = (SM_SSL *)param; X *x; K *k; + LIST *chain; // Validate arguments if (hWnd == NULL) { @@ -17222,16 +17225,18 @@ UINT SmSslDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param case B_IMPORT: // Import - if (CmLoadXAndK(hWnd, &x, &k)) + if (CmLoadXListAndK(hWnd, &x, &k, &chain)) { wchar_t tmp[MAX_SIZE]; LABEL_APPLY_NEW_CERT: FreeX(s->Cert); FreeK(s->Key); + FreeXList(s->Chain); s->Cert = x; s->Key = k; s->SetCertAndKey = true; + s->Chain = chain; // Show the Certificate Information SmGetCertInfoStr(tmp, sizeof(tmp), s->Cert); SetText(hWnd, S_CERT_INFO, tmp); @@ -17310,6 +17315,7 @@ void SmSslDlg(HWND hWnd, SM_SERVER *p) // Cleanup FreeX(s.Cert); FreeK(s.Key); + FreeXList(s.Chain); } // Listener creation dialog procedure diff --git a/src/Cedar/SMInner.h b/src/Cedar/SMInner.h index b1cd699c..6fd8b8e3 100644 --- a/src/Cedar/SMInner.h +++ b/src/Cedar/SMInner.h @@ -112,6 +112,7 @@ typedef struct SM_SSL SM_SERVER *p; // P X *Cert; // Certificate K *Key; // Secret key + LIST *Chain; // Trust chain bool SetCertAndKey; // Set the key } SM_SSL; diff --git a/src/Cedar/Server.c b/src/Cedar/Server.c index 50e70fcd..523cfc7f 100644 --- a/src/Cedar/Server.c +++ b/src/Cedar/Server.c @@ -5608,6 +5608,7 @@ void SiLoadServerCfg(SERVER *s, FOLDER *f) char tmp[MAX_SIZE]; X *x = NULL; K *k = NULL; + LIST *chain = NewList(NULL); FOLDER *params_folder; UINT i; // Validate arguments @@ -5847,10 +5848,14 @@ void SiLoadServerCfg(SERVER *s, FOLDER *f) FreeBuf(b); } + // Server trust chain + SiLoadCertList(chain, CfgGetFolder(f, "ServerChain")); + if (x == NULL || k == NULL || CheckXandK(x, k) == false) { FreeX(x); FreeK(k); + FreeXList(chain); SiGenerateDefaultCert(&x, &k); SetCedarCert(c, x, k); @@ -5860,10 +5865,18 @@ void SiLoadServerCfg(SERVER *s, FOLDER *f) } else { - SetCedarCert(c, x, k); + if (LIST_NUM(chain) == 0) + { + SetCedarCert(c, x, k); + } + else + { + SetCedarCertAndChain(c, x, k, chain); + } FreeX(x); FreeK(k); + FreeXList(chain); } // Character which separates the username from the hub name @@ -6246,6 +6259,9 @@ void SiWriteServerCfg(FOLDER *f, SERVER *s) CfgAddBuf(f, "ServerKey", b); FreeBuf(b); + // Server trust chain + SiWriteCertList(CfgCreateFolder(f, "ServerChain"), c->ServerChain); + { // Character which separates the username from the hub name char str[2]; diff --git a/src/Mayaqua/Encrypt.c b/src/Mayaqua/Encrypt.c index 7afaec34..263ef9af 100644 --- a/src/Mayaqua/Encrypt.c +++ b/src/Mayaqua/Encrypt.c @@ -1079,6 +1079,41 @@ X *CloneX(X *x) return ret; } +// Clone of certificate chain +LIST *CloneXList(LIST *chain) +{ + BUF *b; + X *x; + LIST *ret; + // Validate arguments + if (chain == NULL) + { + return NULL; + } + + ret = NewList(NULL); + LockList(chain); + { + UINT i; + for (i = 0;i < LIST_NUM(chain);i++) + { + x = LIST_DATA(chain, i); + b = XToBuf(x, false); + if (b == NULL) + { + continue; + } + + x = BufToX(b, false); + Add(ret, x); + FreeBuf(b); + } + } + UnlockList(chain); + + return ret; +} + // Generate a P12 P12 *NewP12(X *x, K *k, char *password) { @@ -1133,9 +1168,15 @@ bool IsEncryptedP12(P12 *p12) // Extract the X and the K from the P12 bool ParseP12(P12 *p12, X **x, K **k, char *password) +{ + return ParseP12Ex(p12, x, k, NULL, password); +} +// Extract the X, the K and the chain from the P12 +bool ParseP12Ex(P12 *p12, X **x, K **k, LIST **cc, char *password) { EVP_PKEY *pkey; X509 *x509; + STACK_OF(X509) *sk = NULL; // Validate arguments if (p12 == NULL || x == NULL || k == NULL) { @@ -1165,9 +1206,9 @@ bool ParseP12(P12 *p12, X **x, K **k, char *password) // Extraction Lock(openssl_lock); { - if (PKCS12_parse(p12->pkcs12, password, &pkey, &x509, NULL) == false) + if (PKCS12_parse(p12->pkcs12, password, &pkey, &x509, &sk) == false) { - if (PKCS12_parse(p12->pkcs12, NULL, &pkey, &x509, NULL) == false) + if (PKCS12_parse(p12->pkcs12, NULL, &pkey, &x509, &sk) == false) { Unlock(openssl_lock); return false; @@ -1182,6 +1223,7 @@ bool ParseP12(P12 *p12, X **x, K **k, char *password) if (*x == NULL) { FreePKey(pkey); + sk_X509_free(sk); return false; } @@ -1189,6 +1231,37 @@ bool ParseP12(P12 *p12, X **x, K **k, char *password) (*k)->private_key = true; (*k)->pkey = pkey; + if (sk == NULL || cc == NULL) + { + if (cc != NULL) + { + *cc = NULL; + } + if (sk != NULL) + { + sk_X509_free(sk); + } + return true; + } + + LIST *chain = NewList(NULL); + X *x1; + while (sk_X509_num(sk)) { + x509 = sk_X509_shift(sk); + x1 = X509ToX(x509); + if (x1 != NULL) + { + Add(chain, x1); + } + else + { + X509_free(x509); + } + } + sk_X509_free(sk); + + *cc = chain; + return true; } @@ -3369,6 +3442,29 @@ void FreeX(X *x) Free(x); } +// Release of an X chain +void FreeXList(LIST *chain) +{ + // Validate arguments + if (chain == NULL) + { + return; + } + + LockList(chain); + { + UINT i; + for (i = 0; i < LIST_NUM(chain); i++) + { + X *x = LIST_DATA(chain, i); + FreeX(x); + } + } + UnlockList(chain); + + ReleaseList(chain); +} + // Release of the X509 void FreeX509(X509 *x509) { @@ -3410,6 +3506,31 @@ X *BufToX(BUF *b, bool text) return x; } +// Convert the BUF to X chain +LIST *BufToXList(BUF *b, bool text) +{ + LIST *chain; + BIO *bio; + // Validate arguments + if (b == NULL) + { + return NULL; + } + + bio = BufToBio(b); + if (bio == NULL) + { + FreeBuf(b); + return NULL; + } + + chain = BioToXList(bio, text); + + FreeBio(bio); + + return chain; +} + // Get a digest of the X void GetXDigest(X *x, UCHAR *buf, bool sha1) { @@ -3473,6 +3594,49 @@ X *BioToX(BIO *bio, bool text) return x; } +// Convert BIO to X chain +LIST *BioToXList(BIO *bio, bool text) +{ + X *x; + STACK_OF(X509_INFO) *sk = NULL; + X509_INFO *xi; + LIST *chain; + + // Validate arguments + if (bio == NULL || text == false) + { + return NULL; + } + + Lock(openssl_lock); + { + sk = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); + if (sk == NULL) + { + return NULL; + } + + chain = NewList(NULL); + + while (sk_X509_INFO_num(sk)) + { + xi = sk_X509_INFO_shift(sk); + x = X509ToX(xi->x509); + if (x != NULL) + { + Add(chain, x); + xi->x509 = NULL; + } + X509_INFO_free(xi); + } + + sk_X509_INFO_free(sk); + } + Unlock(openssl_lock); + + return chain; +} + // Convert the X509 to X X *X509ToX(X509 *x509) { diff --git a/src/Mayaqua/Encrypt.h b/src/Mayaqua/Encrypt.h index ab92fc75..642e2081 100644 --- a/src/Mayaqua/Encrypt.h +++ b/src/Mayaqua/Encrypt.h @@ -293,9 +293,12 @@ BUF *BioToBuf(BIO *bio); BIO *NewBio(); void FreeBio(BIO *bio); X *BioToX(BIO *bio, bool text); +LIST *BioToXList(BIO *bio, bool text); X *BufToX(BUF *b, bool text); +LIST *BufToXList(BUF *b, bool text); void FreeX509(X509 *x509); void FreeX(X *x); +void FreeXList(LIST *chain); BIO *XToBio(X *x, bool text); BUF *XToBuf(X *x, bool text); K *BioToK(BIO *bio, bool private_key, bool text, char *password); @@ -357,9 +360,11 @@ void FreePKCS12(PKCS12 *pkcs12); void FreeP12(P12 *p12); bool P12ToFileW(P12 *p12, wchar_t *filename); bool ParseP12(P12 *p12, X **x, K **k, char *password); +bool ParseP12Ex(P12 *p12, X **x, K **k, LIST **cc, char *password); bool IsEncryptedP12(P12 *p12); P12 *NewP12(X *x, K *k, char *password); X *CloneX(X *x); +LIST *CloneXList(LIST *chain); K *CloneK(K *k); void FreeCryptLibrary(); void GetPrintNameFromX(wchar_t *str, UINT size, X *x); diff --git a/src/Mayaqua/Network.c b/src/Mayaqua/Network.c index 009bc65e..f8a72fe4 100644 --- a/src/Mayaqua/Network.c +++ b/src/Mayaqua/Network.c @@ -5702,6 +5702,10 @@ SSL_PIPE *NewSslPipe(bool server_mode, X *x, K *k, DH_CTX *dh) // Create a new SSL pipe with extended options SSL_PIPE *NewSslPipeEx(bool server_mode, X *x, K *k, DH_CTX *dh, bool verify_peer, struct SslClientCertInfo *clientcert) +{ + return NewSslPipeEx2(server_mode, x, k, NULL, dh, verify_peer, clientcert); +} +SSL_PIPE *NewSslPipeEx2(bool server_mode, X *x, K *k, LIST *chain, DH_CTX *dh, bool verify_peer, struct SslClientCertInfo *clientcert) { SSL_PIPE *s; SSL *ssl; @@ -5715,7 +5719,24 @@ SSL_PIPE *NewSslPipeEx(bool server_mode, X *x, K *k, DH_CTX *dh, bool verify_pee SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_3); // For some reason pppd under linux doesn't like it #endif - AddChainSslCertOnDirectory(ssl_ctx); + if (chain == NULL) + { + AddChainSslCertOnDirectory(ssl_ctx); + } + else + { + UINT i; + X *x; + LockList(chain); + { + for (i = 0;i < LIST_NUM(chain);i++) + { + x = LIST_DATA(chain, i); + AddChainSslCert(ssl_ctx, x); + } + } + UnlockList(chain); + } if (dh != NULL) { @@ -11565,6 +11586,10 @@ bool StartSSL(SOCK *sock, X *x, K *priv) return StartSSLEx(sock, x, priv, 0, NULL); } bool StartSSLEx(SOCK *sock, X *x, K *priv, UINT ssl_timeout, char *sni_hostname) +{ + return StartSSLEx2(sock, x, priv, NULL, ssl_timeout, sni_hostname); +} +bool StartSSLEx2(SOCK *sock, X *x, K *priv, LIST *chain, UINT ssl_timeout, char *sni_hostname) { X509 *x509; EVP_PKEY *key; @@ -11660,7 +11685,24 @@ bool StartSSLEx(SOCK *sock, X *x, K *priv, UINT ssl_timeout, char *sni_hostname) #endif Unlock(openssl_lock); - AddChainSslCertOnDirectory(ssl_ctx); + if (chain == NULL) + { + AddChainSslCertOnDirectory(ssl_ctx); + } + else + { + UINT i; + X *x; + LockList(chain); + { + for (i = 0;i < LIST_NUM(chain);i++) + { + x = LIST_DATA(chain, i); + AddChainSslCert(ssl_ctx, x); + } + } + UnlockList(chain); + } Lock(openssl_lock); } diff --git a/src/Mayaqua/Network.h b/src/Mayaqua/Network.h index bf58dceb..b7cbf9a2 100644 --- a/src/Mayaqua/Network.h +++ b/src/Mayaqua/Network.h @@ -1078,6 +1078,7 @@ UINT SecureSend(SOCK *sock, void *data, UINT size); UINT SecureRecv(SOCK *sock, void *data, UINT size); bool StartSSL(SOCK *sock, X *x, K *priv); bool StartSSLEx(SOCK *sock, X *x, K *priv, UINT ssl_timeout, char *sni_hostname); +bool StartSSLEx2(SOCK *sock, X *x, K *priv, LIST *chain, UINT ssl_timeout, char *sni_hostname); bool AddChainSslCert(struct ssl_ctx_st *ctx, X *x); void AddChainSslCertOnDirectory(struct ssl_ctx_st *ctx); bool SendAll(SOCK *sock, void *data, UINT size, bool secure); @@ -1353,6 +1354,7 @@ struct SslClientCertInfo { SSL_PIPE *NewSslPipe(bool server_mode, X *x, K *k, DH_CTX *dh); SSL_PIPE *NewSslPipeEx(bool server_mode, X *x, K *k, DH_CTX *dh, bool verify_peer, struct SslClientCertInfo *clientcert); +SSL_PIPE *NewSslPipeEx2(bool server_mode, X *x, K *k, LIST *chain, DH_CTX *dh, bool verify_peer, struct SslClientCertInfo *clientcert); void FreeSslPipe(SSL_PIPE *s); bool SyncSslPipe(SSL_PIPE *s); diff --git a/src/Mayaqua/Pack.c b/src/Mayaqua/Pack.c index 0306c149..ab4d39b3 100644 --- a/src/Mayaqua/Pack.c +++ b/src/Mayaqua/Pack.c @@ -870,6 +870,50 @@ X *PackGetX(PACK *p, char *name) return x; } +// Get the X chain from the PACK +LIST *PackGetXList(PACK *p, char *name) +{ + X *x; + BUF *b; + LIST *chain; + UINT i; + // Validate arguments + if (p == NULL || name == NULL) + { + return NULL; + } + + ELEMENT *e = GetElement(p, name, VALUE_DATA); + if (e == NULL) + { + return NULL; + } + + chain = NewList(NULL); + for (i = 0;i < e->num_value;i++) + { + b = PackGetBufEx(p, name, i); + if (b == NULL) + { + FreeXList(chain); + return NULL; + } + + x = BufToX(b, false); + + if (x == NULL) + { + x = BufToX(b, true); + } + + FreeBuf(b); + + Add(chain, x); + } + + return chain; +} + // Add the K to the PACK ELEMENT *PackAddK(PACK *p, char *name, K *k) { @@ -916,6 +960,36 @@ ELEMENT *PackAddX(PACK *p, char *name, X *x) return e; } +// Add an X chain into the PACK +ELEMENT *PackAddXList(PACK *p, char *name, LIST *chain) +{ + BUF *b; + X *x; + ELEMENT *e = NULL; + // Validate arguments + if (p == NULL || name == NULL || chain == NULL) + { + return NULL; + } + + UINT i; + for (i = 0;i < LIST_NUM(chain);i++) + { + x = LIST_DATA(chain, i); + b = XToBuf(x, false); + + if (b == NULL) + { + return NULL; + } + + e = PackAddBufEx(p, name, b, i, LIST_NUM(chain)); + FreeBuf(b); + } + + return e; +} + // Get a buffer from the PACK BUF *PackGetBuf(PACK *p, char *name) { diff --git a/src/Mayaqua/Pack.h b/src/Mayaqua/Pack.h index 0113f670..1f93cd58 100644 --- a/src/Mayaqua/Pack.h +++ b/src/Mayaqua/Pack.h @@ -127,8 +127,10 @@ VALUE *NewInt64Value(UINT64 i); TOKEN_LIST *GetPackElementNames(PACK *p); X *PackGetX(PACK *p, char *name); +LIST *PackGetXList(PACK *p, char *name); K *PackGetK(PACK *p, char *name); ELEMENT *PackAddX(PACK *p, char *name, X *x); +ELEMENT *PackAddXList(PACK *p, char *name, LIST *chain); ELEMENT *PackAddK(PACK *p, char *name, K *k); ELEMENT *PackAddStr(PACK *p, char *name, char *str); ELEMENT *PackAddStrEx(PACK *p, char *name, char *str, UINT index, UINT total);