wincng: Added explicit clear memory feature to WinCNG backend

This re-introduces the original feature proposed during
the development of the WinCNG crypto backend. It still needs
to be added to libssh2 itself and probably other backends.

Memory is cleared using the function SecureZeroMemory which is
available on Windows systems, just like the WinCNG backend.
diff --git a/configure.ac b/configure.ac
index 563fb04..24f1067 100644
--- a/configure.ac
+++ b/configure.ac
@@ -97,6 +97,7 @@
   use_libz=$withval,use_libz=auto)
 
 found_crypto=none
+support_clear_memory=no
 
 # Look for OpenSSL
 if test "$found_crypto" = "none" && test "$use_openssl" != "no"; then
@@ -150,6 +151,7 @@
     LIBS="$LIBS -lcrypt32"
   fi
   found_crypto="Windows Cryptography API: Next Generation"
+  support_clear_memory=yes
 fi
 AM_CONDITIONAL(WINCNG, test "$ac_cv_libbcrypt" = "yes")
 
@@ -197,6 +199,26 @@
   AC_DEFINE(LIBSSH2_DH_GEX_NEW, 1, [Enable newer diffie-hellman-group-exchange-sha1 syntax])
 fi
 
+AC_ARG_ENABLE(clear-memory,
+  AC_HELP_STRING([--disable-clear-memory],[Disable clearing of memory before being freed]),
+  [CLEAR_MEMORY=$enableval])
+if test "$CLEAR_MEMORY" != "no"; then
+  if test "$support_clear_memory" = "yes"; then
+    AC_DEFINE(LIBSSH2_CLEAR_MEMORY, 1, [Enable clearing of memory before being freed])
+    enable_clear_memory=yes
+  else
+    AC_MSG_ERROR([secure clearing/zeroing of memory is not supported by the selected crypto backend])
+    enable_clear_memory=unsupported
+  fi
+else
+  if test "$support_clear_memory" = "yes"; then
+    enable_clear_memory=no
+  else
+    AC_MSG_WARN([secure clearing/zeroing of memory is not supported by the selected crypto backend])
+    enable_clear_memory=unsupported
+  fi
+fi
+
 dnl ************************************************************
 dnl option to switch on compiler debug options
 dnl
@@ -362,6 +384,7 @@
   Compiler flags:   ${CFLAGS}
   Library types:    Shared=${enable_shared}, Static=${enable_static}
   Crypto library:   ${found_crypto}
+  Clear memory:     $enable_clear_memory
   Debug build:      $enable_debug
   Build examples:   $build_examples
   Path to sshd:     $ac_cv_path_SSHD (only for self-tests)
diff --git a/src/wincng.c b/src/wincng.c
index 0e165ed..ed6c6e9 100644
--- a/src/wincng.c
+++ b/src/wincng.c
@@ -285,6 +285,20 @@
     return BCRYPT_SUCCESS(ret) ? 0 : -1;
 }
 
