crypt32: Partially implement CertGetIssuerCertificateFromStore.
diff --git a/dlls/crypt32/crypt32.spec b/dlls/crypt32/crypt32.spec
index 3fa460c..836c6b9 100644
--- a/dlls/crypt32/crypt32.spec
+++ b/dlls/crypt32/crypt32.spec
@@ -52,7 +52,7 @@
 @ stdcall CertGetCertificateContextProperty(ptr long ptr ptr)
 @ stdcall CertGetEnhancedKeyUsage(ptr long ptr ptr)
 @ stub CertGetIntendedKeyUsage
-@ stub CertGetIssuerCertificateFromStore
+@ stdcall CertGetIssuerCertificateFromStore(long ptr ptr ptr)
 @ stdcall CertGetNameStringA(ptr long long ptr ptr long)
 @ stdcall CertGetNameStringW(ptr long long ptr ptr long)
 @ stub CertGetPublicKeyLength
diff --git a/dlls/crypt32/store.c b/dlls/crypt32/store.c
index 712c93d..1a7b72b 100644
--- a/dlls/crypt32/store.c
+++ b/dlls/crypt32/store.c
@@ -2646,6 +2646,13 @@
     return ret;
 }
 
+static BOOL compare_cert_by_issuer(PCCERT_CONTEXT pCertContext,
+ DWORD dwType, DWORD dwFlags, const void *pvPara)
+{
+    return compare_cert_by_subject_cert(pCertContext, dwType, dwFlags,
+     ((PCCERT_CONTEXT)pvPara)->pCertInfo);
+}
+
 PCCERT_CONTEXT WINAPI CertFindCertificateInStore(HCERTSTORE hCertStore,
 		DWORD dwCertEncodingType, DWORD dwFlags, DWORD dwType,
 		const void *pvPara, PCCERT_CONTEXT pPrevCertContext)
@@ -2673,6 +2680,9 @@
     case CERT_COMPARE_SUBJECT_CERT:
         compare = compare_cert_by_subject_cert;
         break;
+    case CERT_COMPARE_ISSUER_OF:
+        compare = compare_cert_by_issuer;
+        break;
     default:
         FIXME("find type %08lx unimplemented\n", dwType);
         compare = NULL;
@@ -2713,6 +2723,50 @@
      CERT_FIND_SUBJECT_CERT, pCertId, NULL);
 }
 
+PCCERT_CONTEXT WINAPI CertGetIssuerCertificateFromStore(HCERTSTORE hCertStore,
+ PCCERT_CONTEXT pSubjectContext, PCCERT_CONTEXT pPrevIssuerContext,
+ DWORD *pdwFlags)
+{
+    static const DWORD supportedFlags = CERT_STORE_REVOCATION_FLAG |
+     CERT_STORE_SIGNATURE_FLAG | CERT_STORE_TIME_VALIDITY_FLAG;
+    PCCERT_CONTEXT ret;
+
+    TRACE("(%p, %p, %p, %08lx)\n", hCertStore, pSubjectContext,
+     pPrevIssuerContext, *pdwFlags);
+
+    if (*pdwFlags & ~supportedFlags)
+    {
+        SetLastError(E_INVALIDARG);
+        return NULL;
+    }
+    ret = CertFindCertificateInStore(hCertStore,
+     pSubjectContext->dwCertEncodingType, 0, CERT_FIND_ISSUER_OF,
+     pSubjectContext, pPrevIssuerContext);
+    if (ret)
+    {
+        if (*pdwFlags & CERT_STORE_REVOCATION_FLAG)
+        {
+            FIXME("revocation check requires CRL support\n");
+            *pdwFlags |= CERT_STORE_NO_CRL_FLAG;
+        }
+        if (*pdwFlags & CERT_STORE_TIME_VALIDITY_FLAG)
+        {
+            if (0 == CertVerifyTimeValidity(NULL, pSubjectContext->pCertInfo))
+                *pdwFlags &= ~CERT_STORE_TIME_VALIDITY_FLAG;
+        }
+        if (*pdwFlags & CERT_STORE_SIGNATURE_FLAG)
+        {
+            if (CryptVerifyCertificateSignatureEx(0,
+             pSubjectContext->dwCertEncodingType,
+             CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, (void *)pSubjectContext,
+             CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *)ret, 0, NULL))
+                *pdwFlags &= ~CERT_STORE_SIGNATURE_FLAG;
+        }
+    }
+
+    return ret;
+}
+
 BOOL WINAPI CertAddStoreToCollection(HCERTSTORE hCollectionStore,
  HCERTSTORE hSiblingStore, DWORD dwUpdateFlags, DWORD dwPriority)
 {
diff --git a/dlls/crypt32/tests/store.c b/dlls/crypt32/tests/store.c
index b5dd1d6..f9ecc6a 100644
--- a/dlls/crypt32/tests/store.c
+++ b/dlls/crypt32/tests/store.c
@@ -427,6 +427,129 @@
     }
 }
 
