diff --git a/contrib/win32/openssh/libssh.vcxproj b/contrib/win32/openssh/libssh.vcxproj index 49889c4..061950b 100644 --- a/contrib/win32/openssh/libssh.vcxproj +++ b/contrib/win32/openssh/libssh.vcxproj @@ -258,6 +258,13 @@ + + + true + + + + diff --git a/contrib/win32/openssh/libssh.vcxproj.filters b/contrib/win32/openssh/libssh.vcxproj.filters index 730bc3d..87cba0c 100644 --- a/contrib/win32/openssh/libssh.vcxproj.filters +++ b/contrib/win32/openssh/libssh.vcxproj.filters @@ -288,5 +288,16 @@ Source Files + + Source Files + + + Source Files + + + + + Header Files + \ No newline at end of file diff --git a/contrib/win32/openssh/win32compat.vcxproj b/contrib/win32/openssh/win32compat.vcxproj index 11ac11a..874ec42 100644 --- a/contrib/win32/openssh/win32compat.vcxproj +++ b/contrib/win32/openssh/win32compat.vcxproj @@ -159,11 +159,13 @@ - - - - - + + true + true + true + true + + diff --git a/contrib/win32/openssh/win32compat.vcxproj.filters b/contrib/win32/openssh/win32compat.vcxproj.filters index 6b28eaa..9f07cc8 100644 --- a/contrib/win32/openssh/win32compat.vcxproj.filters +++ b/contrib/win32/openssh/win32compat.vcxproj.filters @@ -81,19 +81,10 @@ Source Files - + Source Files - - Source Files - - - Source Files - - - Source Files - - + Source Files @@ -140,7 +131,10 @@ Header Files - + + Header Files + + Header Files diff --git a/contrib/win32/win32compat/cng_dh.c b/contrib/win32/win32compat/cng_dh.c new file mode 100644 index 0000000..b2ecab9 --- /dev/null +++ b/contrib/win32/win32compat/cng_dh.c @@ -0,0 +1,324 @@ + +#include +#include +#include +#include +#include +#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; +} + + + + diff --git a/contrib/win32/win32compat/cng_kex.c b/contrib/win32/win32compat/cng_kex.c deleted file mode 100644 index 6661455..0000000 --- a/contrib/win32/win32compat/cng_kex.c +++ /dev/null @@ -1,471 +0,0 @@ - -#include -#include - -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); - -} - diff --git a/contrib/win32/win32compat/cng_kex.h b/contrib/win32/win32compat/cng_kex.h deleted file mode 100644 index 1b97239..0000000 --- a/contrib/win32/win32compat/cng_kex.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include -#include -#include -// 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); diff --git a/contrib/win32/win32compat/cng_kexdhc.c b/contrib/win32/win32compat/cng_kexdhc.c deleted file mode 100644 index b3ef54e..0000000 --- a/contrib/win32/win32compat/cng_kexdhc.c +++ /dev/null @@ -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 - -#include - -#include -#include -#include -#include - -#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 -#include - - - - -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; -} - diff --git a/contrib/win32/win32compat/cng_kexdhs.c b/contrib/win32/win32compat/cng_kexdhs.c deleted file mode 100644 index 7a1a035..0000000 --- a/contrib/win32/win32compat/cng_kexdhs.c +++ /dev/null @@ -1,198 +0,0 @@ - -#include "includes.h" - -#ifdef WIN32_FIXME -#undef GSSAPI -#undef KRB5 -#endif - -#ifdef WITH_OPENSSL - -#include - -#include -#include -#include - -#include - -#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 -#include - - -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 */ diff --git a/contrib/win32/win32compat/cng_kexgexc.c b/contrib/win32/win32compat/cng_kexgexc.c deleted file mode 100644 index a99ac62..0000000 --- a/contrib/win32/win32compat/cng_kexgexc.c +++ /dev/null @@ -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 -#include - -#include - -#include -#include -#include -#include - -#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 -#include - -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 */ diff --git a/contrib/win32/win32compat/cng_kexgexs.c b/contrib/win32/win32compat/cng_kexgexs.c deleted file mode 100644 index de44859..0000000 --- a/contrib/win32/win32compat/cng_kexgexs.c +++ /dev/null @@ -1,283 +0,0 @@ - -#include "includes.h" - -#ifdef WIN32_FIXME -#undef GSSAPI -#undef KRB5 -#endif - -#ifdef WITH_OPENSSL - -#include /* MIN MAX */ - -#include -#include -#include -#include - -#include - -#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 -#include - -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 */ diff --git a/contrib/win32/win32compat/cng_openssl_dh.c b/contrib/win32/win32compat/cng_openssl_dh.c new file mode 100644 index 0000000..97b90a4 --- /dev/null +++ b/contrib/win32/win32compat/cng_openssl_dh.c @@ -0,0 +1,487 @@ + +#include + + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sshbuf.h" +#include "packet.h" +#include "ssherr.h" +#include +#include "crypto-wrap.h" +#include + + +// 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; +} + + + + diff --git a/crypto-wrap.h b/crypto-wrap.h new file mode 100644 index 0000000..f1e4934 --- /dev/null +++ b/crypto-wrap.h @@ -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 */ \ No newline at end of file diff --git a/dh.c b/dh.c index 4c639ac..1d2a3bc 100644 --- a/dh.c +++ b/dh.c @@ -41,6 +41,7 @@ #include "log.h" #include "misc.h" #include "ssherr.h" +#include "crypto-wrap.h" static int 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; const char *errstr = NULL; long long n; + int r; dhg->p = dhg->g = NULL; cp = line; @@ -109,53 +111,45 @@ parse_prime(int linenum, char *line, struct dhgroup *dhg) goto fail; } - if ((dhg->g = BN_new()) == NULL || - (dhg->p = BN_new()) == NULL) { - error("parse_prime: BN_new failed"); + if ((r = sshbn_from_hex(gen, &dhg->g)) != 0 || + (r = sshbn_from_hex(prime, &dhg->p)) != 0) + { goto fail; } - if (BN_hex2bn(&dhg->g, gen) == 0) { - error("moduli:%d: could not parse generator value", linenum); + if (sshbn_bits(dhg->p) != dhg->size) { + error("moduli:%d: prime has wrong size: actual %zu listed %zu", + linenum, sshbn_bits(dhg->p), dhg->size - 1); goto fail; } - if (BN_hex2bn(&dhg->p, prime) == 0) { - error("moduli:%d: could not parse prime value", linenum); - 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) { + + if (sshbn_cmp(dhg->g, sshbn_value_1()) <= 0) { error("moduli:%d: generator is invalid", linenum); goto fail; } return 1; fail: - if (dhg->g != NULL) - BN_clear_free(dhg->g); - if (dhg->p != NULL) - BN_clear_free(dhg->p); + sshbn_free(dhg->g); + sshbn_free(dhg->p); dhg->g = dhg->p = NULL; return 0; } -DH * -choose_dh(int min, int wantbits, int max) +struct sshdh * +choose_dh(u_int min, u_int wantbits, u_int max) { FILE *f; char line[4096]; - int best, bestcount, which; - int linenum; + u_int best, bestcount, which, linenum; + int r; struct dhgroup dhg; + struct sshdh *dh = NULL; if ((f = fopen(_PATH_DH_MODULI, "r")) == NULL && (f = fopen(_PATH_DH_PRIMES, "r")) == NULL) { logit("WARNING: %s does not exist, using fixed modulus", _PATH_DH_MODULI); - return (dh_new_group_fallback(max)); + goto fallback; } linenum = 0; @@ -164,8 +158,8 @@ choose_dh(int min, int wantbits, int max) linenum++; if (!parse_prime(linenum, line, &dhg)) continue; - BN_clear_free(dhg.g); - BN_clear_free(dhg.p); + sshbn_free(dhg.g); + sshbn_free(dhg.p); if (dhg.size > max || dhg.size < min) continue; @@ -183,7 +177,7 @@ choose_dh(int min, int wantbits, int max) if (bestcount == 0) { fclose(f); logit("WARNING: no suitable primes in %s", _PATH_DH_PRIMES); - return (dh_new_group_fallback(max)); + goto fallback; } linenum = 0; @@ -194,8 +188,8 @@ choose_dh(int min, int wantbits, int max) if ((dhg.size > max || dhg.size < min) || dhg.size != best || linenum++ != which) { - BN_clear_free(dhg.g); - BN_clear_free(dhg.p); + sshbn_free(dhg.g); + sshbn_free(dhg.p); continue; } break; @@ -204,187 +198,181 @@ choose_dh(int min, int wantbits, int max) if (linenum != which+1) { logit("WARNING: line %d disappeared in %s, giving up", 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 */ - int -dh_pub_is_valid(DH *dh, BIGNUM *dh_pub) +dh_pub_is_valid(struct sshdh *dh, struct sshbn *dh_pub) { - int i; - int n = BN_num_bits(dh_pub); - int bits_set = 0; - BIGNUM *tmp; + size_t i; + size_t n; + int r, freeme = 0, bits_set = 0; + struct sshbn *dh_p = NULL, *tmp = NULL; - if (dh_pub->neg) { - logit("invalid public DH value: negative"); - return 0; + if (dh_pub == NULL) { + if ((dh_pub = sshdh_pubkey(dh)) == NULL) + 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"); - return 0; + r = SSH_ERR_INVALID_FORMAT; + goto out; } - - if ((tmp = BN_new()) == NULL) { - error("%s: BN_new failed", __func__); - return 0; + if ((dh_p = sshdh_p(dh)) == NULL) { + error("%s: sshdh_p failed", __func__); + r = SSH_ERR_ALLOC_FAIL; + goto out; } - if (!BN_sub(tmp, dh->p, BN_value_one()) || - BN_cmp(dh_pub, tmp) != -1) { /* pub_exp > p-2 */ - BN_clear_free(tmp); + if ((tmp = sshbn_new()) == NULL) { + error("%s: sshbn_new failed", __func__); + 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"); - return 0; + r = SSH_ERR_INVALID_FORMAT; + goto out; } - BN_clear_free(tmp); - for (i = 0; i <= n; i++) - if (BN_is_bit_set(dh_pub, i)) + if (sshbn_is_bit_set(dh_pub, i)) 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 (bits_set > 1) - return 1; + if (bits_set <= 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; } int -dh_gen_key(DH *dh, int need) -{ - 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) +dh_new_group1(struct sshdh **dhp) { static char *gen = "2", *group1 = - "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" - "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" - "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" - "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" - "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381" - "FFFFFFFF" "FFFFFFFF"; + "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" + "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" + "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" + "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" + "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381" + "FFFFFFFF" "FFFFFFFF"; - return (dh_new_group_asc(gen, group1)); + return sshdh_new_group_hex(gen, group1, dhp); } -DH * -dh_new_group14(void) +int +dh_new_group14(struct sshdh **dhp) { static char *gen = "2", *group14 = - "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" - "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" - "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" - "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" - "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D" - "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F" - "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D" - "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B" - "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9" - "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510" - "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF"; + "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" + "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" + "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" + "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" + "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D" + "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F" + "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D" + "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B" + "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9" + "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510" + "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. - * Source: MODP group 16 from RFC3526. - */ -DH * -dh_new_group_fallback(int max) +* 4k bit fallback group used by DH-GEX if moduli file cannot be read. +* Source: MODP group 16 from RFC3526. +*/ +int +dh_new_group_fallback(int max, struct sshdh **dhp) { static char *gen = "2", *group16 = - "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" - "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" - "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" - "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" - "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D" - "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F" - "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D" - "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B" - "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9" - "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510" - "15728E5A" "8AAAC42D" "AD33170D" "04507A33" "A85521AB" "DF1CBA64" - "ECFB8504" "58DBEF0A" "8AEA7157" "5D060C7D" "B3970F85" "A6E1E4C7" - "ABF5AE8C" "DB0933D7" "1E8C94E0" "4A25619D" "CEE3D226" "1AD2EE6B" - "F12FFA06" "D98A0864" "D8760273" "3EC86A64" "521F2B18" "177B200C" - "BBE11757" "7A615D6C" "770988C0" "BAD946E2" "08E24FA0" "74E5AB31" - "43DB5BFC" "E0FD108E" "4B82D120" "A9210801" "1A723C12" "A787E6D7" - "88719A10" "BDBA5B26" "99C32718" "6AF4E23C" "1A946834" "B6150BDA" - "2583E9CA" "2AD44CE8" "DBBBC2DB" "04DE8EF9" "2E8EFC14" "1FBECAA6" - "287C5947" "4E6BC05D" "99B2964F" "A090C3A2" "233BA186" "515BE7ED" - "1F612970" "CEE2D7AF" "B81BDD76" "2170481C" "D0069127" "D5B05AA9" - "93B4EA98" "8D8FDDC1" "86FFB7DC" "90A6C08F" "4DF435C9" "34063199" - "FFFFFFFF" "FFFFFFFF"; + "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" + "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" + "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" + "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" + "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D" + "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F" + "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D" + "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B" + "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9" + "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510" + "15728E5A" "8AAAC42D" "AD33170D" "04507A33" "A85521AB" "DF1CBA64" + "ECFB8504" "58DBEF0A" "8AEA7157" "5D060C7D" "B3970F85" "A6E1E4C7" + "ABF5AE8C" "DB0933D7" "1E8C94E0" "4A25619D" "CEE3D226" "1AD2EE6B" + "F12FFA06" "D98A0864" "D8760273" "3EC86A64" "521F2B18" "177B200C" + "BBE11757" "7A615D6C" "770988C0" "BAD946E2" "08E24FA0" "74E5AB31" + "43DB5BFC" "E0FD108E" "4B82D120" "A9210801" "1A723C12" "A787E6D7" + "88719A10" "BDBA5B26" "99C32718" "6AF4E23C" "1A946834" "B6150BDA" + "2583E9CA" "2AD44CE8" "DBBBC2DB" "04DE8EF9" "2E8EFC14" "1FBECAA6" + "287C5947" "4E6BC05D" "99B2964F" "A090C3A2" "233BA186" "515BE7ED" + "1F612970" "CEE2D7AF" "B81BDD76" "2170481C" "D0069127" "D5B05AA9" + "93B4EA98" "8D8FDDC1" "86FFB7DC" "90A6C08F" "4DF435C9" "34063199" + "FFFFFFFF" "FFFFFFFF"; if (max < 4096) { 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"); - return (dh_new_group_asc(gen, group16)); + return sshdh_new_group_hex(gen, group16, dhp); } /* - * Estimates the group order for a Diffie-Hellman group that has an - * attack complexity approximately the same as O(2**bits). - * Values from NIST Special Publication 800-57: Recommendation for Key - * Management Part 1 (rev 3) limited by the recommended maximum value - * from RFC4419 section 3. - */ - +* Estimates the group order for a Diffie-Hellman group that has an +* attack complexity approximately the same as O(2**bits). +* Values from NIST Special Publication 800-57: Recommendation for Key +* Management Part 1 (rev 3) limited by the recommended maximum value +* from RFC4419 section 3. +*/ u_int dh_estimate(int bits) { diff --git a/dh.h b/dh.h index 6546953..bff6720 100644 --- a/dh.h +++ b/dh.h @@ -26,32 +26,35 @@ #ifndef DH_H #define DH_H +#include + struct dhgroup { - int size; - BIGNUM *g; - BIGNUM *p; + size_t size; + struct sshbn *g; + struct sshbn *p; }; -DH *choose_dh(int, int, int); -DH *dh_new_group_asc(const char *, const char *); -DH *dh_new_group(BIGNUM *, BIGNUM *); -DH *dh_new_group1(void); -DH *dh_new_group14(void); -DH *dh_new_group_fallback(int); +struct sshdh *choose_dh(u_int, u_int, u_int); +int dh_new_group1(struct sshdh **dhp); +int dh_new_group14(struct sshdh **dhp); +int dh_new_group_fallback(int, struct sshdh **dhp); -int dh_gen_key(DH *, int); -int dh_pub_is_valid(DH *, BIGNUM *); +int dh_gen_key(struct sshdh *dh, u_int); +int dh_pub_is_valid(struct sshdh *dh, struct sshbn *dh_pub); 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 /* - * Values for "type" field of moduli(5) - * Specifies the internal structure of the prime modulus. - */ +* Values for "type" field of moduli(5) +* Specifies the internal structure of the prime modulus. +*/ #define MODULI_TYPE_UNKNOWN (0) #define MODULI_TYPE_UNSTRUCTURED (1) #define MODULI_TYPE_SAFE (2) @@ -60,10 +63,10 @@ u_int dh_estimate(int); #define MODULI_TYPE_STRONG (5) /* - * Values for "tests" field of moduli(5) - * Specifies the methods used in checking for primality. - * Usually, more than one test is used. - */ +* Values for "tests" field of moduli(5) +* Specifies the methods used in checking for primality. +* Usually, more than one test is used. +*/ #define MODULI_TESTS_UNTESTED (0x00) #define MODULI_TESTS_COMPOSITE (0x01) #define MODULI_TESTS_SIEVE (0x02) @@ -71,5 +74,4 @@ u_int dh_estimate(int); #define MODULI_TESTS_JACOBI (0x08) #define MODULI_TESTS_ELLIPTIC (0x10) - #endif diff --git a/kex.c b/kex.c index 5100c66..9bd5eb8 100644 --- a/kex.c +++ b/kex.c @@ -55,18 +55,23 @@ #include "sshbuf.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_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 { char *name; u_int type; @@ -75,27 +80,19 @@ struct kexalg { }; static const struct kexalg kexalgs[] = { #ifdef WITH_OPENSSL - { KEX_DH1, KEX_DH_GRP1_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 }, -#ifdef HAVE_EVP_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, - NID_X9_62_prime256v1, SSH_DIGEST_SHA256 }, - { KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1, - SSH_DIGEST_SHA384 }, -# ifdef OPENSSL_HAS_NISTP521 - { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1, - SSH_DIGEST_SHA512 }, -# endif /* OPENSSL_HAS_NISTP521 */ -#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 }, -#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ - { NULL, -1, -1, -1}, +{ KEX_DH1, KEX_DH_GRP1_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_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 }, +{ KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2, +NID_X9_62_prime256v1, SSH_DIGEST_SHA256 }, +{ KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1, +SSH_DIGEST_SHA384 }, +{ KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1, +SSH_DIGEST_SHA512 }, +#endif +{ KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, +{ NULL, -1, -1, -1 }, }; char * @@ -143,7 +140,7 @@ kex_names_valid(const char *names) if ((s = cp = strdup(names)) == NULL) return 0; for ((p = strsep(&cp, ",")); p && *p != '\0'; - (p = strsep(&cp, ","))) { + (p = strsep(&cp, ","))) { if (kex_alg_by_name(p) == NULL) { error("Unsupported KEX algorithm \"%.100s\"", p); free(s); @@ -156,9 +153,9 @@ kex_names_valid(const char *names) } /* - * Concatenate algorithm names, avoiding duplicates in the process. - * Caller must free returned string. - */ +* Concatenate algorithm names, avoiding duplicates in the process. +* Caller must free returned string. +*/ char * kex_names_cat(const char *a, const char *b) { @@ -169,11 +166,11 @@ kex_names_cat(const char *a, const char *b) return NULL; if (b == NULL || *b == '\0') return strdup(a); - if (strlen(b) > 1024*1024) + if (strlen(b) > 1024 * 1024) return NULL; len = strlen(a) + strlen(b) + 2; if ((tmp = cp = strdup(b)) == NULL || - (ret = calloc(1, len)) == NULL) { + (ret = calloc(1, len)) == NULL) { free(tmp); return NULL; } @@ -182,7 +179,7 @@ kex_names_cat(const char *a, const char *b) if (match_list(ret, p, NULL) != NULL) continue; /* Algorithm already present */ if (strlcat(ret, ",", len) >= len || - strlcat(ret, p, len) >= len) { + strlcat(ret, p, len) >= len) { free(tmp); free(ret); return NULL; /* Shouldn't happen */ @@ -193,10 +190,10 @@ kex_names_cat(const char *a, const char *b) } /* - * Assemble a list of algorithms from a default list and a string from a - * configuration file. The user-provided string may begin with '+' to - * indicate that it should be appended to the default. - */ +* Assemble a list of algorithms from a default list and a string from a +* configuration file. The user-provided string may begin with '+' to +* indicate that it should be appended to the default. +*/ int kex_assemble_names(const char *def, char **list) { @@ -227,9 +224,9 @@ kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) sshbuf_reset(b); /* - * add a dummy cookie, the cookie will be overwritten by - * kex_send_kexinit(), each time a kexinit is set - */ + * add a dummy cookie, the cookie will be overwritten by + * kex_send_kexinit(), each time a kexinit is set + */ for (i = 0; i < KEX_COOKIE_LEN; i++) { if ((r = sshbuf_put_u8(b, 0)) != 0) return r; @@ -239,7 +236,7 @@ kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) return r; } if ((r = sshbuf_put_u8(b, 0)) != 0 || /* first_kex_packet_follows */ - (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */ + (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */ return r; return 0; } @@ -267,19 +264,19 @@ kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp) for (i = 0; i < PROPOSAL_MAX; i++) { if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) goto out; - debug2("kex_parse_kexinit: %s", proposal[i]); + debug2("%s: %s", proposal_names[i], proposal[i]); } /* first kex follows / reserved */ if ((r = sshbuf_get_u8(b, &v)) != 0 || - (r = sshbuf_get_u32(b, &i)) != 0) + (r = sshbuf_get_u32(b, &i)) != 0) goto out; if (first_kex_follows != NULL) *first_kex_follows = i; - debug2("kex_parse_kexinit: first_kex_follows %d ", v); - debug2("kex_parse_kexinit: reserved %u ", i); + debug2("first_kex_follows %d ", v); + debug2("reserved %u ", i); r = 0; *propp = proposal; - out: +out: if (r != 0 && proposal != NULL) kex_prop_free(proposal); sshbuf_free(b); @@ -302,7 +299,14 @@ kex_prop_free(char **proposal) static int 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; } @@ -310,7 +314,7 @@ static void kex_reset_dispatch(struct ssh *ssh) { ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN, - SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error); + SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error); ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); } @@ -321,7 +325,7 @@ kex_send_newkeys(struct ssh *ssh) kex_reset_dispatch(ssh); if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 || - (r = sshpkt_send(ssh)) != 0) + (r = sshpkt_send(ssh)) != 0) return r; debug("SSH2_MSG_NEWKEYS sent"); debug("expecting SSH2_MSG_NEWKEYS"); @@ -370,8 +374,8 @@ kex_send_kexinit(struct ssh *ssh) arc4random_buf(cookie, KEX_COOKIE_LEN); if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 || - (r = sshpkt_putb(ssh, kex->my)) != 0 || - (r = sshpkt_send(ssh)) != 0) + (r = sshpkt_putb(ssh, kex->my)) != 0 || + (r = sshpkt_send(ssh)) != 0) return r; debug("SSH2_MSG_KEXINIT sent"); kex->flags |= KEX_INIT_SENT; @@ -405,19 +409,19 @@ kex_input_kexinit(int type, u_int32_t seq, void *ctxt) if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) return r; /* - * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported - * KEX method has the server move first, but a server might be using - * a custom method or one that we otherwise don't support. We should - * be prepared to remember first_kex_follows here so we can eat a - * packet later. - * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means - * for cases where the server *doesn't* go first. I guess we should - * ignore it when it is set for these cases, which is what we do now. - */ + * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported + * KEX method has the server move first, but a server might be using + * a custom method or one that we otherwise don't support. We should + * be prepared to remember first_kex_follows here so we can eat a + * packet later. + * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means + * for cases where the server *doesn't* go first. I guess we should + * ignore it when it is set for these cases, which is what we do now. + */ if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */ - (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */ - (r = sshpkt_get_end(ssh)) != 0) - return r; + (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */ + (r = sshpkt_get_end(ssh)) != 0) + return r; if (!(kex->flags & KEX_INIT_SENT)) if ((r = kex_send_kexinit(ssh)) != 0) @@ -441,7 +445,7 @@ kex_new(struct ssh *ssh, char *proposal[PROPOSAL_MAX], struct kex **kexp) if ((kex = calloc(1, sizeof(*kex))) == NULL) return SSH_ERR_ALLOC_FAIL; if ((kex->peer = sshbuf_new()) == NULL || - (kex->my = sshbuf_new()) == NULL) { + (kex->my = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } @@ -451,7 +455,7 @@ kex_new(struct ssh *ssh, char *proposal[PROPOSAL_MAX], struct kex **kexp) kex_reset_dispatch(ssh); r = 0; *kexp = kex; - out: +out: if (r != 0) kex_free(kex); return r; @@ -468,7 +472,7 @@ kex_free_newkeys(struct newkeys *newkeys) newkeys->enc.key = NULL; } 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); newkeys->enc.iv = NULL; } @@ -494,13 +498,10 @@ kex_free(struct kex *kex) u_int mode; #ifdef WITH_OPENSSL - if (kex->dh) - DH_free(kex->dh); -#ifdef OPENSSL_HAS_ECC + sshdh_free(kex->dh); if (kex->ec_client_key) EC_KEY_free(kex->ec_client_key); -#endif /* OPENSSL_HAS_ECC */ -#endif /* WITH_OPENSSL */ +#endif for (mode = 0; mode < MODE_MAX; mode++) { kex_free_newkeys(kex->newkeys[mode]); kex->newkeys[mode] = NULL; @@ -575,11 +576,14 @@ choose_comp(struct sshcomp *comp, char *client, char *server) return SSH_ERR_NO_COMPRESS_ALG_MATCH; if (strcmp(name, "zlib@openssh.com") == 0) { comp->type = COMP_DELAYED; - } else if (strcmp(name, "zlib") == 0) { + } + else if (strcmp(name, "zlib") == 0) { comp->type = COMP_ZLIB; - } else if (strcmp(name, "none") == 0) { + } + else if (strcmp(name, "none") == 0) { comp->type = COMP_NONE; - } else { + } + else { return SSH_ERR_INTERNAL_ERROR; } comp->name = name; @@ -593,6 +597,7 @@ choose_kex(struct kex *k, char *client, char *server) k->name = match_list(client, server, NULL); + debug("kex: algorithm: %s", k->name ? k->name : "(no match)"); if (k->name == NULL) return SSH_ERR_NO_KEX_ALG_MATCH; 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); + debug("kex: host key algorithm: %s", + hostkeyalg ? hostkeyalg : "(no match)"); if (hostkeyalg == NULL) return SSH_ERR_NO_HOSTKEY_ALG_MATCH; k->hostkey_type = sshkey_type_from_name(hostkeyalg); @@ -634,7 +641,7 @@ proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX]) *p = '\0'; if (strcmp(my[*idx], peer[*idx]) != 0) { debug2("proposal mismatch: my %s peer %s", - my[*idx], peer[*idx]); + my[*idx], peer[*idx]); return (0); } } @@ -653,22 +660,26 @@ kex_choose_conf(struct ssh *ssh) u_int mode, ctos, need, dh_need, authlen; int r, first_kex_follows; - if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0 || - (r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0) + debug2("local %s KEXINIT proposal", kex->server ? "server" : "client"); + 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; if (kex->server) { - cprop=peer; - sprop=my; - } else { - cprop=my; - sprop=peer; + cprop = peer; + sprop = my; + } + else { + cprop = my; + sprop = peer; } /* Check whether server offers roaming */ if (!kex->server) { char *roaming = match_list(KEX_RESUME, - peer[PROPOSAL_KEX_ALGS], NULL); + peer[PROPOSAL_KEX_ALGS], NULL); if (roaming) { kex->roaming = 1; @@ -677,6 +688,18 @@ kex_choose_conf(struct ssh *ssh) } /* 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++) { if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) { r = SSH_ERR_ALLOC_FAIL; @@ -684,12 +707,12 @@ kex_choose_conf(struct ssh *ssh) } kex->newkeys[mode] = newkeys; ctos = (!kex->server && mode == MODE_OUT) || - (kex->server && mode == MODE_IN); - nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC; - nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC; + (kex->server && mode == MODE_IN); + nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC; + nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC; ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC; if ((r = choose_enc(&newkeys->enc, cprop[nenc], - sprop[nenc])) != 0) { + sprop[nenc])) != 0) { kex->failed_choice = peer[nenc]; peer[nenc] = NULL; goto out; @@ -697,35 +720,23 @@ kex_choose_conf(struct ssh *ssh) authlen = cipher_authlen(newkeys->enc.cipher); /* ignore mac for authenticated encryption */ if (authlen == 0 && - (r = choose_mac(ssh, &newkeys->mac, cprop[nmac], - sprop[nmac])) != 0) { + (r = choose_mac(ssh, &newkeys->mac, cprop[nmac], + sprop[nmac])) != 0) { kex->failed_choice = peer[nmac]; peer[nmac] = NULL; goto out; } if ((r = choose_comp(&newkeys->comp, cprop[ncomp], - sprop[ncomp])) != 0) { + sprop[ncomp])) != 0) { kex->failed_choice = peer[ncomp]; peer[ncomp] = NULL; goto out; } - debug("kex: %s %s %s %s", - ctos ? "client->server" : "server->client", - newkeys->enc.name, - authlen == 0 ? newkeys->mac.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; + debug("kex: %s cipher: %s MAC: %s compression: %s", + ctos ? "client->server" : "server->client", + newkeys->enc.name, + authlen == 0 ? newkeys->mac.name : "", + newkeys->comp.name); } need = dh_need = 0; for (mode = 0; mode < MODE_MAX; mode++) { @@ -745,10 +756,10 @@ kex_choose_conf(struct ssh *ssh) /* ignore the next message if the proposals do not match */ if (first_kex_follows && !proposals_match(my, peer) && - !(ssh->compat & SSH_BUG_FIRSTKEX)) + !(ssh->compat & SSH_BUG_FIRSTKEX)) ssh->dispatch_skip_packets = 1; r = 0; - out: +out: kex_prop_free(my); kex_prop_free(peer); return r; @@ -756,7 +767,7 @@ kex_choose_conf(struct ssh *ssh) static int derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen, - const struct sshbuf *shared_secret, u_char **keyp) + const struct sshbuf *shared_secret, u_char **keyp) { struct kex *kex = ssh->kex; struct ssh_digest_ctx *hashctx = NULL; @@ -775,12 +786,12 @@ derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen, /* K1 = HASH(K || H || "A" || session_id) */ if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL || - ssh_digest_update_buffer(hashctx, shared_secret) != 0 || - ssh_digest_update(hashctx, hash, hashlen) != 0 || - ssh_digest_update(hashctx, &c, 1) != 0 || - ssh_digest_update(hashctx, kex->session_id, - kex->session_id_len) != 0 || - ssh_digest_final(hashctx, digest, mdsz) != 0) { + ssh_digest_update_buffer(hashctx, shared_secret) != 0 || + ssh_digest_update(hashctx, hash, hashlen) != 0 || + ssh_digest_update(hashctx, &c, 1) != 0 || + ssh_digest_update(hashctx, kex->session_id, + kex->session_id_len) != 0 || + ssh_digest_final(hashctx, digest, mdsz) != 0) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } @@ -788,16 +799,16 @@ derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen, hashctx = NULL; /* - * expand key: - * Kn = HASH(K || H || K1 || K2 || ... || Kn-1) - * Key = K1 || K2 || ... || Kn - */ + * expand key: + * Kn = HASH(K || H || K1 || K2 || ... || Kn-1) + * Key = K1 || K2 || ... || Kn + */ for (have = mdsz; need > have; have += mdsz) { if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL || - ssh_digest_update_buffer(hashctx, shared_secret) != 0 || - ssh_digest_update(hashctx, hash, hashlen) != 0 || - ssh_digest_update(hashctx, digest, have) != 0 || - ssh_digest_final(hashctx, digest + have, mdsz) != 0) { + ssh_digest_update_buffer(hashctx, shared_secret) != 0 || + ssh_digest_update(hashctx, hash, hashlen) != 0 || + ssh_digest_update(hashctx, digest, have) != 0 || + ssh_digest_final(hashctx, digest + have, mdsz) != 0) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } @@ -811,7 +822,7 @@ derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen, *keyp = digest; digest = NULL; r = 0; - out: +out: if (digest) free(digest); ssh_digest_free(hashctx); @@ -821,7 +832,7 @@ derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen, #define NKEYS 6 int kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen, - const struct sshbuf *shared_secret) + const struct sshbuf *shared_secret) { struct kex *kex = ssh->kex; u_char *keys[NKEYS]; @@ -829,8 +840,8 @@ kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen, int r; for (i = 0; i < NKEYS; i++) { - if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen, - shared_secret, &keys[i])) != 0) { + if ((r = derive_key(ssh, 'A' + i, kex->we_need, hash, hashlen, + shared_secret, &keys[i])) != 0) { for (j = 0; j < i; j++) free(keys[j]); return r; @@ -838,8 +849,8 @@ kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen, } for (mode = 0; mode < MODE_MAX; mode++) { ctos = (!kex->server && mode == MODE_OUT) || - (kex->server && mode == MODE_IN); - kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1]; + (kex->server && mode == MODE_IN); + kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1]; kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3]; kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5]; } @@ -849,14 +860,14 @@ kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen, #ifdef WITH_OPENSSL int kex_derive_keys_bn(struct ssh *ssh, u_char *hash, u_int hashlen, - const BIGNUM *secret) + const struct sshbn *secret) { struct sshbuf *shared_secret; int r; if ((shared_secret = sshbuf_new()) == NULL) 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); sshbuf_free(shared_secret); return r; @@ -866,7 +877,7 @@ kex_derive_keys_bn(struct ssh *ssh, u_char *hash, u_int hashlen, #ifdef WITH_SSH1 int derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus, - u_int8_t cookie[8], u_int8_t id[16]) + u_int8_t cookie[8], u_int8_t id[16]) { u_int8_t hbuf[2048], sbuf[2048], obuf[SSH_DIGEST_MAX_LENGTH]; struct ssh_digest_ctx *hashctx = NULL; @@ -876,10 +887,10 @@ derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus, hlen = BN_num_bytes(host_modulus); slen = BN_num_bytes(server_modulus); if (hlen < (512 / 8) || (u_int)hlen > sizeof(hbuf) || - slen < (512 / 8) || (u_int)slen > sizeof(sbuf)) + slen < (512 / 8) || (u_int)slen > sizeof(sbuf)) return SSH_ERR_KEY_BITS_MISMATCH; if (BN_bn2bin(host_modulus, hbuf) <= 0 || - BN_bn2bin(server_modulus, sbuf) <= 0) { + BN_bn2bin(server_modulus, sbuf) <= 0) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } @@ -888,15 +899,15 @@ derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus, goto out; } if (ssh_digest_update(hashctx, hbuf, hlen) != 0 || - ssh_digest_update(hashctx, sbuf, slen) != 0 || - ssh_digest_update(hashctx, cookie, 8) != 0 || - ssh_digest_final(hashctx, obuf, sizeof(obuf)) != 0) { + ssh_digest_update(hashctx, sbuf, slen) != 0 || + ssh_digest_update(hashctx, cookie, 8) != 0 || + ssh_digest_final(hashctx, obuf, sizeof(obuf)) != 0) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } memcpy(id, obuf, ssh_digest_bytes(SSH_DIGEST_MD5)); r = 0; - out: +out: ssh_digest_free(hashctx); explicit_bzero(hbuf, sizeof(hbuf)); explicit_bzero(sbuf, sizeof(sbuf)); diff --git a/kex.h b/kex.h index d71b532..1c2be3e 100644 --- a/kex.h +++ b/kex.h @@ -34,20 +34,6 @@ #include "leakmalloc.h" #endif -#ifdef WITH_OPENSSL -# ifdef OPENSSL_HAS_ECC -# include -# 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_DH1 "diffie-hellman-group1-sha1" @@ -142,15 +128,15 @@ struct kex { char *client_version_string; char *server_version_string; char *failed_choice; - int (*verify_host_key)(struct sshkey *, struct ssh *); + int(*verify_host_key)(struct sshkey *, struct ssh *); struct sshkey *(*load_host_public_key)(int, int, struct ssh *); struct sshkey *(*load_host_private_key)(int, int, struct ssh *); - int (*host_key_index)(struct sshkey *, int, struct ssh *); - int (*sign)(struct sshkey *, struct sshkey *, - u_char **, size_t *, const u_char *, size_t, u_int); - int (*kex[KEX_MAX])(struct ssh *); + int(*host_key_index)(struct sshkey *, int, struct ssh *); + int(*sign)(struct sshkey *, struct sshkey *, + u_char **, size_t *, const u_char *, size_t, u_int); + int(*kex[KEX_MAX])(struct ssh *); /* kex specific state */ - DH *dh; /* DH */ + struct sshdh *dh; /* DH */ u_int min, max, nbits; /* GEX */ EC_KEY *ec_client_key; /* ECDH */ const EC_GROUP *ec_group; /* ECDH */ @@ -175,7 +161,8 @@ void kex_prop_free(char **); int kex_send_kexinit(struct ssh *); 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_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 kexdh_client(struct ssh *); @@ -188,29 +175,30 @@ int kexc25519_client(struct ssh *); int kexc25519_server(struct ssh *); int kex_dh_hash(const char *, const char *, - 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 u_char *, size_t, const u_char *, size_t, const 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 *, - const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, - int, int, int, - const BIGNUM *, const BIGNUM *, const BIGNUM *, - const BIGNUM *, const BIGNUM *, - u_char *, size_t *); + const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, + int, int, int, + const struct sshbn *, const struct sshbn *, const struct sshbn *, + const struct sshbn *, const struct sshbn *, + u_char *, size_t *); int kex_ecdh_hash(int, const EC_GROUP *, const char *, const char *, - const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, - const EC_POINT *, const EC_POINT *, const BIGNUM *, u_char *, size_t *); + const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, + const EC_POINT *, const EC_POINT *, const BIGNUM *, u_char *, size_t *); int kex_c25519_hash(int, const char *, const char *, const char *, size_t, - const char *, size_t, const u_char *, size_t, const u_char *, const u_char *, - const u_char *, size_t, u_char *, size_t *); + const char *, size_t, const u_char *, size_t, const u_char *, const u_char *, + const u_char *, size_t, u_char *, size_t *); void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) - __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) - __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))); +__attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) +__attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))); int kexc25519_shared_key(const u_char key[CURVE25519_SIZE], - const u_char pub[CURVE25519_SIZE], struct sshbuf *out) + const u_char pub[CURVE25519_SIZE], struct sshbuf *out) __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))); @@ -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); #endif -#if !defined(WITH_OPENSSL) || !defined(OPENSSL_HAS_ECC) -# undef EC_KEY -# undef EC_GROUP -# undef EC_POINT -#endif - #endif diff --git a/kexdh.c b/kexdh.c index feea669..d368fca 100644 --- a/kexdh.c +++ b/kexdh.c @@ -25,7 +25,6 @@ #include "includes.h" -#ifdef WITH_OPENSSL #include @@ -43,15 +42,15 @@ int kex_dh_hash( - 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, - const BIGNUM *client_dh_pub, - const BIGNUM *server_dh_pub, - const BIGNUM *shared_secret, - u_char *hash, size_t *hashlen) + 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, + 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; @@ -61,18 +60,18 @@ kex_dh_hash( 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 || - (r = sshbuf_put_bignum2(b, client_dh_pub)) != 0 || - (r = sshbuf_put_bignum2(b, server_dh_pub)) != 0 || - (r = sshbuf_put_bignum2(b, shared_secret)) != 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 || + (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; } @@ -90,4 +89,3 @@ kex_dh_hash( #endif return 0; } -#endif /* WITH_OPENSSL */ diff --git a/kexdhc.c b/kexdhc.c index af259f1..4a174b6 100644 --- a/kexdhc.c +++ b/kexdhc.c @@ -25,7 +25,6 @@ #include "includes.h" -#ifdef WITH_OPENSSL #include @@ -55,15 +54,18 @@ int kexdh_client(struct ssh *ssh) { struct kex *kex = ssh->kex; + struct sshbn *dh_client_pub = NULL; int r; /* generate and send 'e', client DH public key */ switch (kex->kex_type) { case KEX_DH_GRP1_SHA1: - kex->dh = dh_new_group1(); + if ((r = dh_new_group1(&kex->dh)) != 0) + return r; break; case KEX_DH_GRP14_SHA1: - kex->dh = dh_new_group14(); + if ((r = dh_new_group14(&kex->dh)) != 0) + return r; break; default: r = SSH_ERR_INVALID_ARGUMENT; @@ -73,22 +75,25 @@ kexdh_client(struct ssh *ssh) r = SSH_ERR_ALLOC_FAIL; 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"); - if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0 || - (r = sshpkt_start(ssh, SSH2_MSG_KEXDH_INIT)) != 0 || - (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || - (r = sshpkt_send(ssh)) != 0) + if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_INIT)) != 0 || + (r = sshpkt_put_bignum2_wrap(ssh, dh_client_pub)) != 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"); + sshdh_dump(kex->dh); #endif debug("expecting SSH2_MSG_KEXDH_REPLY"); ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_REPLY, &input_kex_dh); r = 0; - out: +out: + sshbn_free(dh_client_pub); return r; } @@ -97,12 +102,14 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = ctxt; 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; - 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]; - size_t klen = 0, slen, sbloblen, hashlen; - int kout, r; + size_t slen, sbloblen, hashlen; + int r; if (kex->verify_host_key == NULL) { r = SSH_ERR_INVALID_ARGUMENT; @@ -110,13 +117,13 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt) } /* 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) + &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)) { + (kex->hostkey_type == KEY_ECDSA && + server_host_key->ecdsa_nid != kex->hostkey_nid)) { r = SSH_ERR_KEY_TYPE_MISMATCH; goto out; } @@ -125,14 +132,14 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt) goto out; } /* 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; 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) + if ((r = sshpkt_get_bignum2_wrap(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= "); @@ -140,23 +147,17 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt) fprintf(stderr, "\n"); debug("bits %d", BN_num_bits(dh_server_pub)); #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"); - r = SSH_ERR_MESSAGE_INCOMPLETE; goto out; } - - klen = DH_size(kex->dh); - if ((kbuf = malloc(klen)) == NULL || - (shared_secret = BN_new()) == NULL) { - r = SSH_ERR_ALLOC_FAIL; + if ((dh_client_pub = sshdh_pubkey(kex->dh)) == NULL) { + r = SSH_ERR_INTERNAL_ERROR; goto out; } - if ((kout = DH_compute_key(kbuf, dh_server_pub, kex->dh)) < 0 || - BN_bin2bn(kbuf, kout, shared_secret) == NULL) { - r = SSH_ERR_LIBCRYPTO_ERROR; + if ((r = sshdh_compute_key(kex->dh, dh_server_pub, + &shared_secret)) != 0) goto out; - } #ifdef DEBUG_KEXDH dump_digest("shared secret", kbuf, kout); #endif @@ -164,19 +165,19 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt) /* 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, - kex->dh->pub_key, - dh_server_pub, - shared_secret, - hash, &hashlen)) != 0) + 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_client_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) + ssh->compat)) != 0) goto out; /* save session id */ @@ -192,21 +193,15 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt) if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) r = kex_send_newkeys(ssh); - out: +out: 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); + 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(signature); return r; } -#endif /* WITH_OPENSSL */ diff --git a/kexdhs.c b/kexdhs.c index f510aba..06ef313 100644 --- a/kexdhs.c +++ b/kexdhs.c @@ -34,7 +34,6 @@ #undef KRB5 #endif -#ifdef WITH_OPENSSL #include @@ -69,27 +68,24 @@ kexdh_server(struct ssh *ssh) /* generate server DH public key */ switch (kex->kex_type) { case KEX_DH_GRP1_SHA1: - kex->dh = dh_new_group1(); + if ((r = dh_new_group1(&kex->dh)) != 0) + return r; break; case KEX_DH_GRP14_SHA1: - kex->dh = dh_new_group14(); + if ((r = dh_new_group14(&kex->dh)) != 0) + return r; break; default: - r = SSH_ERR_INVALID_ARGUMENT; - goto out; - } - if (kex->dh == NULL) { - r = SSH_ERR_ALLOC_FAIL; - goto out; + return SSH_ERR_INVALID_ARGUMENT; } + if (kex->dh == NULL) + return SSH_ERR_ALLOC_FAIL; if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) - goto out; + return r; debug("expecting SSH2_MSG_KEXDH_INIT"); ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_INIT, &input_kex_dh_init); - r = 0; - out: - return r; + return 0; } int @@ -97,35 +93,37 @@ 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; + 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; - 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]; size_t sbloblen, slen; - size_t klen = 0, hashlen; - int kout, r; + size_t hashlen; + int r; if (kex->load_host_public_key == NULL || - kex->load_host_private_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); + kex->hostkey_nid, ssh); server_host_private = kex->load_host_private_key(kex->hostkey_type, - kex->hostkey_nid, ssh); + 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) { + if ((dh_client_pub = sshbn_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) + if ((r = sshpkt_get_bignum2_wrap(ssh, dh_client_pub)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) goto out; #ifdef DEBUG_KEXDH @@ -136,46 +134,37 @@ input_kex_dh_init(int type, u_int32_t seq, void *ctxt) #endif #ifdef DEBUG_KEXDH - DHparams_print_fp(stderr, kex->dh); - fprintf(stderr, "pub= "); - BN_print_fp(stderr, kex->dh->pub_key); - fprintf(stderr, "\n"); + sshdh_dump(kex->dh); #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"); - r = SSH_ERR_MESSAGE_INCOMPLETE; goto out; } - - klen = DH_size(kex->dh); - if ((kbuf = malloc(klen)) == NULL || - (shared_secret = BN_new()) == NULL) { - r = SSH_ERR_ALLOC_FAIL; + if ((dh_server_pub = sshdh_pubkey(kex->dh)) == NULL) { + r = SSH_ERR_INTERNAL_ERROR; goto out; } - if ((kout = DH_compute_key(kbuf, dh_client_pub, kex->dh)) < 0 || - BN_bin2bn(kbuf, kout, shared_secret) == NULL) { - r = SSH_ERR_LIBCRYPTO_ERROR; + if ((r = sshdh_compute_key(kex->dh, dh_client_pub, + &shared_secret)) != 0) goto out; - } #ifdef DEBUG_KEXDH - dump_digest("shared secret", kbuf, kout); + dump_digest("shared secret", kbuf, klen); #endif if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, - &sbloblen)) != 0) + &sbloblen)) != 0) goto out; /* 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, - kex->dh->pub_key, - shared_secret, - hash, &hashlen)) != 0) + 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_server_pub, + shared_secret, + hash, &hashlen)) != 0) goto out; /* save session id := H */ @@ -191,35 +180,29 @@ input_kex_dh_init(int type, u_int32_t seq, void *ctxt) /* sign H */ if ((r = kex->sign(server_host_private, server_host_public, - &signature, &slen, hash, hashlen, ssh->compat)) < 0) + &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, kex->dh->pub_key)) != 0 || /* f */ - (r = sshpkt_put_string(ssh, signature, slen)) != 0 || - (r = sshpkt_send(ssh)) != 0) + (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || + (r = sshpkt_put_bignum2_wrap(ssh, dh_server_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: +out: 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; - 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(signature); return r; } -#endif /* WITH_OPENSSL */ diff --git a/kexecdhc.c b/kexecdhc.c index 90220ce..4c1865f 100644 --- a/kexecdhc.c +++ b/kexecdhc.c @@ -26,8 +26,6 @@ #include "includes.h" -#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) - #include #include @@ -72,8 +70,8 @@ kexecdh_client(struct ssh *ssh) public_key = EC_KEY_get0_public_key(client_key); if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_ECDH_INIT)) != 0 || - (r = sshpkt_put_ec(ssh, public_key, group)) != 0 || - (r = sshpkt_send(ssh)) != 0) + (r = sshpkt_put_ec(ssh, public_key, group)) != 0 || + (r = sshpkt_send(ssh)) != 0) goto out; debug("sending SSH2_MSG_KEX_ECDH_INIT"); @@ -88,7 +86,7 @@ kexecdh_client(struct ssh *ssh) debug("expecting SSH2_MSG_KEX_ECDH_REPLY"); ssh_dispatch_set(ssh, SSH2_MSG_KEX_ECDH_REPLY, &input_kex_ecdh_reply); r = 0; - out: +out: if (client_key) EC_KEY_free(client_key); return r; @@ -103,6 +101,7 @@ input_kex_ecdh_reply(int type, u_int32_t seq, void *ctxt) EC_POINT *server_public = NULL; EC_KEY *client_key; BIGNUM *shared_secret = NULL; + struct sshbn *xxx_shared_secret = NULL; struct sshkey *server_host_key = NULL; u_char *server_host_key_blob = NULL, *signature = NULL; u_char *kbuf = NULL; @@ -120,13 +119,13 @@ input_kex_ecdh_reply(int type, u_int32_t seq, void *ctxt) /* hostkey */ 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) + &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)) { + (kex->hostkey_type == KEY_ECDSA && + server_host_key->ecdsa_nid != kex->hostkey_nid)) { r = SSH_ERR_KEY_TYPE_MISMATCH; goto out; } @@ -142,8 +141,8 @@ input_kex_ecdh_reply(int type, u_int32_t seq, void *ctxt) goto out; } if ((r = sshpkt_get_ec(ssh, server_public, group)) != 0 || - (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 || - (r = sshpkt_get_end(ssh)) != 0) + (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) goto out; #ifdef DEBUG_KEXECDH @@ -158,13 +157,13 @@ input_kex_ecdh_reply(int type, u_int32_t seq, void *ctxt) klen = (EC_GROUP_get_degree(group) + 7) / 8; if ((kbuf = malloc(klen)) == NULL || - (shared_secret = BN_new()) == NULL) { + (shared_secret = BN_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if (ECDH_compute_key(kbuf, klen, server_public, - client_key, NULL) != (int)klen || - BN_bin2bn(kbuf, klen, shared_secret) == NULL) { + client_key, NULL) != (int)klen || + BN_bin2bn(kbuf, klen, shared_secret) == NULL) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } @@ -175,21 +174,21 @@ input_kex_ecdh_reply(int type, u_int32_t seq, void *ctxt) /* calc and verify H */ hashlen = sizeof(hash); if ((r = kex_ecdh_hash( - kex->hash_alg, - group, - 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, - EC_KEY_get0_public_key(client_key), - server_public, - shared_secret, - hash, &hashlen)) != 0) + kex->hash_alg, + group, + 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, + EC_KEY_get0_public_key(client_key), + server_public, + shared_secret, + hash, &hashlen)) != 0) goto out; if ((r = sshkey_verify(server_host_key, signature, slen, hash, - hashlen, ssh->compat)) != 0) + hashlen, ssh->compat)) != 0) goto out; /* save session id */ @@ -203,9 +202,15 @@ input_kex_ecdh_reply(int type, u_int32_t seq, void *ctxt) 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); - out: +out: explicit_bzero(hash, sizeof(hash)); if (kex->ec_client_key) { EC_KEY_free(kex->ec_client_key); @@ -219,10 +224,9 @@ input_kex_ecdh_reply(int type, u_int32_t seq, void *ctxt) } if (shared_secret) BN_clear_free(shared_secret); + sshbn_free(xxx_shared_secret); sshkey_free(server_host_key); free(server_host_key_blob); free(signature); return r; } -#endif /* defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) */ - diff --git a/kexecdhs.c b/kexecdhs.c index 0adb80e..98d66cc 100644 --- a/kexecdhs.c +++ b/kexecdhs.c @@ -26,7 +26,7 @@ #include "includes.h" -#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) + #include #include @@ -67,6 +67,7 @@ input_kex_ecdh_init(int type, u_int32_t seq, void *ctxt) const EC_GROUP *group; const EC_POINT *public_key; BIGNUM *shared_secret = NULL; + struct sshbn *xxx_shared_secret = NULL; struct sshkey *server_host_private, *server_host_public; u_char *server_host_key_blob = NULL, *signature = NULL; u_char *kbuf = NULL; @@ -91,14 +92,14 @@ input_kex_ecdh_init(int type, u_int32_t seq, void *ctxt) #endif if (kex->load_host_public_key == NULL || - kex->load_host_private_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); + kex->hostkey_nid, ssh); server_host_private = kex->load_host_private_key(kex->hostkey_type, - kex->hostkey_nid, ssh); + kex->hostkey_nid, ssh); if (server_host_public == NULL) { r = SSH_ERR_NO_HOSTKEY_LOADED; goto out; @@ -108,7 +109,7 @@ input_kex_ecdh_init(int type, u_int32_t seq, void *ctxt) goto out; } if ((r = sshpkt_get_ec(ssh, client_public, group)) != 0 || - (r = sshpkt_get_end(ssh)) != 0) + (r = sshpkt_get_end(ssh)) != 0) goto out; #ifdef DEBUG_KEXECDH @@ -124,13 +125,13 @@ input_kex_ecdh_init(int type, u_int32_t seq, void *ctxt) /* Calculate shared_secret */ klen = (EC_GROUP_get_degree(group) + 7) / 8; if ((kbuf = malloc(klen)) == NULL || - (shared_secret = BN_new()) == NULL) { + (shared_secret = BN_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if (ECDH_compute_key(kbuf, klen, client_public, - server_key, NULL) != (int)klen || - BN_bin2bn(kbuf, klen, shared_secret) == NULL) { + server_key, NULL) != (int)klen || + BN_bin2bn(kbuf, klen, shared_secret) == NULL) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } @@ -140,21 +141,21 @@ input_kex_ecdh_init(int type, u_int32_t seq, void *ctxt) #endif /* calc H */ if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, - &sbloblen)) != 0) + &sbloblen)) != 0) goto out; hashlen = sizeof(hash); if ((r = kex_ecdh_hash( - kex->hash_alg, - group, - 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, - client_public, - EC_KEY_get0_public_key(server_key), - shared_secret, - hash, &hashlen)) != 0) + kex->hash_alg, + group, + 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, + client_public, + EC_KEY_get0_public_key(server_key), + shared_secret, + hash, &hashlen)) != 0) goto out; /* save session id := H */ @@ -170,7 +171,7 @@ input_kex_ecdh_init(int type, u_int32_t seq, void *ctxt) /* sign H */ if ((r = kex->sign(server_host_private, server_host_public, - &signature, &slen, hash, hashlen, ssh->compat)) < 0) + &signature, &slen, hash, hashlen, ssh->compat)) < 0) goto out; /* destroy_sensitive_data(); */ @@ -178,15 +179,20 @@ input_kex_ecdh_init(int type, u_int32_t seq, void *ctxt) public_key = EC_KEY_get0_public_key(server_key); /* send server hostkey, ECDH pubkey 'Q_S' and signed H */ if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_ECDH_REPLY)) != 0 || - (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || - (r = sshpkt_put_ec(ssh, public_key, group)) != 0 || - (r = sshpkt_put_string(ssh, signature, slen)) != 0 || - (r = sshpkt_send(ssh)) != 0) + (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || + (r = sshpkt_put_ec(ssh, public_key, group)) != 0 || + (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) + /* 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); - out: +out: explicit_bzero(hash, sizeof(hash)); if (kex->ec_client_key) { EC_KEY_free(kex->ec_client_key); @@ -200,9 +206,8 @@ input_kex_ecdh_init(int type, u_int32_t seq, void *ctxt) } if (shared_secret) BN_clear_free(shared_secret); + sshbn_free(xxx_shared_secret); free(server_host_key_blob); free(signature); return r; } -#endif /* defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) */ - diff --git a/kexgex.c b/kexgex.c index 63be885..f16a439 100644 --- a/kexgex.c +++ b/kexgex.c @@ -35,8 +35,6 @@ #undef KRB5 #endif -#ifdef WITH_OPENSSL - #include #include @@ -49,22 +47,23 @@ #include "ssherr.h" #include "sshbuf.h" #include "digest.h" +#include "crypto-wrap.h" int 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 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 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; @@ -74,23 +73,23 @@ kexgex_hash( 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(b, prime)) != 0 || - (r = sshbuf_put_bignum2(b, gen)) != 0 || - (r = sshbuf_put_bignum2(b, client_dh_pub)) != 0 || - (r = sshbuf_put_bignum2(b, server_dh_pub)) != 0 || - (r = sshbuf_put_bignum2(b, shared_secret)) != 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; } @@ -108,4 +107,63 @@ kexgex_hash( #endif return 0; } -#endif /* WITH_OPENSSL */ + + +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) +{ + 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(b, prime)) != 0 || + (r = sshbuf_put_bignum2(b, gen)) != 0 || + (r = sshbuf_put_bignum2(b, client_dh_pub)) != 0 || + (r = sshbuf_put_bignum2(b, server_dh_pub)) != 0 || + (r = sshbuf_put_bignum2(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; +} \ No newline at end of file diff --git a/kexgexc.c b/kexgexc.c index 71ff133..e18e9b3 100644 --- a/kexgexc.c +++ b/kexgexc.c @@ -26,14 +26,11 @@ #include "includes.h" -#ifdef WITH_OPENSSL - #include #include #include -#include #include #include #include @@ -54,6 +51,23 @@ static int input_kex_dh_gex_group(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 kexgex_client(struct ssh *ssh) { @@ -70,21 +84,21 @@ kexgex_client(struct ssh *ssh) 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) + (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); + kex->min, kex->nbits, kex->max); #ifdef DEBUG_KEXDH fprintf(stderr, "\nmin = %d, nbits = %d, max = %d\n", - kex->min, kex->nbits, kex->max); + kex->min, kex->nbits, kex->max); #endif ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP, - &input_kex_dh_gex_group); + &input_kex_dh_gex_group); r = 0; - out: +out: return r; } @@ -93,52 +107,55 @@ 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; + struct sshbn *dh_client_pub = NULL; + struct sshbn *dh_g = NULL, *dh_p = NULL; + int r; + size_t bits; debug("got SSH2_MSG_KEX_DH_GEX_GROUP"); - if ((p = BN_new()) == NULL || - (g = BN_new()) == NULL) { + if ((dh_p = sshbn_new()) == NULL || + (dh_g = sshbn_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) + if ((r = sshpkt_get_bignum2_wrap(ssh, dh_p)) != 0 || + (r = sshpkt_get_bignum2_wrap(ssh, dh_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) { + if ((bits = sshbn_bits(dh_p)) == 0 || + bits < kex->min || bits > kex->max) { r = SSH_ERR_DH_GEX_OUT_OF_RANGE; 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; 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 */ - if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0 || - (r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_INIT)) != 0 || - (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || - (r = sshpkt_send(ssh)) != 0) + /* generate and send 'e', client DH public key */ + 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_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) 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"); + sshdh_dump(kex->dh); #endif 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); r = 0; out: - if (p) - BN_clear_free(p); - if (g) - BN_clear_free(g); + sshbn_free(dh_p); + sshbn_free(dh_g); + sshbn_free(dh_client_pub); return r; } @@ -147,12 +164,15 @@ input_kex_dh_gex_reply(int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = ctxt; 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; - 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]; - size_t klen = 0, slen, sbloblen, hashlen; - int kout, r; + size_t slen, sbloblen, hashlen; + int r; debug("got SSH2_MSG_KEX_DH_GEX_REPLY"); if (kex->verify_host_key == NULL) { @@ -161,17 +181,17 @@ input_kex_dh_gex_reply(int type, u_int32_t seq, void *ctxt) } /* 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) + &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)) { + (kex->hostkey_type == KEY_ECDSA && + server_host_key->ecdsa_nid != kex->hostkey_nid)) { r = SSH_ERR_KEY_TYPE_MISMATCH; goto out; } @@ -180,14 +200,14 @@ input_kex_dh_gex_reply(int type, u_int32_t seq, void *ctxt) goto out; } /* 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; 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) + if ((r = sshpkt_get_bignum2_wrap(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= "); @@ -195,23 +215,19 @@ input_kex_dh_gex_reply(int type, u_int32_t seq, void *ctxt) fprintf(stderr, "\n"); debug("bits %d", BN_num_bits(dh_server_pub)); #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"); - r = SSH_ERR_MESSAGE_INCOMPLETE; goto out; } - - klen = DH_size(kex->dh); - if ((kbuf = malloc(klen)) == NULL || - (shared_secret = BN_new()) == NULL) { - r = SSH_ERR_ALLOC_FAIL; + if ((dh_client_pub = sshdh_pubkey(kex->dh)) == NULL || + (dh_p = sshdh_p(kex->dh)) == NULL || + (dh_g = sshdh_g(kex->dh)) == NULL) { + r = SSH_ERR_INTERNAL_ERROR; goto out; } - if ((kout = DH_compute_key(kbuf, dh_server_pub, kex->dh)) < 0 || - BN_bin2bn(kbuf, kout, shared_secret) == NULL) { - r = SSH_ERR_LIBCRYPTO_ERROR; + if ((r = sshdh_compute_key(kex->dh, dh_server_pub, + &shared_secret)) != 0) goto out; - } #ifdef DEBUG_KEXDH dump_digest("shared secret", kbuf, kout); #endif @@ -221,22 +237,22 @@ input_kex_dh_gex_reply(int type, u_int32_t seq, void *ctxt) /* 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, - kex->dh->p, kex->dh->g, - kex->dh->pub_key, - dh_server_pub, - shared_secret, - hash, &hashlen)) != 0) + 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, + dh_p, dh_g, + dh_client_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) + hashlen, ssh->compat)) != 0) goto out; /* save session id */ @@ -252,21 +268,16 @@ input_kex_dh_gex_reply(int type, u_int32_t seq, void *ctxt) if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) r = kex_send_newkeys(ssh); - out: +out: 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; - 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(signature); return r; } -#endif /* WITH_OPENSSL */ diff --git a/kexgexs.c b/kexgexs.c index cd1d620..dddc2bc 100644 --- a/kexgexs.c +++ b/kexgexs.c @@ -30,16 +30,8 @@ * We support only client side kerberos on Windows. */ -#ifdef WIN32_FIXME - #undef GSSAPI - #undef KRB5 -#endif - -#ifdef WITH_OPENSSL - #include /* MIN MAX */ -#include #include #include #include @@ -70,7 +62,7 @@ int kexgex_server(struct ssh *ssh) { ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST, - &input_kex_dh_gex_request); + &input_kex_dh_gex_request); debug("expecting SSH2_MSG_KEX_DH_GEX_REQUEST"); return 0; } @@ -80,14 +72,15 @@ input_kex_dh_gex_request(int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = ctxt; struct kex *kex = ssh->kex; + struct sshbn *dh_g = NULL, *dh_p = NULL; int r; u_int min = 0, max = 0, nbits = 0; 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) + (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; @@ -98,7 +91,7 @@ input_kex_dh_gex_request(int type, u_int32_t seq, void *ctxt) nbits = MIN(DH_GRP_MAX, nbits); if (kex->max < kex->min || kex->nbits < kex->min || - kex->max < kex->nbits) { + kex->max < kex->nbits) { r = SSH_ERR_DH_GEX_OUT_OF_RANGE; goto out; } @@ -110,11 +103,16 @@ input_kex_dh_gex_request(int type, u_int32_t seq, void *ctxt) r = SSH_ERR_ALLOC_FAIL; 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"); 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(ssh, kex->dh->g)) != 0 || - (r = sshpkt_send(ssh)) != 0) + (r = sshpkt_put_bignum2_wrap(ssh, dh_p)) != 0 || + (r = sshpkt_put_bignum2_wrap(ssh, dh_g)) != 0 || + (r = sshpkt_send(ssh)) != 0) goto out; /* Compute our exchange value in parallel with the client */ @@ -124,7 +122,9 @@ input_kex_dh_gex_request(int type, u_int32_t seq, void *ctxt) debug("expecting SSH2_MSG_KEX_DH_GEX_INIT"); ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init); r = 0; - out: +out: + sshbn_free(dh_g); + sshbn_free(dh_p); return r; } @@ -133,35 +133,37 @@ 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; + 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; - 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]; - size_t sbloblen, slen; - size_t klen = 0, hashlen; - int kout, r; + size_t sbloblen, slen, hashlen; + int r; if (kex->load_host_public_key == NULL || - kex->load_host_private_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); + kex->hostkey_nid, ssh); server_host_private = kex->load_host_private_key(kex->hostkey_type, - kex->hostkey_nid, ssh); + 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) { + if ((dh_client_pub = sshbn_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) + if ((r = sshpkt_get_bignum2_wrap(ssh, dh_client_pub)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) goto out; #ifdef DEBUG_KEXDH @@ -177,44 +179,40 @@ input_kex_dh_gex_init(int type, u_int32_t seq, void *ctxt) BN_print_fp(stderr, kex->dh->pub_key); fprintf(stderr, "\n"); #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"); - r = SSH_ERR_MESSAGE_INCOMPLETE; goto out; } - - klen = DH_size(kex->dh); - if ((kbuf = malloc(klen)) == NULL || - (shared_secret = BN_new()) == NULL) { - r = SSH_ERR_ALLOC_FAIL; + if ((dh_server_pub = sshdh_pubkey(kex->dh)) == NULL || + (dh_p = sshdh_p(kex->dh)) == NULL || + (dh_g = sshdh_g(kex->dh)) == NULL) { + r = SSH_ERR_INTERNAL_ERROR; goto out; } - if ((kout = DH_compute_key(kbuf, dh_client_pub, kex->dh)) < 0 || - BN_bin2bn(kbuf, kout, shared_secret) == NULL) { - r = SSH_ERR_LIBCRYPTO_ERROR; + if ((r = sshdh_compute_key(kex->dh, dh_client_pub, + &shared_secret)) != 0) 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) + &sbloblen)) != 0) goto out; /* 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, - kex->dh->p, kex->dh->g, - dh_client_pub, - kex->dh->pub_key, - shared_secret, - hash, &hashlen)) != 0) + 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, + dh_p, dh_g, + dh_client_pub, + dh_server_pub, + shared_secret, + hash, &hashlen)) != 0) goto out; /* save session id := H */ @@ -230,34 +228,31 @@ input_kex_dh_gex_init(int type, u_int32_t seq, void *ctxt) /* sign H */ if ((r = kex->sign(server_host_private, server_host_public, - &signature, &slen, hash, hashlen, ssh->compat)) < 0) + &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_KEX_DH_GEX_REPLY)) != 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_string(ssh, signature, slen)) != 0 || - (r = sshpkt_send(ssh)) != 0) + (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || + (r = sshpkt_put_bignum2_wrap(ssh, dh_server_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: - DH_free(kex->dh); +out: + 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; - 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(signature); return r; } -#endif /* WITH_OPENSSL */ diff --git a/monitor.c b/monitor.c index 9bae4e1..95d96f7 100644 --- a/monitor.c +++ b/monitor.c @@ -664,8 +664,11 @@ monitor_reset_key_state(void) int mm_answer_moduli(int sock, Buffer *m) { - DH *dh; + struct sshdh *dh; int min, want, max; + struct sshbn * dh_p = NULL; + struct sshbn * dh_g = NULL; + int ret = 0; min = buffer_get_int(m); want = buffer_get_int(m); @@ -681,18 +684,25 @@ mm_answer_moduli(int sock, Buffer *m) buffer_clear(m); dh = choose_dh(min, want, max); + + if (dh == NULL) { buffer_put_char(m, 0); return (0); } else { - /* Send first bignum */ - buffer_put_char(m, 1); - buffer_put_bignum2(m, dh->p); - buffer_put_bignum2(m, dh->g); + if ((dh_p = sshdh_p(dh)) != NULL && + (dh_g = sshdh_g(dh)) != NULL) { - DH_free(dh); + /* Send first bignum */ + buffer_put_char(m, 1); + sshbuf_put_bignum2_wrap(m, dh_p); + sshbuf_put_bignum2_wrap(m, dh_g); + mm_request_send(sock, MONITOR_ANS_MODULI, m); + } + sshdh_free(dh); + sshbn_free(dh_p); + sshbn_free(dh_g); } - mm_request_send(sock, MONITOR_ANS_MODULI, m); return (0); } #endif diff --git a/monitor_wrap.c b/monitor_wrap.c index 851ed33..2a56fc5 100644 --- a/monitor_wrap.c +++ b/monitor_wrap.c @@ -94,6 +94,7 @@ #include "roaming.h" #include "ssherr.h" +#include "crypto-wrap.h" /* Imports */ extern int compat20; @@ -116,17 +117,17 @@ mm_log_handler(LogLevel level, const char *msg, void *ctx) buffer_init(&log_msg); /* - * Placeholder for packet length. Will be filled in with the actual - * packet length once the packet has been constucted. This saves - * fragile math. - */ + * Placeholder for packet length. Will be filled in with the actual + * packet length once the packet has been constucted. This saves + * fragile math. + */ buffer_put_int(&log_msg, 0); buffer_put_int(&log_msg, level); buffer_put_cstring(&log_msg, msg); put_u32(buffer_ptr(&log_msg), buffer_len(&log_msg) - 4); if (atomicio(vwrite, mon->m_log_sendfd, buffer_ptr(&log_msg), - buffer_len(&log_msg)) != buffer_len(&log_msg)) + buffer_len(&log_msg)) != buffer_len(&log_msg)) fatal("%s: write: %s", __func__, strerror(errno)); buffer_free(&log_msg); } @@ -135,9 +136,9 @@ int mm_is_monitor(void) { /* - * m_pid is only set in the privileged part, and - * points to the unprivileged child. - */ + * m_pid is only set in the privileged part, and + * points to the unprivileged child. + */ return (pmonitor && pmonitor->m_pid > 0); } @@ -150,7 +151,7 @@ mm_request_send(int sock, enum monitor_reqtype type, Buffer *m) debug3("%s entering: type %d", __func__, type); put_u32(buf, mlen + 1); - buf[4] = (u_char) type; /* 1st byte of payload is mesg-type */ + buf[4] = (u_char)type; /* 1st byte of payload is mesg-type */ if (atomicio(vwrite, sock, buf, sizeof(buf)) != sizeof(buf)) fatal("%s: write: %s", __func__, strerror(errno)); if (atomicio(vwrite, sock, buffer_ptr(m), mlen) != mlen) @@ -190,15 +191,15 @@ mm_request_receive_expect(int sock, enum monitor_reqtype type, Buffer *m) rtype = buffer_get_char(m); if (rtype != type) fatal("%s: read: rtype %d != type %d", __func__, - rtype, type); + rtype, type); } #ifdef WITH_OPENSSL -DH * -mm_choose_dh(int min, int nbits, int max) +struct sshdh * + mm_choose_dh(int min, int nbits, int max) { - BIGNUM *p, *g; - int success = 0; + struct sshbn *p, *g; + int r, success = 0; Buffer m; buffer_init(&m); @@ -215,23 +216,22 @@ mm_choose_dh(int min, int nbits, int max) if (success == 0) 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__); - if ((g = BN_new()) == NULL) - fatal("%s: BN_new failed", __func__); - buffer_get_bignum2(&m, p); - buffer_get_bignum2(&m, g); + if ((r = sshbuf_get_bignum2_wrap(&m, p)) != 0 || + (r = sshbuf_get_bignum2_wrap(&m, g)) != 0) + fatal("%s: sshbuf_get_bignum2_wrap: %s", __func__, ssh_err(r)); debug3("%s: remaining %d", __func__, buffer_len(&m)); buffer_free(&m); - return (dh_new_group(g, p)); + return (sshdh_new_group(g, p)); } #endif int mm_key_sign(Key *key, u_char **sigp, u_int *lenp, - const u_char *data, u_int datalen) + const u_char *data, u_int datalen) { struct kex *kex = *pmonitor->m_pkex; Buffer m; @@ -246,14 +246,14 @@ mm_key_sign(Key *key, u_char **sigp, u_int *lenp, debug3("%s: waiting for MONITOR_ANS_SIGN", __func__); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_SIGN, &m); - *sigp = buffer_get_string(&m, lenp); + *sigp = buffer_get_string(&m, lenp); buffer_free(&m); return (0); } struct passwd * -mm_getpwnamallow(const char *username) + mm_getpwnamallow(const char *username) { Buffer m; struct passwd *pw; @@ -279,12 +279,8 @@ mm_getpwnamallow(const char *username) fatal("%s: struct passwd size mismatch", __func__); pw->pw_name = 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); -#endif -#ifdef HAVE_STRUCT_PASSWD_PW_CLASS - pw->pw_class = buffer_get_string(&m, NULL); -#endif +// pw->pw_class = buffer_get_string(&m, NULL); pw->pw_dir = buffer_get_string(&m, NULL); pw->pw_shell = buffer_get_string(&m, NULL); @@ -328,7 +324,7 @@ mm_auth2_read_banner(void) buffer_clear(&m); mm_request_receive_expect(pmonitor->m_recvfd, - MONITOR_ANS_AUTH2_READ_BANNER, &m); + MONITOR_ANS_AUTH2_READ_BANNER, &m); banner = buffer_get_string(&m, NULL); buffer_free(&m); @@ -379,7 +375,7 @@ mm_auth_password(Authctxt *authctxt, char *password) buffer_free(&m); debug3("%s: user %sauthenticated", - __func__, authenticated ? "" : "not "); + __func__, authenticated ? "" : "not "); return (authenticated); } @@ -387,19 +383,19 @@ int mm_user_key_allowed(struct passwd *pw, Key *key, int pubkey_auth_attempt) { return (mm_key_allowed(MM_USERKEY, NULL, NULL, key, - pubkey_auth_attempt)); + pubkey_auth_attempt)); } int mm_hostbased_key_allowed(struct passwd *pw, char *user, char *host, - Key *key) + Key *key) { return (mm_key_allowed(MM_HOSTKEY, user, host, key, 0)); } int mm_auth_rhosts_rsa_key_allowed(struct passwd *pw, char *user, - char *host, Key *key) + char *host, Key *key) { int ret; @@ -411,7 +407,7 @@ mm_auth_rhosts_rsa_key_allowed(struct passwd *pw, char *user, int mm_key_allowed(enum mm_keytype type, char *user, char *host, Key *key, - int pubkey_auth_attempt) + int pubkey_auth_attempt) { Buffer m; u_char *blob; @@ -450,10 +446,10 @@ mm_key_allowed(enum mm_keytype type, char *user, char *host, Key *key, } /* - * This key verify needs to send the key type along, because the - * privileged parent makes the decision if the key is allowed - * for authentication. - */ +* This key verify needs to send the key type along, because the +* privileged parent makes the decision if the key is allowed +* for authentication. +*/ int mm_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen) @@ -498,7 +494,7 @@ mm_send_keystate(struct monitor *monitor) fatal("%s: sshbuf_new failed", __func__); if ((r = ssh_packet_get_state(ssh, m)) != 0) fatal("%s: get_state failed: %s", - __func__, ssh_err(r)); + __func__, ssh_err(r)); mm_request_send(monitor->m_recvfd, MONITOR_REQ_KEYEXPORT, m); debug3("%s: Finished sending state", __func__); sshbuf_free(m); @@ -513,7 +509,7 @@ mm_pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen) /* Kludge: ensure there are fds free to receive the pty/tty */ if ((tmp1 = dup(pmonitor->m_recvfd)) == -1 || - (tmp2 = dup(pmonitor->m_recvfd)) == -1) { + (tmp2 = dup(pmonitor->m_recvfd)) == -1) { error("%s: cannot allocate fds for pty", __func__); if (tmp1 > 0) close(tmp1); @@ -547,7 +543,7 @@ mm_pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen) free(msg); if ((*ptyfd = mm_receive_fd(pmonitor->m_recvfd)) == -1 || - (*ttyfd = mm_receive_fd(pmonitor->m_recvfd)) == -1) + (*ttyfd = mm_receive_fd(pmonitor->m_recvfd)) == -1) fatal("%s: receive fds failed", __func__); /* Success */ @@ -569,143 +565,12 @@ mm_session_pty_cleanup2(Session *s) /* closed dup'ed master */ if (s->ptymaster != -1 && close(s->ptymaster) < 0) error("close(s->ptymaster/%d): %s", - s->ptymaster, strerror(errno)); + s->ptymaster, strerror(errno)); /* unlink pty from session */ 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 */ void @@ -742,7 +607,7 @@ mm_ssh1_session_key(BIGNUM *num) static void mm_chall_setup(char **name, char **infotxt, u_int *numprompts, - char ***prompts, u_int **echo_on) + char ***prompts, u_int **echo_on) { *name = xstrdup(""); *infotxt = xstrdup(""); @@ -754,7 +619,7 @@ mm_chall_setup(char **name, char **infotxt, u_int *numprompts, int mm_bsdauth_query(void *ctx, char **name, char **infotxt, - u_int *numprompts, char ***prompts, u_int **echo_on) + u_int *numprompts, char ***prompts, u_int **echo_on) { Buffer m; u_int success; @@ -766,7 +631,7 @@ mm_bsdauth_query(void *ctx, char **name, char **infotxt, mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_BSDAUTHQUERY, &m); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_BSDAUTHQUERY, - &m); + &m); success = buffer_get_int(&m); if (success == 0) { debug3("%s: no challenge", __func__); @@ -775,7 +640,7 @@ mm_bsdauth_query(void *ctx, char **name, char **infotxt, } /* Get the challenge, and format the response */ - challenge = buffer_get_string(&m, NULL); + challenge = buffer_get_string(&m, NULL); buffer_free(&m); mm_chall_setup(name, infotxt, numprompts, prompts, echo_on); @@ -801,7 +666,7 @@ mm_bsdauth_respond(void *ctx, u_int numresponses, char **responses) mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_BSDAUTHRESPOND, &m); mm_request_receive_expect(pmonitor->m_recvfd, - MONITOR_ANS_BSDAUTHRESPOND, &m); + MONITOR_ANS_BSDAUTHRESPOND, &m); authok = buffer_get_int(&m); buffer_free(&m); @@ -809,66 +674,6 @@ mm_bsdauth_respond(void *ctx, u_int numresponses, char **responses) 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 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 -#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 OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID goid) @@ -1038,7 +813,7 @@ mm_ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID goid) OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *in, - gss_buffer_desc *out, OM_uint32 *flags) + gss_buffer_desc *out, OM_uint32 *flags) { Buffer m; OM_uint32 major; @@ -1073,7 +848,7 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSCHECKMIC, &m); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSCHECKMIC, - &m); + &m); major = buffer_get_int(&m); buffer_free(&m); @@ -1090,12 +865,12 @@ mm_ssh_gssapi_userok(char *user) mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, &m); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUSEROK, - &m); + &m); authenticated = buffer_get_int(&m); buffer_free(&m); - debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); + debug3("%s: user %sauthenticated", __func__, authenticated ? "" : "not "); return (authenticated); } #endif /* GSSAPI */ diff --git a/monitor_wrap.h b/monitor_wrap.h index de4a08f..95741f7 100644 --- a/monitor_wrap.h +++ b/monitor_wrap.h @@ -39,7 +39,7 @@ struct Authctxt; void mm_log_handler(LogLevel, const char *, 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); void mm_inform_authserv(char *, char *); struct passwd *mm_getpwnamallow(const char *); diff --git a/openssl-bn.c b/openssl-bn.c new file mode 100644 index 0000000..b80ef1e --- /dev/null +++ b/openssl-bn.c @@ -0,0 +1,214 @@ +/* +* Copyright (c) 2015 Damien Miller +* +* 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 + + +#include +#include +#include +#include +#include + + +#include + + +#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); +} + diff --git a/openssl-dh.c b/openssl-dh.c new file mode 100644 index 0000000..319f92d --- /dev/null +++ b/openssl-dh.c @@ -0,0 +1,199 @@ +/* +* Copyright (c) 2015 Damien Miller +* +* 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 + + +#include +#include +#include +#include +#include + +#include +#include + +#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; +} \ No newline at end of file diff --git a/sshconnect2.c b/sshconnect2.c index d7ede03..22f3ab5 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -76,11 +76,7 @@ #include "ssh-gss.h" #endif -#ifdef USE_MSCNG -/* CNG KEX imports */ -int cng_kexgex_client(struct ssh *ssh); -int cng_kexdh_client(struct ssh *ssh); -#endif + /* import */ 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) fatal("kex_setup: %s", ssh_err(r)); 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_GRP14_SHA1] = kexdh_client; kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; -#endif + # ifdef OPENSSL_HAS_ECC kex->kex[KEX_ECDH_SHA2] = kexecdh_client; # endif -#endif + kex->kex[KEX_C25519_SHA256] = kexc25519_client; kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; diff --git a/sshd.c b/sshd.c index 1075a2b..250b973 100644 --- a/sshd.c +++ b/sshd.c @@ -145,11 +145,7 @@ #define O_NOCTTY 0 #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 */ #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) @@ -3384,22 +3380,17 @@ do_ssh2_kex(void) if ((r = kex_setup(active_state, myproposal)) != 0) fatal("kex_setup: %s", ssh_err(r)); 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_GRP14_SHA1] = kexdh_server; kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; -#endif + # ifdef OPENSSL_HAS_ECC kex->kex[KEX_ECDH_SHA2] = kexecdh_server; # endif -#endif + kex->kex[KEX_C25519_SHA256] = kexc25519_server; kex->server = 1; kex->client_version_string=client_version_string;