+static void
+_libssh2_wincng_safe_free(void *buf, int len)
+{
+    if (!buf)
+        return;
+
+#ifdef LIBSSH2_CLEAR_MEMORY
+    if (len > 0)
+        SecureZeroMemory(buf, len);
+#endif
+
+    free(buf);
+}
+
 
 /*******************************************************************/
 /*
@@ -327,7 +341,7 @@
                            pbHashObject, dwHashObject,
                            key, keylen, 0);
     if (!BCRYPT_SUCCESS(ret)) {
-        free(pbHashObject);
+        _libssh2_wincng_safe_free(pbHashObject, dwHashObject);
         return -1;
     }
 
@@ -360,11 +374,11 @@
     ret = BCryptFinishHash(ctx->hHash, hash, ctx->cbHash, 0);
 
     BCryptDestroyHash(ctx->hHash);
+    ctx->hHash = NULL;
 
-    if (ctx->pbHashObject)
-        free(ctx->pbHashObject);
-
-    memset(ctx, 0, sizeof(_libssh2_wincng_hash_ctx));
+    _libssh2_wincng_safe_free(ctx->pbHashObject, ctx->dwHashObject);
+    ctx->pbHashObject = NULL;
+    ctx->dwHashObject = 0;
 
     return ret;
 }
@@ -408,11 +422,11 @@
 _libssh2_wincng_hmac_cleanup(_libssh2_wincng_hash_ctx *ctx)
 {
     BCryptDestroyHash(ctx->hHash);
+    ctx->hHash = NULL;
 
-    if (ctx->pbHashObject)
-        free(ctx->pbHashObject);
-
-    memset(ctx, 0, sizeof(_libssh2_wincng_hash_ctx));
+    _libssh2_wincng_safe_free(ctx->pbHashObject, ctx->dwHashObject);
+    ctx->pbHashObject = NULL;
+    ctx->dwHashObject = 0;
 }
 
 
@@ -454,17 +468,17 @@
                                _libssh2_wincng.hAlgHashSHA1,
                                hash, hashlen);
 
-    free(data);
+    _libssh2_wincng_safe_free(data, datalen);
 
     if (ret) {
-        free(hash);
+        _libssh2_wincng_safe_free(hash, hashlen);
         return -1;
     }
 
     datalen = sig_len;
     data = malloc(datalen);
     if (!data) {
-        free(hash);
+        _libssh2_wincng_safe_free(hash, hashlen);
         return -1;
     }
 
@@ -479,8 +493,8 @@
     ret = BCryptVerifySignature(ctx->hKey, pPaddingInfo,
                                 hash, hashlen, data, datalen, flags);
 
-    free(hash);
-    free(data);
+    _libssh2_wincng_safe_free(hash, hashlen);
+    _libssh2_wincng_safe_free(data, datalen);
 
     return BCRYPT_SUCCESS(ret) ? 0 : -1;
 }
@@ -611,7 +625,7 @@
                               pbEncoded, cbEncoded, 0, NULL,
                               pbDecoded, &cbDecoded);
     if (!ret) {
-        free(pbDecoded);
+        _libssh2_wincng_safe_free(pbDecoded, cbDecoded);
         return -1;
     }
 
@@ -682,7 +696,7 @@
             *ppbDecoded = pbDecoded;
             *pcbDecoded = cbDecoded;
         }
-        free(pbInteger);
+        _libssh2_wincng_safe_free(pbInteger, cbInteger);
     }
 
     return ret;
@@ -727,10 +741,10 @@
                     *pcbCount = length;
                 } else {
                     for (length = 0; length < index; length++) {
-                        if (rpbDecoded[length]) {
-                            free(rpbDecoded[length]);
-                            rpbDecoded[length] = NULL;
-                        }
+                        _libssh2_wincng_safe_free(rpbDecoded[length],
+                                                  rcbDecoded[length]);
+                        rpbDecoded[length] = NULL;
+                        rcbDecoded[length] = 0;
                     }
                     free(rpbDecoded);
                     free(rcbDecoded);
@@ -743,7 +757,7 @@
             ret = -1;
         }
 
-        free(pbDecoded);
+        _libssh2_wincng_safe_free(pbDecoded, cbDecoded);
     }
 
     return ret;
@@ -889,7 +903,7 @@
     ret = BCryptImportKeyPair(_libssh2_wincng.hAlgRSA, NULL, lpszBlobType,
                               &hKey, key, keylen, 0);
     if (!BCRYPT_SUCCESS(ret)) {
-        free(key);
+        _libssh2_wincng_safe_free(key, keylen);
         return -1;
     }
 
@@ -897,7 +911,7 @@
     *rsa = malloc(sizeof(libssh2_rsa_ctx));
     if (!(*rsa)) {
         BCryptDestroyKey(hKey);
-        free(key);
+        _libssh2_wincng_safe_free(key, keylen);
         return -1;
     }
 
@@ -926,7 +940,7 @@
                                      PKCS_RSA_PRIVATE_KEY,
                                      &pbStructInfo, &cbStructInfo);
 
-    free(pbEncoded);
+    _libssh2_wincng_safe_free(pbEncoded, cbEncoded);
 
     if (ret) {
         return -1;
@@ -937,7 +951,7 @@
                               LEGACY_RSAPRIVATE_BLOB, &hKey,
                               pbStructInfo, cbStructInfo, 0);
     if (!BCRYPT_SUCCESS(ret)) {
-        free(pbStructInfo);
+        _libssh2_wincng_safe_free(pbStructInfo, cbStructInfo);
         return -1;
     }
 
@@ -945,7 +959,7 @@
     *rsa = malloc(sizeof(libssh2_rsa_ctx));
     if (!(*rsa)) {
         BCryptDestroyKey(hKey);
-        free(pbStructInfo);
+        _libssh2_wincng_safe_free(pbStructInfo, cbStructInfo);
         return -1;
     }
 
@@ -1079,7 +1093,7 @@
             ret = STATUS_NO_MEMORY;
     }
 
-    free(data);
+    _libssh2_wincng_safe_free(data, datalen);
 
     return BCRYPT_SUCCESS(ret) ? 0 : -1;
 }
@@ -1091,12 +1105,10 @@
         return;
 
     BCryptDestroyKey(rsa->hKey);
+    rsa->hKey = NULL;
 
-    if (rsa->pbKeyObject)
-        free(rsa->pbKeyObject);
-
-    memset(rsa, 0, sizeof(libssh2_rsa_ctx));
-    free(rsa);
+    _libssh2_wincng_safe_free(rsa->pbKeyObject, rsa->cbKeyObject);
+    _libssh2_wincng_safe_free(rsa, sizeof(libssh2_rsa_ctx));
 }
 
 
@@ -1190,7 +1202,7 @@
     ret = BCryptImportKeyPair(_libssh2_wincng.hAlgDSA, NULL, lpszBlobType,
                               &hKey, key, keylen, 0);
     if (!BCRYPT_SUCCESS(ret)) {
-        free(key);
+        _libssh2_wincng_safe_free(key, keylen);
         return -1;
     }
 
@@ -1198,7 +1210,7 @@
     *dsa = malloc(sizeof(libssh2_dsa_ctx));
     if (!(*dsa)) {
         BCryptDestroyKey(hKey);
-        free(key);
+        _libssh2_wincng_safe_free(key, keylen);
         return -1;
     }
 
@@ -1225,7 +1237,7 @@
     ret = _libssh2_wincng_asn_decode_bns(pbEncoded, cbEncoded,
                                          &rpbDecoded, &rcbDecoded, &length);
 
-    free(pbEncoded);
+    _libssh2_wincng_safe_free(pbEncoded, cbEncoded);
 
     if (ret) {
         return -1;
@@ -1244,10 +1256,9 @@
     }
 
     for (index = 0; index < length; index++) {
-        if (rpbDecoded[index]) {
-            free(rpbDecoded[index]);
-            rpbDecoded[index] = NULL;
-        }
+        _libssh2_wincng_safe_free(rpbDecoded[index], rcbDecoded[index]);
+        rpbDecoded[index] = NULL;
+        rcbDecoded[index] = 0;
     }
 
     free(rpbDecoded);
@@ -1361,14 +1372,14 @@
                     memcpy(sig_fixed, sig, siglen);
                 }
 
-                free(sig);
+                _libssh2_wincng_safe_free(sig, siglen);
             } else
                 ret = STATUS_NO_MEMORY;
         } else
             ret = STATUS_NO_MEMORY;
     }
 
-    free(data);
+    _libssh2_wincng_safe_free(data, datalen);
 
     return BCRYPT_SUCCESS(ret) ? 0 : -1;
 }
@@ -1380,12 +1391,10 @@
         return;
 
     BCryptDestroyKey(dsa->hKey);
+    dsa->hKey = NULL;
 
-    if (dsa->pbKeyObject)
-        free(dsa->pbKeyObject);
-
-    memset(dsa, 0, sizeof(libssh2_dsa_ctx));
-    free(dsa);
+    _libssh2_wincng_safe_free(dsa->pbKeyObject, dsa->cbKeyObject);
+    _libssh2_wincng_safe_free(dsa, sizeof(libssh2_dsa_ctx));
 }
 #endif
 
@@ -1430,7 +1439,7 @@
     ret = _libssh2_wincng_asn_decode_bns(pbEncoded, cbEncoded,
                                          &rpbDecoded, &rcbDecoded, &length);
 
-    free(pbEncoded);
+    _libssh2_wincng_safe_free(pbEncoded, cbEncoded);
 
     if (ret) {
         return -1;
@@ -1503,10 +1512,9 @@
 
 
     for (index = 0; index < length; index++) {
-        if (rpbDecoded[index]) {
-            free(rpbDecoded[index]);
-            rpbDecoded[index] = NULL;
-        }
+        _libssh2_wincng_safe_free(rpbDecoded[index], rcbDecoded[index]);
+        rpbDecoded[index] = NULL;
+        rcbDecoded[index] = 0;
     }
 
     free(rpbDecoded);
@@ -1667,10 +1675,10 @@
     ret = BCryptImportKey(*type.phAlg, NULL, BCRYPT_KEY_DATA_BLOB, &hKey,
                           pbKeyObject, dwKeyObject, key, keylen, 0);
 
-    free(key);
+    _libssh2_wincng_safe_free(key, keylen);
 
     if (!BCRYPT_SUCCESS(ret)) {
-        free(pbKeyObject);
+        _libssh2_wincng_safe_free(pbKeyObject, dwKeyObject);
         return -1;
     }
 
@@ -1678,7 +1686,7 @@
         pbIV = malloc(dwBlockLength);
         if (!pbIV) {
             BCryptDestroyKey(hKey);
-            free(pbKeyObject);
+            _libssh2_wincng_safe_free(pbKeyObject, dwKeyObject);
             return -1;
         }
         dwIV = dwBlockLength;
@@ -1737,7 +1745,7 @@
                 memcpy(block, pbOutput, cbOutput);
             }
 
-            free(pbOutput);
+            _libssh2_wincng_safe_free(pbOutput, cbOutput);
         } else
             ret = STATUS_NO_MEMORY;
     }
@@ -1749,13 +1757,11 @@
 _libssh2_wincng_cipher_dtor(_libssh2_cipher_ctx *ctx)
 {
     BCryptDestroyKey(ctx->hKey);
+    ctx->hKey = NULL;
 
-    if (ctx->pbKeyObject) {
-        free(ctx->pbKeyObject);
-        ctx->pbKeyObject = NULL;
-    }
-
-    memset(ctx, 0, sizeof(_libssh2_cipher_ctx));
+    _libssh2_wincng_safe_free(ctx->pbKeyObject, ctx->dwKeyObject);
+    ctx->pbKeyObject = NULL;
+    ctx->dwKeyObject = 0;
 }
 
 
@@ -1789,6 +1795,12 @@
     if (length == bn->length)
         return 0;
 
+#ifdef LIBSSH2_CLEAR_MEMORY
+    if (bn->bignum && bn->length > 0 && length < bn->length) {
+        SecureZeroMemory(bn->bignum + length, bn->length - length);
+    }
+#endif
+
     bignum = realloc(bn->bignum, length);
     if (!bignum)
         return -1;
@@ -1896,7 +1908,7 @@
                                         r->bignum, r->length, &offset,
                                         BCRYPT_PAD_NONE);
 
-                    free(bignum);
+                    _libssh2_wincng_safe_free(bignum, length);
 
                     if (BCRYPT_SUCCESS(ret)) {
                         _libssh2_wincng_bignum_resize(r, offset);
@@ -1910,7 +1922,7 @@
         BCryptDestroyKey(hKey);
     }
 
-    free(key);
+    _libssh2_wincng_safe_free(key, keylen);
 
     return BCRYPT_SUCCESS(ret) ? 0 : -1;
 }
@@ -1988,6 +2000,10 @@
     if (offset > 0) {
         memmove(bn->bignum, bn->bignum + offset, length);
 
+#ifdef LIBSSH2_CLEAR_MEMORY
+        SecureZeroMemory(bn->bignum + length, offset);
+#endif
+
         bignum = realloc(bn->bignum, length);
         if (bignum) {
             bn->bignum = bignum;
@@ -2009,11 +2025,11 @@
 {
     if (bn) {
         if (bn->bignum) {
-            free(bn->bignum);
+            _libssh2_wincng_safe_free(bn->bignum, bn->length);
             bn->bignum = NULL;
         }
         bn->length = 0;
-        free(bn);
+        _libssh2_wincng_safe_free(bn, sizeof(_libssh2_bn));
     }
 }