diff --git a/contrib/win32/openssh/win32compat.vcxproj b/contrib/win32/openssh/win32compat.vcxproj index 77bf815..11ac11a 100644 --- a/contrib/win32/openssh/win32compat.vcxproj +++ b/contrib/win32/openssh/win32compat.vcxproj @@ -159,6 +159,11 @@ + + + + + @@ -175,6 +180,7 @@ + diff --git a/contrib/win32/openssh/win32compat.vcxproj.filters b/contrib/win32/openssh/win32compat.vcxproj.filters index 4c3321a..6b28eaa 100644 --- a/contrib/win32/openssh/win32compat.vcxproj.filters +++ b/contrib/win32/openssh/win32compat.vcxproj.filters @@ -81,6 +81,21 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -125,5 +140,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/contrib/win32/win32compat/cng_kex.c b/contrib/win32/win32compat/cng_kex.c new file mode 100644 index 0000000..6661455 --- /dev/null +++ b/contrib/win32/win32compat/cng_kex.c @@ -0,0 +1,471 @@ + +#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 new file mode 100644 index 0000000..1b97239 --- /dev/null +++ b/contrib/win32/win32compat/cng_kex.h @@ -0,0 +1,31 @@ +#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 new file mode 100644 index 0000000..b3ef54e --- /dev/null +++ b/contrib/win32/win32compat/cng_kexdhc.c @@ -0,0 +1,195 @@ +/* +* 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 new file mode 100644 index 0000000..7a1a035 --- /dev/null +++ b/contrib/win32/win32compat/cng_kexdhs.c @@ -0,0 +1,198 @@ + +#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 new file mode 100644 index 0000000..a99ac62 --- /dev/null +++ b/contrib/win32/win32compat/cng_kexgexc.c @@ -0,0 +1,283 @@ +/* $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 new file mode 100644 index 0000000..de44859 --- /dev/null +++ b/contrib/win32/win32compat/cng_kexgexs.c @@ -0,0 +1,283 @@ + +#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 */