+/* This expires in 1970 or so */
+static const BYTE expiredCert[] = { 0x30, 0x82, 0x01, 0x33, 0x30, 0x81, 0xe2,
+ 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0xc4, 0xd7, 0x7f, 0x0e, 0x6f, 0xa6,
+ 0x8c, 0xaa, 0x47, 0x47, 0x40, 0xe7, 0xb7, 0x0b, 0x4a, 0x7f, 0x30, 0x09, 0x06,
+ 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d, 0x05, 0x00, 0x30, 0x1f, 0x31, 0x1d, 0x30,
+ 0x1b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x14, 0x61, 0x72, 0x69, 0x63, 0x40,
+ 0x63, 0x6f, 0x64, 0x65, 0x77, 0x65, 0x61, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x36, 0x39, 0x30, 0x31, 0x30, 0x31, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x37, 0x30, 0x30, 0x31, 0x30,
+ 0x31, 0x30, 0x36, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x1f, 0x31, 0x1d, 0x30,
+ 0x1b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x14, 0x61, 0x72, 0x69, 0x63, 0x40,
+ 0x63, 0x6f, 0x64, 0x65, 0x77, 0x65, 0x61, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41,
+ 0x00, 0xa1, 0xaf, 0x4a, 0xea, 0xa7, 0x83, 0x57, 0xc0, 0x37, 0x33, 0x7e, 0x29,
+ 0x5e, 0x0d, 0xfc, 0x44, 0x74, 0x3a, 0x1d, 0xc3, 0x1b, 0x1d, 0x96, 0xed, 0x4e,
+ 0xf4, 0x1b, 0x98, 0xec, 0x69, 0x1b, 0x04, 0xea, 0x25, 0xcf, 0xb3, 0x2a, 0xf5,
+ 0xd9, 0x22, 0xd9, 0x8d, 0x08, 0x39, 0x81, 0xc6, 0xe0, 0x4f, 0x12, 0x37, 0x2a,
+ 0x3f, 0x80, 0xa6, 0x6c, 0x67, 0x43, 0x3a, 0xdd, 0x95, 0x0c, 0xbb, 0x2f, 0x6b,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02,
+ 0x1d, 0x05, 0x00, 0x03, 0x41, 0x00, 0x8f, 0xa2, 0x5b, 0xd6, 0xdf, 0x34, 0xd0,
+ 0xa2, 0xa7, 0x47, 0xf1, 0x13, 0x79, 0xd3, 0xf3, 0x39, 0xbd, 0x4e, 0x2b, 0xa3,
+ 0xf4, 0x63, 0x37, 0xac, 0x5a, 0x0c, 0x5e, 0x4d, 0x0d, 0x54, 0x87, 0x4f, 0x31,
+ 0xfb, 0xa0, 0xce, 0x8f, 0x9a, 0x2f, 0x4d, 0x48, 0xc6, 0x84, 0x8d, 0xf5, 0x70,
+ 0x74, 0x17, 0xa5, 0xf3, 0x66, 0x47, 0x06, 0xd6, 0x64, 0x45, 0xbc, 0x52, 0xef,
+ 0x49, 0xe5, 0xf9, 0x65, 0xf3 };
+
+/* This expires in 2036 or so */
+static const BYTE childOfExpired[] = { 0x30, 0x81, 0xcc, 0x30, 0x78, 0xa0,
+ 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x1f, 0x31, 0x1d,
+ 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x14, 0x61, 0x72, 0x69, 0x63,
+ 0x40, 0x63, 0x6f, 0x64, 0x65, 0x77, 0x65, 0x61, 0x76, 0x65, 0x72, 0x73, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x30, 0x35, 0x30, 0x35,
+ 0x31, 0x37, 0x31, 0x32, 0x34, 0x39, 0x5a, 0x17, 0x0d, 0x33, 0x36, 0x30, 0x35,
+ 0x30, 0x35, 0x31, 0x37, 0x31, 0x32, 0x34, 0x39, 0x5a, 0x30, 0x15, 0x31, 0x13,
+ 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0a, 0x4a, 0x75, 0x61, 0x6e,
+ 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x00, 0x30, 0x07, 0x30, 0x02, 0x06, 0x00, 0x03,
+ 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x41, 0x00, 0x20, 0x3b, 0xdb, 0x4d, 0x67, 0x50,
+ 0xec, 0x73, 0x9d, 0xf9, 0x85, 0x5d, 0x18, 0xe9, 0xb4, 0x98, 0xe3, 0x31, 0xb7,
+ 0x03, 0x0b, 0xc0, 0x39, 0x93, 0x56, 0x81, 0x0a, 0xfc, 0x78, 0xa8, 0x29, 0x42,
+ 0x5f, 0x69, 0xfb, 0xbc, 0x5b, 0xf2, 0xa6, 0x2a, 0xbe, 0x91, 0x2c, 0xfc, 0x89,
+ 0x69, 0x15, 0x18, 0x58, 0xe5, 0x02, 0x75, 0xf7, 0x2a, 0xb6, 0xa9, 0xfb, 0x47,
+ 0x6a, 0x6e, 0x0a, 0x9b, 0xe9, 0xdc };
+
+static void testGetIssuerCert(void)
+{
+    BOOL ret;
+    PCCERT_CONTEXT parent, child;
+    DWORD flags = 0xffffffff;
+    HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
+     CERT_STORE_CREATE_NEW_FLAG, NULL);
+
+    ok(store != NULL, "CertOpenStore failed: %08lx\n", GetLastError());
+
+    ret = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING,
+     expiredCert, sizeof(expiredCert), CERT_STORE_ADD_ALWAYS, NULL);
+    ok(ret, "CertAddEncodedCertificateToStore failed: %08lx\n",
+     GetLastError());
+
+    ret = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING,
+     childOfExpired, sizeof(childOfExpired), CERT_STORE_ADD_ALWAYS, &child);
+    ok(ret, "CertAddEncodedCertificateToStore failed: %08lx\n",
+     GetLastError());
+
+    /* These crash:
+    parent = CertGetIssuerCertificateFromStore(NULL, NULL, NULL, NULL);
+    parent = CertGetIssuerCertificateFromStore(store, NULL, NULL, NULL);
+     */
+    parent = CertGetIssuerCertificateFromStore(NULL, NULL, NULL, &flags);
+    ok(!parent && GetLastError() == E_INVALIDARG,
+     "Expected E_INVALIDARG, got %08lx\n", GetLastError());
+    parent = CertGetIssuerCertificateFromStore(store, NULL, NULL, &flags);
+    ok(!parent && GetLastError() == E_INVALIDARG,
+     "Expected E_INVALIDARG, got %08lx\n", GetLastError());
+    parent = CertGetIssuerCertificateFromStore(store, child, NULL, &flags);
+    ok(!parent && GetLastError() == E_INVALIDARG,
+     "Expected E_INVALIDARG, got %08lx\n", GetLastError());
+    /* Confusing: the caller cannot set either of the
+     * CERT_STORE_NO_*_FLAGs, as these are not checks,
+     * they're results:
+     */
+    flags = CERT_STORE_NO_CRL_FLAG | CERT_STORE_NO_ISSUER_FLAG;
+    parent = CertGetIssuerCertificateFromStore(store, child, NULL, &flags);
+    ok(!parent && GetLastError() == E_INVALIDARG,
+     "Expected E_INVALIDARG, got %08lx\n", GetLastError());
+    /* Perform no checks */
+    flags = 0;
+    parent = CertGetIssuerCertificateFromStore(store, child, NULL, &flags);
+    ok(parent != NULL, "CertGetIssuerCertificateFromStore failed: %08lx\n",
+     GetLastError());
+    if (parent)
+        CertFreeCertificateContext(parent);
+    /* Check revocation and signature only */
+    flags = CERT_STORE_REVOCATION_FLAG | CERT_STORE_SIGNATURE_FLAG;
+    parent = CertGetIssuerCertificateFromStore(store, child, NULL, &flags);
+    ok(parent != NULL, "CertGetIssuerCertificateFromStore failed: %08lx\n",
+     GetLastError());
+    /* Confusing: CERT_STORE_REVOCATION_FLAG succeeds when there is no CRL by
+     * setting CERT_STORE_NO_CRL_FLAG.
+     */
+    ok(flags == (CERT_STORE_REVOCATION_FLAG | CERT_STORE_NO_CRL_FLAG),
+     "Expected CERT_STORE_REVOCATION_FLAG | CERT_STORE_NO_CRL_FLAG, got %08lx\n",
+     flags);
+    if (parent)
+        CertFreeCertificateContext(parent);
+    /* Now check just the time */
+    flags = CERT_STORE_TIME_VALIDITY_FLAG;
+    parent = CertGetIssuerCertificateFromStore(store, child, NULL, &flags);
+    ok(parent != NULL, "CertGetIssuerCertificateFromStore failed: %08lx\n",
+     GetLastError());
+    /* Oops: the child is not expired, so the time validity check actually
+     * succeeds, even though the signing cert is expired.
+     */
+    ok(!flags, "Expected check to succeed, got %08lx\n", flags);
+    if (parent)
+        CertFreeCertificateContext(parent);
+
+    CertFreeCertificateContext(child);
+    CertCloseStore(store, 0);
+}
+
 static void testMemStore(void)
 {
     HCERTSTORE store1, store2;
@@ -1702,6 +1825,7 @@
     testDupCert();
     testFindCert();
     testGetSubjectCert();
+    testGetIssuerCert();
 
     /* various combinations of CertOpenStore */
     testMemStore();
diff --git a/include/wincrypt.h b/include/wincrypt.h
index 32e3c8d..7017f5a 100644
--- a/include/wincrypt.h
+++ b/include/wincrypt.h
@@ -2389,6 +2389,15 @@
 #define CRYPT_UNICODE_NAME_DECODE_DISABLE_IE4_UTF8_FLAG \
  CERT_RDN_DISABLE_IE4_UTF8_FLAG
 
+#define CERT_STORE_SIGNATURE_FLAG     0x00000001
+#define CERT_STORE_TIME_VALIDITY_FLAG 0x00000002
+#define CERT_STORE_REVOCATION_FLAG    0x00000004
+#define CERT_STORE_NO_CRL_FLAG        0x00010000
+#define CERT_STORE_NO_ISSUER_FLAG     0x00020000
+
+#define CERT_STORE_BASE_CRL_FLAG  0x00000100
+#define CERT_STORE_DELTA_CRL_FLAG 0x00000200
+
 /* subject types for CryptVerifyCertificateSignatureEx */
 #define CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB 1
 #define CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT 2
@@ -2728,6 +2737,10 @@
  DWORD dwCertEncodingType, DWORD dwFindFlags, DWORD dwFindType,
  const void *pvFindPara, PCCTL_CONTEXT pPrevCtlContext);
 
+PCCERT_CONTEXT WINAPI CertGetIssuerCertificateFromStore(HCERTSTORE hCertStore,
+ PCCERT_CONTEXT pSubjectContext, PCCERT_CONTEXT pPrevIssuerContext,
+ DWORD *pdwFlags);
+
 PCCERT_CONTEXT WINAPI CertGetSubjectCertificateFromStore(HCERTSTORE hCertStore,
  DWORD dwCertEncodingType, PCERT_INFO pCertId);