mirror of
https://github.com/PowerShell/Win32-OpenSSH.git
synced 2025-07-03 20:24:43 +02:00
Implement generic interface for bignum and diffie-hellman
Implementation of a generic wrap interface for bignum and diffie-hellman based upon Damien's wrap code in openssh-openbsd. This commit adds the generic interface along with the backing code for openssl, cng, and cng with an openssl fallback. Currently, openssl is the only provider for bignum and the diffie-hellman generic interface is only for static and negotiated oakley groups..
This commit is contained in:
parent
fe136bc352
commit
44d8ddddb1
@ -258,6 +258,13 @@
|
|||||||
<ClCompile Include="$(OpenSSH-Src-Path)uuencode.c" />
|
<ClCompile Include="$(OpenSSH-Src-Path)uuencode.c" />
|
||||||
<ClCompile Include="$(OpenSSH-Src-Path)verify.c" />
|
<ClCompile Include="$(OpenSSH-Src-Path)verify.c" />
|
||||||
<ClCompile Include="$(OpenSSH-Src-Path)xmalloc.c" />
|
<ClCompile Include="$(OpenSSH-Src-Path)xmalloc.c" />
|
||||||
|
<ClCompile Include="$(OpenSSH-Src-Path)openssl-bn.c" />
|
||||||
|
<ClCompile Include="$(OpenSSH-Src-Path)openssl-dh.c">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="$(OpenSSH-Src-Path)crypto-wrap.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
@ -288,5 +288,16 @@
|
|||||||
<ClCompile Include="$(OpenSSH-Src-Path)xmalloc.c">
|
<ClCompile Include="$(OpenSSH-Src-Path)xmalloc.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\..\openssl-dh.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\..\openssl-bn.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="..\..\..\crypto-wrap.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -159,11 +159,13 @@
|
|||||||
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\tncon.c" />
|
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\tncon.c" />
|
||||||
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\tnnet.c" />
|
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\tnnet.c" />
|
||||||
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\win32auth.c" />
|
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\win32auth.c" />
|
||||||
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\cng_kex.c" />
|
<ClCompile Include="..\win32compat\cng_dh.c">
|
||||||
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\cng_kexdhc.c" />
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||||
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\cng_kexdhs.c" />
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||||
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\cng_kexgexc.c" />
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||||
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\cng_kexgexs.c" />
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\cng_openssl_dh.c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ansiprsr.h" />
|
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ansiprsr.h" />
|
||||||
|
@ -81,19 +81,10 @@
|
|||||||
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\win32auth.c">
|
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\win32auth.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\win32compat\cng_kex.c">
|
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\cng_kex.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\win32compat\cng_kexdhc.c">
|
<ClCompile Include="..\win32compat\cng_openssl_dh.c">
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\win32compat\cng_kexdhs.c">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\win32compat\cng_kexgexc.c">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\win32compat\cng_kexgexs.c">
|
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@ -140,7 +131,10 @@
|
|||||||
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\win32auth.h">
|
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\win32auth.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\win32compat\cng_kex.h">
|
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\cng_kex.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\win32compat\cng-wrap.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
324
contrib/win32/win32compat/cng_dh.c
Normal file
324
contrib/win32/win32compat/cng_dh.c
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <bcrypt.h>
|
||||||
|
#include <openssl/bn.h>
|
||||||
|
#include <VersionHelpers.h>
|
||||||
|
#include <dh.h>
|
||||||
|
#include "crypto-wrap.h"
|
||||||
|
|
||||||
|
|
||||||
|
// CNG Diffie-hellman Kex context
|
||||||
|
typedef struct cng_dh_ctx {
|
||||||
|
int size;
|
||||||
|
PBYTE g;
|
||||||
|
PBYTE p;
|
||||||
|
PBYTE pub;
|
||||||
|
BCRYPT_KEY_HANDLE hPrivate;
|
||||||
|
BCRYPT_ALG_HANDLE hAlg;
|
||||||
|
} CNG_DH_CTX;
|
||||||
|
|
||||||
|
|
||||||
|
struct sshdh {
|
||||||
|
CNG_DH_CTX *dh;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//function to reverse the order of a byte array
|
||||||
|
static void
|
||||||
|
cng_dh_swap_bytes(void *pv, size_t n)
|
||||||
|
{
|
||||||
|
char *p = (char*)pv;
|
||||||
|
size_t lo, hi;
|
||||||
|
for (lo = 0, hi = n - 1; hi>lo; lo++, hi--)
|
||||||
|
{
|
||||||
|
char tmp = p[lo];
|
||||||
|
p[lo] = p[hi];
|
||||||
|
p[hi] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* DH wrappers */
|
||||||
|
|
||||||
|
struct sshdh *
|
||||||
|
sshdh_new(void)
|
||||||
|
{
|
||||||
|
return (struct sshdh *)(malloc(sizeof(struct sshdh)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sshdh_free(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
if (dh != NULL) {
|
||||||
|
if (dh->dh != NULL)
|
||||||
|
{
|
||||||
|
CNG_DH_CTX * pCtx = (CNG_DH_CTX*)dh->dh;
|
||||||
|
|
||||||
|
if (pCtx->hAlg)
|
||||||
|
BCryptCloseAlgorithmProvider(pCtx->hAlg, 0);
|
||||||
|
|
||||||
|
if (pCtx->hPrivate)
|
||||||
|
BCryptDestroyKey(pCtx->hPrivate);
|
||||||
|
|
||||||
|
ZeroMemory(pCtx, sizeof(*pCtx));
|
||||||
|
free(pCtx);
|
||||||
|
}
|
||||||
|
free(dh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sshbn *
|
||||||
|
sshdh_pubkey(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
CNG_DH_CTX *pCtx = dh->dh;
|
||||||
|
struct sshbn * bn = NULL;
|
||||||
|
|
||||||
|
sshbn_from(pCtx->pub, pCtx->size, &bn);
|
||||||
|
return bn;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sshbn *
|
||||||
|
sshdh_p(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
CNG_DH_CTX *pCtx = dh->dh;
|
||||||
|
struct sshbn * bn = NULL;
|
||||||
|
|
||||||
|
sshbn_from(pCtx->p, pCtx->size, &bn);
|
||||||
|
|
||||||
|
return bn;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sshbn *
|
||||||
|
sshdh_g(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
CNG_DH_CTX *pCtx = dh->dh;
|
||||||
|
struct sshbn * bn = NULL;
|
||||||
|
|
||||||
|
sshbn_from(pCtx->g, pCtx->size, &bn);
|
||||||
|
|
||||||
|
return bn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sshdh_dump(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX needed?
|
||||||
|
size_t
|
||||||
|
sshdh_shared_key_size(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
int sz;
|
||||||
|
CNG_DH_CTX *pCtx = dh->dh;
|
||||||
|
|
||||||
|
if (dh == NULL || dh->dh == NULL || (sz = pCtx->size) < 0)
|
||||||
|
return 0;
|
||||||
|
return (size_t)sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
// import a bignum public key
|
||||||
|
static BCRYPT_KEY_HANDLE
|
||||||
|
cng_dh_set_remote_pub_key2(struct sshdh *dh, struct sshbn *b)
|
||||||
|
{
|
||||||
|
BCRYPT_KEY_HANDLE hPub = NULL;
|
||||||
|
CNG_DH_CTX *pCtx = dh->dh;
|
||||||
|
|
||||||
|
if (sshbn_bytes(b) > pCtx->size)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
DWORD cbBlob = sizeof(BCRYPT_DH_KEY_BLOB) + (pCtx->size * 3);
|
||||||
|
|
||||||
|
PBYTE pBlob = (PBYTE)malloc(cbBlob);
|
||||||
|
|
||||||
|
BCRYPT_DH_KEY_BLOB *pKeyBlob = (BCRYPT_DH_KEY_BLOB *)pBlob;
|
||||||
|
pKeyBlob->cbKey = pCtx->size;
|
||||||
|
pKeyBlob->dwMagic = BCRYPT_DH_PUBLIC_MAGIC;
|
||||||
|
PBYTE pModulus = pBlob + sizeof(BCRYPT_DH_KEY_BLOB);
|
||||||
|
PBYTE pGenerator = pModulus + pKeyBlob->cbKey;
|
||||||
|
PBYTE pPublic = pGenerator + pKeyBlob->cbKey;
|
||||||
|
|
||||||
|
memcpy(pModulus, pCtx->p, pCtx->size);
|
||||||
|
memcpy(pGenerator, pCtx->g, pCtx->size);
|
||||||
|
memset(pPublic, 0, pCtx->size);
|
||||||
|
|
||||||
|
sshbn_to(b, pPublic + pCtx->size - (sshbn_bytes(b)));
|
||||||
|
|
||||||
|
|
||||||
|
HRESULT Status = 0;
|
||||||
|
|
||||||
|
if (S_OK != (Status = BCryptImportKeyPair(pCtx->hAlg, NULL, BCRYPT_DH_PUBLIC_BLOB, &hPub, pBlob, cbBlob, 0)))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (pBlob) free(pBlob);
|
||||||
|
|
||||||
|
return hPub;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int sshdh_compute_key(struct sshdh *dh, struct sshbn *pubkey,
|
||||||
|
struct sshbn **shared_secretp)
|
||||||
|
{
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
CNG_DH_CTX *pCtx = dh->dh;
|
||||||
|
PBYTE pSecret = (PBYTE)malloc(pCtx->size);
|
||||||
|
DWORD pSecret_len = pCtx->size;
|
||||||
|
DWORD size = pCtx->size;
|
||||||
|
struct sshbn * bn = NULL;
|
||||||
|
BCRYPT_SECRET_HANDLE hSecret;
|
||||||
|
|
||||||
|
BCRYPT_KEY_HANDLE hRemotePub = cng_dh_set_remote_pub_key2(dh, pubkey);
|
||||||
|
if (hRemotePub != NULL)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (S_OK == (hr = BCryptSecretAgreement(pCtx->hPrivate, hRemotePub, &hSecret, 0)))
|
||||||
|
{
|
||||||
|
|
||||||
|
hr = BCryptDeriveKey(hSecret, L"TRUNCATE", NULL, pSecret, pSecret_len, &size, 0);
|
||||||
|
if (S_OK == hr)
|
||||||
|
{
|
||||||
|
cng_dh_swap_bytes(pSecret, size);
|
||||||
|
|
||||||
|
sshbn_from(pSecret, size, &bn);
|
||||||
|
memset(pSecret, 0, size);
|
||||||
|
free(pSecret);
|
||||||
|
*shared_secretp = bn;
|
||||||
|
}
|
||||||
|
BCryptDestroySecret(hSecret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return S_OK==hr?0:-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
sshdh_generate(struct sshdh *dh, size_t len)
|
||||||
|
{
|
||||||
|
HRESULT Status;
|
||||||
|
BCRYPT_DH_PARAMETER_HEADER *DhParamHdrPointer = NULL;
|
||||||
|
DWORD DhParamBlobLength = 0;
|
||||||
|
PBYTE DhParamBlob = NULL;
|
||||||
|
BCRYPT_ALG_HANDLE hAlg = NULL;
|
||||||
|
PBYTE pBlob = NULL;
|
||||||
|
BCRYPT_KEY_HANDLE hPriv = NULL;
|
||||||
|
CNG_DH_CTX *pCtx = dh->dh;
|
||||||
|
DWORD cbBlob = 0;
|
||||||
|
|
||||||
|
|
||||||
|
DhParamBlobLength = sizeof(BCRYPT_DH_PARAMETER_HEADER) + (pCtx->size * 2);
|
||||||
|
|
||||||
|
if (NULL == (DhParamBlob = (PBYTE)malloc(DhParamBlobLength)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
DhParamHdrPointer = (BCRYPT_DH_PARAMETER_HEADER *)DhParamBlob;
|
||||||
|
DhParamHdrPointer->cbLength = DhParamBlobLength;
|
||||||
|
DhParamHdrPointer->cbKeyLength = pCtx->size;
|
||||||
|
DhParamHdrPointer->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC;
|
||||||
|
|
||||||
|
memcpy(DhParamBlob + sizeof(BCRYPT_DH_PARAMETER_HEADER), pCtx->p, pCtx->size);
|
||||||
|
memcpy(DhParamBlob + sizeof(BCRYPT_DH_PARAMETER_HEADER) + pCtx->size, pCtx->g, pCtx->size);
|
||||||
|
|
||||||
|
if (S_OK != (Status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_DH_ALGORITHM, NULL, 0)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
|
||||||
|
if (S_OK != (Status = BCryptGenerateKeyPair(hAlg, &hPriv, pCtx->size*8, 0)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
|
||||||
|
if (S_OK != (Status = BCryptSetProperty(hPriv, BCRYPT_DH_PARAMETERS, DhParamBlob, DhParamBlobLength, 0)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (S_OK != (Status = BCryptFinalizeKeyPair(hPriv, 0)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (S_OK != (Status = BCryptExportKey(hPriv, NULL, BCRYPT_DH_PUBLIC_BLOB, NULL, 0, &cbBlob, 0)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (NULL == (pBlob = (PBYTE)malloc(cbBlob)))
|
||||||
|
{
|
||||||
|
Status = STATUS_NO_MEMORY;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_OK != (Status = BCryptExportKey(hPriv, NULL, BCRYPT_DH_PUBLIC_BLOB, pBlob, cbBlob, &cbBlob, 0)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BCRYPT_DH_KEY_BLOB *pKeyBlob = (BCRYPT_DH_KEY_BLOB *)pBlob;
|
||||||
|
PBYTE pModulus = pBlob + sizeof(BCRYPT_DH_KEY_BLOB);
|
||||||
|
PBYTE pGenerator = pModulus + pKeyBlob->cbKey;
|
||||||
|
PBYTE pPublic = pGenerator + pKeyBlob->cbKey;
|
||||||
|
|
||||||
|
pCtx->hAlg = hAlg;
|
||||||
|
pCtx->hPrivate = hPriv;
|
||||||
|
memcpy(pCtx->pub, pPublic, pCtx->size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
error:
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sshdh_new_group_hex(const char *gen, const char *modulus, struct sshdh **dhp)
|
||||||
|
{
|
||||||
|
struct sshdh *ret;
|
||||||
|
struct sshbn * g = NULL;
|
||||||
|
struct sshbn * p = NULL;
|
||||||
|
|
||||||
|
sshbn_from_hex(gen, &g);
|
||||||
|
sshbn_from_hex(modulus, &p);
|
||||||
|
*dhp = NULL;
|
||||||
|
|
||||||
|
ret = sshdh_new_group(g, p);
|
||||||
|
|
||||||
|
if (g != NULL)
|
||||||
|
sshbn_free(g);
|
||||||
|
if (p != NULL)
|
||||||
|
sshbn_free(p);
|
||||||
|
|
||||||
|
*dhp = ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* XXX transfers ownership of gen, modulus */
|
||||||
|
struct sshdh *
|
||||||
|
sshdh_new_group(struct sshbn *gen, struct sshbn *modulus)
|
||||||
|
{
|
||||||
|
struct sshdh *dh;
|
||||||
|
|
||||||
|
PBYTE pBlob = NULL;
|
||||||
|
DWORD keysize = sshbn_bytes(modulus);
|
||||||
|
DWORD cbBlob = 0;
|
||||||
|
|
||||||
|
dh = sshdh_new();
|
||||||
|
|
||||||
|
pBlob = (PBYTE)malloc(sizeof(CNG_DH_CTX) + (3 * keysize));
|
||||||
|
memset(pBlob, 0, sizeof(CNG_DH_CTX) + (3 * keysize));
|
||||||
|
|
||||||
|
CNG_DH_CTX * pCtx = (CNG_DH_CTX *)pBlob;
|
||||||
|
|
||||||
|
pCtx->size = keysize;
|
||||||
|
pCtx->p = pBlob + sizeof(CNG_DH_CTX);
|
||||||
|
pCtx->g = pCtx->p + keysize;
|
||||||
|
pCtx->pub = pCtx->g + keysize;
|
||||||
|
|
||||||
|
sshbn_to(gen, pCtx->g + keysize - sshbn_bytes(gen));
|
||||||
|
sshbn_to(modulus, pCtx->p + keysize - sshbn_bytes(modulus));
|
||||||
|
|
||||||
|
dh->dh = pCtx;
|
||||||
|
return dh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,471 +0,0 @@
|
|||||||
|
|
||||||
#include <cng_kex.h>
|
|
||||||
#include <dh.h>
|
|
||||||
|
|
||||||
typedef struct dh_st DH;
|
|
||||||
|
|
||||||
char *strdelim(char **);
|
|
||||||
char *strsep(char **stringp, const char *delim);
|
|
||||||
// Prime in big-endian format. (Group 2 prime from RFC 2409)
|
|
||||||
static
|
|
||||||
const
|
|
||||||
BYTE OakleyGroup2P[] =
|
|
||||||
{
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F,
|
|
||||||
0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B,
|
|
||||||
0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67,
|
|
||||||
0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
|
|
||||||
0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95,
|
|
||||||
0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
|
|
||||||
0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51,
|
|
||||||
0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
|
|
||||||
0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF,
|
|
||||||
0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB,
|
|
||||||
0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B,
|
|
||||||
0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generator in big-endian format. (Group 2 generator from RFC 2409)
|
|
||||||
static
|
|
||||||
const
|
|
||||||
BYTE OakleyGroup2G[] =
|
|
||||||
{
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Prime in big-endian format. (Group 14 prime from RFC 3526)
|
|
||||||
static
|
|
||||||
const
|
|
||||||
BYTE OakleyGroup14P[] =
|
|
||||||
{
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
|
|
||||||
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
|
|
||||||
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
|
|
||||||
0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
|
|
||||||
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
|
|
||||||
0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
|
|
||||||
0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
|
|
||||||
0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
|
|
||||||
0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
|
|
||||||
0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
|
|
||||||
0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generator in big-endian format. (Group 14 generator from RFC 3526)
|
|
||||||
static
|
|
||||||
const
|
|
||||||
BYTE OakleyGroup14G[] =
|
|
||||||
{
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DH *
|
|
||||||
cng_dh_new_group_asc(const char *gen, const char *modulus)
|
|
||||||
{
|
|
||||||
|
|
||||||
DH *dh;
|
|
||||||
BIGNUM * p = NULL;
|
|
||||||
BIGNUM * g = NULL;
|
|
||||||
|
|
||||||
if (BN_hex2bn(&p, modulus) == 0 ||
|
|
||||||
BN_hex2bn(&g, gen) == 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
dh = cng_dh_new_group(g, p);
|
|
||||||
BN_free(p);
|
|
||||||
BN_free(g);
|
|
||||||
return dh;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int cng_kex_supported(void)
|
|
||||||
{
|
|
||||||
return IsWindows8Point1OrGreater() == FALSE ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
DH * cng_setup_group1(void)
|
|
||||||
{
|
|
||||||
PBYTE pBlob = NULL;
|
|
||||||
DWORD keysize = sizeof(OakleyGroup2P);
|
|
||||||
DWORD cbBlob = 0;
|
|
||||||
|
|
||||||
pBlob = (PBYTE)malloc(sizeof(CNG_DH_CTX) + (3 * keysize));
|
|
||||||
memset(pBlob, 0, sizeof(CNG_DH_CTX) + (3 * keysize));
|
|
||||||
|
|
||||||
CNG_DH_CTX * pCtx = (CNG_DH_CTX *)pBlob;
|
|
||||||
|
|
||||||
pCtx->size = keysize;
|
|
||||||
pCtx->p = pBlob + sizeof(CNG_DH_CTX);
|
|
||||||
pCtx->g = pCtx->p + keysize;
|
|
||||||
pCtx->pub = pCtx->g + keysize;
|
|
||||||
|
|
||||||
memcpy(pCtx->p, OakleyGroup2P, sizeof(OakleyGroup2P));
|
|
||||||
memcpy(pCtx->g, OakleyGroup2G, sizeof(OakleyGroup2G));
|
|
||||||
|
|
||||||
return (DH *)pCtx;
|
|
||||||
}
|
|
||||||
|
|
||||||
DH * cng_setup_group14(void)
|
|
||||||
{
|
|
||||||
PBYTE pBlob = NULL;
|
|
||||||
DWORD keysize = sizeof(OakleyGroup14P);
|
|
||||||
DWORD cbBlob = 0;
|
|
||||||
|
|
||||||
pBlob = (PBYTE)malloc(sizeof(CNG_DH_CTX) + (3 * keysize));
|
|
||||||
memset(pBlob, 0, sizeof(CNG_DH_CTX) + (3 * keysize));
|
|
||||||
|
|
||||||
CNG_DH_CTX * pCtx = (CNG_DH_CTX *)pBlob;
|
|
||||||
|
|
||||||
pCtx->size = keysize;
|
|
||||||
pCtx->p = pBlob + sizeof(CNG_DH_CTX);
|
|
||||||
pCtx->g = pCtx->p + keysize;
|
|
||||||
pCtx->pub = pCtx->g + keysize;
|
|
||||||
|
|
||||||
memcpy(pCtx->p, OakleyGroup14P, sizeof(OakleyGroup14P));
|
|
||||||
memcpy(pCtx->g, OakleyGroup14G, sizeof(OakleyGroup14G));
|
|
||||||
|
|
||||||
return (DH *)pCtx;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//function to reverse the order of a byte array
|
|
||||||
static void
|
|
||||||
cng_dh_swap_bytes(void *pv, size_t n)
|
|
||||||
{
|
|
||||||
char *p = (char*)pv;
|
|
||||||
size_t lo, hi;
|
|
||||||
for (lo = 0, hi = n - 1; hi>lo; lo++, hi--)
|
|
||||||
{
|
|
||||||
char tmp = p[lo];
|
|
||||||
p[lo] = p[hi];
|
|
||||||
p[hi] = tmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// import a bignum public key
|
|
||||||
BCRYPT_KEY_HANDLE
|
|
||||||
cng_dh_set_remote_pub_key(DH * dh, BIGNUM * b)
|
|
||||||
{
|
|
||||||
BCRYPT_KEY_HANDLE hPub = NULL;
|
|
||||||
CNG_DH_CTX *pCtx = (CNG_DH_CTX *)dh;
|
|
||||||
|
|
||||||
if (BN_num_bytes(b) > pCtx->size)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
DWORD cbBlob = sizeof(BCRYPT_DH_KEY_BLOB) + (pCtx->size * 3);
|
|
||||||
|
|
||||||
PBYTE pBlob = (PBYTE)malloc(cbBlob);
|
|
||||||
|
|
||||||
BCRYPT_DH_KEY_BLOB *pKeyBlob = (BCRYPT_DH_KEY_BLOB *)pBlob;
|
|
||||||
pKeyBlob->cbKey = pCtx->size;
|
|
||||||
pKeyBlob->dwMagic = BCRYPT_DH_PUBLIC_MAGIC;
|
|
||||||
PBYTE pModulus = pBlob + sizeof(BCRYPT_DH_KEY_BLOB);
|
|
||||||
PBYTE pGenerator = pModulus + pKeyBlob->cbKey;
|
|
||||||
PBYTE pPublic = pGenerator + pKeyBlob->cbKey;
|
|
||||||
|
|
||||||
memcpy(pModulus, pCtx->p, pCtx->size);
|
|
||||||
memcpy(pGenerator, pCtx->g, pCtx->size);
|
|
||||||
memset(pPublic, 0, pCtx->size);
|
|
||||||
|
|
||||||
BN_bn2bin(b, pPublic + pCtx->size - BN_num_bytes(b));
|
|
||||||
|
|
||||||
|
|
||||||
HRESULT Status = 0;
|
|
||||||
|
|
||||||
if (S_OK != (Status = BCryptImportKeyPair(pCtx->hAlg, NULL, BCRYPT_DH_PUBLIC_BLOB, &hPub, pBlob, cbBlob, 0)))
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
if (pBlob) free(pBlob);
|
|
||||||
|
|
||||||
return hPub;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// generate KEX private/public keys for this side
|
|
||||||
int
|
|
||||||
cng_dh_gen_key(DH *dh, int need)
|
|
||||||
{
|
|
||||||
HRESULT Status;
|
|
||||||
BCRYPT_DH_PARAMETER_HEADER *DhParamHdrPointer = NULL;
|
|
||||||
DWORD DhParamBlobLength = 0;
|
|
||||||
PBYTE DhParamBlob = NULL;
|
|
||||||
BCRYPT_ALG_HANDLE hAlg = NULL;
|
|
||||||
PBYTE pBlob = NULL;
|
|
||||||
BCRYPT_KEY_HANDLE hPriv = NULL;
|
|
||||||
CNG_DH_CTX *pCtx = (CNG_DH_CTX *)dh;
|
|
||||||
DWORD cbBlob = 0;
|
|
||||||
|
|
||||||
|
|
||||||
DhParamBlobLength = sizeof(BCRYPT_DH_PARAMETER_HEADER) + (pCtx->size * 2);
|
|
||||||
|
|
||||||
if (NULL == (DhParamBlob = (PBYTE)malloc(DhParamBlobLength)))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
DhParamHdrPointer = (BCRYPT_DH_PARAMETER_HEADER *)DhParamBlob;
|
|
||||||
DhParamHdrPointer->cbLength = DhParamBlobLength;
|
|
||||||
DhParamHdrPointer->cbKeyLength = pCtx->size;
|
|
||||||
DhParamHdrPointer->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC;
|
|
||||||
|
|
||||||
memcpy(DhParamBlob + sizeof(BCRYPT_DH_PARAMETER_HEADER), pCtx->p, pCtx->size);
|
|
||||||
memcpy(DhParamBlob + sizeof(BCRYPT_DH_PARAMETER_HEADER) + pCtx->size, pCtx->g, pCtx->size);
|
|
||||||
|
|
||||||
if (S_OK != (Status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_DH_ALGORITHM, NULL, 0)))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
|
|
||||||
if (S_OK != (Status = BCryptGenerateKeyPair(hAlg, &hPriv, pCtx->size * 8, 0)))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
|
|
||||||
if (S_OK != (Status = BCryptSetProperty(hPriv, BCRYPT_DH_PARAMETERS, DhParamBlob, DhParamBlobLength, 0)))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (S_OK != (Status = BCryptFinalizeKeyPair(hPriv, 0)))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (S_OK != (Status = BCryptExportKey(hPriv, NULL, BCRYPT_DH_PUBLIC_BLOB, NULL, 0, &cbBlob, 0)))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (NULL == (pBlob = (PBYTE)malloc(cbBlob)))
|
|
||||||
{
|
|
||||||
Status = STATUS_NO_MEMORY;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (S_OK != (Status = BCryptExportKey(hPriv, NULL, BCRYPT_DH_PUBLIC_BLOB, pBlob, cbBlob, &cbBlob, 0)))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BCRYPT_DH_KEY_BLOB *pKeyBlob = (BCRYPT_DH_KEY_BLOB *)pBlob;
|
|
||||||
PBYTE pModulus = pBlob + sizeof(BCRYPT_DH_KEY_BLOB);
|
|
||||||
PBYTE pGenerator = pModulus + pKeyBlob->cbKey;
|
|
||||||
PBYTE pPublic = pGenerator + pKeyBlob->cbKey;
|
|
||||||
|
|
||||||
pCtx->hAlg = hAlg;
|
|
||||||
pCtx->hPrivate = hPriv;
|
|
||||||
memcpy(pCtx->pub, pPublic, pCtx->size);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
error:
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BIGNUM *
|
|
||||||
cng_dh_export_secret(DH * dh, BCRYPT_SECRET_HANDLE hSecret)
|
|
||||||
{
|
|
||||||
HRESULT hr = S_OK;
|
|
||||||
CNG_DH_CTX *pCtx = (CNG_DH_CTX *)dh;
|
|
||||||
PBYTE pSecret = (PBYTE)malloc(pCtx->size);
|
|
||||||
DWORD pSecret_len = pCtx->size;
|
|
||||||
DWORD size = pCtx->size;
|
|
||||||
BIGNUM * bn = NULL;
|
|
||||||
|
|
||||||
hr = BCryptDeriveKey(hSecret, L"TRUNCATE", NULL, pSecret, pSecret_len, &size, 0);
|
|
||||||
if (S_OK == hr)
|
|
||||||
{
|
|
||||||
cng_dh_swap_bytes(pSecret, size);
|
|
||||||
bn = BN_bin2bn(pSecret, size, NULL);
|
|
||||||
memset(pSecret, 0, size);
|
|
||||||
free(pSecret);
|
|
||||||
}
|
|
||||||
return bn;
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate and return secret as a bignum
|
|
||||||
BIGNUM *
|
|
||||||
cng_dh_get_secret(DH * dh, BIGNUM * remote_pub)
|
|
||||||
{
|
|
||||||
HRESULT hr = S_OK;
|
|
||||||
CNG_DH_CTX *pCtx = (CNG_DH_CTX *)dh;
|
|
||||||
PBYTE pSecret = (PBYTE)malloc(pCtx->size);
|
|
||||||
DWORD pSecret_len = pCtx->size;
|
|
||||||
DWORD size = pCtx->size;
|
|
||||||
BIGNUM * bn = NULL;
|
|
||||||
BCRYPT_SECRET_HANDLE hSecret;
|
|
||||||
|
|
||||||
BCRYPT_KEY_HANDLE hRemotePub = cng_dh_set_remote_pub_key(dh, remote_pub);
|
|
||||||
if (hRemotePub != NULL)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (S_OK == (hr = BCryptSecretAgreement(pCtx->hPrivate, hRemotePub, &hSecret, 0)))
|
|
||||||
{
|
|
||||||
|
|
||||||
hr = BCryptDeriveKey(hSecret, L"TRUNCATE", NULL, pSecret, pSecret_len, &size, 0);
|
|
||||||
if (S_OK == hr)
|
|
||||||
{
|
|
||||||
cng_dh_swap_bytes(pSecret, size);
|
|
||||||
bn = BN_bin2bn(pSecret, size, NULL);
|
|
||||||
memset(pSecret, 0, size);
|
|
||||||
free(pSecret);
|
|
||||||
}
|
|
||||||
BCryptDestroySecret(hSecret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bn;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// return our public key as a bignum (for sending to the other side)
|
|
||||||
BIGNUM *
|
|
||||||
cng_dh_get_local_pub_key(DH * dh)
|
|
||||||
{
|
|
||||||
CNG_DH_CTX *pCtx = (CNG_DH_CTX *)dh;
|
|
||||||
BIGNUM * bn = NULL;
|
|
||||||
|
|
||||||
bn = BN_bin2bn(pCtx->pub, pCtx->size, NULL);
|
|
||||||
|
|
||||||
return bn;
|
|
||||||
}
|
|
||||||
|
|
||||||
BIGNUM *
|
|
||||||
cng_dh_get_p(DH * dh)
|
|
||||||
{
|
|
||||||
CNG_DH_CTX *pCtx = (CNG_DH_CTX *)dh;
|
|
||||||
BIGNUM * bn = NULL;
|
|
||||||
|
|
||||||
bn = BN_bin2bn(pCtx->p, pCtx->size, NULL);
|
|
||||||
return bn;
|
|
||||||
}
|
|
||||||
|
|
||||||
BIGNUM *
|
|
||||||
cng_dh_get_g(DH * dh)
|
|
||||||
{
|
|
||||||
CNG_DH_CTX *pCtx = (CNG_DH_CTX *)dh;
|
|
||||||
BIGNUM * bn = NULL;
|
|
||||||
|
|
||||||
bn = BN_bin2bn(pCtx->g, pCtx->size, NULL);
|
|
||||||
return bn;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
cng_dh_pub_is_valid(DH * dh, BIGNUM *dh_pub)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int n = BN_num_bits(dh_pub);
|
|
||||||
int bits_set = 0;
|
|
||||||
BIGNUM *tmp;
|
|
||||||
BIGNUM *p = cng_dh_get_p(dh);
|
|
||||||
|
|
||||||
if (NULL == p) {
|
|
||||||
logit("invalid DH prime value");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (dh_pub->neg) {
|
|
||||||
logit("invalid public DH value: negative");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (BN_cmp(dh_pub, BN_value_one()) != 1) { /* pub_exp <= 1 */
|
|
||||||
logit("invalid public DH value: <= 1");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((tmp = BN_new()) == NULL) {
|
|
||||||
error("%s: BN_new failed", __FUNCTION__);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (!BN_sub(tmp, p, BN_value_one()) ||
|
|
||||||
BN_cmp(dh_pub, tmp) != -1) { /* pub_exp > p-2 */
|
|
||||||
BN_clear_free(tmp);
|
|
||||||
logit("invalid public DH value: >= p-1");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
BN_clear_free(tmp);
|
|
||||||
|
|
||||||
for (i = 0; i <= n; i++)
|
|
||||||
if (BN_is_bit_set(dh_pub, i))
|
|
||||||
bits_set++;
|
|
||||||
debug2("bits set: %d/%d", bits_set, BN_num_bits(p));
|
|
||||||
|
|
||||||
/* if g==2 and bits_set==1 then computing log_g(dh_pub) is trivial */
|
|
||||||
if (bits_set > 1)
|
|
||||||
{
|
|
||||||
BN_clear_free(p);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
logit("invalid public DH value (%d/%d)", bits_set, BN_num_bits(p));
|
|
||||||
BN_clear_free(p);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
DH *
|
|
||||||
cng_dh_new_group(BIGNUM *gen, BIGNUM *modulus)
|
|
||||||
{
|
|
||||||
|
|
||||||
PBYTE pBlob = NULL;
|
|
||||||
DWORD keysize = BN_num_bytes(modulus);;
|
|
||||||
DWORD cbBlob = 0;
|
|
||||||
|
|
||||||
|
|
||||||
pBlob = (PBYTE)malloc(sizeof(CNG_DH_CTX) + (3 * keysize));
|
|
||||||
memset(pBlob, 0, sizeof(CNG_DH_CTX) + (3 * keysize));
|
|
||||||
|
|
||||||
CNG_DH_CTX * pCtx = (CNG_DH_CTX *)pBlob;
|
|
||||||
|
|
||||||
pCtx->size = keysize;
|
|
||||||
pCtx->p = pBlob + sizeof(CNG_DH_CTX);
|
|
||||||
pCtx->g = pCtx->p + keysize;
|
|
||||||
pCtx->pub = pCtx->g + keysize;
|
|
||||||
|
|
||||||
BN_bn2bin(gen, pCtx->g + keysize - BN_num_bytes(gen));
|
|
||||||
BN_bn2bin(modulus, pCtx->p + keysize - BN_num_bytes(modulus));
|
|
||||||
|
|
||||||
return (DH *)pCtx;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cng_dh_free_context(DH * dh)
|
|
||||||
{
|
|
||||||
CNG_DH_CTX * pCtx = (CNG_DH_CTX*)dh;
|
|
||||||
|
|
||||||
if (pCtx->hAlg)
|
|
||||||
BCryptCloseAlgorithmProvider(pCtx->hAlg, 0);
|
|
||||||
|
|
||||||
if (pCtx->hPrivate)
|
|
||||||
BCryptDestroyKey(pCtx->hPrivate);
|
|
||||||
|
|
||||||
free(pCtx);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <bcrypt.h>
|
|
||||||
#include <openssl/bn.h>
|
|
||||||
#include <VersionHelpers.h>
|
|
||||||
// CNG Diffie-hellman Kex context
|
|
||||||
typedef struct cng_dh_ctx {
|
|
||||||
int size;
|
|
||||||
PBYTE g;
|
|
||||||
PBYTE p;
|
|
||||||
PBYTE pub;
|
|
||||||
BCRYPT_KEY_HANDLE hPrivate;
|
|
||||||
BCRYPT_ALG_HANDLE hAlg;
|
|
||||||
} CNG_DH_CTX;
|
|
||||||
|
|
||||||
|
|
||||||
DH * cng_setup_group1(void);
|
|
||||||
DH * cng_setup_group14(void);
|
|
||||||
DH * cng_dh_new_group(BIGNUM *gen, BIGNUM *modulus);
|
|
||||||
BCRYPT_KEY_HANDLE cng_dh_set_remote_pub_key(DH *pCtx, BIGNUM * b);
|
|
||||||
int cng_dh_gen_key(DH *dh, int need);
|
|
||||||
BIGNUM * cng_dh_get_secret(DH * pCtx, BIGNUM * remote_pub);
|
|
||||||
BIGNUM * cng_dh_get_local_pub_key(DH * dh);
|
|
||||||
BIGNUM * cng_dh_get_p(DH * dh);
|
|
||||||
BIGNUM * cng_dh_get_g(DH * dh);
|
|
||||||
int cng_dh_pub_is_valid(DH * dh, BIGNUM *dh_pub);
|
|
||||||
void cng_dh_swap_bytes(void *pv, size_t n);
|
|
||||||
int cng_kex_supported(void);
|
|
||||||
DH * cng_dh_new_group_asc(const char *gen, const char *modulus);
|
|
||||||
void cng_dh_free_context(DH * dh);
|
|
@ -1,195 +0,0 @@
|
|||||||
/*
|
|
||||||
* cng implementation of Diffie-Hellman Key Exchange for Oakley groups 2 and 14 (ssh group1 and group14)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "includes.h"
|
|
||||||
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <openssl/dh.h>
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include "sshkey.h"
|
|
||||||
#include "cipher.h"
|
|
||||||
#include "digest.h"
|
|
||||||
#include "kex.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "packet.h"
|
|
||||||
#include "dh.h"
|
|
||||||
#include "ssh2.h"
|
|
||||||
#include "dispatch.h"
|
|
||||||
#include "compat.h"
|
|
||||||
#include "ssherr.h"
|
|
||||||
#include "sshbuf.h"
|
|
||||||
|
|
||||||
#include <bcrypt.h>
|
|
||||||
#include <cng_kex.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int cng_input_kex_dh(int, u_int32_t, void *);
|
|
||||||
|
|
||||||
int
|
|
||||||
cng_kexdh_client(struct ssh *ssh)
|
|
||||||
{
|
|
||||||
|
|
||||||
// switch to standard openssl version if not supported
|
|
||||||
if (!cng_kex_supported())
|
|
||||||
return kexdh_client(ssh);
|
|
||||||
|
|
||||||
|
|
||||||
struct kex *kex = ssh->kex;
|
|
||||||
int r;
|
|
||||||
BIGNUM * pub_key;
|
|
||||||
/* generate and send 'e', client DH public key */
|
|
||||||
switch (kex->kex_type) {
|
|
||||||
case KEX_DH_GRP1_SHA1:
|
|
||||||
kex->dh = cng_setup_group1();
|
|
||||||
break;
|
|
||||||
case KEX_DH_GRP14_SHA1:
|
|
||||||
kex->dh = cng_setup_group14();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
r = SSH_ERR_INVALID_ARGUMENT;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (kex->dh == NULL) {
|
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
debug("sending SSH2_MSG_KEXDH_INIT");
|
|
||||||
if ((r = cng_dh_gen_key(kex->dh, kex->we_need * 8)) != 0 ||
|
|
||||||
(pub_key = cng_dh_get_local_pub_key(kex->dh)) == 0 ||
|
|
||||||
(r = sshpkt_start(ssh, SSH2_MSG_KEXDH_INIT)) != 0 ||
|
|
||||||
(r = sshpkt_put_bignum2(ssh, pub_key)) != 0 ||
|
|
||||||
(r = sshpkt_send(ssh)) != 0)
|
|
||||||
goto out;
|
|
||||||
#ifdef DEBUG_KEXDH
|
|
||||||
DHparams_print_fp(stderr, kex->dh);
|
|
||||||
fprintf(stderr, "pub= ");
|
|
||||||
BN_print_fp(stderr, kex->dh->pub_key);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
#endif
|
|
||||||
debug("expecting SSH2_MSG_KEXDH_REPLY");
|
|
||||||
ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_REPLY, &cng_input_kex_dh);
|
|
||||||
r = 0;
|
|
||||||
out:
|
|
||||||
if (pub_key)
|
|
||||||
BN_free(pub_key);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
cng_input_kex_dh(int type, u_int32_t seq, void *ctxt)
|
|
||||||
{
|
|
||||||
struct ssh *ssh = ctxt;
|
|
||||||
struct kex *kex = ssh->kex;
|
|
||||||
CNG_DH_CTX *pCtx = (CNG_DH_CTX *)kex->dh;
|
|
||||||
BIGNUM *dh_server_pub = NULL, *shared_secret = NULL,*dh_local_pub=NULL;
|
|
||||||
struct sshkey *server_host_key = NULL;
|
|
||||||
u_char *kbuf = NULL, *server_host_key_blob = NULL, *signature = NULL;
|
|
||||||
u_char hash[SSH_DIGEST_MAX_LENGTH];
|
|
||||||
size_t klen = 0, slen, sbloblen, hashlen;
|
|
||||||
int kout, r;
|
|
||||||
|
|
||||||
if (kex->verify_host_key == NULL) {
|
|
||||||
r = SSH_ERR_INVALID_ARGUMENT;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
/* key, cert */
|
|
||||||
if ((r = sshpkt_get_string(ssh, &server_host_key_blob,
|
|
||||||
&sbloblen)) != 0 ||
|
|
||||||
(r = sshkey_from_blob(server_host_key_blob, sbloblen,
|
|
||||||
&server_host_key)) != 0)
|
|
||||||
goto out;
|
|
||||||
if (server_host_key->type != kex->hostkey_type ||
|
|
||||||
(kex->hostkey_type == KEY_ECDSA &&
|
|
||||||
server_host_key->ecdsa_nid != kex->hostkey_nid)) {
|
|
||||||
r = SSH_ERR_KEY_TYPE_MISMATCH;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (kex->verify_host_key(server_host_key, ssh) == -1) {
|
|
||||||
r = SSH_ERR_SIGNATURE_INVALID;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
/* DH parameter f, server public DH key */
|
|
||||||
if ((dh_server_pub = BN_new()) == NULL) {
|
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
/* signed H */
|
|
||||||
if ((r = sshpkt_get_bignum2(ssh, dh_server_pub)) != 0 ||
|
|
||||||
(r = sshpkt_get_string(ssh, &signature, &slen)) != 0 ||
|
|
||||||
(r = sshpkt_get_end(ssh)) != 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (!cng_dh_pub_is_valid(kex->dh, dh_server_pub)) {
|
|
||||||
sshpkt_disconnect(ssh, "bad server public DH value");
|
|
||||||
r = SSH_ERR_MESSAGE_INCOMPLETE;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_secret = cng_dh_get_secret(kex->dh, dh_server_pub);
|
|
||||||
if (shared_secret == NULL)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
dh_local_pub = cng_dh_get_local_pub_key(kex->dh);
|
|
||||||
|
|
||||||
/* calc and verify H */
|
|
||||||
hashlen = sizeof(hash);
|
|
||||||
if ((r = kex_dh_hash(
|
|
||||||
kex->client_version_string,
|
|
||||||
kex->server_version_string,
|
|
||||||
sshbuf_ptr(kex->my), sshbuf_len(kex->my),
|
|
||||||
sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
|
|
||||||
server_host_key_blob, sbloblen,
|
|
||||||
dh_local_pub,
|
|
||||||
dh_server_pub,
|
|
||||||
shared_secret,
|
|
||||||
hash, &hashlen)) != 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if ((r = sshkey_verify(server_host_key, signature, slen, hash, hashlen,
|
|
||||||
ssh->compat)) != 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* save session id */
|
|
||||||
if (kex->session_id == NULL) {
|
|
||||||
kex->session_id_len = hashlen;
|
|
||||||
kex->session_id = malloc(kex->session_id_len);
|
|
||||||
if (kex->session_id == NULL) {
|
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
memcpy(kex->session_id, hash, kex->session_id_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
|
|
||||||
r = kex_send_newkeys(ssh);
|
|
||||||
out:
|
|
||||||
explicit_bzero(hash, sizeof(hash));
|
|
||||||
cng_dh_free_context(kex->dh);
|
|
||||||
kex->dh = NULL;
|
|
||||||
if (dh_server_pub)
|
|
||||||
BN_clear_free(dh_server_pub);
|
|
||||||
if (dh_local_pub)
|
|
||||||
BN_clear_free(dh_local_pub);
|
|
||||||
if (kbuf) {
|
|
||||||
explicit_bzero(kbuf, klen);
|
|
||||||
free(kbuf);
|
|
||||||
}
|
|
||||||
if (shared_secret)
|
|
||||||
BN_clear_free(shared_secret);
|
|
||||||
sshkey_free(server_host_key);
|
|
||||||
free(server_host_key_blob);
|
|
||||||
free(signature);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
@ -1,198 +0,0 @@
|
|||||||
|
|
||||||
#include "includes.h"
|
|
||||||
|
|
||||||
#ifdef WIN32_FIXME
|
|
||||||
#undef GSSAPI
|
|
||||||
#undef KRB5
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef WITH_OPENSSL
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include <openssl/dh.h>
|
|
||||||
|
|
||||||
#include "sshkey.h"
|
|
||||||
#include "cipher.h"
|
|
||||||
#include "digest.h"
|
|
||||||
#include "kex.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "packet.h"
|
|
||||||
#include "dh.h"
|
|
||||||
#include "ssh2.h"
|
|
||||||
|
|
||||||
#include "dispatch.h"
|
|
||||||
#include "compat.h"
|
|
||||||
#include "ssherr.h"
|
|
||||||
#include "sshbuf.h"
|
|
||||||
#include <bcrypt.h>
|
|
||||||
#include <cng_kex.h>
|
|
||||||
|
|
||||||
|
|
||||||
static int cng_input_kex_dh_init(int, u_int32_t, void *);
|
|
||||||
|
|
||||||
int
|
|
||||||
cng_kexdh_server(struct ssh *ssh)
|
|
||||||
{
|
|
||||||
if (!cng_kex_supported())
|
|
||||||
return kexdh_server(ssh);
|
|
||||||
|
|
||||||
|
|
||||||
struct kex *kex = ssh->kex;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
/* generate server DH public key */
|
|
||||||
switch (kex->kex_type) {
|
|
||||||
case KEX_DH_GRP1_SHA1:
|
|
||||||
kex->dh = cng_setup_group1();
|
|
||||||
break;
|
|
||||||
case KEX_DH_GRP14_SHA1:
|
|
||||||
kex->dh = cng_setup_group14();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
r = SSH_ERR_INVALID_ARGUMENT;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (kex->dh == NULL) {
|
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if ((r = cng_dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
debug("expecting SSH2_MSG_KEXDH_INIT");
|
|
||||||
ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_INIT, &cng_input_kex_dh_init);
|
|
||||||
r = 0;
|
|
||||||
out:
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
cng_input_kex_dh_init(int type, u_int32_t seq, void *ctxt)
|
|
||||||
{
|
|
||||||
struct ssh *ssh = ctxt;
|
|
||||||
struct kex *kex = ssh->kex;
|
|
||||||
BIGNUM *shared_secret = NULL, *dh_client_pub = NULL, *dh_local_pub = NULL;
|
|
||||||
struct sshkey *server_host_public, *server_host_private;
|
|
||||||
u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL;
|
|
||||||
u_char hash[SSH_DIGEST_MAX_LENGTH];
|
|
||||||
size_t sbloblen, slen;
|
|
||||||
size_t klen = 0, hashlen;
|
|
||||||
int kout, r;
|
|
||||||
|
|
||||||
if (kex->load_host_public_key == NULL ||
|
|
||||||
kex->load_host_private_key == NULL) {
|
|
||||||
r = SSH_ERR_INVALID_ARGUMENT;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
server_host_public = kex->load_host_public_key(kex->hostkey_type,
|
|
||||||
kex->hostkey_nid, ssh);
|
|
||||||
server_host_private = kex->load_host_private_key(kex->hostkey_type,
|
|
||||||
kex->hostkey_nid, ssh);
|
|
||||||
if (server_host_public == NULL) {
|
|
||||||
r = SSH_ERR_NO_HOSTKEY_LOADED;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* key, cert */
|
|
||||||
if ((dh_client_pub = BN_new()) == NULL) {
|
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if ((r = sshpkt_get_bignum2(ssh, dh_client_pub)) != 0 ||
|
|
||||||
(r = sshpkt_get_end(ssh)) != 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
#ifdef DEBUG_KEXDH
|
|
||||||
fprintf(stderr, "dh_client_pub= ");
|
|
||||||
BN_print_fp(stderr, dh_client_pub);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
debug("bits %d", BN_num_bits(dh_client_pub));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef DEBUG_KEXDH
|
|
||||||
DHparams_print_fp(stderr, kex->dh);
|
|
||||||
fprintf(stderr, "pub= ");
|
|
||||||
BN_print_fp(stderr, kex->dh->pub_key);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
#endif
|
|
||||||
if (!cng_dh_pub_is_valid(kex->dh, dh_client_pub)) {
|
|
||||||
sshpkt_disconnect(ssh, "bad client public DH value");
|
|
||||||
r = SSH_ERR_MESSAGE_INCOMPLETE;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_secret = cng_dh_get_secret(kex->dh, dh_client_pub);
|
|
||||||
if (shared_secret == NULL)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
#ifdef DEBUG_KEXDH
|
|
||||||
dump_digest("shared secret", kbuf, kout);
|
|
||||||
#endif
|
|
||||||
if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob,
|
|
||||||
&sbloblen)) != 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
dh_local_pub = cng_dh_get_local_pub_key(kex->dh);
|
|
||||||
|
|
||||||
/* calc H */
|
|
||||||
hashlen = sizeof(hash);
|
|
||||||
if ((r = kex_dh_hash(
|
|
||||||
kex->client_version_string,
|
|
||||||
kex->server_version_string,
|
|
||||||
sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
|
|
||||||
sshbuf_ptr(kex->my), sshbuf_len(kex->my),
|
|
||||||
server_host_key_blob, sbloblen,
|
|
||||||
dh_client_pub,
|
|
||||||
dh_local_pub,
|
|
||||||
shared_secret,
|
|
||||||
hash, &hashlen)) != 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* save session id := H */
|
|
||||||
if (kex->session_id == NULL) {
|
|
||||||
kex->session_id_len = hashlen;
|
|
||||||
kex->session_id = malloc(kex->session_id_len);
|
|
||||||
if (kex->session_id == NULL) {
|
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
memcpy(kex->session_id, hash, kex->session_id_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* sign H */
|
|
||||||
if ((r = kex->sign(server_host_private, server_host_public,
|
|
||||||
&signature, &slen, hash, hashlen, ssh->compat)) < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* destroy_sensitive_data(); */
|
|
||||||
|
|
||||||
/* send server hostkey, DH pubkey 'f' and singed H */
|
|
||||||
if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_REPLY)) != 0 ||
|
|
||||||
(r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 ||
|
|
||||||
(r = sshpkt_put_bignum2(ssh, dh_local_pub)) != 0 || /* f */
|
|
||||||
(r = sshpkt_put_string(ssh, signature, slen)) != 0 ||
|
|
||||||
(r = sshpkt_send(ssh)) != 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
|
|
||||||
r = kex_send_newkeys(ssh);
|
|
||||||
out:
|
|
||||||
explicit_bzero(hash, sizeof(hash));
|
|
||||||
cng_dh_free_context(kex->dh);
|
|
||||||
kex->dh = NULL;
|
|
||||||
if (dh_client_pub)
|
|
||||||
BN_clear_free(dh_client_pub);
|
|
||||||
if (dh_local_pub)
|
|
||||||
BN_clear_free(dh_local_pub);
|
|
||||||
if (shared_secret)
|
|
||||||
BN_clear_free(shared_secret);
|
|
||||||
free(server_host_key_blob);
|
|
||||||
free(signature);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
#endif /* WITH_OPENSSL */
|
|
@ -1,283 +0,0 @@
|
|||||||
/* $OpenBSD: kexgexc.c,v 1.22 2015/05/26 23:23:40 dtucker Exp $ */
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2000 Niels Provos. All rights reserved.
|
|
||||||
* Copyright (c) 2001 Markus Friedl. All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
||||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
||||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
||||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
||||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
||||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "includes.h"
|
|
||||||
|
|
||||||
#ifdef WITH_OPENSSL
|
|
||||||
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <openssl/dh.h>
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include "sshkey.h"
|
|
||||||
#include "cipher.h"
|
|
||||||
#include "digest.h"
|
|
||||||
#include "kex.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "packet.h"
|
|
||||||
#include "dh.h"
|
|
||||||
#include "ssh2.h"
|
|
||||||
#include "compat.h"
|
|
||||||
#include "dispatch.h"
|
|
||||||
#include "ssherr.h"
|
|
||||||
#include "sshbuf.h"
|
|
||||||
#include <bcrypt.h>
|
|
||||||
#include <cng_kex.h>
|
|
||||||
|
|
||||||
static int cng_input_kex_dh_gex_group(int, u_int32_t, void *);
|
|
||||||
static int cng_input_kex_dh_gex_reply(int, u_int32_t, void *);
|
|
||||||
|
|
||||||
int
|
|
||||||
cng_kexgex_client(struct ssh *ssh)
|
|
||||||
{
|
|
||||||
// switch to standard openssl version if not supported
|
|
||||||
if (!cng_kex_supported())
|
|
||||||
return kexgex_client(ssh);
|
|
||||||
|
|
||||||
struct kex *kex = ssh->kex;
|
|
||||||
int r;
|
|
||||||
u_int nbits;
|
|
||||||
|
|
||||||
nbits = dh_estimate(kex->dh_need * 8);
|
|
||||||
|
|
||||||
kex->min = DH_GRP_MIN;
|
|
||||||
kex->max = DH_GRP_MAX;
|
|
||||||
kex->nbits = nbits;
|
|
||||||
if (datafellows & SSH_BUG_DHGEX_LARGE)
|
|
||||||
kex->nbits = MIN(kex->nbits, 4096);
|
|
||||||
/* New GEX request */
|
|
||||||
if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST)) != 0 ||
|
|
||||||
(r = sshpkt_put_u32(ssh, kex->min)) != 0 ||
|
|
||||||
(r = sshpkt_put_u32(ssh, kex->nbits)) != 0 ||
|
|
||||||
(r = sshpkt_put_u32(ssh, kex->max)) != 0 ||
|
|
||||||
(r = sshpkt_send(ssh)) != 0)
|
|
||||||
goto out;
|
|
||||||
debug("SSH2_MSG_KEX_DH_GEX_REQUEST(%u<%u<%u) sent",
|
|
||||||
kex->min, kex->nbits, kex->max);
|
|
||||||
#ifdef DEBUG_KEXDH
|
|
||||||
fprintf(stderr, "\nmin = %d, nbits = %d, max = %d\n",
|
|
||||||
kex->min, kex->nbits, kex->max);
|
|
||||||
#endif
|
|
||||||
ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP,
|
|
||||||
&cng_input_kex_dh_gex_group);
|
|
||||||
r = 0;
|
|
||||||
out:
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
cng_input_kex_dh_gex_group(int type, u_int32_t seq, void *ctxt)
|
|
||||||
{
|
|
||||||
struct ssh *ssh = ctxt;
|
|
||||||
struct kex *kex = ssh->kex;
|
|
||||||
BIGNUM *p = NULL, *g = NULL;
|
|
||||||
int r, bits;
|
|
||||||
BIGNUM * pub_key = NULL;
|
|
||||||
|
|
||||||
debug("got SSH2_MSG_KEX_DH_GEX_GROUP");
|
|
||||||
|
|
||||||
if ((p = BN_new()) == NULL ||
|
|
||||||
(g = BN_new()) == NULL) {
|
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if ((r = sshpkt_get_bignum2(ssh, p)) != 0 ||
|
|
||||||
(r = sshpkt_get_bignum2(ssh, g)) != 0 ||
|
|
||||||
(r = sshpkt_get_end(ssh)) != 0)
|
|
||||||
goto out;
|
|
||||||
if ((bits = BN_num_bits(p)) < 0 ||
|
|
||||||
(u_int)bits < kex->min || (u_int)bits > kex->max) {
|
|
||||||
r = SSH_ERR_DH_GEX_OUT_OF_RANGE;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if ((kex->dh = cng_dh_new_group(g, p)) == NULL) {
|
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
p = g = NULL; /* belong to kex->dh now */
|
|
||||||
|
|
||||||
/* generate and send 'e', client DH public key */
|
|
||||||
if ((r = cng_dh_gen_key(kex->dh, kex->we_need * 8)) != 0 ||
|
|
||||||
(pub_key = cng_dh_get_local_pub_key(kex->dh)) == 0 ||
|
|
||||||
(r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_INIT)) != 0 ||
|
|
||||||
(r = sshpkt_put_bignum2(ssh, pub_key)) != 0 ||
|
|
||||||
(r = sshpkt_send(ssh)) != 0)
|
|
||||||
goto out;
|
|
||||||
debug("SSH2_MSG_KEX_DH_GEX_INIT sent");
|
|
||||||
#ifdef DEBUG_KEXDH
|
|
||||||
DHparams_print_fp(stderr, kex->dh);
|
|
||||||
fprintf(stderr, "pub= ");
|
|
||||||
BN_print_fp(stderr, kex->dh->pub_key);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
#endif
|
|
||||||
ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP, NULL);
|
|
||||||
ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REPLY, &cng_input_kex_dh_gex_reply);
|
|
||||||
r = 0;
|
|
||||||
out:
|
|
||||||
if (p)
|
|
||||||
BN_clear_free(p);
|
|
||||||
if (g)
|
|
||||||
BN_clear_free(g);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
cng_input_kex_dh_gex_reply(int type, u_int32_t seq, void *ctxt)
|
|
||||||
{
|
|
||||||
struct ssh *ssh = ctxt;
|
|
||||||
struct kex *kex = ssh->kex;
|
|
||||||
CNG_DH_CTX *pCtx = (CNG_DH_CTX *)kex->dh;
|
|
||||||
BIGNUM *dh_server_pub = NULL, *shared_secret = NULL,*dh_local_pub = NULL,*p = NULL,*g = NULL;
|
|
||||||
struct sshkey *server_host_key = NULL;
|
|
||||||
u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL;
|
|
||||||
u_char hash[SSH_DIGEST_MAX_LENGTH];
|
|
||||||
size_t klen = 0, slen, sbloblen, hashlen;
|
|
||||||
int kout, r;
|
|
||||||
|
|
||||||
debug("got SSH2_MSG_KEX_DH_GEX_REPLY");
|
|
||||||
if (kex->verify_host_key == NULL) {
|
|
||||||
r = SSH_ERR_INVALID_ARGUMENT;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
/* key, cert */
|
|
||||||
if ((r = sshpkt_get_string(ssh, &server_host_key_blob,
|
|
||||||
&sbloblen)) != 0 ||
|
|
||||||
(r = sshkey_from_blob(server_host_key_blob, sbloblen,
|
|
||||||
&server_host_key)) != 0)
|
|
||||||
goto out;
|
|
||||||
if (server_host_key->type != kex->hostkey_type) {
|
|
||||||
r = SSH_ERR_KEY_TYPE_MISMATCH;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (server_host_key->type != kex->hostkey_type ||
|
|
||||||
(kex->hostkey_type == KEY_ECDSA &&
|
|
||||||
server_host_key->ecdsa_nid != kex->hostkey_nid)) {
|
|
||||||
r = SSH_ERR_KEY_TYPE_MISMATCH;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (kex->verify_host_key(server_host_key, ssh) == -1) {
|
|
||||||
r = SSH_ERR_SIGNATURE_INVALID;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
/* DH parameter f, server public DH key */
|
|
||||||
if ((dh_server_pub = BN_new()) == NULL) {
|
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
/* signed H */
|
|
||||||
if ((r = sshpkt_get_bignum2(ssh, dh_server_pub)) != 0 ||
|
|
||||||
(r = sshpkt_get_string(ssh, &signature, &slen)) != 0 ||
|
|
||||||
(r = sshpkt_get_end(ssh)) != 0)
|
|
||||||
goto out;
|
|
||||||
#ifdef DEBUG_KEXDH
|
|
||||||
fprintf(stderr, "dh_server_pub= ");
|
|
||||||
BN_print_fp(stderr, dh_server_pub);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
debug("bits %d", BN_num_bits(dh_server_pub));
|
|
||||||
#endif
|
|
||||||
if (!cng_dh_pub_is_valid(kex->dh, dh_server_pub)) {
|
|
||||||
sshpkt_disconnect(ssh, "bad server public DH value");
|
|
||||||
r = SSH_ERR_MESSAGE_INCOMPLETE;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_secret = cng_dh_get_secret(kex->dh, dh_server_pub);
|
|
||||||
if (shared_secret == NULL)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (ssh->compat & SSH_OLD_DHGEX)
|
|
||||||
kex->min = kex->max = -1;
|
|
||||||
|
|
||||||
dh_local_pub = cng_dh_get_local_pub_key(kex->dh);
|
|
||||||
p = cng_dh_get_p(kex->dh);
|
|
||||||
g = cng_dh_get_g(kex->dh);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* calc and verify H */
|
|
||||||
hashlen = sizeof(hash);
|
|
||||||
if ((r = kexgex_hash(
|
|
||||||
kex->hash_alg,
|
|
||||||
kex->client_version_string,
|
|
||||||
kex->server_version_string,
|
|
||||||
sshbuf_ptr(kex->my), sshbuf_len(kex->my),
|
|
||||||
sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
|
|
||||||
server_host_key_blob, sbloblen,
|
|
||||||
kex->min, kex->nbits, kex->max,
|
|
||||||
p, g,
|
|
||||||
dh_local_pub,
|
|
||||||
dh_server_pub,
|
|
||||||
shared_secret,
|
|
||||||
hash, &hashlen)) != 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if ((r = sshkey_verify(server_host_key, signature, slen, hash,
|
|
||||||
hashlen, ssh->compat)) != 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* save session id */
|
|
||||||
if (kex->session_id == NULL) {
|
|
||||||
kex->session_id_len = hashlen;
|
|
||||||
kex->session_id = malloc(kex->session_id_len);
|
|
||||||
if (kex->session_id == NULL) {
|
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
memcpy(kex->session_id, hash, kex->session_id_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
|
|
||||||
r = kex_send_newkeys(ssh);
|
|
||||||
out:
|
|
||||||
explicit_bzero(hash, sizeof(hash));
|
|
||||||
cng_dh_free_context(kex->dh);
|
|
||||||
kex->dh = NULL;
|
|
||||||
if (dh_server_pub)
|
|
||||||
BN_clear_free(dh_server_pub);
|
|
||||||
if (dh_local_pub)
|
|
||||||
BN_clear_free(dh_local_pub);
|
|
||||||
if (p)
|
|
||||||
BN_clear_free(p);
|
|
||||||
if (g)
|
|
||||||
BN_clear_free(g);
|
|
||||||
if (kbuf) {
|
|
||||||
explicit_bzero(kbuf, klen);
|
|
||||||
free(kbuf);
|
|
||||||
}
|
|
||||||
if (shared_secret)
|
|
||||||
BN_clear_free(shared_secret);
|
|
||||||
sshkey_free(server_host_key);
|
|
||||||
free(server_host_key_blob);
|
|
||||||
free(signature);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
#endif /* WITH_OPENSSL */
|
|
@ -1,283 +0,0 @@
|
|||||||
|
|
||||||
#include "includes.h"
|
|
||||||
|
|
||||||
#ifdef WIN32_FIXME
|
|
||||||
#undef GSSAPI
|
|
||||||
#undef KRB5
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef WITH_OPENSSL
|
|
||||||
|
|
||||||
#include <sys/param.h> /* MIN MAX */
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include <openssl/dh.h>
|
|
||||||
|
|
||||||
#include "sshkey.h"
|
|
||||||
#include "cipher.h"
|
|
||||||
#include "digest.h"
|
|
||||||
#include "kex.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "packet.h"
|
|
||||||
#include "dh.h"
|
|
||||||
#include "ssh2.h"
|
|
||||||
#include "compat.h"
|
|
||||||
#include "monitor_wrap.h"
|
|
||||||
#include "dispatch.h"
|
|
||||||
#include "ssherr.h"
|
|
||||||
#include "sshbuf.h"
|
|
||||||
#include <cng_kex.h>
|
|
||||||
#include <pathnames.h>
|
|
||||||
|
|
||||||
static int cng_input_kex_dh_gex_request(int, u_int32_t, void *);
|
|
||||||
static int cng_input_kex_dh_gex_init(int, u_int32_t, void *);
|
|
||||||
|
|
||||||
/*
|
|
||||||
let openssl do the choosing of the group. Once it has, we
|
|
||||||
can grab the group parameters and create a cng dh context.
|
|
||||||
*/
|
|
||||||
static
|
|
||||||
DH * cng_context_from_dh(DH * dh)
|
|
||||||
{
|
|
||||||
DH *pCtx = NULL;
|
|
||||||
|
|
||||||
pCtx = cng_dh_new_group(dh->g, dh->p);
|
|
||||||
|
|
||||||
return (DH *)pCtx;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
let openssl do the choosing of the group. Once it has, we
|
|
||||||
can grab the group parameters and create a cng dh context.
|
|
||||||
*/
|
|
||||||
static DH *
|
|
||||||
cng_choose_dh(int min, int wantbits, int max)
|
|
||||||
{
|
|
||||||
DH *pCtx = NULL;
|
|
||||||
|
|
||||||
DH * dh = choose_dh(min, wantbits, max);
|
|
||||||
if (dh != NULL)
|
|
||||||
{
|
|
||||||
pCtx = cng_context_from_dh(dh);
|
|
||||||
|
|
||||||
DH_free(dh);
|
|
||||||
}
|
|
||||||
return (DH *)pCtx;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
cng_kexgex_server(struct ssh *ssh)
|
|
||||||
{
|
|
||||||
if (!cng_kex_supported())
|
|
||||||
return kexgex_server(ssh);
|
|
||||||
|
|
||||||
ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST,
|
|
||||||
&cng_input_kex_dh_gex_request);
|
|
||||||
debug("expecting SSH2_MSG_KEX_DH_GEX_REQUEST");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
cng_input_kex_dh_gex_request(int type, u_int32_t seq, void *ctxt)
|
|
||||||
{
|
|
||||||
struct ssh *ssh = ctxt;
|
|
||||||
struct kex *kex = ssh->kex;
|
|
||||||
int r;
|
|
||||||
u_int min = 0, max = 0, nbits = 0;
|
|
||||||
BIGNUM * p = NULL;;
|
|
||||||
BIGNUM * g = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
debug("SSH2_MSG_KEX_DH_GEX_REQUEST received");
|
|
||||||
if ((r = sshpkt_get_u32(ssh, &min)) != 0 ||
|
|
||||||
(r = sshpkt_get_u32(ssh, &nbits)) != 0 ||
|
|
||||||
(r = sshpkt_get_u32(ssh, &max)) != 0 ||
|
|
||||||
(r = sshpkt_get_end(ssh)) != 0)
|
|
||||||
goto out;
|
|
||||||
kex->nbits = nbits;
|
|
||||||
kex->min = min;
|
|
||||||
kex->max = max;
|
|
||||||
min = MAX(DH_GRP_MIN, min);
|
|
||||||
max = MIN(DH_GRP_MAX, max);
|
|
||||||
nbits = MAX(DH_GRP_MIN, nbits);
|
|
||||||
nbits = MIN(DH_GRP_MAX, nbits);
|
|
||||||
|
|
||||||
if (kex->max < kex->min || kex->nbits < kex->min ||
|
|
||||||
kex->max < kex->nbits) {
|
|
||||||
r = SSH_ERR_DH_GEX_OUT_OF_RANGE;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Contact privileged parent */
|
|
||||||
kex->dh = cng_choose_dh(min, nbits, max);
|
|
||||||
if (kex->dh == NULL) {
|
|
||||||
sshpkt_disconnect(ssh, "no matching DH grp found");
|
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
p = cng_dh_get_p(kex->dh);
|
|
||||||
g = cng_dh_get_g(kex->dh);
|
|
||||||
debug("SSH2_MSG_KEX_DH_GEX_GROUP sent");
|
|
||||||
if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 ||
|
|
||||||
(r = sshpkt_put_bignum2(ssh,p )) != 0 ||
|
|
||||||
(r = sshpkt_put_bignum2(ssh, g)) != 0 ||
|
|
||||||
(r = sshpkt_send(ssh)) != 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* Compute our exchange value in parallel with the client */
|
|
||||||
if ((r = cng_dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
debug("expecting SSH2_MSG_KEX_DH_GEX_INIT");
|
|
||||||
ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &cng_input_kex_dh_gex_init);
|
|
||||||
r = 0;
|
|
||||||
out:
|
|
||||||
if (p != NULL)
|
|
||||||
BN_free(p);
|
|
||||||
if (g != NULL)
|
|
||||||
BN_free(g);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
cng_input_kex_dh_gex_init(int type, u_int32_t seq, void *ctxt)
|
|
||||||
{
|
|
||||||
struct ssh *ssh = ctxt;
|
|
||||||
struct kex *kex = ssh->kex;
|
|
||||||
BIGNUM *shared_secret = NULL, *dh_client_pub = NULL, *dh_local_pub=NULL, *p=NULL, *g=NULL;
|
|
||||||
struct sshkey *server_host_public, *server_host_private;
|
|
||||||
u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL;
|
|
||||||
u_char hash[SSH_DIGEST_MAX_LENGTH];
|
|
||||||
size_t sbloblen, slen;
|
|
||||||
size_t klen = 0, hashlen;
|
|
||||||
int kout, r;
|
|
||||||
|
|
||||||
if (kex->load_host_public_key == NULL ||
|
|
||||||
kex->load_host_private_key == NULL) {
|
|
||||||
r = SSH_ERR_INVALID_ARGUMENT;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
server_host_public = kex->load_host_public_key(kex->hostkey_type,
|
|
||||||
kex->hostkey_nid, ssh);
|
|
||||||
server_host_private = kex->load_host_private_key(kex->hostkey_type,
|
|
||||||
kex->hostkey_nid, ssh);
|
|
||||||
if (server_host_public == NULL) {
|
|
||||||
r = SSH_ERR_NO_HOSTKEY_LOADED;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* key, cert */
|
|
||||||
if ((dh_client_pub = BN_new()) == NULL) {
|
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if ((r = sshpkt_get_bignum2(ssh, dh_client_pub)) != 0 ||
|
|
||||||
(r = sshpkt_get_end(ssh)) != 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
#ifdef DEBUG_KEXDH
|
|
||||||
fprintf(stderr, "dh_client_pub= ");
|
|
||||||
BN_print_fp(stderr, dh_client_pub);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
debug("bits %d", BN_num_bits(dh_client_pub));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef DEBUG_KEXDH
|
|
||||||
DHparams_print_fp(stderr, kex->dh);
|
|
||||||
fprintf(stderr, "pub= ");
|
|
||||||
BN_print_fp(stderr, kex->dh->pub_key);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
#endif
|
|
||||||
if (!cng_dh_pub_is_valid(kex->dh, dh_client_pub)) {
|
|
||||||
sshpkt_disconnect(ssh, "bad client public DH value");
|
|
||||||
r = SSH_ERR_MESSAGE_INCOMPLETE;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_secret = cng_dh_get_secret(kex->dh, dh_client_pub);
|
|
||||||
if (shared_secret == NULL)
|
|
||||||
goto out;
|
|
||||||
#ifdef DEBUG_KEXDH
|
|
||||||
dump_digest("shared secret", kbuf, kout);
|
|
||||||
#endif
|
|
||||||
if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob,
|
|
||||||
&sbloblen)) != 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
p = cng_dh_get_p(kex->dh);
|
|
||||||
g = cng_dh_get_g(kex->dh);
|
|
||||||
dh_local_pub = cng_dh_get_local_pub_key(kex->dh);
|
|
||||||
/* calc H */
|
|
||||||
hashlen = sizeof(hash);
|
|
||||||
if ((r = kexgex_hash(
|
|
||||||
kex->hash_alg,
|
|
||||||
kex->client_version_string,
|
|
||||||
kex->server_version_string,
|
|
||||||
sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
|
|
||||||
sshbuf_ptr(kex->my), sshbuf_len(kex->my),
|
|
||||||
server_host_key_blob, sbloblen,
|
|
||||||
kex->min, kex->nbits, kex->max,
|
|
||||||
p, g,
|
|
||||||
dh_client_pub,
|
|
||||||
dh_local_pub,
|
|
||||||
shared_secret,
|
|
||||||
hash, &hashlen)) != 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* save session id := H */
|
|
||||||
if (kex->session_id == NULL) {
|
|
||||||
kex->session_id_len = hashlen;
|
|
||||||
kex->session_id = malloc(kex->session_id_len);
|
|
||||||
if (kex->session_id == NULL) {
|
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
memcpy(kex->session_id, hash, kex->session_id_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* sign H */
|
|
||||||
if ((r = kex->sign(server_host_private, server_host_public,
|
|
||||||
&signature, &slen, hash, hashlen, ssh->compat)) < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* send server hostkey, DH pubkey 'f' and singed H */
|
|
||||||
if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REPLY)) != 0 ||
|
|
||||||
(r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 ||
|
|
||||||
(r = sshpkt_put_bignum2(ssh, dh_local_pub)) != 0 || /* f */
|
|
||||||
(r = sshpkt_put_string(ssh, signature, slen)) != 0 ||
|
|
||||||
(r = sshpkt_send(ssh)) != 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
|
|
||||||
r = kex_send_newkeys(ssh);
|
|
||||||
out:
|
|
||||||
cng_dh_free_context(kex->dh);
|
|
||||||
kex->dh = NULL;
|
|
||||||
if (dh_client_pub)
|
|
||||||
BN_clear_free(dh_client_pub);
|
|
||||||
if (dh_local_pub)
|
|
||||||
BN_clear_free(dh_local_pub);
|
|
||||||
if (p)
|
|
||||||
BN_clear_free(p);
|
|
||||||
if (g)
|
|
||||||
BN_clear_free(g);
|
|
||||||
if (kbuf) {
|
|
||||||
explicit_bzero(kbuf, klen);
|
|
||||||
free(kbuf);
|
|
||||||
}
|
|
||||||
if (shared_secret)
|
|
||||||
BN_clear_free(shared_secret);
|
|
||||||
free(server_host_key_blob);
|
|
||||||
free(signature);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
#endif /* WITH_OPENSSL */
|
|
487
contrib/win32/win32compat/cng_openssl_dh.c
Normal file
487
contrib/win32/win32compat/cng_openssl_dh.c
Normal file
@ -0,0 +1,487 @@
|
|||||||
|
|
||||||
|
#include <includes.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <openssl/dh.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/bn.h>
|
||||||
|
|
||||||
|
#include "sshbuf.h"
|
||||||
|
#include "packet.h"
|
||||||
|
#include "ssherr.h"
|
||||||
|
#include <bcrypt.h>
|
||||||
|
#include "crypto-wrap.h"
|
||||||
|
#include <VersionHelpers.h>
|
||||||
|
|
||||||
|
|
||||||
|
// CNG Diffie-hellman Kex context
|
||||||
|
typedef struct cng_dh_ctx {
|
||||||
|
int size;
|
||||||
|
PBYTE g;
|
||||||
|
PBYTE p;
|
||||||
|
PBYTE pub;
|
||||||
|
BCRYPT_KEY_HANDLE hPrivate;
|
||||||
|
BCRYPT_ALG_HANDLE hAlg;
|
||||||
|
} CNG_DH_CTX;
|
||||||
|
|
||||||
|
|
||||||
|
struct sshdh {
|
||||||
|
void *dh;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sshbn {
|
||||||
|
BIGNUM *bn;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int cng_supported(void)
|
||||||
|
{
|
||||||
|
return (IsWindows8Point1OrGreater());
|
||||||
|
}
|
||||||
|
|
||||||
|
//function to reverse the order of a byte array
|
||||||
|
static void
|
||||||
|
cng_dh_swap_bytes(void *pv, size_t n)
|
||||||
|
{
|
||||||
|
char *p = (char*)pv;
|
||||||
|
size_t lo, hi;
|
||||||
|
for (lo = 0, hi = n - 1; hi>lo; lo++, hi--)
|
||||||
|
{
|
||||||
|
char tmp = p[lo];
|
||||||
|
p[lo] = p[hi];
|
||||||
|
p[hi] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct sshbn *
|
||||||
|
bnwrap(BIGNUM *bn)
|
||||||
|
{
|
||||||
|
struct sshbn *ret;
|
||||||
|
|
||||||
|
if (bn == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if ((ret = calloc(1, sizeof(*ret))) == NULL)
|
||||||
|
return NULL;
|
||||||
|
if ((ret->bn = BN_dup(bn)) == NULL) {
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* DH wrappers */
|
||||||
|
|
||||||
|
struct sshdh *
|
||||||
|
sshdh_new(void)
|
||||||
|
{
|
||||||
|
if (!cng_supported())
|
||||||
|
{
|
||||||
|
struct sshdh *ret;
|
||||||
|
|
||||||
|
if ((ret = calloc(1, sizeof(*ret))) == NULL)
|
||||||
|
return NULL;
|
||||||
|
if ((ret->dh = DH_new()) == NULL) {
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (struct sshdh *)(malloc(sizeof(struct sshdh)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sshdh_free(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
if (!cng_supported())
|
||||||
|
{
|
||||||
|
if (dh != NULL) {
|
||||||
|
if (dh->dh != NULL)
|
||||||
|
DH_free(dh->dh);
|
||||||
|
explicit_bzero(dh, sizeof(*dh));
|
||||||
|
free(dh);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dh != NULL) {
|
||||||
|
if (dh->dh != NULL)
|
||||||
|
{
|
||||||
|
CNG_DH_CTX * pCtx = (CNG_DH_CTX*)dh->dh;
|
||||||
|
|
||||||
|
if (pCtx->hAlg)
|
||||||
|
BCryptCloseAlgorithmProvider(pCtx->hAlg, 0);
|
||||||
|
|
||||||
|
if (pCtx->hPrivate)
|
||||||
|
BCryptDestroyKey(pCtx->hPrivate);
|
||||||
|
|
||||||
|
ZeroMemory(pCtx, sizeof(*pCtx));
|
||||||
|
free(pCtx);
|
||||||
|
}
|
||||||
|
free(dh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sshbn *
|
||||||
|
sshdh_pubkey(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
if (!cng_supported())
|
||||||
|
{
|
||||||
|
return bnwrap(((DH *)(dh->dh))->pub_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
CNG_DH_CTX *pCtx = (CNG_DH_CTX*)dh->dh;
|
||||||
|
struct sshbn * bn = NULL;
|
||||||
|
|
||||||
|
sshbn_from(pCtx->pub, pCtx->size, &bn);
|
||||||
|
return bn;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sshbn *
|
||||||
|
sshdh_p(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
if (!cng_supported())
|
||||||
|
{
|
||||||
|
return bnwrap(((DH *)(dh->dh))->p);
|
||||||
|
}
|
||||||
|
|
||||||
|
CNG_DH_CTX *pCtx = (CNG_DH_CTX*)dh->dh;
|
||||||
|
struct sshbn * bn = NULL;
|
||||||
|
|
||||||
|
sshbn_from(pCtx->p, pCtx->size, &bn);
|
||||||
|
|
||||||
|
return bn;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sshbn *
|
||||||
|
sshdh_g(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
if (!cng_supported())
|
||||||
|
{
|
||||||
|
return bnwrap(((DH *)(dh->dh))->g);
|
||||||
|
}
|
||||||
|
|
||||||
|
CNG_DH_CTX *pCtx = (CNG_DH_CTX*)dh->dh;
|
||||||
|
struct sshbn * bn = NULL;
|
||||||
|
|
||||||
|
sshbn_from(pCtx->g, pCtx->size, &bn);
|
||||||
|
|
||||||
|
return bn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sshdh_dump(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
if (!cng_supported())
|
||||||
|
{
|
||||||
|
DHparams_print_fp(stderr, dh->dh);
|
||||||
|
fprintf(stderr, "pub= ");
|
||||||
|
BN_print_fp(stderr, ((DH*)(dh->dh))->pub_key);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX needed?
|
||||||
|
size_t
|
||||||
|
sshdh_shared_key_size(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
if (!cng_supported())
|
||||||
|
{
|
||||||
|
int sz;
|
||||||
|
|
||||||
|
if (dh == NULL || dh->dh == NULL || (sz = DH_size(dh->dh)) < 0)
|
||||||
|
return 0;
|
||||||
|
return (size_t)sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sz;
|
||||||
|
CNG_DH_CTX *pCtx = (CNG_DH_CTX*)dh->dh;
|
||||||
|
|
||||||
|
if (dh == NULL || dh->dh == NULL || (sz = pCtx->size) < 0)
|
||||||
|
return 0;
|
||||||
|
return (size_t)sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
// import a bignum public key
|
||||||
|
static BCRYPT_KEY_HANDLE
|
||||||
|
cng_dh_set_remote_pub_key2(struct sshdh *dh, struct sshbn *b)
|
||||||
|
{
|
||||||
|
|
||||||
|
BCRYPT_KEY_HANDLE hPub = NULL;
|
||||||
|
CNG_DH_CTX *pCtx = (CNG_DH_CTX*)dh->dh;
|
||||||
|
|
||||||
|
if (sshbn_bytes(b) > pCtx->size)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
DWORD cbBlob = sizeof(BCRYPT_DH_KEY_BLOB) + (pCtx->size * 3);
|
||||||
|
|
||||||
|
PBYTE pBlob = (PBYTE)malloc(cbBlob);
|
||||||
|
|
||||||
|
BCRYPT_DH_KEY_BLOB *pKeyBlob = (BCRYPT_DH_KEY_BLOB *)pBlob;
|
||||||
|
pKeyBlob->cbKey = pCtx->size;
|
||||||
|
pKeyBlob->dwMagic = BCRYPT_DH_PUBLIC_MAGIC;
|
||||||
|
PBYTE pModulus = pBlob + sizeof(BCRYPT_DH_KEY_BLOB);
|
||||||
|
PBYTE pGenerator = pModulus + pKeyBlob->cbKey;
|
||||||
|
PBYTE pPublic = pGenerator + pKeyBlob->cbKey;
|
||||||
|
|
||||||
|
memcpy(pModulus, pCtx->p, pCtx->size);
|
||||||
|
memcpy(pGenerator, pCtx->g, pCtx->size);
|
||||||
|
memset(pPublic, 0, pCtx->size);
|
||||||
|
|
||||||
|
sshbn_to(b, pPublic + pCtx->size - (sshbn_bytes(b)));
|
||||||
|
|
||||||
|
|
||||||
|
HRESULT Status = 0;
|
||||||
|
|
||||||
|
if (S_OK != (Status = BCryptImportKeyPair(pCtx->hAlg, NULL, BCRYPT_DH_PUBLIC_BLOB, &hPub, pBlob, cbBlob, 0)))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (pBlob) free(pBlob);
|
||||||
|
|
||||||
|
return hPub;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int sshdh_compute_key(struct sshdh *dh, struct sshbn *pubkey,
|
||||||
|
struct sshbn **shared_secretp)
|
||||||
|
{
|
||||||
|
if (!cng_supported())
|
||||||
|
{
|
||||||
|
u_char *sbuf;
|
||||||
|
int r, slen;
|
||||||
|
|
||||||
|
*shared_secretp = NULL;
|
||||||
|
if ((slen = DH_size(dh->dh)) <= 0)
|
||||||
|
return SSH_ERR_INVALID_ARGUMENT;
|
||||||
|
if ((sbuf = calloc(1, slen)) == NULL)
|
||||||
|
return SSH_ERR_ALLOC_FAIL;
|
||||||
|
if ((r = DH_compute_key(sbuf, pubkey->bn, dh->dh)) < 0 ||
|
||||||
|
r != slen) {
|
||||||
|
r = SSH_ERR_LIBCRYPTO_ERROR;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if ((r = sshbn_from(sbuf, slen, shared_secretp)) != 0)
|
||||||
|
goto out;
|
||||||
|
/* success */
|
||||||
|
r = 0;
|
||||||
|
out:
|
||||||
|
explicit_bzero(sbuf, slen);
|
||||||
|
free(sbuf);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
CNG_DH_CTX *pCtx = (CNG_DH_CTX*)dh->dh;
|
||||||
|
PBYTE pSecret = (PBYTE)malloc(pCtx->size);
|
||||||
|
DWORD pSecret_len = pCtx->size;
|
||||||
|
DWORD size = pCtx->size;
|
||||||
|
struct sshbn * bn = NULL;
|
||||||
|
BCRYPT_SECRET_HANDLE hSecret;
|
||||||
|
|
||||||
|
BCRYPT_KEY_HANDLE hRemotePub = cng_dh_set_remote_pub_key2(dh, pubkey);
|
||||||
|
if (hRemotePub != NULL)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (S_OK == (hr = BCryptSecretAgreement(pCtx->hPrivate, hRemotePub, &hSecret, 0)))
|
||||||
|
{
|
||||||
|
|
||||||
|
hr = BCryptDeriveKey(hSecret, L"TRUNCATE", NULL, pSecret, pSecret_len, &size, 0);
|
||||||
|
if (S_OK == hr)
|
||||||
|
{
|
||||||
|
cng_dh_swap_bytes(pSecret, size);
|
||||||
|
|
||||||
|
sshbn_from(pSecret, size, &bn);
|
||||||
|
memset(pSecret, 0, size);
|
||||||
|
free(pSecret);
|
||||||
|
*shared_secretp = bn;
|
||||||
|
}
|
||||||
|
BCryptDestroySecret(hSecret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return S_OK == hr ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
sshdh_generate(struct sshdh *dh, size_t len)
|
||||||
|
{
|
||||||
|
if (!cng_supported())
|
||||||
|
{
|
||||||
|
if (len > INT_MAX)
|
||||||
|
return SSH_ERR_INVALID_ARGUMENT;
|
||||||
|
if (len != 0)
|
||||||
|
((DH*)(dh->dh))->length = (int)len;
|
||||||
|
if (DH_generate_key(dh->dh) != 1)
|
||||||
|
return SSH_ERR_LIBCRYPTO_ERROR;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT Status;
|
||||||
|
BCRYPT_DH_PARAMETER_HEADER *DhParamHdrPointer = NULL;
|
||||||
|
DWORD DhParamBlobLength = 0;
|
||||||
|
PBYTE DhParamBlob = NULL;
|
||||||
|
BCRYPT_ALG_HANDLE hAlg = NULL;
|
||||||
|
PBYTE pBlob = NULL;
|
||||||
|
BCRYPT_KEY_HANDLE hPriv = NULL;
|
||||||
|
CNG_DH_CTX *pCtx = (CNG_DH_CTX*)dh->dh;
|
||||||
|
DWORD cbBlob = 0;
|
||||||
|
|
||||||
|
|
||||||
|
DhParamBlobLength = sizeof(BCRYPT_DH_PARAMETER_HEADER) + (pCtx->size * 2);
|
||||||
|
|
||||||
|
if (NULL == (DhParamBlob = (PBYTE)malloc(DhParamBlobLength)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
DhParamHdrPointer = (BCRYPT_DH_PARAMETER_HEADER *)DhParamBlob;
|
||||||
|
DhParamHdrPointer->cbLength = DhParamBlobLength;
|
||||||
|
DhParamHdrPointer->cbKeyLength = pCtx->size;
|
||||||
|
DhParamHdrPointer->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC;
|
||||||
|
|
||||||
|
memcpy(DhParamBlob + sizeof(BCRYPT_DH_PARAMETER_HEADER), pCtx->p, pCtx->size);
|
||||||
|
memcpy(DhParamBlob + sizeof(BCRYPT_DH_PARAMETER_HEADER) + pCtx->size, pCtx->g, pCtx->size);
|
||||||
|
|
||||||
|
if (S_OK != (Status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_DH_ALGORITHM, NULL, 0)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
|
||||||
|
if (S_OK != (Status = BCryptGenerateKeyPair(hAlg, &hPriv, pCtx->size * 8, 0)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
|
||||||
|
if (S_OK != (Status = BCryptSetProperty(hPriv, BCRYPT_DH_PARAMETERS, DhParamBlob, DhParamBlobLength, 0)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (S_OK != (Status = BCryptFinalizeKeyPair(hPriv, 0)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (S_OK != (Status = BCryptExportKey(hPriv, NULL, BCRYPT_DH_PUBLIC_BLOB, NULL, 0, &cbBlob, 0)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (NULL == (pBlob = (PBYTE)malloc(cbBlob)))
|
||||||
|
{
|
||||||
|
Status = STATUS_NO_MEMORY;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_OK != (Status = BCryptExportKey(hPriv, NULL, BCRYPT_DH_PUBLIC_BLOB, pBlob, cbBlob, &cbBlob, 0)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BCRYPT_DH_KEY_BLOB *pKeyBlob = (BCRYPT_DH_KEY_BLOB *)pBlob;
|
||||||
|
PBYTE pModulus = pBlob + sizeof(BCRYPT_DH_KEY_BLOB);
|
||||||
|
PBYTE pGenerator = pModulus + pKeyBlob->cbKey;
|
||||||
|
PBYTE pPublic = pGenerator + pKeyBlob->cbKey;
|
||||||
|
|
||||||
|
pCtx->hAlg = hAlg;
|
||||||
|
pCtx->hPrivate = hPriv;
|
||||||
|
memcpy(pCtx->pub, pPublic, pCtx->size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
error:
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sshdh_new_group_hex(const char *gen, const char *modulus, struct sshdh **dhp)
|
||||||
|
{
|
||||||
|
if (!cng_supported())
|
||||||
|
{
|
||||||
|
struct sshdh *ret;
|
||||||
|
|
||||||
|
*dhp = NULL;
|
||||||
|
if ((ret = sshdh_new()) == NULL)
|
||||||
|
return SSH_ERR_ALLOC_FAIL;
|
||||||
|
if (BN_hex2bn(&(((DH*)(ret->dh))->p), modulus) == 0 ||
|
||||||
|
BN_hex2bn(&(((DH*)(ret->dh))->g), gen) == 0) {
|
||||||
|
sshdh_free(ret);
|
||||||
|
return SSH_ERR_LIBCRYPTO_ERROR;
|
||||||
|
}
|
||||||
|
*dhp = ret;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sshdh *ret;
|
||||||
|
struct sshbn * g = NULL;
|
||||||
|
struct sshbn * p = NULL;
|
||||||
|
|
||||||
|
sshbn_from_hex(gen, &g);
|
||||||
|
sshbn_from_hex(modulus, &p);
|
||||||
|
*dhp = NULL;
|
||||||
|
|
||||||
|
ret = sshdh_new_group(g, p);
|
||||||
|
|
||||||
|
if (g != NULL)
|
||||||
|
sshbn_free(g);
|
||||||
|
if (p != NULL)
|
||||||
|
sshbn_free(p);
|
||||||
|
|
||||||
|
*dhp = ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* XXX transfers ownership of gen, modulus */
|
||||||
|
struct sshdh *
|
||||||
|
sshdh_new_group(struct sshbn *gen, struct sshbn *modulus)
|
||||||
|
{
|
||||||
|
if (!cng_supported())
|
||||||
|
{
|
||||||
|
struct sshdh *dh;
|
||||||
|
|
||||||
|
if ((dh = sshdh_new()) == NULL)
|
||||||
|
return NULL;
|
||||||
|
((DH*)(dh->dh))->p = modulus->bn;
|
||||||
|
((DH*)(dh->dh))->g = gen->bn;
|
||||||
|
modulus->bn = gen->bn = NULL;
|
||||||
|
sshbn_free(gen);
|
||||||
|
sshbn_free(modulus);
|
||||||
|
return (dh);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sshdh *dh;
|
||||||
|
|
||||||
|
PBYTE pBlob = NULL;
|
||||||
|
DWORD keysize = sshbn_bytes(modulus);
|
||||||
|
DWORD cbBlob = 0;
|
||||||
|
|
||||||
|
dh = sshdh_new();
|
||||||
|
|
||||||
|
pBlob = (PBYTE)malloc(sizeof(CNG_DH_CTX) + (3 * keysize));
|
||||||
|
memset(pBlob, 0, sizeof(CNG_DH_CTX) + (3 * keysize));
|
||||||
|
|
||||||
|
CNG_DH_CTX * pCtx = (CNG_DH_CTX *)pBlob;
|
||||||
|
|
||||||
|
pCtx->size = keysize;
|
||||||
|
pCtx->p = pBlob + sizeof(CNG_DH_CTX);
|
||||||
|
pCtx->g = pCtx->p + keysize;
|
||||||
|
pCtx->pub = pCtx->g + keysize;
|
||||||
|
|
||||||
|
sshbn_to(gen, pCtx->g + keysize - sshbn_bytes(gen));
|
||||||
|
sshbn_to(modulus, pCtx->p + keysize - sshbn_bytes(modulus));
|
||||||
|
|
||||||
|
dh->dh = (void *)pCtx;
|
||||||
|
return dh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
51
crypto-wrap.h
Normal file
51
crypto-wrap.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
|
||||||
|
#ifndef _OPENSSL_WRAP_H
|
||||||
|
#define _OPENSSL_WRAP_H
|
||||||
|
|
||||||
|
struct sshdh;
|
||||||
|
struct sshbn;
|
||||||
|
struct sshbuf;
|
||||||
|
struct ssh;
|
||||||
|
|
||||||
|
struct sshdh *sshdh_new(void);
|
||||||
|
void sshdh_free(struct sshdh *dh);
|
||||||
|
struct sshbn *sshdh_pubkey(struct sshdh *dh);
|
||||||
|
struct sshbn *sshdh_p(struct sshdh *dh);
|
||||||
|
struct sshbn *sshdh_g(struct sshdh *dh);
|
||||||
|
void sshdh_dump(struct sshdh *dh);
|
||||||
|
size_t sshdh_shared_key_size(struct sshdh *dh);
|
||||||
|
int sshdh_compute_key(struct sshdh *dh, struct sshbn *pubkey,
|
||||||
|
struct sshbn **shared_secretp);
|
||||||
|
int sshdh_generate(struct sshdh *dh, size_t len);
|
||||||
|
int sshdh_new_group_hex(const char *gen, const char *modulus,
|
||||||
|
struct sshdh **dhp);
|
||||||
|
struct sshdh *sshdh_new_group(struct sshbn *gen, struct sshbn *modulus);
|
||||||
|
|
||||||
|
struct sshbn *sshbn_new(void);
|
||||||
|
void sshbn_free(struct sshbn *bn);
|
||||||
|
int sshbn_from(const void *d, size_t l, struct sshbn **retp);
|
||||||
|
int sshbn_from_hex(const char *hex, struct sshbn **retp);
|
||||||
|
size_t sshbn_bits(const struct sshbn *bn);
|
||||||
|
const struct sshbn *sshbn_value_0(void);
|
||||||
|
const struct sshbn *sshbn_value_1(void);
|
||||||
|
int sshbn_cmp(const struct sshbn *a, const struct sshbn *b);
|
||||||
|
int sshbn_sub(struct sshbn *r, const struct sshbn *a, const struct sshbn *b);
|
||||||
|
int sshbn_is_bit_set(const struct sshbn *bn, size_t i);
|
||||||
|
int sshbn_to(const struct sshbn *a, unsigned char *to);
|
||||||
|
size_t sshbn_bytes(const struct sshbn *bn);
|
||||||
|
|
||||||
|
/* XXX move to sshbuf.h; rename s/_wrap$// */
|
||||||
|
int sshbuf_get_bignum2_wrap(struct sshbuf *buf, struct sshbn *bn);
|
||||||
|
int sshbuf_get_bignum1_wrap(struct sshbuf *buf, struct sshbn *bn);
|
||||||
|
int sshbuf_put_bignum2_wrap(struct sshbuf *buf, const struct sshbn *bn);
|
||||||
|
int sshbuf_put_bignum1_wrap(struct sshbuf *buf, const struct sshbn *bn);
|
||||||
|
int sshpkt_get_bignum2_wrap(struct ssh *ssh, struct sshbn *bn);
|
||||||
|
int sshpkt_put_bignum2_wrap(struct ssh *ssh, const struct sshbn *bn);
|
||||||
|
|
||||||
|
/* bridge to unwrapped OpenSSL APIs; XXX remove later */
|
||||||
|
struct sshbn *sshbn_from_bignum(BIGNUM *bn);
|
||||||
|
BIGNUM *sshbn_bignum(struct sshbn *bn);
|
||||||
|
DH *sshdh_dh(struct sshdh *dh);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _OPENSSL_WRAP_H */
|
224
dh.c
224
dh.c
@ -41,6 +41,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "ssherr.h"
|
#include "ssherr.h"
|
||||||
|
#include "crypto-wrap.h"
|
||||||
|
|
||||||
static int
|
static int
|
||||||
parse_prime(int linenum, char *line, struct dhgroup *dhg)
|
parse_prime(int linenum, char *line, struct dhgroup *dhg)
|
||||||
@ -49,6 +50,7 @@ parse_prime(int linenum, char *line, struct dhgroup *dhg)
|
|||||||
char *strsize, *gen, *prime;
|
char *strsize, *gen, *prime;
|
||||||
const char *errstr = NULL;
|
const char *errstr = NULL;
|
||||||
long long n;
|
long long n;
|
||||||
|
int r;
|
||||||
|
|
||||||
dhg->p = dhg->g = NULL;
|
dhg->p = dhg->g = NULL;
|
||||||
cp = line;
|
cp = line;
|
||||||
@ -109,53 +111,45 @@ parse_prime(int linenum, char *line, struct dhgroup *dhg)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((dhg->g = BN_new()) == NULL ||
|
if ((r = sshbn_from_hex(gen, &dhg->g)) != 0 ||
|
||||||
(dhg->p = BN_new()) == NULL) {
|
(r = sshbn_from_hex(prime, &dhg->p)) != 0)
|
||||||
error("parse_prime: BN_new failed");
|
{
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (BN_hex2bn(&dhg->g, gen) == 0) {
|
if (sshbn_bits(dhg->p) != dhg->size) {
|
||||||
error("moduli:%d: could not parse generator value", linenum);
|
error("moduli:%d: prime has wrong size: actual %zu listed %zu",
|
||||||
|
linenum, sshbn_bits(dhg->p), dhg->size - 1);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (BN_hex2bn(&dhg->p, prime) == 0) {
|
|
||||||
error("moduli:%d: could not parse prime value", linenum);
|
if (sshbn_cmp(dhg->g, sshbn_value_1()) <= 0) {
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if (BN_num_bits(dhg->p) != dhg->size) {
|
|
||||||
error("moduli:%d: prime has wrong size: actual %d listed %d",
|
|
||||||
linenum, BN_num_bits(dhg->p), dhg->size - 1);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if (BN_cmp(dhg->g, BN_value_one()) <= 0) {
|
|
||||||
error("moduli:%d: generator is invalid", linenum);
|
error("moduli:%d: generator is invalid", linenum);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
if (dhg->g != NULL)
|
sshbn_free(dhg->g);
|
||||||
BN_clear_free(dhg->g);
|
sshbn_free(dhg->p);
|
||||||
if (dhg->p != NULL)
|
|
||||||
BN_clear_free(dhg->p);
|
|
||||||
dhg->g = dhg->p = NULL;
|
dhg->g = dhg->p = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DH *
|
struct sshdh *
|
||||||
choose_dh(int min, int wantbits, int max)
|
choose_dh(u_int min, u_int wantbits, u_int max)
|
||||||
{
|
{
|
||||||
FILE *f;
|
FILE *f;
|
||||||
char line[4096];
|
char line[4096];
|
||||||
int best, bestcount, which;
|
u_int best, bestcount, which, linenum;
|
||||||
int linenum;
|
int r;
|
||||||
struct dhgroup dhg;
|
struct dhgroup dhg;
|
||||||
|
struct sshdh *dh = NULL;
|
||||||
|
|
||||||
if ((f = fopen(_PATH_DH_MODULI, "r")) == NULL &&
|
if ((f = fopen(_PATH_DH_MODULI, "r")) == NULL &&
|
||||||
(f = fopen(_PATH_DH_PRIMES, "r")) == NULL) {
|
(f = fopen(_PATH_DH_PRIMES, "r")) == NULL) {
|
||||||
logit("WARNING: %s does not exist, using fixed modulus",
|
logit("WARNING: %s does not exist, using fixed modulus",
|
||||||
_PATH_DH_MODULI);
|
_PATH_DH_MODULI);
|
||||||
return (dh_new_group_fallback(max));
|
goto fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
linenum = 0;
|
linenum = 0;
|
||||||
@ -164,8 +158,8 @@ choose_dh(int min, int wantbits, int max)
|
|||||||
linenum++;
|
linenum++;
|
||||||
if (!parse_prime(linenum, line, &dhg))
|
if (!parse_prime(linenum, line, &dhg))
|
||||||
continue;
|
continue;
|
||||||
BN_clear_free(dhg.g);
|
sshbn_free(dhg.g);
|
||||||
BN_clear_free(dhg.p);
|
sshbn_free(dhg.p);
|
||||||
|
|
||||||
if (dhg.size > max || dhg.size < min)
|
if (dhg.size > max || dhg.size < min)
|
||||||
continue;
|
continue;
|
||||||
@ -183,7 +177,7 @@ choose_dh(int min, int wantbits, int max)
|
|||||||
if (bestcount == 0) {
|
if (bestcount == 0) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
logit("WARNING: no suitable primes in %s", _PATH_DH_PRIMES);
|
logit("WARNING: no suitable primes in %s", _PATH_DH_PRIMES);
|
||||||
return (dh_new_group_fallback(max));
|
goto fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
linenum = 0;
|
linenum = 0;
|
||||||
@ -194,8 +188,8 @@ choose_dh(int min, int wantbits, int max)
|
|||||||
if ((dhg.size > max || dhg.size < min) ||
|
if ((dhg.size > max || dhg.size < min) ||
|
||||||
dhg.size != best ||
|
dhg.size != best ||
|
||||||
linenum++ != which) {
|
linenum++ != which) {
|
||||||
BN_clear_free(dhg.g);
|
sshbn_free(dhg.g);
|
||||||
BN_clear_free(dhg.p);
|
sshbn_free(dhg.p);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -204,109 +198,104 @@ choose_dh(int min, int wantbits, int max)
|
|||||||
if (linenum != which+1) {
|
if (linenum != which+1) {
|
||||||
logit("WARNING: line %d disappeared in %s, giving up",
|
logit("WARNING: line %d disappeared in %s, giving up",
|
||||||
which, _PATH_DH_PRIMES);
|
which, _PATH_DH_PRIMES);
|
||||||
return (dh_new_group_fallback(max));
|
fallback:
|
||||||
|
if ((r = dh_new_group_fallback(max, &dh)) != 0)
|
||||||
|
fatal("%s: dh_new_group_fallback: %s",
|
||||||
|
__func__, ssh_err(r));
|
||||||
|
return dh;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (dh_new_group(dhg.g, dhg.p));
|
return (sshdh_new_group(dhg.g, dhg.p));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* diffie-hellman-groupN-sha1 */
|
/* diffie-hellman-groupN-sha1 */
|
||||||
|
|
||||||
int
|
int
|
||||||
dh_pub_is_valid(DH *dh, BIGNUM *dh_pub)
|
dh_pub_is_valid(struct sshdh *dh, struct sshbn *dh_pub)
|
||||||
{
|
{
|
||||||
int i;
|
size_t i;
|
||||||
int n = BN_num_bits(dh_pub);
|
size_t n;
|
||||||
int bits_set = 0;
|
int r, freeme = 0, bits_set = 0;
|
||||||
BIGNUM *tmp;
|
struct sshbn *dh_p = NULL, *tmp = NULL;
|
||||||
|
|
||||||
if (dh_pub->neg) {
|
if (dh_pub == NULL) {
|
||||||
logit("invalid public DH value: negative");
|
if ((dh_pub = sshdh_pubkey(dh)) == NULL)
|
||||||
return 0;
|
return SSH_ERR_ALLOC_FAIL;
|
||||||
|
freeme = 1;
|
||||||
}
|
}
|
||||||
if (BN_cmp(dh_pub, BN_value_one()) != 1) { /* pub_exp <= 1 */
|
n = sshbn_bits(dh_pub);
|
||||||
|
if (sshbn_cmp(dh_pub, sshbn_value_1()) != 1) { /* pub_exp <= 1 */
|
||||||
logit("invalid public DH value: <= 1");
|
logit("invalid public DH value: <= 1");
|
||||||
return 0;
|
r = SSH_ERR_INVALID_FORMAT;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
if ((dh_p = sshdh_p(dh)) == NULL) {
|
||||||
if ((tmp = BN_new()) == NULL) {
|
error("%s: sshdh_p failed", __func__);
|
||||||
error("%s: BN_new failed", __func__);
|
r = SSH_ERR_ALLOC_FAIL;
|
||||||
return 0;
|
goto out;
|
||||||
}
|
}
|
||||||
if (!BN_sub(tmp, dh->p, BN_value_one()) ||
|
if ((tmp = sshbn_new()) == NULL) {
|
||||||
BN_cmp(dh_pub, tmp) != -1) { /* pub_exp > p-2 */
|
error("%s: sshbn_new failed", __func__);
|
||||||
BN_clear_free(tmp);
|
r = SSH_ERR_ALLOC_FAIL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if ((r = sshbn_sub(tmp, dh_p, sshbn_value_1())) != 0) {
|
||||||
|
error("%s: sshbn_sub: %s", __func__, ssh_err(r));
|
||||||
|
r = SSH_ERR_LIBCRYPTO_ERROR;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if ((r = sshbn_cmp(dh_pub, tmp)) != -1) { /* pub_exp > p-2 */
|
||||||
logit("invalid public DH value: >= p-1");
|
logit("invalid public DH value: >= p-1");
|
||||||
return 0;
|
r = SSH_ERR_INVALID_FORMAT;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
BN_clear_free(tmp);
|
|
||||||
|
|
||||||
for (i = 0; i <= n; i++)
|
for (i = 0; i <= n; i++)
|
||||||
if (BN_is_bit_set(dh_pub, i))
|
if (sshbn_is_bit_set(dh_pub, i))
|
||||||
bits_set++;
|
bits_set++;
|
||||||
debug2("bits set: %d/%d", bits_set, BN_num_bits(dh->p));
|
debug2("bits set: %d/%zu", bits_set, sshbn_bits(dh_p));
|
||||||
|
|
||||||
/* if g==2 and bits_set==1 then computing log_g(dh_pub) is trivial */
|
/* if g==2 and bits_set==1 then computing log_g(dh_pub) is trivial */
|
||||||
if (bits_set > 1)
|
if (bits_set <= 1) {
|
||||||
return 1;
|
logit("invalid public DH value (%d/%zu)",
|
||||||
|
bits_set, sshbn_bits(dh_p));
|
||||||
|
r = SSH_ERR_INVALID_FORMAT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
logit("invalid public DH value (%d/%d)", bits_set, BN_num_bits(dh->p));
|
/* success */
|
||||||
|
r = 0;
|
||||||
|
out:
|
||||||
|
sshbn_free(dh_p);
|
||||||
|
sshbn_free(tmp);
|
||||||
|
if (freeme)
|
||||||
|
sshbn_free(dh_pub);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
dh_gen_key(struct sshdh *dh, u_int need)
|
||||||
|
{
|
||||||
|
size_t pbits;
|
||||||
|
struct sshbn *dh_p;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if ((dh_p = sshdh_p(dh)) == NULL) {
|
||||||
|
error("%s: sshdh_p failed", __func__);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (need == 0 ||
|
||||||
|
(pbits = sshbn_bits(dh_p)) == 0 ||
|
||||||
|
need > INT_MAX / 2 || 2 * need > pbits) {
|
||||||
|
sshbn_free(dh_p);
|
||||||
|
return SSH_ERR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
if ((r = sshdh_generate(dh, MIN(need * 2, pbits - 1))) != 0 ||
|
||||||
|
(r = dh_pub_is_valid(dh, NULL)) != 0)
|
||||||
|
return r;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
dh_gen_key(DH *dh, int need)
|
dh_new_group1(struct sshdh **dhp)
|
||||||
{
|
|
||||||
int pbits;
|
|
||||||
|
|
||||||
if (need < 0 || dh->p == NULL ||
|
|
||||||
(pbits = BN_num_bits(dh->p)) <= 0 ||
|
|
||||||
need > INT_MAX / 2 || 2 * need > pbits)
|
|
||||||
return SSH_ERR_INVALID_ARGUMENT;
|
|
||||||
dh->length = MIN(need * 2, pbits - 1);
|
|
||||||
if (DH_generate_key(dh) == 0 ||
|
|
||||||
!dh_pub_is_valid(dh, dh->pub_key)) {
|
|
||||||
BN_clear_free(dh->priv_key);
|
|
||||||
return SSH_ERR_LIBCRYPTO_ERROR;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DH *
|
|
||||||
dh_new_group_asc(const char *gen, const char *modulus)
|
|
||||||
{
|
|
||||||
DH *dh;
|
|
||||||
|
|
||||||
if ((dh = DH_new()) == NULL)
|
|
||||||
return NULL;
|
|
||||||
if (BN_hex2bn(&dh->p, modulus) == 0 ||
|
|
||||||
BN_hex2bn(&dh->g, gen) == 0) {
|
|
||||||
DH_free(dh);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return (dh);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This just returns the group, we still need to generate the exchange
|
|
||||||
* value.
|
|
||||||
*/
|
|
||||||
|
|
||||||
DH *
|
|
||||||
dh_new_group(BIGNUM *gen, BIGNUM *modulus)
|
|
||||||
{
|
|
||||||
DH *dh;
|
|
||||||
|
|
||||||
if ((dh = DH_new()) == NULL)
|
|
||||||
return NULL;
|
|
||||||
dh->p = modulus;
|
|
||||||
dh->g = gen;
|
|
||||||
|
|
||||||
return (dh);
|
|
||||||
}
|
|
||||||
|
|
||||||
DH *
|
|
||||||
dh_new_group1(void)
|
|
||||||
{
|
{
|
||||||
static char *gen = "2", *group1 =
|
static char *gen = "2", *group1 =
|
||||||
"FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
|
"FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
|
||||||
@ -316,11 +305,11 @@ dh_new_group1(void)
|
|||||||
"EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
|
"EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
|
||||||
"FFFFFFFF" "FFFFFFFF";
|
"FFFFFFFF" "FFFFFFFF";
|
||||||
|
|
||||||
return (dh_new_group_asc(gen, group1));
|
return sshdh_new_group_hex(gen, group1, dhp);
|
||||||
}
|
}
|
||||||
|
|
||||||
DH *
|
int
|
||||||
dh_new_group14(void)
|
dh_new_group14(struct sshdh **dhp)
|
||||||
{
|
{
|
||||||
static char *gen = "2", *group14 =
|
static char *gen = "2", *group14 =
|
||||||
"FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
|
"FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
|
||||||
@ -335,15 +324,15 @@ dh_new_group14(void)
|
|||||||
"DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
|
"DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
|
||||||
"15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF";
|
"15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF";
|
||||||
|
|
||||||
return (dh_new_group_asc(gen, group14));
|
return sshdh_new_group_hex(gen, group14, dhp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 4k bit fallback group used by DH-GEX if moduli file cannot be read.
|
* 4k bit fallback group used by DH-GEX if moduli file cannot be read.
|
||||||
* Source: MODP group 16 from RFC3526.
|
* Source: MODP group 16 from RFC3526.
|
||||||
*/
|
*/
|
||||||
DH *
|
int
|
||||||
dh_new_group_fallback(int max)
|
dh_new_group_fallback(int max, struct sshdh **dhp)
|
||||||
{
|
{
|
||||||
static char *gen = "2", *group16 =
|
static char *gen = "2", *group16 =
|
||||||
"FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
|
"FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
|
||||||
@ -371,10 +360,10 @@ dh_new_group_fallback(int max)
|
|||||||
|
|
||||||
if (max < 4096) {
|
if (max < 4096) {
|
||||||
debug3("requested max size %d, using 2k bit group 14", max);
|
debug3("requested max size %d, using 2k bit group 14", max);
|
||||||
return dh_new_group14();
|
return dh_new_group14(dhp);
|
||||||
}
|
}
|
||||||
debug3("using 4k bit group 16");
|
debug3("using 4k bit group 16");
|
||||||
return (dh_new_group_asc(gen, group16));
|
return sshdh_new_group_hex(gen, group16, dhp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -384,7 +373,6 @@ dh_new_group_fallback(int max)
|
|||||||
* Management Part 1 (rev 3) limited by the recommended maximum value
|
* Management Part 1 (rev 3) limited by the recommended maximum value
|
||||||
* from RFC4419 section 3.
|
* from RFC4419 section 3.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
u_int
|
u_int
|
||||||
dh_estimate(int bits)
|
dh_estimate(int bits)
|
||||||
{
|
{
|
||||||
|
30
dh.h
30
dh.h
@ -26,26 +26,29 @@
|
|||||||
#ifndef DH_H
|
#ifndef DH_H
|
||||||
#define DH_H
|
#define DH_H
|
||||||
|
|
||||||
|
#include <crypto-wrap.h>
|
||||||
|
|
||||||
struct dhgroup {
|
struct dhgroup {
|
||||||
int size;
|
size_t size;
|
||||||
BIGNUM *g;
|
struct sshbn *g;
|
||||||
BIGNUM *p;
|
struct sshbn *p;
|
||||||
};
|
};
|
||||||
|
|
||||||
DH *choose_dh(int, int, int);
|
struct sshdh *choose_dh(u_int, u_int, u_int);
|
||||||
DH *dh_new_group_asc(const char *, const char *);
|
int dh_new_group1(struct sshdh **dhp);
|
||||||
DH *dh_new_group(BIGNUM *, BIGNUM *);
|
int dh_new_group14(struct sshdh **dhp);
|
||||||
DH *dh_new_group1(void);
|
int dh_new_group_fallback(int, struct sshdh **dhp);
|
||||||
DH *dh_new_group14(void);
|
|
||||||
DH *dh_new_group_fallback(int);
|
|
||||||
|
|
||||||
int dh_gen_key(DH *, int);
|
int dh_gen_key(struct sshdh *dh, u_int);
|
||||||
int dh_pub_is_valid(DH *, BIGNUM *);
|
int dh_pub_is_valid(struct sshdh *dh, struct sshbn *dh_pub);
|
||||||
|
|
||||||
u_int dh_estimate(int);
|
u_int dh_estimate(int);
|
||||||
|
|
||||||
/* Min and max values from RFC4419. */
|
/*
|
||||||
#define DH_GRP_MIN 1024
|
* Max value from RFC4419.
|
||||||
|
* Miniumum increased in light of DH precomputation attacks.
|
||||||
|
*/
|
||||||
|
#define DH_GRP_MIN 2048
|
||||||
#define DH_GRP_MAX 8192
|
#define DH_GRP_MAX 8192
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -71,5 +74,4 @@ u_int dh_estimate(int);
|
|||||||
#define MODULI_TESTS_JACOBI (0x08)
|
#define MODULI_TESTS_JACOBI (0x08)
|
||||||
#define MODULI_TESTS_ELLIPTIC (0x10)
|
#define MODULI_TESTS_ELLIPTIC (0x10)
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
107
kex.c
107
kex.c
@ -55,18 +55,23 @@
|
|||||||
#include "sshbuf.h"
|
#include "sshbuf.h"
|
||||||
#include "digest.h"
|
#include "digest.h"
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER >= 0x00907000L
|
|
||||||
# if defined(HAVE_EVP_SHA256)
|
|
||||||
# define evp_ssh_sha256 EVP_sha256
|
|
||||||
# else
|
|
||||||
extern const EVP_MD *evp_ssh_sha256(void);
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* prototype */
|
/* prototype */
|
||||||
static int kex_choose_conf(struct ssh *);
|
static int kex_choose_conf(struct ssh *);
|
||||||
static int kex_input_newkeys(int, u_int32_t, void *);
|
static int kex_input_newkeys(int, u_int32_t, void *);
|
||||||
|
|
||||||
|
static const char *proposal_names[PROPOSAL_MAX] = {
|
||||||
|
"KEX algorithms",
|
||||||
|
"host key algorithms",
|
||||||
|
"ciphers ctos",
|
||||||
|
"ciphers stoc",
|
||||||
|
"MACs ctos",
|
||||||
|
"MACs stoc",
|
||||||
|
"compression ctos",
|
||||||
|
"compression stoc",
|
||||||
|
"languages ctos",
|
||||||
|
"languages stoc",
|
||||||
|
};
|
||||||
|
|
||||||
struct kexalg {
|
struct kexalg {
|
||||||
char *name;
|
char *name;
|
||||||
u_int type;
|
u_int type;
|
||||||
@ -78,23 +83,15 @@ static const struct kexalg kexalgs[] = {
|
|||||||
{ KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
|
{ KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
|
||||||
{ KEX_DH14, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
|
{ KEX_DH14, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
|
||||||
{ KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
|
{ KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
|
||||||
#ifdef HAVE_EVP_SHA256
|
|
||||||
{ KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
|
{ KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
|
||||||
#endif /* HAVE_EVP_SHA256 */
|
|
||||||
#ifdef OPENSSL_HAS_ECC
|
|
||||||
{ KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
|
{ KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
|
||||||
NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
|
NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
|
||||||
{ KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1,
|
{ KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1,
|
||||||
SSH_DIGEST_SHA384 },
|
SSH_DIGEST_SHA384 },
|
||||||
# ifdef OPENSSL_HAS_NISTP521
|
|
||||||
{ KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
|
{ KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
|
||||||
SSH_DIGEST_SHA512 },
|
SSH_DIGEST_SHA512 },
|
||||||
# endif /* OPENSSL_HAS_NISTP521 */
|
#endif
|
||||||
#endif /* OPENSSL_HAS_ECC */
|
|
||||||
#endif /* WITH_OPENSSL */
|
|
||||||
#if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL)
|
|
||||||
{ KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
|
{ KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
|
||||||
#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
|
|
||||||
{ NULL, -1, -1, -1 },
|
{ NULL, -1, -1, -1 },
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -267,7 +264,7 @@ kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp)
|
|||||||
for (i = 0; i < PROPOSAL_MAX; i++) {
|
for (i = 0; i < PROPOSAL_MAX; i++) {
|
||||||
if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0)
|
if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
debug2("kex_parse_kexinit: %s", proposal[i]);
|
debug2("%s: %s", proposal_names[i], proposal[i]);
|
||||||
}
|
}
|
||||||
/* first kex follows / reserved */
|
/* first kex follows / reserved */
|
||||||
if ((r = sshbuf_get_u8(b, &v)) != 0 ||
|
if ((r = sshbuf_get_u8(b, &v)) != 0 ||
|
||||||
@ -275,8 +272,8 @@ kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp)
|
|||||||
goto out;
|
goto out;
|
||||||
if (first_kex_follows != NULL)
|
if (first_kex_follows != NULL)
|
||||||
*first_kex_follows = i;
|
*first_kex_follows = i;
|
||||||
debug2("kex_parse_kexinit: first_kex_follows %d ", v);
|
debug2("first_kex_follows %d ", v);
|
||||||
debug2("kex_parse_kexinit: reserved %u ", i);
|
debug2("reserved %u ", i);
|
||||||
r = 0;
|
r = 0;
|
||||||
*propp = proposal;
|
*propp = proposal;
|
||||||
out:
|
out:
|
||||||
@ -302,7 +299,14 @@ kex_prop_free(char **proposal)
|
|||||||
static int
|
static int
|
||||||
kex_protocol_error(int type, u_int32_t seq, void *ctxt)
|
kex_protocol_error(int type, u_int32_t seq, void *ctxt)
|
||||||
{
|
{
|
||||||
error("Hm, kex protocol error: type %d seq %u", type, seq);
|
struct ssh *ssh = active_state; /* XXX */
|
||||||
|
int r;
|
||||||
|
|
||||||
|
error("kex protocol error: type %d seq %u", type, seq);
|
||||||
|
if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 ||
|
||||||
|
(r = sshpkt_put_u32(ssh, seq)) != 0 ||
|
||||||
|
(r = sshpkt_send(ssh)) != 0)
|
||||||
|
return r;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -468,7 +472,7 @@ kex_free_newkeys(struct newkeys *newkeys)
|
|||||||
newkeys->enc.key = NULL;
|
newkeys->enc.key = NULL;
|
||||||
}
|
}
|
||||||
if (newkeys->enc.iv) {
|
if (newkeys->enc.iv) {
|
||||||
explicit_bzero(newkeys->enc.iv, newkeys->enc.block_size);
|
explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len);
|
||||||
free(newkeys->enc.iv);
|
free(newkeys->enc.iv);
|
||||||
newkeys->enc.iv = NULL;
|
newkeys->enc.iv = NULL;
|
||||||
}
|
}
|
||||||
@ -494,13 +498,10 @@ kex_free(struct kex *kex)
|
|||||||
u_int mode;
|
u_int mode;
|
||||||
|
|
||||||
#ifdef WITH_OPENSSL
|
#ifdef WITH_OPENSSL
|
||||||
if (kex->dh)
|
sshdh_free(kex->dh);
|
||||||
DH_free(kex->dh);
|
|
||||||
#ifdef OPENSSL_HAS_ECC
|
|
||||||
if (kex->ec_client_key)
|
if (kex->ec_client_key)
|
||||||
EC_KEY_free(kex->ec_client_key);
|
EC_KEY_free(kex->ec_client_key);
|
||||||
#endif /* OPENSSL_HAS_ECC */
|
#endif
|
||||||
#endif /* WITH_OPENSSL */
|
|
||||||
for (mode = 0; mode < MODE_MAX; mode++) {
|
for (mode = 0; mode < MODE_MAX; mode++) {
|
||||||
kex_free_newkeys(kex->newkeys[mode]);
|
kex_free_newkeys(kex->newkeys[mode]);
|
||||||
kex->newkeys[mode] = NULL;
|
kex->newkeys[mode] = NULL;
|
||||||
@ -575,11 +576,14 @@ choose_comp(struct sshcomp *comp, char *client, char *server)
|
|||||||
return SSH_ERR_NO_COMPRESS_ALG_MATCH;
|
return SSH_ERR_NO_COMPRESS_ALG_MATCH;
|
||||||
if (strcmp(name, "zlib@openssh.com") == 0) {
|
if (strcmp(name, "zlib@openssh.com") == 0) {
|
||||||
comp->type = COMP_DELAYED;
|
comp->type = COMP_DELAYED;
|
||||||
} else if (strcmp(name, "zlib") == 0) {
|
}
|
||||||
|
else if (strcmp(name, "zlib") == 0) {
|
||||||
comp->type = COMP_ZLIB;
|
comp->type = COMP_ZLIB;
|
||||||
} else if (strcmp(name, "none") == 0) {
|
}
|
||||||
|
else if (strcmp(name, "none") == 0) {
|
||||||
comp->type = COMP_NONE;
|
comp->type = COMP_NONE;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return SSH_ERR_INTERNAL_ERROR;
|
return SSH_ERR_INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
comp->name = name;
|
comp->name = name;
|
||||||
@ -593,6 +597,7 @@ choose_kex(struct kex *k, char *client, char *server)
|
|||||||
|
|
||||||
k->name = match_list(client, server, NULL);
|
k->name = match_list(client, server, NULL);
|
||||||
|
|
||||||
|
debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
|
||||||
if (k->name == NULL)
|
if (k->name == NULL)
|
||||||
return SSH_ERR_NO_KEX_ALG_MATCH;
|
return SSH_ERR_NO_KEX_ALG_MATCH;
|
||||||
if ((kexalg = kex_alg_by_name(k->name)) == NULL)
|
if ((kexalg = kex_alg_by_name(k->name)) == NULL)
|
||||||
@ -608,6 +613,8 @@ choose_hostkeyalg(struct kex *k, char *client, char *server)
|
|||||||
{
|
{
|
||||||
char *hostkeyalg = match_list(client, server, NULL);
|
char *hostkeyalg = match_list(client, server, NULL);
|
||||||
|
|
||||||
|
debug("kex: host key algorithm: %s",
|
||||||
|
hostkeyalg ? hostkeyalg : "(no match)");
|
||||||
if (hostkeyalg == NULL)
|
if (hostkeyalg == NULL)
|
||||||
return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
|
return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
|
||||||
k->hostkey_type = sshkey_type_from_name(hostkeyalg);
|
k->hostkey_type = sshkey_type_from_name(hostkeyalg);
|
||||||
@ -653,14 +660,18 @@ kex_choose_conf(struct ssh *ssh)
|
|||||||
u_int mode, ctos, need, dh_need, authlen;
|
u_int mode, ctos, need, dh_need, authlen;
|
||||||
int r, first_kex_follows;
|
int r, first_kex_follows;
|
||||||
|
|
||||||
if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0 ||
|
debug2("local %s KEXINIT proposal", kex->server ? "server" : "client");
|
||||||
(r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
|
if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0)
|
||||||
|
goto out;
|
||||||
|
debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server");
|
||||||
|
if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (kex->server) {
|
if (kex->server) {
|
||||||
cprop = peer;
|
cprop = peer;
|
||||||
sprop = my;
|
sprop = my;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
cprop = my;
|
cprop = my;
|
||||||
sprop = peer;
|
sprop = peer;
|
||||||
}
|
}
|
||||||
@ -677,6 +688,18 @@ kex_choose_conf(struct ssh *ssh)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Algorithm Negotiation */
|
/* Algorithm Negotiation */
|
||||||
|
if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
|
||||||
|
sprop[PROPOSAL_KEX_ALGS])) != 0) {
|
||||||
|
kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
|
||||||
|
peer[PROPOSAL_KEX_ALGS] = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
|
||||||
|
sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
|
||||||
|
kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
|
||||||
|
peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
for (mode = 0; mode < MODE_MAX; mode++) {
|
for (mode = 0; mode < MODE_MAX; mode++) {
|
||||||
if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
|
if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
r = SSH_ERR_ALLOC_FAIL;
|
||||||
@ -709,24 +732,12 @@ kex_choose_conf(struct ssh *ssh)
|
|||||||
peer[ncomp] = NULL;
|
peer[ncomp] = NULL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
debug("kex: %s %s %s %s",
|
debug("kex: %s cipher: %s MAC: %s compression: %s",
|
||||||
ctos ? "client->server" : "server->client",
|
ctos ? "client->server" : "server->client",
|
||||||
newkeys->enc.name,
|
newkeys->enc.name,
|
||||||
authlen == 0 ? newkeys->mac.name : "<implicit>",
|
authlen == 0 ? newkeys->mac.name : "<implicit>",
|
||||||
newkeys->comp.name);
|
newkeys->comp.name);
|
||||||
}
|
}
|
||||||
if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
|
|
||||||
sprop[PROPOSAL_KEX_ALGS])) != 0) {
|
|
||||||
kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
|
|
||||||
peer[PROPOSAL_KEX_ALGS] = NULL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
|
|
||||||
sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
|
|
||||||
kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
|
|
||||||
peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
need = dh_need = 0;
|
need = dh_need = 0;
|
||||||
for (mode = 0; mode < MODE_MAX; mode++) {
|
for (mode = 0; mode < MODE_MAX; mode++) {
|
||||||
newkeys = kex->newkeys[mode];
|
newkeys = kex->newkeys[mode];
|
||||||
@ -849,14 +860,14 @@ kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
|
|||||||
#ifdef WITH_OPENSSL
|
#ifdef WITH_OPENSSL
|
||||||
int
|
int
|
||||||
kex_derive_keys_bn(struct ssh *ssh, u_char *hash, u_int hashlen,
|
kex_derive_keys_bn(struct ssh *ssh, u_char *hash, u_int hashlen,
|
||||||
const BIGNUM *secret)
|
const struct sshbn *secret)
|
||||||
{
|
{
|
||||||
struct sshbuf *shared_secret;
|
struct sshbuf *shared_secret;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if ((shared_secret = sshbuf_new()) == NULL)
|
if ((shared_secret = sshbuf_new()) == NULL)
|
||||||
return SSH_ERR_ALLOC_FAIL;
|
return SSH_ERR_ALLOC_FAIL;
|
||||||
if ((r = sshbuf_put_bignum2(shared_secret, secret)) == 0)
|
if ((r = sshbuf_put_bignum2_wrap(shared_secret, secret)) == 0)
|
||||||
r = kex_derive_keys(ssh, hash, hashlen, shared_secret);
|
r = kex_derive_keys(ssh, hash, hashlen, shared_secret);
|
||||||
sshbuf_free(shared_secret);
|
sshbuf_free(shared_secret);
|
||||||
return r;
|
return r;
|
||||||
|
32
kex.h
32
kex.h
@ -34,20 +34,6 @@
|
|||||||
#include "leakmalloc.h"
|
#include "leakmalloc.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_OPENSSL
|
|
||||||
# ifdef OPENSSL_HAS_ECC
|
|
||||||
# include <openssl/ec.h>
|
|
||||||
# else /* OPENSSL_HAS_ECC */
|
|
||||||
# define EC_KEY void
|
|
||||||
# define EC_GROUP void
|
|
||||||
# define EC_POINT void
|
|
||||||
# endif /* OPENSSL_HAS_ECC */
|
|
||||||
#else /* WITH_OPENSSL */
|
|
||||||
# define EC_KEY void
|
|
||||||
# define EC_GROUP void
|
|
||||||
# define EC_POINT void
|
|
||||||
#endif /* WITH_OPENSSL */
|
|
||||||
|
|
||||||
#define KEX_COOKIE_LEN 16
|
#define KEX_COOKIE_LEN 16
|
||||||
|
|
||||||
#define KEX_DH1 "diffie-hellman-group1-sha1"
|
#define KEX_DH1 "diffie-hellman-group1-sha1"
|
||||||
@ -150,7 +136,7 @@ struct kex {
|
|||||||
u_char **, size_t *, const u_char *, size_t, u_int);
|
u_char **, size_t *, const u_char *, size_t, u_int);
|
||||||
int(*kex[KEX_MAX])(struct ssh *);
|
int(*kex[KEX_MAX])(struct ssh *);
|
||||||
/* kex specific state */
|
/* kex specific state */
|
||||||
DH *dh; /* DH */
|
struct sshdh *dh; /* DH */
|
||||||
u_int min, max, nbits; /* GEX */
|
u_int min, max, nbits; /* GEX */
|
||||||
EC_KEY *ec_client_key; /* ECDH */
|
EC_KEY *ec_client_key; /* ECDH */
|
||||||
const EC_GROUP *ec_group; /* ECDH */
|
const EC_GROUP *ec_group; /* ECDH */
|
||||||
@ -175,7 +161,8 @@ void kex_prop_free(char **);
|
|||||||
int kex_send_kexinit(struct ssh *);
|
int kex_send_kexinit(struct ssh *);
|
||||||
int kex_input_kexinit(int, u_int32_t, void *);
|
int kex_input_kexinit(int, u_int32_t, void *);
|
||||||
int kex_derive_keys(struct ssh *, u_char *, u_int, const struct sshbuf *);
|
int kex_derive_keys(struct ssh *, u_char *, u_int, const struct sshbuf *);
|
||||||
int kex_derive_keys_bn(struct ssh *, u_char *, u_int, const BIGNUM *);
|
int kex_derive_keys_bn(struct ssh *, u_char *, u_int,
|
||||||
|
const struct sshbn *);
|
||||||
int kex_send_newkeys(struct ssh *);
|
int kex_send_newkeys(struct ssh *);
|
||||||
|
|
||||||
int kexdh_client(struct ssh *);
|
int kexdh_client(struct ssh *);
|
||||||
@ -189,13 +176,14 @@ int kexc25519_server(struct ssh *);
|
|||||||
|
|
||||||
int kex_dh_hash(const char *, const char *,
|
int kex_dh_hash(const char *, const char *,
|
||||||
const u_char *, size_t, const u_char *, size_t, const u_char *, size_t,
|
const u_char *, size_t, const u_char *, size_t, const u_char *, size_t,
|
||||||
const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *);
|
const struct sshbn *, const struct sshbn *,
|
||||||
|
const struct sshbn *, u_char *, size_t *);
|
||||||
|
|
||||||
int kexgex_hash(int, const char *, const char *,
|
int kexgex_hash(int, const char *, const char *,
|
||||||
const u_char *, size_t, const u_char *, size_t, const u_char *, size_t,
|
const u_char *, size_t, const u_char *, size_t, const u_char *, size_t,
|
||||||
int, int, int,
|
int, int, int,
|
||||||
const BIGNUM *, const BIGNUM *, const BIGNUM *,
|
const struct sshbn *, const struct sshbn *, const struct sshbn *,
|
||||||
const BIGNUM *, const BIGNUM *,
|
const struct sshbn *, const struct sshbn *,
|
||||||
u_char *, size_t *);
|
u_char *, size_t *);
|
||||||
|
|
||||||
int kex_ecdh_hash(int, const EC_GROUP *, const char *, const char *,
|
int kex_ecdh_hash(int, const EC_GROUP *, const char *, const char *,
|
||||||
@ -221,10 +209,4 @@ derive_ssh1_session_id(BIGNUM *, BIGNUM *, u_int8_t[8], u_int8_t[16]);
|
|||||||
void dump_digest(char *, u_char *, int);
|
void dump_digest(char *, u_char *, int);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(WITH_OPENSSL) || !defined(OPENSSL_HAS_ECC)
|
|
||||||
# undef EC_KEY
|
|
||||||
# undef EC_GROUP
|
|
||||||
# undef EC_POINT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
14
kexdh.c
14
kexdh.c
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
#include "includes.h"
|
#include "includes.h"
|
||||||
|
|
||||||
#ifdef WITH_OPENSSL
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
@ -48,9 +47,9 @@ kex_dh_hash(
|
|||||||
const u_char *ckexinit, size_t ckexinitlen,
|
const u_char *ckexinit, size_t ckexinitlen,
|
||||||
const u_char *skexinit, size_t skexinitlen,
|
const u_char *skexinit, size_t skexinitlen,
|
||||||
const u_char *serverhostkeyblob, size_t sbloblen,
|
const u_char *serverhostkeyblob, size_t sbloblen,
|
||||||
const BIGNUM *client_dh_pub,
|
const struct sshbn *client_dh_pub,
|
||||||
const BIGNUM *server_dh_pub,
|
const struct sshbn *server_dh_pub,
|
||||||
const BIGNUM *shared_secret,
|
const struct sshbn *shared_secret,
|
||||||
u_char *hash, size_t *hashlen)
|
u_char *hash, size_t *hashlen)
|
||||||
{
|
{
|
||||||
struct sshbuf *b;
|
struct sshbuf *b;
|
||||||
@ -70,9 +69,9 @@ kex_dh_hash(
|
|||||||
(r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 ||
|
(r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 ||
|
||||||
(r = sshbuf_put(b, skexinit, skexinitlen)) != 0 ||
|
(r = sshbuf_put(b, skexinit, skexinitlen)) != 0 ||
|
||||||
(r = sshbuf_put_string(b, serverhostkeyblob, sbloblen)) != 0 ||
|
(r = sshbuf_put_string(b, serverhostkeyblob, sbloblen)) != 0 ||
|
||||||
(r = sshbuf_put_bignum2(b, client_dh_pub)) != 0 ||
|
(r = sshbuf_put_bignum2_wrap(b, client_dh_pub)) != 0 ||
|
||||||
(r = sshbuf_put_bignum2(b, server_dh_pub)) != 0 ||
|
(r = sshbuf_put_bignum2_wrap(b, server_dh_pub)) != 0 ||
|
||||||
(r = sshbuf_put_bignum2(b, shared_secret)) != 0) {
|
(r = sshbuf_put_bignum2_wrap(b, shared_secret)) != 0) {
|
||||||
sshbuf_free(b);
|
sshbuf_free(b);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@ -90,4 +89,3 @@ kex_dh_hash(
|
|||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif /* WITH_OPENSSL */
|
|
||||||
|
73
kexdhc.c
73
kexdhc.c
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
#include "includes.h"
|
#include "includes.h"
|
||||||
|
|
||||||
#ifdef WITH_OPENSSL
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
@ -55,15 +54,18 @@ int
|
|||||||
kexdh_client(struct ssh *ssh)
|
kexdh_client(struct ssh *ssh)
|
||||||
{
|
{
|
||||||
struct kex *kex = ssh->kex;
|
struct kex *kex = ssh->kex;
|
||||||
|
struct sshbn *dh_client_pub = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
/* generate and send 'e', client DH public key */
|
/* generate and send 'e', client DH public key */
|
||||||
switch (kex->kex_type) {
|
switch (kex->kex_type) {
|
||||||
case KEX_DH_GRP1_SHA1:
|
case KEX_DH_GRP1_SHA1:
|
||||||
kex->dh = dh_new_group1();
|
if ((r = dh_new_group1(&kex->dh)) != 0)
|
||||||
|
return r;
|
||||||
break;
|
break;
|
||||||
case KEX_DH_GRP14_SHA1:
|
case KEX_DH_GRP14_SHA1:
|
||||||
kex->dh = dh_new_group14();
|
if ((r = dh_new_group14(&kex->dh)) != 0)
|
||||||
|
return r;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
r = SSH_ERR_INVALID_ARGUMENT;
|
r = SSH_ERR_INVALID_ARGUMENT;
|
||||||
@ -73,22 +75,25 @@ kexdh_client(struct ssh *ssh)
|
|||||||
r = SSH_ERR_ALLOC_FAIL;
|
r = SSH_ERR_ALLOC_FAIL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
|
||||||
|
goto out;
|
||||||
|
if ((dh_client_pub = sshdh_pubkey(kex->dh)) == NULL) {
|
||||||
|
r = SSH_ERR_ALLOC_FAIL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
debug("sending SSH2_MSG_KEXDH_INIT");
|
debug("sending SSH2_MSG_KEXDH_INIT");
|
||||||
if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0 ||
|
if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_INIT)) != 0 ||
|
||||||
(r = sshpkt_start(ssh, SSH2_MSG_KEXDH_INIT)) != 0 ||
|
(r = sshpkt_put_bignum2_wrap(ssh, dh_client_pub)) != 0 ||
|
||||||
(r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 ||
|
|
||||||
(r = sshpkt_send(ssh)) != 0)
|
(r = sshpkt_send(ssh)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
#ifdef DEBUG_KEXDH
|
#ifdef DEBUG_KEXDH
|
||||||
DHparams_print_fp(stderr, kex->dh);
|
sshdh_dump(kex->dh);
|
||||||
fprintf(stderr, "pub= ");
|
|
||||||
BN_print_fp(stderr, kex->dh->pub_key);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
#endif
|
#endif
|
||||||
debug("expecting SSH2_MSG_KEXDH_REPLY");
|
debug("expecting SSH2_MSG_KEXDH_REPLY");
|
||||||
ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_REPLY, &input_kex_dh);
|
ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_REPLY, &input_kex_dh);
|
||||||
r = 0;
|
r = 0;
|
||||||
out:
|
out:
|
||||||
|
sshbn_free(dh_client_pub);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,12 +102,14 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt)
|
|||||||
{
|
{
|
||||||
struct ssh *ssh = ctxt;
|
struct ssh *ssh = ctxt;
|
||||||
struct kex *kex = ssh->kex;
|
struct kex *kex = ssh->kex;
|
||||||
BIGNUM *dh_server_pub = NULL, *shared_secret = NULL;
|
struct sshbn *dh_client_pub = NULL;
|
||||||
|
struct sshbn *dh_server_pub = NULL;
|
||||||
|
struct sshbn *shared_secret = NULL;
|
||||||
struct sshkey *server_host_key = NULL;
|
struct sshkey *server_host_key = NULL;
|
||||||
u_char *kbuf = NULL, *server_host_key_blob = NULL, *signature = NULL;
|
u_char *server_host_key_blob = NULL, *signature = NULL;
|
||||||
u_char hash[SSH_DIGEST_MAX_LENGTH];
|
u_char hash[SSH_DIGEST_MAX_LENGTH];
|
||||||
size_t klen = 0, slen, sbloblen, hashlen;
|
size_t slen, sbloblen, hashlen;
|
||||||
int kout, r;
|
int r;
|
||||||
|
|
||||||
if (kex->verify_host_key == NULL) {
|
if (kex->verify_host_key == NULL) {
|
||||||
r = SSH_ERR_INVALID_ARGUMENT;
|
r = SSH_ERR_INVALID_ARGUMENT;
|
||||||
@ -125,12 +132,12 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
/* DH parameter f, server public DH key */
|
/* DH parameter f, server public DH key */
|
||||||
if ((dh_server_pub = BN_new()) == NULL) {
|
if ((dh_server_pub = sshbn_new()) == NULL) {
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
r = SSH_ERR_ALLOC_FAIL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
/* signed H */
|
/* signed H */
|
||||||
if ((r = sshpkt_get_bignum2(ssh, dh_server_pub)) != 0 ||
|
if ((r = sshpkt_get_bignum2_wrap(ssh, dh_server_pub)) != 0 ||
|
||||||
(r = sshpkt_get_string(ssh, &signature, &slen)) != 0 ||
|
(r = sshpkt_get_string(ssh, &signature, &slen)) != 0 ||
|
||||||
(r = sshpkt_get_end(ssh)) != 0)
|
(r = sshpkt_get_end(ssh)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
@ -140,23 +147,17 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt)
|
|||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
debug("bits %d", BN_num_bits(dh_server_pub));
|
debug("bits %d", BN_num_bits(dh_server_pub));
|
||||||
#endif
|
#endif
|
||||||
if (!dh_pub_is_valid(kex->dh, dh_server_pub)) {
|
if ((r = dh_pub_is_valid(kex->dh, dh_server_pub)) != 0) {
|
||||||
sshpkt_disconnect(ssh, "bad server public DH value");
|
sshpkt_disconnect(ssh, "bad server public DH value");
|
||||||
r = SSH_ERR_MESSAGE_INCOMPLETE;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
if ((dh_client_pub = sshdh_pubkey(kex->dh)) == NULL) {
|
||||||
klen = DH_size(kex->dh);
|
r = SSH_ERR_INTERNAL_ERROR;
|
||||||
if ((kbuf = malloc(klen)) == NULL ||
|
|
||||||
(shared_secret = BN_new()) == NULL) {
|
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if ((kout = DH_compute_key(kbuf, dh_server_pub, kex->dh)) < 0 ||
|
if ((r = sshdh_compute_key(kex->dh, dh_server_pub,
|
||||||
BN_bin2bn(kbuf, kout, shared_secret) == NULL) {
|
&shared_secret)) != 0)
|
||||||
r = SSH_ERR_LIBCRYPTO_ERROR;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
#ifdef DEBUG_KEXDH
|
#ifdef DEBUG_KEXDH
|
||||||
dump_digest("shared secret", kbuf, kout);
|
dump_digest("shared secret", kbuf, kout);
|
||||||
#endif
|
#endif
|
||||||
@ -169,7 +170,7 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt)
|
|||||||
sshbuf_ptr(kex->my), sshbuf_len(kex->my),
|
sshbuf_ptr(kex->my), sshbuf_len(kex->my),
|
||||||
sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
|
sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
|
||||||
server_host_key_blob, sbloblen,
|
server_host_key_blob, sbloblen,
|
||||||
kex->dh->pub_key,
|
dh_client_pub,
|
||||||
dh_server_pub,
|
dh_server_pub,
|
||||||
shared_secret,
|
shared_secret,
|
||||||
hash, &hashlen)) != 0)
|
hash, &hashlen)) != 0)
|
||||||
@ -194,19 +195,13 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt)
|
|||||||
r = kex_send_newkeys(ssh);
|
r = kex_send_newkeys(ssh);
|
||||||
out:
|
out:
|
||||||
explicit_bzero(hash, sizeof(hash));
|
explicit_bzero(hash, sizeof(hash));
|
||||||
DH_free(kex->dh);
|
|
||||||
kex->dh = NULL;
|
|
||||||
if (dh_server_pub)
|
|
||||||
BN_clear_free(dh_server_pub);
|
|
||||||
if (kbuf) {
|
|
||||||
explicit_bzero(kbuf, klen);
|
|
||||||
free(kbuf);
|
|
||||||
}
|
|
||||||
if (shared_secret)
|
|
||||||
BN_clear_free(shared_secret);
|
|
||||||
sshkey_free(server_host_key);
|
sshkey_free(server_host_key);
|
||||||
|
sshbn_free(shared_secret);
|
||||||
|
sshbn_free(dh_server_pub);
|
||||||
|
sshbn_free(dh_client_pub);
|
||||||
|
sshdh_free(kex->dh);
|
||||||
|
kex->dh = NULL;
|
||||||
free(server_host_key_blob);
|
free(server_host_key_blob);
|
||||||
free(signature);
|
free(signature);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
#endif /* WITH_OPENSSL */
|
|
||||||
|
77
kexdhs.c
77
kexdhs.c
@ -34,7 +34,6 @@
|
|||||||
#undef KRB5
|
#undef KRB5
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_OPENSSL
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
@ -69,27 +68,24 @@ kexdh_server(struct ssh *ssh)
|
|||||||
/* generate server DH public key */
|
/* generate server DH public key */
|
||||||
switch (kex->kex_type) {
|
switch (kex->kex_type) {
|
||||||
case KEX_DH_GRP1_SHA1:
|
case KEX_DH_GRP1_SHA1:
|
||||||
kex->dh = dh_new_group1();
|
if ((r = dh_new_group1(&kex->dh)) != 0)
|
||||||
|
return r;
|
||||||
break;
|
break;
|
||||||
case KEX_DH_GRP14_SHA1:
|
case KEX_DH_GRP14_SHA1:
|
||||||
kex->dh = dh_new_group14();
|
if ((r = dh_new_group14(&kex->dh)) != 0)
|
||||||
|
return r;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
r = SSH_ERR_INVALID_ARGUMENT;
|
return SSH_ERR_INVALID_ARGUMENT;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (kex->dh == NULL) {
|
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
if (kex->dh == NULL)
|
||||||
|
return SSH_ERR_ALLOC_FAIL;
|
||||||
if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
|
if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
|
||||||
goto out;
|
return r;
|
||||||
|
|
||||||
debug("expecting SSH2_MSG_KEXDH_INIT");
|
debug("expecting SSH2_MSG_KEXDH_INIT");
|
||||||
ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_INIT, &input_kex_dh_init);
|
ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_INIT, &input_kex_dh_init);
|
||||||
r = 0;
|
return 0;
|
||||||
out:
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -97,13 +93,15 @@ input_kex_dh_init(int type, u_int32_t seq, void *ctxt)
|
|||||||
{
|
{
|
||||||
struct ssh *ssh = ctxt;
|
struct ssh *ssh = ctxt;
|
||||||
struct kex *kex = ssh->kex;
|
struct kex *kex = ssh->kex;
|
||||||
BIGNUM *shared_secret = NULL, *dh_client_pub = NULL;
|
struct sshbn *dh_client_pub = NULL;
|
||||||
|
struct sshbn *dh_server_pub = NULL;
|
||||||
|
struct sshbn *shared_secret = NULL;
|
||||||
struct sshkey *server_host_public, *server_host_private;
|
struct sshkey *server_host_public, *server_host_private;
|
||||||
u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL;
|
u_char *signature = NULL, *server_host_key_blob = NULL;
|
||||||
u_char hash[SSH_DIGEST_MAX_LENGTH];
|
u_char hash[SSH_DIGEST_MAX_LENGTH];
|
||||||
size_t sbloblen, slen;
|
size_t sbloblen, slen;
|
||||||
size_t klen = 0, hashlen;
|
size_t hashlen;
|
||||||
int kout, r;
|
int r;
|
||||||
|
|
||||||
if (kex->load_host_public_key == NULL ||
|
if (kex->load_host_public_key == NULL ||
|
||||||
kex->load_host_private_key == NULL) {
|
kex->load_host_private_key == NULL) {
|
||||||
@ -120,11 +118,11 @@ input_kex_dh_init(int type, u_int32_t seq, void *ctxt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* key, cert */
|
/* key, cert */
|
||||||
if ((dh_client_pub = BN_new()) == NULL) {
|
if ((dh_client_pub = sshbn_new()) == NULL) {
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
r = SSH_ERR_ALLOC_FAIL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if ((r = sshpkt_get_bignum2(ssh, dh_client_pub)) != 0 ||
|
if ((r = sshpkt_get_bignum2_wrap(ssh, dh_client_pub)) != 0 ||
|
||||||
(r = sshpkt_get_end(ssh)) != 0)
|
(r = sshpkt_get_end(ssh)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -136,30 +134,21 @@ input_kex_dh_init(int type, u_int32_t seq, void *ctxt)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef DEBUG_KEXDH
|
#ifdef DEBUG_KEXDH
|
||||||
DHparams_print_fp(stderr, kex->dh);
|
sshdh_dump(kex->dh);
|
||||||
fprintf(stderr, "pub= ");
|
|
||||||
BN_print_fp(stderr, kex->dh->pub_key);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
#endif
|
#endif
|
||||||
if (!dh_pub_is_valid(kex->dh, dh_client_pub)) {
|
if ((r = dh_pub_is_valid(kex->dh, dh_client_pub)) != 0) {
|
||||||
sshpkt_disconnect(ssh, "bad client public DH value");
|
sshpkt_disconnect(ssh, "bad client public DH value");
|
||||||
r = SSH_ERR_MESSAGE_INCOMPLETE;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
if ((dh_server_pub = sshdh_pubkey(kex->dh)) == NULL) {
|
||||||
klen = DH_size(kex->dh);
|
r = SSH_ERR_INTERNAL_ERROR;
|
||||||
if ((kbuf = malloc(klen)) == NULL ||
|
|
||||||
(shared_secret = BN_new()) == NULL) {
|
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if ((kout = DH_compute_key(kbuf, dh_client_pub, kex->dh)) < 0 ||
|
if ((r = sshdh_compute_key(kex->dh, dh_client_pub,
|
||||||
BN_bin2bn(kbuf, kout, shared_secret) == NULL) {
|
&shared_secret)) != 0)
|
||||||
r = SSH_ERR_LIBCRYPTO_ERROR;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
#ifdef DEBUG_KEXDH
|
#ifdef DEBUG_KEXDH
|
||||||
dump_digest("shared secret", kbuf, kout);
|
dump_digest("shared secret", kbuf, klen);
|
||||||
#endif
|
#endif
|
||||||
if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob,
|
if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob,
|
||||||
&sbloblen)) != 0)
|
&sbloblen)) != 0)
|
||||||
@ -173,7 +162,7 @@ input_kex_dh_init(int type, u_int32_t seq, void *ctxt)
|
|||||||
sshbuf_ptr(kex->my), sshbuf_len(kex->my),
|
sshbuf_ptr(kex->my), sshbuf_len(kex->my),
|
||||||
server_host_key_blob, sbloblen,
|
server_host_key_blob, sbloblen,
|
||||||
dh_client_pub,
|
dh_client_pub,
|
||||||
kex->dh->pub_key,
|
dh_server_pub,
|
||||||
shared_secret,
|
shared_secret,
|
||||||
hash, &hashlen)) != 0)
|
hash, &hashlen)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
@ -199,7 +188,7 @@ input_kex_dh_init(int type, u_int32_t seq, void *ctxt)
|
|||||||
/* send server hostkey, DH pubkey 'f' and singed H */
|
/* send server hostkey, DH pubkey 'f' and singed H */
|
||||||
if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_REPLY)) != 0 ||
|
if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_REPLY)) != 0 ||
|
||||||
(r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 ||
|
(r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 ||
|
||||||
(r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || /* f */
|
(r = sshpkt_put_bignum2_wrap(ssh, dh_server_pub)) != 0 || /* f */
|
||||||
(r = sshpkt_put_string(ssh, signature, slen)) != 0 ||
|
(r = sshpkt_put_string(ssh, signature, slen)) != 0 ||
|
||||||
(r = sshpkt_send(ssh)) != 0)
|
(r = sshpkt_send(ssh)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
@ -208,18 +197,12 @@ input_kex_dh_init(int type, u_int32_t seq, void *ctxt)
|
|||||||
r = kex_send_newkeys(ssh);
|
r = kex_send_newkeys(ssh);
|
||||||
out:
|
out:
|
||||||
explicit_bzero(hash, sizeof(hash));
|
explicit_bzero(hash, sizeof(hash));
|
||||||
DH_free(kex->dh);
|
sshbn_free(shared_secret);
|
||||||
|
sshbn_free(dh_client_pub);
|
||||||
|
sshbn_free(dh_server_pub);
|
||||||
|
sshdh_free(kex->dh);
|
||||||
kex->dh = NULL;
|
kex->dh = NULL;
|
||||||
if (dh_client_pub)
|
|
||||||
BN_clear_free(dh_client_pub);
|
|
||||||
if (kbuf) {
|
|
||||||
explicit_bzero(kbuf, klen);
|
|
||||||
free(kbuf);
|
|
||||||
}
|
|
||||||
if (shared_secret)
|
|
||||||
BN_clear_free(shared_secret);
|
|
||||||
free(server_host_key_blob);
|
free(server_host_key_blob);
|
||||||
free(signature);
|
free(signature);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
#endif /* WITH_OPENSSL */
|
|
||||||
|
14
kexecdhc.c
14
kexecdhc.c
@ -26,8 +26,6 @@
|
|||||||
|
|
||||||
#include "includes.h"
|
#include "includes.h"
|
||||||
|
|
||||||
#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -103,6 +101,7 @@ input_kex_ecdh_reply(int type, u_int32_t seq, void *ctxt)
|
|||||||
EC_POINT *server_public = NULL;
|
EC_POINT *server_public = NULL;
|
||||||
EC_KEY *client_key;
|
EC_KEY *client_key;
|
||||||
BIGNUM *shared_secret = NULL;
|
BIGNUM *shared_secret = NULL;
|
||||||
|
struct sshbn *xxx_shared_secret = NULL;
|
||||||
struct sshkey *server_host_key = NULL;
|
struct sshkey *server_host_key = NULL;
|
||||||
u_char *server_host_key_blob = NULL, *signature = NULL;
|
u_char *server_host_key_blob = NULL, *signature = NULL;
|
||||||
u_char *kbuf = NULL;
|
u_char *kbuf = NULL;
|
||||||
@ -203,7 +202,13 @@ input_kex_ecdh_reply(int type, u_int32_t seq, void *ctxt)
|
|||||||
memcpy(kex->session_id, hash, kex->session_id_len);
|
memcpy(kex->session_id, hash, kex->session_id_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
|
/* XXX */
|
||||||
|
if ((xxx_shared_secret = sshbn_from_bignum(shared_secret)) == NULL) {
|
||||||
|
r = SSH_ERR_ALLOC_FAIL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if ((r = kex_derive_keys_bn(ssh, hash, hashlen,
|
||||||
|
xxx_shared_secret)) == 0)
|
||||||
r = kex_send_newkeys(ssh);
|
r = kex_send_newkeys(ssh);
|
||||||
out:
|
out:
|
||||||
explicit_bzero(hash, sizeof(hash));
|
explicit_bzero(hash, sizeof(hash));
|
||||||
@ -219,10 +224,9 @@ input_kex_ecdh_reply(int type, u_int32_t seq, void *ctxt)
|
|||||||
}
|
}
|
||||||
if (shared_secret)
|
if (shared_secret)
|
||||||
BN_clear_free(shared_secret);
|
BN_clear_free(shared_secret);
|
||||||
|
sshbn_free(xxx_shared_secret);
|
||||||
sshkey_free(server_host_key);
|
sshkey_free(server_host_key);
|
||||||
free(server_host_key_blob);
|
free(server_host_key_blob);
|
||||||
free(signature);
|
free(signature);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
#endif /* defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) */
|
|
||||||
|
|
||||||
|
15
kexecdhs.c
15
kexecdhs.c
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
#include "includes.h"
|
#include "includes.h"
|
||||||
|
|
||||||
#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -67,6 +67,7 @@ input_kex_ecdh_init(int type, u_int32_t seq, void *ctxt)
|
|||||||
const EC_GROUP *group;
|
const EC_GROUP *group;
|
||||||
const EC_POINT *public_key;
|
const EC_POINT *public_key;
|
||||||
BIGNUM *shared_secret = NULL;
|
BIGNUM *shared_secret = NULL;
|
||||||
|
struct sshbn *xxx_shared_secret = NULL;
|
||||||
struct sshkey *server_host_private, *server_host_public;
|
struct sshkey *server_host_private, *server_host_public;
|
||||||
u_char *server_host_key_blob = NULL, *signature = NULL;
|
u_char *server_host_key_blob = NULL, *signature = NULL;
|
||||||
u_char *kbuf = NULL;
|
u_char *kbuf = NULL;
|
||||||
@ -183,8 +184,13 @@ input_kex_ecdh_init(int type, u_int32_t seq, void *ctxt)
|
|||||||
(r = sshpkt_put_string(ssh, signature, slen)) != 0 ||
|
(r = sshpkt_put_string(ssh, signature, slen)) != 0 ||
|
||||||
(r = sshpkt_send(ssh)) != 0)
|
(r = sshpkt_send(ssh)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
/* XXX */
|
||||||
if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
|
if ((xxx_shared_secret = sshbn_from_bignum(shared_secret)) == NULL) {
|
||||||
|
r = SSH_ERR_ALLOC_FAIL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if ((r = kex_derive_keys_bn(ssh, hash, hashlen,
|
||||||
|
xxx_shared_secret)) == 0)
|
||||||
r = kex_send_newkeys(ssh);
|
r = kex_send_newkeys(ssh);
|
||||||
out:
|
out:
|
||||||
explicit_bzero(hash, sizeof(hash));
|
explicit_bzero(hash, sizeof(hash));
|
||||||
@ -200,9 +206,8 @@ input_kex_ecdh_init(int type, u_int32_t seq, void *ctxt)
|
|||||||
}
|
}
|
||||||
if (shared_secret)
|
if (shared_secret)
|
||||||
BN_clear_free(shared_secret);
|
BN_clear_free(shared_secret);
|
||||||
|
sshbn_free(xxx_shared_secret);
|
||||||
free(server_host_key_blob);
|
free(server_host_key_blob);
|
||||||
free(signature);
|
free(signature);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
#endif /* defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) */
|
|
||||||
|
|
||||||
|
64
kexgex.c
64
kexgex.c
@ -35,8 +35,6 @@
|
|||||||
#undef KRB5
|
#undef KRB5
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_OPENSSL
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
@ -49,9 +47,70 @@
|
|||||||
#include "ssherr.h"
|
#include "ssherr.h"
|
||||||
#include "sshbuf.h"
|
#include "sshbuf.h"
|
||||||
#include "digest.h"
|
#include "digest.h"
|
||||||
|
#include "crypto-wrap.h"
|
||||||
|
|
||||||
int
|
int
|
||||||
kexgex_hash(
|
kexgex_hash(
|
||||||
|
int hash_alg,
|
||||||
|
const char *client_version_string,
|
||||||
|
const char *server_version_string,
|
||||||
|
const u_char *ckexinit, size_t ckexinitlen,
|
||||||
|
const u_char *skexinit, size_t skexinitlen,
|
||||||
|
const u_char *serverhostkeyblob, size_t sbloblen,
|
||||||
|
int min, int wantbits, int max,
|
||||||
|
const struct sshbn *prime,
|
||||||
|
const struct sshbn *gen,
|
||||||
|
const struct sshbn *client_dh_pub,
|
||||||
|
const struct sshbn *server_dh_pub,
|
||||||
|
const struct sshbn *shared_secret,
|
||||||
|
u_char *hash, size_t *hashlen)
|
||||||
|
{
|
||||||
|
struct sshbuf *b;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (*hashlen < ssh_digest_bytes(SSH_DIGEST_SHA1))
|
||||||
|
return SSH_ERR_INVALID_ARGUMENT;
|
||||||
|
if ((b = sshbuf_new()) == NULL)
|
||||||
|
return SSH_ERR_ALLOC_FAIL;
|
||||||
|
if ((r = sshbuf_put_cstring(b, client_version_string)) != 0 ||
|
||||||
|
(r = sshbuf_put_cstring(b, server_version_string)) != 0 ||
|
||||||
|
/* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */
|
||||||
|
(r = sshbuf_put_u32(b, ckexinitlen + 1)) != 0 ||
|
||||||
|
(r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 ||
|
||||||
|
(r = sshbuf_put(b, ckexinit, ckexinitlen)) != 0 ||
|
||||||
|
(r = sshbuf_put_u32(b, skexinitlen + 1)) != 0 ||
|
||||||
|
(r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 ||
|
||||||
|
(r = sshbuf_put(b, skexinit, skexinitlen)) != 0 ||
|
||||||
|
(r = sshbuf_put_string(b, serverhostkeyblob, sbloblen)) != 0 ||
|
||||||
|
(min != -1 && (r = sshbuf_put_u32(b, min)) != 0) ||
|
||||||
|
(r = sshbuf_put_u32(b, wantbits)) != 0 ||
|
||||||
|
(max != -1 && (r = sshbuf_put_u32(b, max)) != 0) ||
|
||||||
|
(r = sshbuf_put_bignum2_wrap(b, prime)) != 0 ||
|
||||||
|
(r = sshbuf_put_bignum2_wrap(b, gen)) != 0 ||
|
||||||
|
(r = sshbuf_put_bignum2_wrap(b, client_dh_pub)) != 0 ||
|
||||||
|
(r = sshbuf_put_bignum2_wrap(b, server_dh_pub)) != 0 ||
|
||||||
|
(r = sshbuf_put_bignum2_wrap(b, shared_secret)) != 0) {
|
||||||
|
sshbuf_free(b);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
#ifdef DEBUG_KEXDH
|
||||||
|
sshbuf_dump(b, stderr);
|
||||||
|
#endif
|
||||||
|
if (ssh_digest_buffer(hash_alg, b, hash, *hashlen) != 0) {
|
||||||
|
sshbuf_free(b);
|
||||||
|
return SSH_ERR_LIBCRYPTO_ERROR;
|
||||||
|
}
|
||||||
|
sshbuf_free(b);
|
||||||
|
*hashlen = ssh_digest_bytes(hash_alg);
|
||||||
|
#ifdef DEBUG_KEXDH
|
||||||
|
dump_digest("hash", hash, *hashlen);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
kexgex_hash_old(
|
||||||
int hash_alg,
|
int hash_alg,
|
||||||
const char *client_version_string,
|
const char *client_version_string,
|
||||||
const char *server_version_string,
|
const char *server_version_string,
|
||||||
@ -108,4 +167,3 @@ kexgex_hash(
|
|||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif /* WITH_OPENSSL */
|
|
||||||
|
119
kexgexc.c
119
kexgexc.c
@ -26,14 +26,11 @@
|
|||||||
|
|
||||||
#include "includes.h"
|
#include "includes.h"
|
||||||
|
|
||||||
#ifdef WITH_OPENSSL
|
|
||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <openssl/dh.h>
|
#include <openssl/dh.h>
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@ -54,6 +51,23 @@
|
|||||||
static int input_kex_dh_gex_group(int, u_int32_t, void *);
|
static int input_kex_dh_gex_group(int, u_int32_t, void *);
|
||||||
static int input_kex_dh_gex_reply(int, u_int32_t, void *);
|
static int input_kex_dh_gex_reply(int, u_int32_t, void *);
|
||||||
|
|
||||||
|
int
|
||||||
|
kexgex_hash_old(
|
||||||
|
int hash_alg,
|
||||||
|
const char *client_version_string,
|
||||||
|
const char *server_version_string,
|
||||||
|
const u_char *ckexinit, size_t ckexinitlen,
|
||||||
|
const u_char *skexinit, size_t skexinitlen,
|
||||||
|
const u_char *serverhostkeyblob, size_t sbloblen,
|
||||||
|
int min, int wantbits, int max,
|
||||||
|
const BIGNUM *prime,
|
||||||
|
const BIGNUM *gen,
|
||||||
|
const BIGNUM *client_dh_pub,
|
||||||
|
const BIGNUM *server_dh_pub,
|
||||||
|
const BIGNUM *shared_secret,
|
||||||
|
u_char *hash, size_t *hashlen);
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
kexgex_client(struct ssh *ssh)
|
kexgex_client(struct ssh *ssh)
|
||||||
{
|
{
|
||||||
@ -93,52 +107,55 @@ input_kex_dh_gex_group(int type, u_int32_t seq, void *ctxt)
|
|||||||
{
|
{
|
||||||
struct ssh *ssh = ctxt;
|
struct ssh *ssh = ctxt;
|
||||||
struct kex *kex = ssh->kex;
|
struct kex *kex = ssh->kex;
|
||||||
BIGNUM *p = NULL, *g = NULL;
|
struct sshbn *dh_client_pub = NULL;
|
||||||
int r, bits;
|
struct sshbn *dh_g = NULL, *dh_p = NULL;
|
||||||
|
int r;
|
||||||
|
size_t bits;
|
||||||
|
|
||||||
debug("got SSH2_MSG_KEX_DH_GEX_GROUP");
|
debug("got SSH2_MSG_KEX_DH_GEX_GROUP");
|
||||||
|
|
||||||
if ((p = BN_new()) == NULL ||
|
if ((dh_p = sshbn_new()) == NULL ||
|
||||||
(g = BN_new()) == NULL) {
|
(dh_g = sshbn_new()) == NULL) {
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
r = SSH_ERR_ALLOC_FAIL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if ((r = sshpkt_get_bignum2(ssh, p)) != 0 ||
|
if ((r = sshpkt_get_bignum2_wrap(ssh, dh_p)) != 0 ||
|
||||||
(r = sshpkt_get_bignum2(ssh, g)) != 0 ||
|
(r = sshpkt_get_bignum2_wrap(ssh, dh_g)) != 0 ||
|
||||||
(r = sshpkt_get_end(ssh)) != 0)
|
(r = sshpkt_get_end(ssh)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
if ((bits = BN_num_bits(p)) < 0 ||
|
if ((bits = sshbn_bits(dh_p)) == 0 ||
|
||||||
(u_int)bits < kex->min || (u_int)bits > kex->max) {
|
bits < kex->min || bits > kex->max) {
|
||||||
r = SSH_ERR_DH_GEX_OUT_OF_RANGE;
|
r = SSH_ERR_DH_GEX_OUT_OF_RANGE;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if ((kex->dh = dh_new_group(g, p)) == NULL) {
|
if ((kex->dh = sshdh_new_group(dh_g, dh_p)) == NULL) {
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
r = SSH_ERR_ALLOC_FAIL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
p = g = NULL; /* belong to kex->dh now */
|
dh_p = dh_g = NULL; /* belong to kex->dh now */
|
||||||
|
|
||||||
/* generate and send 'e', client DH public key */
|
/* generate and send 'e', client DH public key */
|
||||||
if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0 ||
|
if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
|
||||||
(r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_INIT)) != 0 ||
|
goto out;
|
||||||
(r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 ||
|
if ((dh_client_pub = sshdh_pubkey(kex->dh)) == NULL) {
|
||||||
|
r = SSH_ERR_INTERNAL_ERROR;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_INIT)) != 0 ||
|
||||||
|
(r = sshpkt_put_bignum2_wrap(ssh, dh_client_pub)) != 0 ||
|
||||||
(r = sshpkt_send(ssh)) != 0)
|
(r = sshpkt_send(ssh)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
debug("SSH2_MSG_KEX_DH_GEX_INIT sent");
|
debug("SSH2_MSG_KEX_DH_GEX_INIT sent");
|
||||||
#ifdef DEBUG_KEXDH
|
#ifdef DEBUG_KEXDH
|
||||||
DHparams_print_fp(stderr, kex->dh);
|
sshdh_dump(kex->dh);
|
||||||
fprintf(stderr, "pub= ");
|
|
||||||
BN_print_fp(stderr, kex->dh->pub_key);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
#endif
|
#endif
|
||||||
ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP, NULL);
|
ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP, NULL);
|
||||||
ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REPLY, &input_kex_dh_gex_reply);
|
ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REPLY, &input_kex_dh_gex_reply);
|
||||||
r = 0;
|
r = 0;
|
||||||
out:
|
out:
|
||||||
if (p)
|
sshbn_free(dh_p);
|
||||||
BN_clear_free(p);
|
sshbn_free(dh_g);
|
||||||
if (g)
|
sshbn_free(dh_client_pub);
|
||||||
BN_clear_free(g);
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,12 +164,15 @@ input_kex_dh_gex_reply(int type, u_int32_t seq, void *ctxt)
|
|||||||
{
|
{
|
||||||
struct ssh *ssh = ctxt;
|
struct ssh *ssh = ctxt;
|
||||||
struct kex *kex = ssh->kex;
|
struct kex *kex = ssh->kex;
|
||||||
BIGNUM *dh_server_pub = NULL, *shared_secret = NULL;
|
struct sshbn *dh_g = NULL, *dh_p = NULL;
|
||||||
|
struct sshbn *dh_client_pub = NULL;
|
||||||
|
struct sshbn *dh_server_pub = NULL;
|
||||||
|
struct sshbn *shared_secret = NULL;
|
||||||
struct sshkey *server_host_key = NULL;
|
struct sshkey *server_host_key = NULL;
|
||||||
u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL;
|
u_char *signature = NULL, *server_host_key_blob = NULL;
|
||||||
u_char hash[SSH_DIGEST_MAX_LENGTH];
|
u_char hash[SSH_DIGEST_MAX_LENGTH];
|
||||||
size_t klen = 0, slen, sbloblen, hashlen;
|
size_t slen, sbloblen, hashlen;
|
||||||
int kout, r;
|
int r;
|
||||||
|
|
||||||
debug("got SSH2_MSG_KEX_DH_GEX_REPLY");
|
debug("got SSH2_MSG_KEX_DH_GEX_REPLY");
|
||||||
if (kex->verify_host_key == NULL) {
|
if (kex->verify_host_key == NULL) {
|
||||||
@ -180,12 +200,12 @@ input_kex_dh_gex_reply(int type, u_int32_t seq, void *ctxt)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
/* DH parameter f, server public DH key */
|
/* DH parameter f, server public DH key */
|
||||||
if ((dh_server_pub = BN_new()) == NULL) {
|
if ((dh_server_pub = sshbn_new()) == NULL) {
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
r = SSH_ERR_ALLOC_FAIL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
/* signed H */
|
/* signed H */
|
||||||
if ((r = sshpkt_get_bignum2(ssh, dh_server_pub)) != 0 ||
|
if ((r = sshpkt_get_bignum2_wrap(ssh, dh_server_pub)) != 0 ||
|
||||||
(r = sshpkt_get_string(ssh, &signature, &slen)) != 0 ||
|
(r = sshpkt_get_string(ssh, &signature, &slen)) != 0 ||
|
||||||
(r = sshpkt_get_end(ssh)) != 0)
|
(r = sshpkt_get_end(ssh)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
@ -195,23 +215,19 @@ input_kex_dh_gex_reply(int type, u_int32_t seq, void *ctxt)
|
|||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
debug("bits %d", BN_num_bits(dh_server_pub));
|
debug("bits %d", BN_num_bits(dh_server_pub));
|
||||||
#endif
|
#endif
|
||||||
if (!dh_pub_is_valid(kex->dh, dh_server_pub)) {
|
if ((r = dh_pub_is_valid(kex->dh, dh_server_pub)) != 0) {
|
||||||
sshpkt_disconnect(ssh, "bad server public DH value");
|
sshpkt_disconnect(ssh, "bad server public DH value");
|
||||||
r = SSH_ERR_MESSAGE_INCOMPLETE;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
if ((dh_client_pub = sshdh_pubkey(kex->dh)) == NULL ||
|
||||||
klen = DH_size(kex->dh);
|
(dh_p = sshdh_p(kex->dh)) == NULL ||
|
||||||
if ((kbuf = malloc(klen)) == NULL ||
|
(dh_g = sshdh_g(kex->dh)) == NULL) {
|
||||||
(shared_secret = BN_new()) == NULL) {
|
r = SSH_ERR_INTERNAL_ERROR;
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if ((kout = DH_compute_key(kbuf, dh_server_pub, kex->dh)) < 0 ||
|
if ((r = sshdh_compute_key(kex->dh, dh_server_pub,
|
||||||
BN_bin2bn(kbuf, kout, shared_secret) == NULL) {
|
&shared_secret)) != 0)
|
||||||
r = SSH_ERR_LIBCRYPTO_ERROR;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
#ifdef DEBUG_KEXDH
|
#ifdef DEBUG_KEXDH
|
||||||
dump_digest("shared secret", kbuf, kout);
|
dump_digest("shared secret", kbuf, kout);
|
||||||
#endif
|
#endif
|
||||||
@ -228,8 +244,8 @@ input_kex_dh_gex_reply(int type, u_int32_t seq, void *ctxt)
|
|||||||
sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
|
sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
|
||||||
server_host_key_blob, sbloblen,
|
server_host_key_blob, sbloblen,
|
||||||
kex->min, kex->nbits, kex->max,
|
kex->min, kex->nbits, kex->max,
|
||||||
kex->dh->p, kex->dh->g,
|
dh_p, dh_g,
|
||||||
kex->dh->pub_key,
|
dh_client_pub,
|
||||||
dh_server_pub,
|
dh_server_pub,
|
||||||
shared_secret,
|
shared_secret,
|
||||||
hash, &hashlen)) != 0)
|
hash, &hashlen)) != 0)
|
||||||
@ -254,19 +270,14 @@ input_kex_dh_gex_reply(int type, u_int32_t seq, void *ctxt)
|
|||||||
r = kex_send_newkeys(ssh);
|
r = kex_send_newkeys(ssh);
|
||||||
out:
|
out:
|
||||||
explicit_bzero(hash, sizeof(hash));
|
explicit_bzero(hash, sizeof(hash));
|
||||||
DH_free(kex->dh);
|
sshbn_free(dh_p);
|
||||||
|
sshbn_free(dh_g);
|
||||||
|
sshbn_free(shared_secret);
|
||||||
|
sshbn_free(dh_client_pub);
|
||||||
|
sshbn_free(dh_server_pub);
|
||||||
|
sshdh_free(kex->dh);
|
||||||
kex->dh = NULL;
|
kex->dh = NULL;
|
||||||
if (dh_server_pub)
|
|
||||||
BN_clear_free(dh_server_pub);
|
|
||||||
if (kbuf) {
|
|
||||||
explicit_bzero(kbuf, klen);
|
|
||||||
free(kbuf);
|
|
||||||
}
|
|
||||||
if (shared_secret)
|
|
||||||
BN_clear_free(shared_secret);
|
|
||||||
sshkey_free(server_host_key);
|
|
||||||
free(server_host_key_blob);
|
free(server_host_key_blob);
|
||||||
free(signature);
|
free(signature);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
#endif /* WITH_OPENSSL */
|
|
||||||
|
77
kexgexs.c
77
kexgexs.c
@ -30,16 +30,8 @@
|
|||||||
* We support only client side kerberos on Windows.
|
* We support only client side kerberos on Windows.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef WIN32_FIXME
|
|
||||||
#undef GSSAPI
|
|
||||||
#undef KRB5
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef WITH_OPENSSL
|
|
||||||
|
|
||||||
#include <sys/param.h> /* MIN MAX */
|
#include <sys/param.h> /* MIN MAX */
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@ -80,6 +72,7 @@ input_kex_dh_gex_request(int type, u_int32_t seq, void *ctxt)
|
|||||||
{
|
{
|
||||||
struct ssh *ssh = ctxt;
|
struct ssh *ssh = ctxt;
|
||||||
struct kex *kex = ssh->kex;
|
struct kex *kex = ssh->kex;
|
||||||
|
struct sshbn *dh_g = NULL, *dh_p = NULL;
|
||||||
int r;
|
int r;
|
||||||
u_int min = 0, max = 0, nbits = 0;
|
u_int min = 0, max = 0, nbits = 0;
|
||||||
|
|
||||||
@ -110,10 +103,15 @@ input_kex_dh_gex_request(int type, u_int32_t seq, void *ctxt)
|
|||||||
r = SSH_ERR_ALLOC_FAIL;
|
r = SSH_ERR_ALLOC_FAIL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
if ((dh_p = sshdh_p(kex->dh)) == NULL ||
|
||||||
|
(dh_g = sshdh_g(kex->dh)) == NULL) {
|
||||||
|
r = SSH_ERR_INTERNAL_ERROR;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
debug("SSH2_MSG_KEX_DH_GEX_GROUP sent");
|
debug("SSH2_MSG_KEX_DH_GEX_GROUP sent");
|
||||||
if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 ||
|
if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 ||
|
||||||
(r = sshpkt_put_bignum2(ssh, kex->dh->p)) != 0 ||
|
(r = sshpkt_put_bignum2_wrap(ssh, dh_p)) != 0 ||
|
||||||
(r = sshpkt_put_bignum2(ssh, kex->dh->g)) != 0 ||
|
(r = sshpkt_put_bignum2_wrap(ssh, dh_g)) != 0 ||
|
||||||
(r = sshpkt_send(ssh)) != 0)
|
(r = sshpkt_send(ssh)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -125,6 +123,8 @@ input_kex_dh_gex_request(int type, u_int32_t seq, void *ctxt)
|
|||||||
ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init);
|
ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init);
|
||||||
r = 0;
|
r = 0;
|
||||||
out:
|
out:
|
||||||
|
sshbn_free(dh_g);
|
||||||
|
sshbn_free(dh_p);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,13 +133,15 @@ input_kex_dh_gex_init(int type, u_int32_t seq, void *ctxt)
|
|||||||
{
|
{
|
||||||
struct ssh *ssh = ctxt;
|
struct ssh *ssh = ctxt;
|
||||||
struct kex *kex = ssh->kex;
|
struct kex *kex = ssh->kex;
|
||||||
BIGNUM *shared_secret = NULL, *dh_client_pub = NULL;
|
struct sshbn *dh_g = NULL, *dh_p = NULL;
|
||||||
|
struct sshbn *dh_client_pub = NULL;
|
||||||
|
struct sshbn *dh_server_pub = NULL;
|
||||||
|
struct sshbn *shared_secret = NULL;
|
||||||
struct sshkey *server_host_public, *server_host_private;
|
struct sshkey *server_host_public, *server_host_private;
|
||||||
u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL;
|
u_char *signature = NULL, *server_host_key_blob = NULL;
|
||||||
u_char hash[SSH_DIGEST_MAX_LENGTH];
|
u_char hash[SSH_DIGEST_MAX_LENGTH];
|
||||||
size_t sbloblen, slen;
|
size_t sbloblen, slen, hashlen;
|
||||||
size_t klen = 0, hashlen;
|
int r;
|
||||||
int kout, r;
|
|
||||||
|
|
||||||
if (kex->load_host_public_key == NULL ||
|
if (kex->load_host_public_key == NULL ||
|
||||||
kex->load_host_private_key == NULL) {
|
kex->load_host_private_key == NULL) {
|
||||||
@ -156,11 +158,11 @@ input_kex_dh_gex_init(int type, u_int32_t seq, void *ctxt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* key, cert */
|
/* key, cert */
|
||||||
if ((dh_client_pub = BN_new()) == NULL) {
|
if ((dh_client_pub = sshbn_new()) == NULL) {
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
r = SSH_ERR_ALLOC_FAIL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if ((r = sshpkt_get_bignum2(ssh, dh_client_pub)) != 0 ||
|
if ((r = sshpkt_get_bignum2_wrap(ssh, dh_client_pub)) != 0 ||
|
||||||
(r = sshpkt_get_end(ssh)) != 0)
|
(r = sshpkt_get_end(ssh)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -177,23 +179,19 @@ input_kex_dh_gex_init(int type, u_int32_t seq, void *ctxt)
|
|||||||
BN_print_fp(stderr, kex->dh->pub_key);
|
BN_print_fp(stderr, kex->dh->pub_key);
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
#endif
|
#endif
|
||||||
if (!dh_pub_is_valid(kex->dh, dh_client_pub)) {
|
if ((r = dh_pub_is_valid(kex->dh, dh_client_pub)) != 0) {
|
||||||
sshpkt_disconnect(ssh, "bad client public DH value");
|
sshpkt_disconnect(ssh, "bad client public DH value");
|
||||||
r = SSH_ERR_MESSAGE_INCOMPLETE;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
if ((dh_server_pub = sshdh_pubkey(kex->dh)) == NULL ||
|
||||||
klen = DH_size(kex->dh);
|
(dh_p = sshdh_p(kex->dh)) == NULL ||
|
||||||
if ((kbuf = malloc(klen)) == NULL ||
|
(dh_g = sshdh_g(kex->dh)) == NULL) {
|
||||||
(shared_secret = BN_new()) == NULL) {
|
r = SSH_ERR_INTERNAL_ERROR;
|
||||||
r = SSH_ERR_ALLOC_FAIL;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if ((kout = DH_compute_key(kbuf, dh_client_pub, kex->dh)) < 0 ||
|
if ((r = sshdh_compute_key(kex->dh, dh_client_pub,
|
||||||
BN_bin2bn(kbuf, kout, shared_secret) == NULL) {
|
&shared_secret)) != 0)
|
||||||
r = SSH_ERR_LIBCRYPTO_ERROR;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
#ifdef DEBUG_KEXDH
|
#ifdef DEBUG_KEXDH
|
||||||
dump_digest("shared secret", kbuf, kout);
|
dump_digest("shared secret", kbuf, kout);
|
||||||
#endif
|
#endif
|
||||||
@ -210,9 +208,9 @@ input_kex_dh_gex_init(int type, u_int32_t seq, void *ctxt)
|
|||||||
sshbuf_ptr(kex->my), sshbuf_len(kex->my),
|
sshbuf_ptr(kex->my), sshbuf_len(kex->my),
|
||||||
server_host_key_blob, sbloblen,
|
server_host_key_blob, sbloblen,
|
||||||
kex->min, kex->nbits, kex->max,
|
kex->min, kex->nbits, kex->max,
|
||||||
kex->dh->p, kex->dh->g,
|
dh_p, dh_g,
|
||||||
dh_client_pub,
|
dh_client_pub,
|
||||||
kex->dh->pub_key,
|
dh_server_pub,
|
||||||
shared_secret,
|
shared_secret,
|
||||||
hash, &hashlen)) != 0)
|
hash, &hashlen)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
@ -238,7 +236,7 @@ input_kex_dh_gex_init(int type, u_int32_t seq, void *ctxt)
|
|||||||
/* send server hostkey, DH pubkey 'f' and singed H */
|
/* send server hostkey, DH pubkey 'f' and singed H */
|
||||||
if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REPLY)) != 0 ||
|
if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REPLY)) != 0 ||
|
||||||
(r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 ||
|
(r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 ||
|
||||||
(r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || /* f */
|
(r = sshpkt_put_bignum2_wrap(ssh, dh_server_pub)) != 0 || /* f */
|
||||||
(r = sshpkt_put_string(ssh, signature, slen)) != 0 ||
|
(r = sshpkt_put_string(ssh, signature, slen)) != 0 ||
|
||||||
(r = sshpkt_send(ssh)) != 0)
|
(r = sshpkt_send(ssh)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
@ -246,18 +244,15 @@ input_kex_dh_gex_init(int type, u_int32_t seq, void *ctxt)
|
|||||||
if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
|
if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
|
||||||
r = kex_send_newkeys(ssh);
|
r = kex_send_newkeys(ssh);
|
||||||
out:
|
out:
|
||||||
DH_free(kex->dh);
|
explicit_bzero(hash, sizeof(hash));
|
||||||
|
sshbn_free(dh_p);
|
||||||
|
sshbn_free(dh_g);
|
||||||
|
sshbn_free(shared_secret);
|
||||||
|
sshbn_free(dh_client_pub);
|
||||||
|
sshbn_free(dh_server_pub);
|
||||||
|
sshdh_free(kex->dh);
|
||||||
kex->dh = NULL;
|
kex->dh = NULL;
|
||||||
if (dh_client_pub)
|
|
||||||
BN_clear_free(dh_client_pub);
|
|
||||||
if (kbuf) {
|
|
||||||
explicit_bzero(kbuf, klen);
|
|
||||||
free(kbuf);
|
|
||||||
}
|
|
||||||
if (shared_secret)
|
|
||||||
BN_clear_free(shared_secret);
|
|
||||||
free(server_host_key_blob);
|
free(server_host_key_blob);
|
||||||
free(signature);
|
free(signature);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
#endif /* WITH_OPENSSL */
|
|
||||||
|
22
monitor.c
22
monitor.c
@ -664,8 +664,11 @@ monitor_reset_key_state(void)
|
|||||||
int
|
int
|
||||||
mm_answer_moduli(int sock, Buffer *m)
|
mm_answer_moduli(int sock, Buffer *m)
|
||||||
{
|
{
|
||||||
DH *dh;
|
struct sshdh *dh;
|
||||||
int min, want, max;
|
int min, want, max;
|
||||||
|
struct sshbn * dh_p = NULL;
|
||||||
|
struct sshbn * dh_g = NULL;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
min = buffer_get_int(m);
|
min = buffer_get_int(m);
|
||||||
want = buffer_get_int(m);
|
want = buffer_get_int(m);
|
||||||
@ -681,18 +684,25 @@ mm_answer_moduli(int sock, Buffer *m)
|
|||||||
buffer_clear(m);
|
buffer_clear(m);
|
||||||
|
|
||||||
dh = choose_dh(min, want, max);
|
dh = choose_dh(min, want, max);
|
||||||
|
|
||||||
|
|
||||||
if (dh == NULL) {
|
if (dh == NULL) {
|
||||||
buffer_put_char(m, 0);
|
buffer_put_char(m, 0);
|
||||||
return (0);
|
return (0);
|
||||||
} else {
|
} else {
|
||||||
|
if ((dh_p = sshdh_p(dh)) != NULL &&
|
||||||
|
(dh_g = sshdh_g(dh)) != NULL) {
|
||||||
|
|
||||||
/* Send first bignum */
|
/* Send first bignum */
|
||||||
buffer_put_char(m, 1);
|
buffer_put_char(m, 1);
|
||||||
buffer_put_bignum2(m, dh->p);
|
sshbuf_put_bignum2_wrap(m, dh_p);
|
||||||
buffer_put_bignum2(m, dh->g);
|
sshbuf_put_bignum2_wrap(m, dh_g);
|
||||||
|
|
||||||
DH_free(dh);
|
|
||||||
}
|
|
||||||
mm_request_send(sock, MONITOR_ANS_MODULI, m);
|
mm_request_send(sock, MONITOR_ANS_MODULI, m);
|
||||||
|
}
|
||||||
|
sshdh_free(dh);
|
||||||
|
sshbn_free(dh_p);
|
||||||
|
sshbn_free(dh_g);
|
||||||
|
}
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
245
monitor_wrap.c
245
monitor_wrap.c
@ -94,6 +94,7 @@
|
|||||||
#include "roaming.h"
|
#include "roaming.h"
|
||||||
|
|
||||||
#include "ssherr.h"
|
#include "ssherr.h"
|
||||||
|
#include "crypto-wrap.h"
|
||||||
|
|
||||||
/* Imports */
|
/* Imports */
|
||||||
extern int compat20;
|
extern int compat20;
|
||||||
@ -194,11 +195,11 @@ mm_request_receive_expect(int sock, enum monitor_reqtype type, Buffer *m)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WITH_OPENSSL
|
#ifdef WITH_OPENSSL
|
||||||
DH *
|
struct sshdh *
|
||||||
mm_choose_dh(int min, int nbits, int max)
|
mm_choose_dh(int min, int nbits, int max)
|
||||||
{
|
{
|
||||||
BIGNUM *p, *g;
|
struct sshbn *p, *g;
|
||||||
int success = 0;
|
int r, success = 0;
|
||||||
Buffer m;
|
Buffer m;
|
||||||
|
|
||||||
buffer_init(&m);
|
buffer_init(&m);
|
||||||
@ -215,17 +216,16 @@ mm_choose_dh(int min, int nbits, int max)
|
|||||||
if (success == 0)
|
if (success == 0)
|
||||||
fatal("%s: MONITOR_ANS_MODULI failed", __func__);
|
fatal("%s: MONITOR_ANS_MODULI failed", __func__);
|
||||||
|
|
||||||
if ((p = BN_new()) == NULL)
|
if ((p = sshbn_new()) == NULL || (g = sshbn_new()) == NULL)
|
||||||
fatal("%s: BN_new failed", __func__);
|
fatal("%s: BN_new failed", __func__);
|
||||||
if ((g = BN_new()) == NULL)
|
if ((r = sshbuf_get_bignum2_wrap(&m, p)) != 0 ||
|
||||||
fatal("%s: BN_new failed", __func__);
|
(r = sshbuf_get_bignum2_wrap(&m, g)) != 0)
|
||||||
buffer_get_bignum2(&m, p);
|
fatal("%s: sshbuf_get_bignum2_wrap: %s", __func__, ssh_err(r));
|
||||||
buffer_get_bignum2(&m, g);
|
|
||||||
|
|
||||||
debug3("%s: remaining %d", __func__, buffer_len(&m));
|
debug3("%s: remaining %d", __func__, buffer_len(&m));
|
||||||
buffer_free(&m);
|
buffer_free(&m);
|
||||||
|
|
||||||
return (dh_new_group(g, p));
|
return (sshdh_new_group(g, p));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -279,12 +279,8 @@ mm_getpwnamallow(const char *username)
|
|||||||
fatal("%s: struct passwd size mismatch", __func__);
|
fatal("%s: struct passwd size mismatch", __func__);
|
||||||
pw->pw_name = buffer_get_string(&m, NULL);
|
pw->pw_name = buffer_get_string(&m, NULL);
|
||||||
pw->pw_passwd = buffer_get_string(&m, NULL);
|
pw->pw_passwd = buffer_get_string(&m, NULL);
|
||||||
#ifdef HAVE_STRUCT_PASSWD_PW_GECOS
|
|
||||||
pw->pw_gecos = buffer_get_string(&m, NULL);
|
pw->pw_gecos = buffer_get_string(&m, NULL);
|
||||||
#endif
|
// pw->pw_class = buffer_get_string(&m, NULL);
|
||||||
#ifdef HAVE_STRUCT_PASSWD_PW_CLASS
|
|
||||||
pw->pw_class = buffer_get_string(&m, NULL);
|
|
||||||
#endif
|
|
||||||
pw->pw_dir = buffer_get_string(&m, NULL);
|
pw->pw_dir = buffer_get_string(&m, NULL);
|
||||||
pw->pw_shell = buffer_get_string(&m, NULL);
|
pw->pw_shell = buffer_get_string(&m, NULL);
|
||||||
|
|
||||||
@ -575,137 +571,6 @@ mm_session_pty_cleanup2(Session *s)
|
|||||||
s->ttyfd = -1;
|
s->ttyfd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_PAM
|
|
||||||
void
|
|
||||||
mm_start_pam(Authctxt *authctxt)
|
|
||||||
{
|
|
||||||
Buffer m;
|
|
||||||
|
|
||||||
debug3("%s entering", __func__);
|
|
||||||
if (!options.use_pam)
|
|
||||||
fatal("UsePAM=no, but ended up in %s anyway", __func__);
|
|
||||||
|
|
||||||
buffer_init(&m);
|
|
||||||
mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_START, &m);
|
|
||||||
|
|
||||||
buffer_free(&m);
|
|
||||||
}
|
|
||||||
|
|
||||||
u_int
|
|
||||||
mm_do_pam_account(void)
|
|
||||||
{
|
|
||||||
Buffer m;
|
|
||||||
u_int ret;
|
|
||||||
char *msg;
|
|
||||||
|
|
||||||
debug3("%s entering", __func__);
|
|
||||||
if (!options.use_pam)
|
|
||||||
fatal("UsePAM=no, but ended up in %s anyway", __func__);
|
|
||||||
|
|
||||||
buffer_init(&m);
|
|
||||||
mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_ACCOUNT, &m);
|
|
||||||
|
|
||||||
mm_request_receive_expect(pmonitor->m_recvfd,
|
|
||||||
MONITOR_ANS_PAM_ACCOUNT, &m);
|
|
||||||
ret = buffer_get_int(&m);
|
|
||||||
msg = buffer_get_string(&m, NULL);
|
|
||||||
buffer_append(&loginmsg, msg, strlen(msg));
|
|
||||||
free(msg);
|
|
||||||
|
|
||||||
buffer_free(&m);
|
|
||||||
|
|
||||||
debug3("%s returning %d", __func__, ret);
|
|
||||||
|
|
||||||
return (ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *
|
|
||||||
mm_sshpam_init_ctx(Authctxt *authctxt)
|
|
||||||
{
|
|
||||||
Buffer m;
|
|
||||||
int success;
|
|
||||||
|
|
||||||
debug3("%s", __func__);
|
|
||||||
buffer_init(&m);
|
|
||||||
mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_INIT_CTX, &m);
|
|
||||||
debug3("%s: waiting for MONITOR_ANS_PAM_INIT_CTX", __func__);
|
|
||||||
mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PAM_INIT_CTX, &m);
|
|
||||||
success = buffer_get_int(&m);
|
|
||||||
if (success == 0) {
|
|
||||||
debug3("%s: pam_init_ctx failed", __func__);
|
|
||||||
buffer_free(&m);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
buffer_free(&m);
|
|
||||||
return (authctxt);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
mm_sshpam_query(void *ctx, char **name, char **info,
|
|
||||||
u_int *num, char ***prompts, u_int **echo_on)
|
|
||||||
{
|
|
||||||
Buffer m;
|
|
||||||
u_int i;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
debug3("%s", __func__);
|
|
||||||
buffer_init(&m);
|
|
||||||
mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_QUERY, &m);
|
|
||||||
debug3("%s: waiting for MONITOR_ANS_PAM_QUERY", __func__);
|
|
||||||
mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PAM_QUERY, &m);
|
|
||||||
ret = buffer_get_int(&m);
|
|
||||||
debug3("%s: pam_query returned %d", __func__, ret);
|
|
||||||
*name = buffer_get_string(&m, NULL);
|
|
||||||
*info = buffer_get_string(&m, NULL);
|
|
||||||
*num = buffer_get_int(&m);
|
|
||||||
if (*num > PAM_MAX_NUM_MSG)
|
|
||||||
fatal("%s: recieved %u PAM messages, expected <= %u",
|
|
||||||
__func__, *num, PAM_MAX_NUM_MSG);
|
|
||||||
*prompts = xcalloc((*num + 1), sizeof(char *));
|
|
||||||
*echo_on = xcalloc((*num + 1), sizeof(u_int));
|
|
||||||
for (i = 0; i < *num; ++i) {
|
|
||||||
(*prompts)[i] = buffer_get_string(&m, NULL);
|
|
||||||
(*echo_on)[i] = buffer_get_int(&m);
|
|
||||||
}
|
|
||||||
buffer_free(&m);
|
|
||||||
return (ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
mm_sshpam_respond(void *ctx, u_int num, char **resp)
|
|
||||||
{
|
|
||||||
Buffer m;
|
|
||||||
u_int i;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
debug3("%s", __func__);
|
|
||||||
buffer_init(&m);
|
|
||||||
buffer_put_int(&m, num);
|
|
||||||
for (i = 0; i < num; ++i)
|
|
||||||
buffer_put_cstring(&m, resp[i]);
|
|
||||||
mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_RESPOND, &m);
|
|
||||||
debug3("%s: waiting for MONITOR_ANS_PAM_RESPOND", __func__);
|
|
||||||
mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PAM_RESPOND, &m);
|
|
||||||
ret = buffer_get_int(&m);
|
|
||||||
debug3("%s: pam_respond returned %d", __func__, ret);
|
|
||||||
buffer_free(&m);
|
|
||||||
return (ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
mm_sshpam_free_ctx(void *ctxtp)
|
|
||||||
{
|
|
||||||
Buffer m;
|
|
||||||
|
|
||||||
debug3("%s", __func__);
|
|
||||||
buffer_init(&m);
|
|
||||||
mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_FREE_CTX, &m);
|
|
||||||
debug3("%s: waiting for MONITOR_ANS_PAM_FREE_CTX", __func__);
|
|
||||||
mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PAM_FREE_CTX, &m);
|
|
||||||
buffer_free(&m);
|
|
||||||
}
|
|
||||||
#endif /* USE_PAM */
|
|
||||||
|
|
||||||
/* Request process termination */
|
/* Request process termination */
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -809,66 +674,6 @@ mm_bsdauth_respond(void *ctx, u_int numresponses, char **responses)
|
|||||||
return ((authok == 0) ? -1 : 0);
|
return ((authok == 0) ? -1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SKEY
|
|
||||||
int
|
|
||||||
mm_skey_query(void *ctx, char **name, char **infotxt,
|
|
||||||
u_int *numprompts, char ***prompts, u_int **echo_on)
|
|
||||||
{
|
|
||||||
Buffer m;
|
|
||||||
u_int success;
|
|
||||||
char *challenge;
|
|
||||||
|
|
||||||
debug3("%s: entering", __func__);
|
|
||||||
|
|
||||||
buffer_init(&m);
|
|
||||||
mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_SKEYQUERY, &m);
|
|
||||||
|
|
||||||
mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_SKEYQUERY,
|
|
||||||
&m);
|
|
||||||
success = buffer_get_int(&m);
|
|
||||||
if (success == 0) {
|
|
||||||
debug3("%s: no challenge", __func__);
|
|
||||||
buffer_free(&m);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the challenge, and format the response */
|
|
||||||
challenge = buffer_get_string(&m, NULL);
|
|
||||||
buffer_free(&m);
|
|
||||||
|
|
||||||
debug3("%s: received challenge: %s", __func__, challenge);
|
|
||||||
|
|
||||||
mm_chall_setup(name, infotxt, numprompts, prompts, echo_on);
|
|
||||||
|
|
||||||
xasprintf(*prompts, "%s%s", challenge, SKEY_PROMPT);
|
|
||||||
free(challenge);
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
mm_skey_respond(void *ctx, u_int numresponses, char **responses)
|
|
||||||
{
|
|
||||||
Buffer m;
|
|
||||||
int authok;
|
|
||||||
|
|
||||||
debug3("%s: entering", __func__);
|
|
||||||
if (numresponses != 1)
|
|
||||||
return (-1);
|
|
||||||
|
|
||||||
buffer_init(&m);
|
|
||||||
buffer_put_cstring(&m, responses[0]);
|
|
||||||
mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_SKEYRESPOND, &m);
|
|
||||||
|
|
||||||
mm_request_receive_expect(pmonitor->m_recvfd,
|
|
||||||
MONITOR_ANS_SKEYRESPOND, &m);
|
|
||||||
|
|
||||||
authok = buffer_get_int(&m);
|
|
||||||
buffer_free(&m);
|
|
||||||
|
|
||||||
return ((authok == 0) ? -1 : 0);
|
|
||||||
}
|
|
||||||
#endif /* SKEY */
|
|
||||||
|
|
||||||
void
|
void
|
||||||
mm_ssh1_session_id(u_char session_id[16])
|
mm_ssh1_session_id(u_char session_id[16])
|
||||||
@ -984,36 +789,6 @@ mm_auth_rsa_verify_response(Key *key, BIGNUM *p, u_char response[16])
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SSH_AUDIT_EVENTS
|
|
||||||
void
|
|
||||||
mm_audit_event(ssh_audit_event_t event)
|
|
||||||
{
|
|
||||||
Buffer m;
|
|
||||||
|
|
||||||
debug3("%s entering", __func__);
|
|
||||||
|
|
||||||
buffer_init(&m);
|
|
||||||
buffer_put_int(&m, event);
|
|
||||||
|
|
||||||
mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_EVENT, &m);
|
|
||||||
buffer_free(&m);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
mm_audit_run_command(const char *command)
|
|
||||||
{
|
|
||||||
Buffer m;
|
|
||||||
|
|
||||||
debug3("%s entering command %s", __func__, command);
|
|
||||||
|
|
||||||
buffer_init(&m);
|
|
||||||
buffer_put_cstring(&m, command);
|
|
||||||
|
|
||||||
mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_COMMAND, &m);
|
|
||||||
buffer_free(&m);
|
|
||||||
}
|
|
||||||
#endif /* SSH_AUDIT_EVENTS */
|
|
||||||
|
|
||||||
#ifdef GSSAPI
|
#ifdef GSSAPI
|
||||||
OM_uint32
|
OM_uint32
|
||||||
mm_ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID goid)
|
mm_ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID goid)
|
||||||
|
@ -39,7 +39,7 @@ struct Authctxt;
|
|||||||
|
|
||||||
void mm_log_handler(LogLevel, const char *, void *);
|
void mm_log_handler(LogLevel, const char *, void *);
|
||||||
int mm_is_monitor(void);
|
int mm_is_monitor(void);
|
||||||
DH *mm_choose_dh(int, int, int);
|
struct sshdh *mm_choose_dh(int, int, int);
|
||||||
int mm_key_sign(Key *, u_char **, u_int *, const u_char *, u_int);
|
int mm_key_sign(Key *, u_char **, u_int *, const u_char *, u_int);
|
||||||
void mm_inform_authserv(char *, char *);
|
void mm_inform_authserv(char *, char *);
|
||||||
struct passwd *mm_getpwnamallow(const char *);
|
struct passwd *mm_getpwnamallow(const char *);
|
||||||
|
214
openssl-bn.c
Normal file
214
openssl-bn.c
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Damien Miller <djm@mindrot.org>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <includes.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include <openssl/bn.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "sshbuf.h"
|
||||||
|
#include "packet.h"
|
||||||
|
#include "ssherr.h"
|
||||||
|
#include "crypto-wrap.h"
|
||||||
|
|
||||||
|
struct sshbn {
|
||||||
|
BIGNUM *bn;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static struct sshbn *
|
||||||
|
bnwrap(BIGNUM *bn)
|
||||||
|
{
|
||||||
|
struct sshbn *ret;
|
||||||
|
|
||||||
|
if (bn == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if ((ret = calloc(1, sizeof(*ret))) == NULL)
|
||||||
|
return NULL;
|
||||||
|
if ((ret->bn = BN_dup(bn)) == NULL) {
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sshbn *
|
||||||
|
sshbn_new(void)
|
||||||
|
{
|
||||||
|
return bnwrap(BN_new());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sshbn_free(struct sshbn *bn)
|
||||||
|
{
|
||||||
|
if (bn != NULL) {
|
||||||
|
if (bn->bn != NULL)
|
||||||
|
BN_clear_free(bn->bn);
|
||||||
|
explicit_bzero(bn, sizeof(*bn));
|
||||||
|
free(bn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sshbn_from(const void *d, size_t l, struct sshbn **retp)
|
||||||
|
{
|
||||||
|
struct sshbn *ret;
|
||||||
|
const u_char *dd = (const u_char *)d;
|
||||||
|
|
||||||
|
*retp = NULL;
|
||||||
|
if (l > INT_MAX)
|
||||||
|
return SSH_ERR_INVALID_ARGUMENT;
|
||||||
|
if ((ret = sshbn_new()) == NULL)
|
||||||
|
return SSH_ERR_ALLOC_FAIL;
|
||||||
|
if (BN_bin2bn(dd, (int)l, ret->bn) == NULL) {
|
||||||
|
sshbn_free(ret);
|
||||||
|
return SSH_ERR_LIBCRYPTO_ERROR;
|
||||||
|
}
|
||||||
|
*retp = ret;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sshbn_from_hex(const char *hex, struct sshbn **retp)
|
||||||
|
{
|
||||||
|
struct sshbn *ret;
|
||||||
|
|
||||||
|
*retp = NULL;
|
||||||
|
if ((ret = sshbn_new()) == NULL)
|
||||||
|
return SSH_ERR_ALLOC_FAIL;
|
||||||
|
|
||||||
|
if (BN_hex2bn(&ret->bn, hex) <= 0) {
|
||||||
|
sshbn_free(ret);
|
||||||
|
return SSH_ERR_LIBCRYPTO_ERROR;
|
||||||
|
}
|
||||||
|
*retp = ret;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sshbn_to(const struct sshbn *a, unsigned char *to)
|
||||||
|
{
|
||||||
|
|
||||||
|
return BN_bn2bin(sshbn_bignum(a), to);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
sshbn_bytes(const struct sshbn *bn)
|
||||||
|
{
|
||||||
|
int bytes = BN_num_bytes(bn->bn);
|
||||||
|
|
||||||
|
return bytes < 0 ? 0 : (size_t)bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
sshbn_bits(const struct sshbn *bn)
|
||||||
|
{
|
||||||
|
int bits = BN_num_bits(bn->bn);
|
||||||
|
|
||||||
|
return bits < 0 ? 0 : (size_t)bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct sshbn *
|
||||||
|
sshbn_value_0(void)
|
||||||
|
{
|
||||||
|
static struct sshbn *ret;
|
||||||
|
|
||||||
|
if (ret == NULL)
|
||||||
|
sshbn_from_hex("0", &ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct sshbn *
|
||||||
|
sshbn_value_1(void)
|
||||||
|
{
|
||||||
|
static struct sshbn *ret;
|
||||||
|
|
||||||
|
if (ret == NULL)
|
||||||
|
sshbn_from_hex("1", &ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
sshbn_cmp(const struct sshbn *a, const struct sshbn *b)
|
||||||
|
{
|
||||||
|
return BN_cmp(a->bn, b->bn);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sshbn_sub(struct sshbn *r, const struct sshbn *a, const struct sshbn *b)
|
||||||
|
{
|
||||||
|
if (BN_sub(r->bn, a->bn, b->bn) != 1)
|
||||||
|
return SSH_ERR_LIBCRYPTO_ERROR;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sshbn_is_bit_set(const struct sshbn *bn, size_t i)
|
||||||
|
{
|
||||||
|
if (i > INT_MAX)
|
||||||
|
return 0;
|
||||||
|
return BN_is_bit_set(bn->bn, (int)i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* XXX move to sshbuf.h */
|
||||||
|
int
|
||||||
|
sshbuf_get_bignum2_wrap(struct sshbuf *buf, struct sshbn *bn)
|
||||||
|
{
|
||||||
|
return sshbuf_get_bignum2(buf, bn->bn);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sshbuf_put_bignum2_wrap(struct sshbuf *buf, const struct sshbn *bn)
|
||||||
|
{
|
||||||
|
return sshbuf_put_bignum2(buf, bn->bn);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sshpkt_get_bignum2_wrap(struct ssh *ssh, struct sshbn *bn)
|
||||||
|
{
|
||||||
|
return sshpkt_get_bignum2(ssh, bn->bn);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sshpkt_put_bignum2_wrap(struct ssh *ssh, const struct sshbn *bn)
|
||||||
|
{
|
||||||
|
return sshpkt_put_bignum2(ssh, bn->bn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bridge to unwrapped OpenSSL APIs; XXX remove later */
|
||||||
|
BIGNUM *
|
||||||
|
sshbn_bignum(struct sshbn *bn)
|
||||||
|
{
|
||||||
|
return bn->bn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct sshbn *
|
||||||
|
sshbn_from_bignum(BIGNUM *bn)
|
||||||
|
{
|
||||||
|
return bnwrap(bn);
|
||||||
|
}
|
||||||
|
|
199
openssl-dh.c
Normal file
199
openssl-dh.c
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Damien Miller <djm@mindrot.org>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <includes.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <openssl/dh.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
|
#include "sshbuf.h"
|
||||||
|
#include "packet.h"
|
||||||
|
#include "ssherr.h"
|
||||||
|
#include "crypto-wrap.h"
|
||||||
|
|
||||||
|
struct sshdh {
|
||||||
|
DH *dh;
|
||||||
|
};
|
||||||
|
struct sshbn {
|
||||||
|
BIGNUM *bn;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static struct sshbn *
|
||||||
|
bnwrap(BIGNUM *bn)
|
||||||
|
{
|
||||||
|
struct sshbn *ret;
|
||||||
|
|
||||||
|
if (bn == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if ((ret = calloc(1, sizeof(*ret))) == NULL)
|
||||||
|
return NULL;
|
||||||
|
if ((ret->bn = BN_dup(bn)) == NULL) {
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DH wrappers */
|
||||||
|
|
||||||
|
struct sshdh *
|
||||||
|
sshdh_new(void)
|
||||||
|
{
|
||||||
|
struct sshdh *ret;
|
||||||
|
|
||||||
|
if ((ret = calloc(1, sizeof(*ret))) == NULL)
|
||||||
|
return NULL;
|
||||||
|
if ((ret->dh = DH_new()) == NULL) {
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sshdh_free(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
if (dh != NULL) {
|
||||||
|
if (dh->dh != NULL)
|
||||||
|
DH_free(dh->dh);
|
||||||
|
explicit_bzero(dh, sizeof(*dh));
|
||||||
|
free(dh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sshbn *
|
||||||
|
sshdh_pubkey(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
return bnwrap(dh->dh->pub_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sshbn *
|
||||||
|
sshdh_p(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
return bnwrap(dh->dh->p);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sshbn *
|
||||||
|
sshdh_g(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
return bnwrap(dh->dh->g);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sshdh_dump(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
DHparams_print_fp(stderr, dh->dh);
|
||||||
|
fprintf(stderr, "pub= ");
|
||||||
|
BN_print_fp(stderr, dh->dh->pub_key);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX needed?
|
||||||
|
size_t
|
||||||
|
sshdh_shared_key_size(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
int sz;
|
||||||
|
|
||||||
|
if (dh == NULL || dh->dh == NULL || (sz = DH_size(dh->dh)) < 0)
|
||||||
|
return 0;
|
||||||
|
return (size_t)sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sshdh_compute_key(struct sshdh *dh, struct sshbn *pubkey,
|
||||||
|
struct sshbn **shared_secretp)
|
||||||
|
{
|
||||||
|
u_char *sbuf;
|
||||||
|
int r, slen;
|
||||||
|
|
||||||
|
*shared_secretp = NULL;
|
||||||
|
if ((slen = DH_size(dh->dh)) <= 0)
|
||||||
|
return SSH_ERR_INVALID_ARGUMENT;
|
||||||
|
if ((sbuf = calloc(1, slen)) == NULL)
|
||||||
|
return SSH_ERR_ALLOC_FAIL;
|
||||||
|
if ((r = DH_compute_key(sbuf, pubkey->bn, dh->dh)) < 0 ||
|
||||||
|
r != slen) {
|
||||||
|
r = SSH_ERR_LIBCRYPTO_ERROR;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if ((r = sshbn_from(sbuf, slen, shared_secretp)) != 0)
|
||||||
|
goto out;
|
||||||
|
/* success */
|
||||||
|
r = 0;
|
||||||
|
out:
|
||||||
|
explicit_bzero(sbuf, slen);
|
||||||
|
free(sbuf);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sshdh_generate(struct sshdh *dh, size_t len)
|
||||||
|
{
|
||||||
|
if (len > INT_MAX)
|
||||||
|
return SSH_ERR_INVALID_ARGUMENT;
|
||||||
|
if (len != 0)
|
||||||
|
dh->dh->length = (int)len;
|
||||||
|
if (DH_generate_key(dh->dh) != 1)
|
||||||
|
return SSH_ERR_LIBCRYPTO_ERROR;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sshdh_new_group_hex(const char *gen, const char *modulus, struct sshdh **dhp)
|
||||||
|
{
|
||||||
|
struct sshdh *ret;
|
||||||
|
|
||||||
|
*dhp = NULL;
|
||||||
|
if ((ret = sshdh_new()) == NULL)
|
||||||
|
return SSH_ERR_ALLOC_FAIL;
|
||||||
|
if (BN_hex2bn(&ret->dh->p, modulus) == 0 ||
|
||||||
|
BN_hex2bn(&ret->dh->g, gen) == 0) {
|
||||||
|
sshdh_free(ret);
|
||||||
|
return SSH_ERR_LIBCRYPTO_ERROR;
|
||||||
|
}
|
||||||
|
*dhp = ret;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* XXX transfers ownership of gen, modulus */
|
||||||
|
struct sshdh *
|
||||||
|
sshdh_new_group(struct sshbn *gen, struct sshbn *modulus)
|
||||||
|
{
|
||||||
|
struct sshdh *dh;
|
||||||
|
|
||||||
|
if ((dh = sshdh_new()) == NULL)
|
||||||
|
return NULL;
|
||||||
|
dh->dh->p = modulus->bn;
|
||||||
|
dh->dh->g = gen->bn;
|
||||||
|
modulus->bn = gen->bn = NULL;
|
||||||
|
sshbn_free(gen);
|
||||||
|
sshbn_free(modulus);
|
||||||
|
return (dh);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DH *sshdh_dh(struct sshdh *dh)
|
||||||
|
{
|
||||||
|
return dh->dh;
|
||||||
|
}
|
@ -76,11 +76,7 @@
|
|||||||
#include "ssh-gss.h"
|
#include "ssh-gss.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_MSCNG
|
|
||||||
/* CNG KEX imports */
|
|
||||||
int cng_kexgex_client(struct ssh *ssh);
|
|
||||||
int cng_kexdh_client(struct ssh *ssh);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* import */
|
/* import */
|
||||||
extern char *client_version_string;
|
extern char *client_version_string;
|
||||||
@ -217,22 +213,16 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
|
|||||||
if ((r = kex_setup(active_state, myproposal)) != 0)
|
if ((r = kex_setup(active_state, myproposal)) != 0)
|
||||||
fatal("kex_setup: %s", ssh_err(r));
|
fatal("kex_setup: %s", ssh_err(r));
|
||||||
kex = active_state->kex;
|
kex = active_state->kex;
|
||||||
#ifdef WITH_OPENSSL
|
|
||||||
#ifdef USE_MSCNG
|
|
||||||
kex->kex[KEX_DH_GRP1_SHA1] = cng_kexdh_client;
|
|
||||||
kex->kex[KEX_DH_GRP14_SHA1] = cng_kexdh_client;
|
|
||||||
kex->kex[KEX_DH_GEX_SHA1] = cng_kexgex_client;
|
|
||||||
kex->kex[KEX_DH_GEX_SHA256] = cng_kexgex_client;
|
|
||||||
#else
|
|
||||||
kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client;
|
kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client;
|
||||||
kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client;
|
kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client;
|
||||||
kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
|
kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
|
||||||
kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
|
kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
|
||||||
#endif
|
|
||||||
# ifdef OPENSSL_HAS_ECC
|
# ifdef OPENSSL_HAS_ECC
|
||||||
kex->kex[KEX_ECDH_SHA2] = kexecdh_client;
|
kex->kex[KEX_ECDH_SHA2] = kexecdh_client;
|
||||||
# endif
|
# endif
|
||||||
#endif
|
|
||||||
kex->kex[KEX_C25519_SHA256] = kexc25519_client;
|
kex->kex[KEX_C25519_SHA256] = kexc25519_client;
|
||||||
kex->client_version_string=client_version_string;
|
kex->client_version_string=client_version_string;
|
||||||
kex->server_version_string=server_version_string;
|
kex->server_version_string=server_version_string;
|
||||||
|
19
sshd.c
19
sshd.c
@ -145,11 +145,7 @@
|
|||||||
#define O_NOCTTY 0
|
#define O_NOCTTY 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_MSCNG
|
|
||||||
/* CNG KEX imports */
|
|
||||||
int cng_kexgex_server(struct ssh *ssh);
|
|
||||||
int cng_kexdh_server(struct ssh *ssh);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Re-exec fds */
|
/* Re-exec fds */
|
||||||
#define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1)
|
#define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1)
|
||||||
@ -3384,22 +3380,17 @@ do_ssh2_kex(void)
|
|||||||
if ((r = kex_setup(active_state, myproposal)) != 0)
|
if ((r = kex_setup(active_state, myproposal)) != 0)
|
||||||
fatal("kex_setup: %s", ssh_err(r));
|
fatal("kex_setup: %s", ssh_err(r));
|
||||||
kex = active_state->kex;
|
kex = active_state->kex;
|
||||||
#ifdef WITH_OPENSSL
|
|
||||||
#ifdef USE_MSCNG
|
|
||||||
kex->kex[KEX_DH_GRP1_SHA1] = cng_kexdh_server;
|
|
||||||
kex->kex[KEX_DH_GRP14_SHA1] = cng_kexdh_server;
|
|
||||||
kex->kex[KEX_DH_GEX_SHA1] = cng_kexgex_server;
|
|
||||||
kex->kex[KEX_DH_GEX_SHA256] = cng_kexgex_server;
|
|
||||||
#else
|
|
||||||
kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
|
kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
|
||||||
kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server;
|
kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server;
|
||||||
kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
|
kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
|
||||||
kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
|
kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
|
||||||
#endif
|
|
||||||
# ifdef OPENSSL_HAS_ECC
|
# ifdef OPENSSL_HAS_ECC
|
||||||
kex->kex[KEX_ECDH_SHA2] = kexecdh_server;
|
kex->kex[KEX_ECDH_SHA2] = kexecdh_server;
|
||||||
# endif
|
# endif
|
||||||
#endif
|
|
||||||
kex->kex[KEX_C25519_SHA256] = kexc25519_server;
|
kex->kex[KEX_C25519_SHA256] = kexc25519_server;
|
||||||
kex->server = 1;
|
kex->server = 1;
|
||||||
kex->client_version_string=client_version_string;
|
kex->client_version_string=client_version_string;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user