//+-------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996 - 1999 // // File: inf.cpp // // Contents: Cert Server INF file processing routines // //--------------------------------------------------------------------------- #include #pragma hdrstop #include #include "csdisp.h" #include "initcert.h" #include "clibres.h" #include "csber.h" #define __dwFILE__ __dwFILE_CERTLIB_INF_CPP__ #define wszBSCAPOLICYFILE L"\\" wszCAPOLICYFILE #define wszINFKEY_CONTINUE L"_continue_" #define wszINFKEY_VERSION L"Version" #define wszINFSECTION_EXTENSIONS L"Extensions" #define cwcINFLINEMAX 1024 #define wcBOM (WCHAR) 0xfffe #define wcBOMBIGENDIAN (WCHAR) 0xfeff static WCHAR *s_pwszSection = NULL; static WCHAR *s_pwszKey = NULL; static WCHAR *s_pwszValue = NULL; static HRESULT s_hr = S_OK; static WCHAR g_wcBOM = L'\0'; static BOOL g_fIgnoreReferencedSections = FALSE; VOID infClearString( IN OUT WCHAR **ppwsz) { if (NULL != ppwsz && NULL != *ppwsz) { LocalFree(*ppwsz); *ppwsz = NULL; } } BOOL infCopyString( OPTIONAL IN WCHAR const *pwszIn, OPTIONAL OUT WCHAR **ppwszOut) { HRESULT hr; if (NULL != pwszIn && NULL == *ppwszOut) { hr = myDupString(pwszIn, ppwszOut); _JumpIfError(hr, error, "myDupString"); } hr = S_OK; error: return(S_OK == hr); } #define INFSTRINGSELECT(pwsz) (NULL != (pwsz)? (pwsz) : L"") #if DBG_CERTSRV # define INFSETERROR(hr, pwszSection, pwszKey, pwszValue) \ { \ _PrintError3(hr, "infSetError", ERROR_LINE_NOT_FOUND, S_FALSE); \ infSetError(hr, pwszSection, pwszKey, pwszValue, __LINE__); \ } #else # define INFSETERROR infSetError #endif VOID infSetError( IN HRESULT hr, OPTIONAL IN WCHAR const *pwszSection, OPTIONAL IN WCHAR const *pwszKey, OPTIONAL IN WCHAR const *pwszValue DBGPARM(IN DWORD dwLine)) { DBGPRINT(( DBG_SS_CERTLIBI, "inf.cpp(%u): infSetError Begin: [%ws] %ws = %ws\n", dwLine, INFSTRINGSELECT(pwszSection), INFSTRINGSELECT(pwszKey), INFSTRINGSELECT(pwszValue))); s_hr = hr; infCopyString(pwszSection, &s_pwszSection); infCopyString(pwszKey, &s_pwszKey); infCopyString(pwszValue, &s_pwszValue); DBGPRINT(( DBG_SS_CERTLIBI, "inf.cpp(%u): infSetError End: [%ws] %ws = %ws\n", dwLine, INFSTRINGSELECT(s_pwszSection), INFSTRINGSELECT(s_pwszKey), INFSTRINGSELECT(s_pwszValue))); } WCHAR * myInfGetError() { DWORD cwc = 1; WCHAR *pwsz = NULL; if (NULL != s_pwszSection) { cwc += wcslen(s_pwszSection) + 2; } if (NULL != s_pwszKey) { cwc += wcslen(s_pwszKey) + 1 + 2; } if (NULL != s_pwszValue) { cwc += wcslen(s_pwszValue) + 1; } if (1 == cwc) { goto error; } pwsz = (WCHAR *) LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR)); if (NULL == pwsz) { goto error; } *pwsz = L'\0'; if (NULL != s_pwszSection) { wcscat(pwsz, wszLBRACKET); wcscat(pwsz, s_pwszSection); wcscat(pwsz, wszRBRACKET); } if (NULL != s_pwszKey && L'\0' != s_pwszKey[0]) { wcscat(pwsz, L" "); wcscat(pwsz, s_pwszKey); wcscat(pwsz, L" ="); } if (NULL != s_pwszValue && L'\0' != s_pwszValue[0]) { wcscat(pwsz, L" "); wcscat(pwsz, s_pwszValue); } error: return(pwsz); } VOID myInfClearError() { s_hr = S_OK; infClearString(&s_pwszSection); infClearString(&s_pwszKey); infClearString(&s_pwszValue); } typedef struct _SECTIONINFO { DWORD dwRefCount; WCHAR *pwszSection; struct _SECTIONINFO *pNext; } SECTIONINFO; SECTIONINFO *g_pSectionInfo = NULL; HRESULT infSaveSectionName( IN WCHAR const *pwszSection) { HRESULT hr; SECTIONINFO *psi = NULL; if (0 != LSTRCMPIS(pwszSection, wszINFKEY_VERSION)) { psi = (SECTIONINFO *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, sizeof(*psi)); if (NULL == psi) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } hr = myDupString(pwszSection, &psi->pwszSection); _JumpIfError(hr, error, "myDupString"); psi->pNext = g_pSectionInfo; g_pSectionInfo = psi; psi = NULL; } hr = S_OK; error: if (NULL != psi) { LocalFree(psi); } return(hr); } VOID infFreeSectionNames() { SECTIONINFO *psi; psi = g_pSectionInfo; g_pSectionInfo = NULL; while (NULL != psi) { SECTIONINFO *psiNext; if (NULL != psi->pwszSection) { LocalFree(psi->pwszSection); } psiNext = psi->pNext; LocalFree(psi); psi = psiNext; } } HRESULT myInfGetUnreferencedSectionNames( OUT WCHAR **ppwszzSectionNames) { HRESULT hr; DWORD cwc; SECTIONINFO *psi; *ppwszzSectionNames = NULL; cwc = 0; for (psi = g_pSectionInfo; NULL != psi; psi = psi->pNext) { if (0 == psi->dwRefCount) { cwc += wcslen(psi->pwszSection) + 3; } } if (0 != cwc) { WCHAR *pwszz; WCHAR *pwsz; pwszz = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwc + 1) * sizeof(WCHAR)); if (NULL == pwszz) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } pwsz = pwszz; for (psi = g_pSectionInfo; NULL != psi; psi = psi->pNext) { if (0 == psi->dwRefCount) { *pwsz++ = wcLBRACKET; wcscpy(pwsz, psi->pwszSection); pwsz += wcslen(pwsz); *pwsz++ = wcRBRACKET; *pwsz++ = L'\0'; } } *pwsz = L'\0'; CSASSERT(cwc == SAFE_SUBTRACT_POINTERS(pwsz, pwszz)); *ppwszzSectionNames = pwszz; } hr = S_OK; error: return(hr); } HRESULT infReferenceSectionName( IN WCHAR const *pwszSection) { HRESULT hr; SECTIONINFO *psi; if (!g_fIgnoreReferencedSections) { for (psi = g_pSectionInfo; NULL != psi; psi = psi->pNext) { if (0 == mylstrcmpiL(pwszSection, psi->pwszSection)) { psi->dwRefCount++; break; } } if (NULL == psi) { hr = SPAPI_E_LINE_NOT_FOUND; // don't ignore this error _JumpError(hr, error, "unexpected section"); } } hr = S_OK; error: return(hr); } HRESULT inffopen( IN WCHAR const *pwszfn, OUT FILE **ppf) { HRESULT hr; FILE *pf = NULL; *ppf = NULL; g_fIgnoreReferencedSections = FALSE; g_wcBOM = L'\0'; pf = _wfopen(pwszfn, L"r"); // Assume Ansi INF file if (NULL == pf) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); _JumpError(hr, error, "_wfopen"); } g_wcBOM = (WCHAR) fgetc(pf) << 8; g_wcBOM |= fgetc(pf); if (!feof(pf)) { if (wcBOM == g_wcBOM) // Oops, Unicode INF file { fclose(pf); pf = _wfopen(pwszfn, L"rb"); if (NULL == pf) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); _JumpError(hr, error, "fopen"); } } else if (wcBOMBIGENDIAN == g_wcBOM) { g_fIgnoreReferencedSections = TRUE; fclose(pf); pf = NULL; } else { if (fseek(pf, 0L, SEEK_SET)) { hr = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); _JumpError(hr, error, "fseek"); } } } *ppf = pf; hr = S_OK; error: return(hr); } WCHAR * inffgetws( OUT WCHAR *pwcLine, IN DWORD cwcLine, IN FILE *pfInf) { HRESULT hr; WCHAR *pwc = NULL; WCHAR *pwsz = NULL; char achLine[cwcINFLINEMAX]; char *pch; if (wcBOM == g_wcBOM) { pwc = fgetws(pwcLine, cwcLine, pfInf); } else { pch = fgets(achLine, ARRAYSIZE(achLine), pfInf); if (NULL == pch) { goto error; } if (!myConvertSzToWsz(&pwsz, achLine, -1)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "myConvertSzToWsz"); } wcsncpy(pwcLine, pwsz, cwcLine); pwcLine[cwcLine - 1] = L'\0'; pwc = pwcLine; } error: if (NULL != pwsz) { LocalFree(pwsz); } return(pwc); } #define ISBLANK(ch) (L' ' == (ch) || L'\t' == (ch)) HRESULT infCollectSectionNames( IN WCHAR const *pwszfnInf) { HRESULT hr; FILE *pfInf = NULL; WCHAR awcLine[cwcINFLINEMAX]; WCHAR *pwszEnd; hr = inffopen(pwszfnInf, &pfInf); _JumpIfError(hr, error, "inffopen"); if (!g_fIgnoreReferencedSections) { while (NULL != inffgetws(awcLine, ARRAYSIZE(awcLine), pfInf)) { WCHAR *pwsz; awcLine[wcscspn(awcLine, L";=\r\n")] = L'\0'; pwsz = wcschr(awcLine, wcLBRACKET); if (NULL == pwsz) { continue; } pwsz++; pwszEnd = wcschr(awcLine, wcRBRACKET); if (NULL == pwszEnd) { continue; } *pwszEnd = L'\0'; while (ISBLANK(*pwsz)) { pwsz++; } while (--pwszEnd >= pwsz && ISBLANK(*pwszEnd)) { *pwszEnd = L'\0'; } hr = infSaveSectionName(pwsz); _JumpIfError(hr, error, "infSaveSectionName"); } } hr = S_OK; error: if (NULL != pfInf) { fclose(pfInf); } return(hr); } HRESULT infGetCurrentKeyValueAndAlloc( IN OUT INFCONTEXT *pInfContext, IN DWORD Index, OUT WCHAR **ppwszValue) { HRESULT hr; WCHAR *pwszValue = NULL; DWORD cwc; if (!SetupGetStringField(pInfContext, Index, NULL, 0, &cwc)) { hr = myHLastError(); _JumpError2(hr, error, "SetupGetStringField", E_INVALIDARG); } pwszValue = (WCHAR *) LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR)); if (NULL == pwszValue) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } if (!SetupGetStringField(pInfContext, Index, pwszValue, cwc, NULL)) { hr = myHLastError(); _JumpError(hr, error, "SetupGetStringField"); } *ppwszValue = pwszValue; pwszValue = NULL; hr = S_OK; error: if (NULL != pwszValue) { LocalFree(pwszValue); } return(hr); } HRESULT infGetCurrentKeyValue( IN OUT INFCONTEXT *pInfContext, OPTIONAL IN WCHAR const *pwszSection, // for error logging only OPTIONAL IN WCHAR const *pwszKey, // for error logging only IN DWORD Index, IN BOOL fLastValue, OUT WCHAR **ppwszValue) { HRESULT hr; WCHAR *pwszValue = NULL; WCHAR *pwszValueExtra = NULL; WCHAR *pwszValueError = NULL; DWORD cwc; WCHAR *pwszT = NULL; INFCONTEXT InfContext; *ppwszValue = NULL; hr = infGetCurrentKeyValueAndAlloc(pInfContext, Index, &pwszValue); _JumpIfError2(hr, error, "infGetCurrentKeyValueAndAlloc", E_INVALIDARG); if (1 == Index) { InfContext = *pInfContext; for (;;) { WCHAR *pwsz; if (!SetupFindNextLine(&InfContext, &InfContext)) { break; } hr = infGetCurrentKeyValueAndAlloc(&InfContext, 0, &pwszT); _JumpIfError2(hr, error, "infGetCurrentKeyValueAndAlloc", E_INVALIDARG); if (0 != LSTRCMPIS(pwszT, wszINFKEY_CONTINUE)) { break; } LocalFree(pwszT); pwszT = NULL; hr = infGetCurrentKeyValueAndAlloc(&InfContext, 1, &pwszT); _JumpIfError2(hr, error, "infGetCurrentKeyValueAndAlloc", E_INVALIDARG); cwc = wcslen(pwszValue) + wcslen(pwszT); pwsz = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwc + 1) * sizeof(WCHAR)); if (NULL == pwsz) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } wcscpy(pwsz, pwszValue); wcscat(pwsz, pwszT); LocalFree(pwszValue); pwszValue = pwsz; LocalFree(pwszT); pwszT = NULL; } } if (fLastValue) { DWORD cValue; cValue = SetupGetFieldCount(pInfContext); if (Index != cValue) { DWORD i; for (i = Index + 5; i > Index; i--) { hr = infGetCurrentKeyValue( pInfContext, pwszSection, pwszKey, i, FALSE, // fLastValue &pwszValueExtra); if (S_OK == hr) { DWORD j; cwc = wcslen(pwszValue) + i - 1 + wcslen(pwszValueExtra); pwszValueError = (WCHAR *) LocalAlloc( LMEM_FIXED, (cwc + 1) * sizeof(WCHAR)); if (NULL == pwszValueError) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } pwszValueError[0] = L'\0'; for (j = 1; j < Index; j++) { wcscat(pwszValueError, L","); } wcscat(pwszValueError, pwszValue); for (j = Index; j < i; j++) { wcscat(pwszValueError, L","); } wcscat(pwszValueError, pwszValueExtra); CSASSERT(wcslen(pwszValueError) == cwc); hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); INFSETERROR(hr, pwszSection, pwszKey, pwszValueError); _PrintErrorStr(hr, "extra values", pwszKey); _JumpErrorStr(hr, error, "extra values", pwszValueError); } } } } *ppwszValue = pwszValue; pwszValue = NULL; hr = S_OK; error: if (NULL != pwszT) { LocalFree(pwszT); } if (NULL != pwszValue) { LocalFree(pwszValue); } if (NULL != pwszValueExtra) { LocalFree(pwszValueExtra); } if (NULL != pwszValueError) { LocalFree(pwszValueError); } return(hr); } HRESULT infFindNextKey( IN WCHAR const *pwszKey, IN OUT INFCONTEXT *pInfContext) { HRESULT hr; WCHAR *pwszKeyT = NULL; for (;;) { if (!SetupFindNextLine(pInfContext, pInfContext)) { hr = myHLastError(); _PrintErrorStr2(hr, "SetupFindNextLine", pwszKey, hr); if ((HRESULT) ERROR_LINE_NOT_FOUND == hr) { hr = S_FALSE; } _JumpError2(hr, error, "SetupFindNextLine", hr); } if (NULL != pwszKeyT) { LocalFree(pwszKeyT); pwszKeyT = NULL; } hr = infGetCurrentKeyValue( pInfContext, NULL, // pwszSection NULL, // pwszKey 0, FALSE, // fLastValue &pwszKeyT); _JumpIfError(hr, error, "infGetCurrentKeyValue"); if (0 == mylstrcmpiL(pwszKey, pwszKeyT)) { break; } } hr = S_OK; error: if (NULL != pwszKeyT) { LocalFree(pwszKeyT); } return(hr); } HRESULT infSetupFindFirstLine( IN HINF hInf, IN WCHAR const *pwszSection, OPTIONAL IN WCHAR const *pwszKey, IN BOOL fUniqueKey, IN DWORD cValueMax, OPTIONAL WCHAR const * const *ppwszValidKeys, IN BOOL fUniqueValidKeys, OUT INFCONTEXT *pInfContext) { HRESULT hr; WCHAR *pwszValue = NULL; WCHAR *pwszKeyT = NULL; WCHAR *pwszValueT = NULL; if (!SetupFindFirstLine(hInf, pwszSection, pwszKey, pInfContext)) { // if the [Section] or Key = does not exist, see if the section is // completely empty. It exists and is empty if SetupGetLineCount // returns 0, or if the Empty key is found in the section. hr = myHLastError(); if ((HRESULT) ERROR_LINE_NOT_FOUND == hr && (0 == SetupGetLineCount(hInf, pwszSection) || SetupFindFirstLine( hInf, pwszSection, wszINFKEY_EMPTY, pInfContext))) { hr = infReferenceSectionName(pwszSection); _JumpIfErrorStr(hr, error, "infReferenceSectionName", pwszKey); hr = S_FALSE; // Section exists, but is empty } _JumpErrorStr3( hr, error, "SetupFindFirstLine", pwszSection, ERROR_LINE_NOT_FOUND, S_FALSE); } hr = infReferenceSectionName(pwszSection); _JumpIfErrorStr(hr, error, "infReferenceSectionName", pwszKey); if (NULL != pwszKey) { if (fUniqueKey) { INFCONTEXT InfContext = *pInfContext; hr = infFindNextKey(pwszKey, &InfContext); if (S_OK == hr) { hr = infGetCurrentKeyValue( &InfContext, pwszSection, pwszKey, 1, FALSE, // fLastValue &pwszValue); _PrintIfError(hr, "infGetCurrentKeyValue"); hr = HRESULT_FROM_WIN32(RPC_S_ENTRY_ALREADY_EXISTS); INFSETERROR(hr, pwszSection, pwszKey, pwszValue); _JumpErrorStr(hr, error, "duplicate key", pwszKey); } } if (0 != cValueMax) { hr = infGetCurrentKeyValue( pInfContext, pwszSection, pwszKey, cValueMax, TRUE, // fLastValue &pwszValue); _JumpIfErrorStr(hr, error, "infGetCurrentKeyValue", pwszKey); } } if (NULL != ppwszValidKeys) { INFCONTEXT InfContext; WCHAR const * const *ppwszKey; if (!SetupFindFirstLine(hInf, pwszSection, NULL, &InfContext)) { hr = myHLastError(); _JumpErrorStr(hr, error, "SetupFindFirstLine", pwszSection) } for (;;) { hr = infGetCurrentKeyValue( &InfContext, pwszSection, NULL, // pwszKey 0, FALSE, // fLastValue &pwszKeyT); _JumpIfErrorStr(hr, error, "infGetCurrentKeyValue", pwszSection); for (ppwszKey = ppwszValidKeys; NULL != *ppwszKey; ppwszKey++) { if (0 == mylstrcmpiL(*ppwszKey, pwszKeyT)) { break; } } if (NULL == *ppwszKey) { hr = infGetCurrentKeyValue( &InfContext, pwszSection, pwszKeyT, 1, FALSE, // fLastValue &pwszValueT); _PrintIfError(hr, "infGetCurrentKeyValue"); hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); INFSETERROR(hr, pwszSection, pwszKeyT, pwszValueT); _JumpErrorStr(hr, error, "invalid key", pwszKeyT); } if (fUniqueValidKeys) { INFCONTEXT InfContextT = InfContext; hr = infFindNextKey(pwszKeyT, &InfContextT); if (S_OK == hr) { hr = infGetCurrentKeyValue( &InfContextT, pwszSection, pwszKeyT, 1, FALSE, // fLastValue &pwszValueT); _PrintIfError(hr, "infGetCurrentKeyValue"); hr = HRESULT_FROM_WIN32(RPC_S_ENTRY_ALREADY_EXISTS); INFSETERROR(hr, pwszSection, pwszKeyT, pwszValueT); _JumpErrorStr(hr, error, "duplicate key", pwszKeyT); } } LocalFree(pwszKeyT); pwszKeyT = NULL; if (!SetupFindNextLine(&InfContext, &InfContext)) { hr = myHLastError(); _PrintErrorStr2(hr, "SetupFindNextLine", pwszSection, hr); break; } } } hr = S_OK; error: if (NULL != pwszKeyT) { LocalFree(pwszKeyT); } if (NULL != pwszValueT) { LocalFree(pwszValueT); } if (NULL != pwszValue) { LocalFree(pwszValue); } return(hr); } HRESULT infBuildPolicyElement( IN OUT INFCONTEXT *pInfContext, OPTIONAL OUT CERT_POLICY_QUALIFIER_INFO *pcpqi) { HRESULT hr; WCHAR *pwszKey = NULL; BOOL fURL = FALSE; BOOL fNotice = FALSE; WCHAR *pwszValue = NULL; WCHAR *pwszURL = NULL; BYTE *pbData = NULL; DWORD cbData; hr = infGetCurrentKeyValue( pInfContext, NULL, // pwszSection NULL, // pwszKey 0, FALSE, // fLastValue &pwszKey); _JumpIfError(hr, error, "infGetCurrentKeyValue"); DBGPRINT((DBG_SS_CERTLIBI, "Element = %ws\n", pwszKey)); if (0 == LSTRCMPIS(pwszKey, wszINFKEY_URL)) { fURL = TRUE; } else if (0 == LSTRCMPIS(pwszKey, wszINFKEY_NOTICE)) { fNotice = TRUE; } else { if (0 != LSTRCMPIS(pwszKey, wszINFKEY_OID) && 0 != LSTRCMPIS(pwszKey, wszINFKEY_CONTINUE)) { hr = E_INVALIDARG; _JumpErrorStr(hr, error, "unknown key", pwszKey); } hr = S_FALSE; // Skip this key _JumpError2(hr, error, "skip OID key", hr); } hr = infGetCurrentKeyValue( pInfContext, NULL, // pwszSection pwszKey, 1, TRUE, // fLastValue &pwszValue); _JumpIfErrorStr(hr, error, "infGetCurrentKeyValue", pwszKey); DBGPRINT((DBG_SS_CERTLIBI, "%ws = %ws\n", pwszKey, pwszValue)); if (fURL) { CERT_NAME_VALUE NameValue; hr = myInternetCanonicalizeUrl(pwszValue, &pwszURL); _JumpIfError(hr, error, "myInternetCanonicalizeUrl"); NameValue.dwValueType = CERT_RDN_IA5_STRING; NameValue.Value.pbData = (BYTE *) pwszURL; NameValue.Value.cbData = 0; if (NULL != pcpqi) { CSILOG(S_OK, IDS_ILOG_CAPOLICY_ELEMENT, pwszURL, NULL, NULL); } if (!myEncodeObject( X509_ASN_ENCODING, X509_UNICODE_NAME_VALUE, &NameValue, 0, CERTLIB_USE_LOCALALLOC, &pbData, &cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } } else { CERT_POLICY_QUALIFIER_USER_NOTICE UserNotice; ZeroMemory(&UserNotice, sizeof(UserNotice)); UserNotice.pszDisplayText = pwszValue; if (NULL != pcpqi) { CSILOG(S_OK, IDS_ILOG_CAPOLICY_ELEMENT, pwszValue, NULL, NULL); } if (!myEncodeObject( X509_ASN_ENCODING, X509_PKIX_POLICY_QUALIFIER_USERNOTICE, &UserNotice, 0, CERTLIB_USE_LOCALALLOC, &pbData, &cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } } if (NULL != pcpqi) { pcpqi->pszPolicyQualifierId = fURL? szOID_PKIX_POLICY_QUALIFIER_CPS : szOID_PKIX_POLICY_QUALIFIER_USERNOTICE; pcpqi->Qualifier.pbData = pbData; pcpqi->Qualifier.cbData = cbData; pbData = NULL; } hr = S_OK; error: if (S_OK != hr && S_FALSE != hr) { INFSETERROR(hr, NULL, pwszKey, pwszValue); } if (NULL != pwszKey) { LocalFree(pwszKey); } if (NULL != pwszValue) { LocalFree(pwszValue); } if (NULL != pwszURL) { LocalFree(pwszURL); } if (NULL != pbData) { LocalFree(pbData); } return(hr); } HRESULT infBuildPolicy( IN HINF hInf, IN WCHAR const *pwszSection, IN OUT CERT_POLICY_INFO *pcpi) { HRESULT hr; INFCONTEXT InfContext; DWORD i; WCHAR *pwszValue = NULL; static WCHAR const * const s_apwszKeys[] = { wszINFKEY_OID, wszINFKEY_URL, wszINFKEY_NOTICE, wszINFKEY_CONTINUE, NULL }; CSASSERT(NULL != hInf && INVALID_HANDLE_VALUE != hInf); hr = infSetupFindFirstLine( hInf, pwszSection, wszINFKEY_OID, TRUE, // fUniqueKey 1, // cValueMax s_apwszKeys, FALSE, // fUniqueValidKeys &InfContext); if (S_OK != hr) { if ((HRESULT) ERROR_LINE_NOT_FOUND == hr) { hr = SPAPI_E_LINE_NOT_FOUND; // don't ignore this error } INFSETERROR(hr, NULL, wszINFKEY_OID, NULL); _JumpErrorStr(hr, error, "infSetupFindFirstLine", pwszSection); } hr = infGetCurrentKeyValue( &InfContext, pwszSection, wszINFKEY_OID, 1, TRUE, // fLastValue &pwszValue); _JumpIfErrorStr(hr, error, "infGetCurrentKeyValue", wszINFKEY_OID); hr = myVerifyObjId(pwszValue); _JumpIfErrorStr(hr, error, "myVerifyObjId", pwszValue); if (!ConvertWszToSz(&pcpi->pszPolicyIdentifier, pwszValue, -1)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "ConvertWszToSz"); } DBGPRINT((DBG_SS_CERTLIBI, "OID = %hs\n", pcpi->pszPolicyIdentifier)); hr = infSetupFindFirstLine( hInf, pwszSection, NULL, // pwszKey FALSE, // fUniqueKey 0, // cValueMax NULL, // apwszKeys FALSE, // fUniqueValidKeys &InfContext); _JumpIfErrorStr(hr, error, "infSetupFindFirstLine", pwszSection); for (i = 0; ; ) { hr = infBuildPolicyElement(&InfContext, NULL); if (S_FALSE != hr) { _JumpIfErrorStr(hr, error, "infBuildPolicyElement", pwszSection); i++; } if (!SetupFindNextLine(&InfContext, &InfContext)) { hr = myHLastError(); _PrintErrorStr2(hr, "SetupFindNextLine", pwszSection, hr); break; } } pcpi->cPolicyQualifier = i; pcpi->rgPolicyQualifier = (CERT_POLICY_QUALIFIER_INFO *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, pcpi->cPolicyQualifier * sizeof(pcpi->rgPolicyQualifier[0])); if (NULL == pcpi->rgPolicyQualifier) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } hr = infSetupFindFirstLine( hInf, pwszSection, NULL, // pwszKey FALSE, // fUniqueKey 0, // cValueMax NULL, // apwszKeys FALSE, // fUniqueValidKeys &InfContext); _JumpIfErrorStr(hr, error, "infSetupFindFirstLine", pwszSection); for (i = 0; ; ) { // handle one URL or text message hr = infBuildPolicyElement(&InfContext, &pcpi->rgPolicyQualifier[i]); if (S_FALSE != hr) { _JumpIfErrorStr(hr, error, "infBuildPolicyElement", pwszSection); i++; } if (!SetupFindNextLine(&InfContext, &InfContext)) { hr = myHLastError(); _PrintErrorStr2(hr, "SetupFindNextLine", pwszSection, hr); break; } } CSASSERT(i == pcpi->cPolicyQualifier); hr = S_OK; error: if (S_OK != hr && S_FALSE != hr) { INFSETERROR(hr, pwszSection, NULL, L""); } CSILOG(hr, IDS_ILOG_CAPOLICY_BUILD, pwszSection, pwszValue, NULL); if (NULL != pwszValue) { LocalFree(pwszValue); } return(hr); } VOID infFreePolicy( IN OUT CERT_POLICY_INFO *pcpi) { DWORD i; CERT_POLICY_QUALIFIER_INFO *pcpqi; if (NULL != pcpi->pszPolicyIdentifier) { LocalFree(pcpi->pszPolicyIdentifier); } if (NULL != pcpi->rgPolicyQualifier) { for (i = 0; i < pcpi->cPolicyQualifier; i++) { pcpqi = &pcpi->rgPolicyQualifier[i]; if (NULL != pcpqi->Qualifier.pbData) { LocalFree(pcpqi->Qualifier.pbData); } } LocalFree(pcpi->rgPolicyQualifier); } } HRESULT myInfOpenFile( OPTIONAL IN WCHAR const *pwszfnPolicy, OUT HINF *phInf, OUT DWORD *pErrorLine) { HRESULT hr; WCHAR wszPath[MAX_PATH]; UINT ErrorLine; DWORD Flags; *phInf = INVALID_HANDLE_VALUE; *pErrorLine = 0; myInfClearError(); if (NULL == pwszfnPolicy) { DWORD cwc; cwc = GetEnvironmentVariable( L"SystemRoot", wszPath, ARRAYSIZE(wszPath) - ARRAYSIZE(wszBSCAPOLICYFILE)); if (0 == cwc || ARRAYSIZE(wszPath) - ARRAYSIZE(wszBSCAPOLICYFILE) <= cwc) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); _JumpError(hr, error, "GetEnvironmentVariable"); } wcscat(wszPath, wszBSCAPOLICYFILE); pwszfnPolicy = wszPath; } else { if (NULL == wcschr(pwszfnPolicy, L'\\')) { if (ARRAYSIZE(wszPath) <= 2 + wcslen(pwszfnPolicy)) { hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); _JumpErrorStr(hr, error, "filename too long", pwszfnPolicy); } wcscpy(wszPath, L".\\"); wcscat(wszPath, pwszfnPolicy); pwszfnPolicy = wszPath; } } hr = infCollectSectionNames(pwszfnPolicy); _JumpIfErrorStr(hr, error, "infCollectSectionNames", pwszfnPolicy); Flags = INF_STYLE_WIN4; for (;;) { ErrorLine = 0; *phInf = SetupOpenInfFile( pwszfnPolicy, NULL, Flags, &ErrorLine); *pErrorLine = ErrorLine; if (INVALID_HANDLE_VALUE != *phInf) { break; } hr = myHLastError(); if ((HRESULT) ERROR_WRONG_INF_STYLE == hr && INF_STYLE_WIN4 == Flags) { Flags = INF_STYLE_OLDNT; continue; } CSILOG( hr, IDS_ILOG_CAPOLICY_OPEN_FAILED, pwszfnPolicy, NULL, 0 == *pErrorLine? NULL : pErrorLine); _JumpErrorStr(hr, error, "SetupOpenInfFile", pwszfnPolicy); } CSILOG(S_OK, IDS_ILOG_CAPOLICY_OPEN, pwszfnPolicy, NULL, NULL); hr = S_OK; error: DBGPRINT(( DBG_SS_CERTLIBI, "myInfOpenFile(%ws) hr=%x --> h=%x\n", pwszfnPolicy, hr, *phInf)); return(hr); } VOID myInfCloseFile( IN HINF hInf) { if (NULL != hInf && INVALID_HANDLE_VALUE != hInf) { WCHAR *pwszInfError = myInfGetError(); SetupCloseInfFile(hInf); CSILOG(S_OK, IDS_ILOG_CAPOLICY_CLOSE, pwszInfError, NULL, NULL); if (NULL != pwszInfError) { LocalFree(pwszInfError); } } myInfClearError(); infFreeSectionNames(); DBGPRINT((DBG_SS_CERTLIBI, "myInfCloseFile(%x)\n", hInf)); } HRESULT myInfParseBooleanValue( IN WCHAR const *pwszValue, OUT BOOL *pfValue) { HRESULT hr; DWORD i; static WCHAR const * const s_apwszTrue[] = { L"True", L"Yes", L"On", L"1" }; static WCHAR const * const s_apwszFalse[] = { L"False", L"No", L"Off", L"0" }; *pfValue = FALSE; for (i = 0; i < ARRAYSIZE(s_apwszTrue); i++) { if (0 == mylstrcmpiL(pwszValue, s_apwszTrue[i])) { *pfValue = TRUE; break; } } if (i == ARRAYSIZE(s_apwszTrue)) { for (i = 0; i < ARRAYSIZE(s_apwszFalse); i++) { if (0 == mylstrcmpiL(pwszValue, s_apwszFalse[i])) { break; } } if (i == ARRAYSIZE(s_apwszFalse)) { hr = E_INVALIDARG; _JumpErrorStr(hr, error, "bad boolean value string", pwszValue); } } hr = S_OK; error: if (S_OK != hr) { INFSETERROR(hr, NULL, NULL, pwszValue); } return(hr); } HRESULT myInfGetBooleanValue( IN HINF hInf, IN WCHAR const *pwszSection, IN WCHAR const *pwszKey, IN BOOL fIgnoreMissingKey, OUT BOOL *pfValue) { HRESULT hr; INFCONTEXT InfContext; WCHAR *pwszValue = NULL; *pfValue = FALSE; myInfClearError(); CSASSERT(NULL != hInf && INVALID_HANDLE_VALUE != hInf); hr = infSetupFindFirstLine( hInf, pwszSection, pwszKey, // pwszKey TRUE, // fUniqueKey 1, // cValueMax NULL, // apwszKeys FALSE, // fUniqueValidKeys &InfContext); _PrintIfErrorStr3( hr, "infSetupFindFirstLine", pwszSection, ERROR_LINE_NOT_FOUND, S_FALSE); _JumpIfErrorStr3( hr, error, "infSetupFindFirstLine", pwszKey, ERROR_LINE_NOT_FOUND, S_FALSE); hr = infGetCurrentKeyValue( &InfContext, pwszSection, pwszKey, 1, TRUE, // fLastValue &pwszValue); _JumpIfErrorStr(hr, error, "infGetCurrentKeyValue", pwszKey); DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetBooleanValue --> '%ws'\n", pwszValue)); hr = myInfParseBooleanValue(pwszValue, pfValue); _JumpIfError(hr, error, "myInfParseBooleanValue"); error: if (S_OK != hr && ((HRESULT) ERROR_LINE_NOT_FOUND != hr || !fIgnoreMissingKey)) { INFSETERROR(hr, pwszSection, pwszKey, pwszValue); CSILOG(hr, IDS_ILOG_BAD_BOOLEAN, pwszSection, pwszKey, NULL); } DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetBooleanValue(%ws, %ws) hr=%x --> f=%d\n", pwszSection, pwszKey, hr, *pfValue)); if (NULL != pwszValue) { LocalFree(pwszValue); } return(hr); } HRESULT infGetCriticalFlag( IN HINF hInf, IN WCHAR const *pwszSection, IN BOOL fDefault, OUT BOOL *pfCritical) { HRESULT hr; hr = myInfGetBooleanValue( hInf, pwszSection, wszINFKEY_CRITICAL, TRUE, pfCritical); if (S_OK != hr) { *pfCritical = fDefault; if ((HRESULT) ERROR_LINE_NOT_FOUND != hr && S_FALSE != hr) { _JumpErrorStr(hr, error, "myInfGetBooleanValue", pwszSection); } hr = S_OK; } error: DBGPRINT(( DBG_SS_CERTLIBI, "infGetCriticalFlag(%ws) hr=%x --> f=%d\n", pwszSection, hr, *pfCritical)); return(hr); } //+------------------------------------------------------------------------ // infGetPolicyStatementExtensionSub -- build policy extension from INF file // // [pwszSection] // Policies = LegalPolicy, LimitedUsePolicy, ExtraPolicy // // [LegalPolicy] // OID = 1.3.6.1.4.1.311.21.43 // Notice = "Legal policy statement text." // // [LimitedUsePolicy] // OID = 1.3.6.1.4.1.311.21.47 // URL = "http://http.site.com/some where/default.asp" // URL = "ftp://ftp.site.com/some where else/default.asp" // Notice = "Limited use policy statement text." // URL = "ldap://ldap.site.com/some where else again/default.asp" // // [ExtraPolicy] // OID = 1.3.6.1.4.1.311.21.53 // URL = http://extra.site.com/Extra Policy/default.asp // // Return S_OK if extension has been constructed from the INF file // Return S_FALSE if empty section detected in INF file // Return other error if no section detected in INF file //------------------------------------------------------------------------- HRESULT infGetPolicyStatementExtensionSub( IN HINF hInf, IN WCHAR const *pwszSection, IN char const *pszObjId, OUT CERT_EXTENSION *pext) { HRESULT hr; CERT_POLICIES_INFO PoliciesInfo; INFCONTEXT InfContext; DWORD i; WCHAR *pwszValue = NULL; static WCHAR const * const s_apwszKeys[] = { wszINFKEY_POLICIES, wszINFKEY_CRITICAL, NULL }; ZeroMemory(&PoliciesInfo, sizeof(PoliciesInfo)); ZeroMemory(pext, sizeof(*pext)); CSASSERT(NULL != hInf && INVALID_HANDLE_VALUE != hInf); hr = infSetupFindFirstLine( hInf, pwszSection, wszINFKEY_POLICIES, TRUE, // fUniqueKey 0, // cValueMax s_apwszKeys, TRUE, // fUniqueValidKeys &InfContext); if (S_OK != hr) { CSILOG( hr, IDS_ILOG_CAPOLICY_NOKEY, pwszSection, wszINFKEY_POLICIES, NULL); _JumpErrorStr3( hr, error, "infSetupFindFirstLine", pwszSection, S_FALSE, (HRESULT) ERROR_LINE_NOT_FOUND); } // First, count the policies. PoliciesInfo.cPolicyInfo = SetupGetFieldCount(&InfContext); if (0 == PoliciesInfo.cPolicyInfo) { hr = S_FALSE; _JumpError(hr, error, "SetupGetFieldCount"); } // Next, allocate memory. PoliciesInfo.rgPolicyInfo = (CERT_POLICY_INFO *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, PoliciesInfo.cPolicyInfo * sizeof(PoliciesInfo.rgPolicyInfo[0])); if (NULL == PoliciesInfo.rgPolicyInfo) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } // Finally! Fill in the policies data. for (i = 0; i < PoliciesInfo.cPolicyInfo; i++) { if (NULL != pwszValue) { LocalFree(pwszValue); pwszValue = NULL; } hr = infGetCurrentKeyValue( &InfContext, pwszSection, wszINFKEY_POLICIES, i + 1, FALSE, // fLastValue &pwszValue); _JumpIfErrorStr(hr, error, "infGetCurrentKeyValue", wszINFKEY_POLICIES); DBGPRINT((DBG_SS_CERTLIBI, "%ws[%u] = %ws\n", wszINFKEY_POLICIES, i, pwszValue)); hr = infBuildPolicy(hInf, pwszValue, &PoliciesInfo.rgPolicyInfo[i]); _JumpIfErrorStr(hr, error, "infBuildPolicy", pwszValue); } hr = infGetCriticalFlag(hInf, pwszSection, FALSE, &pext->fCritical); _JumpIfError(hr, error, "infGetCriticalFlag"); if (!myEncodeObject( X509_ASN_ENCODING, X509_CERT_POLICIES, &PoliciesInfo, 0, CERTLIB_USE_LOCALALLOC, &pext->Value.pbData, &pext->Value.cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } error: if (S_OK != hr && S_FALSE != hr) { INFSETERROR(hr, pwszSection, wszINFKEY_POLICIES, pwszValue); } CSILOG(hr, IDS_ILOG_CAPOLICY_EXTENSION, pwszValue, NULL, NULL); pext->pszObjId = const_cast(pszObjId); // on error, too! if (NULL != PoliciesInfo.rgPolicyInfo) { for (i = 0; i < PoliciesInfo.cPolicyInfo; i++) { infFreePolicy(&PoliciesInfo.rgPolicyInfo[i]); } LocalFree(PoliciesInfo.rgPolicyInfo); } if (NULL != pwszValue) { LocalFree(pwszValue); } return(hr); } //+------------------------------------------------------------------------ // myInfGetPolicyStatementExtension -- build policy extension from INF file // // [PolicyStatementExtension] // Policies = LegalPolicy, LimitedUsePolicy, ExtraPolicy // ... // // OR // // [CAPolicy] // Policies = LegalPolicy, LimitedUsePolicy, ExtraPolicy // ... // // Return S_OK if extension has been constructed from the INF file // Return S_FALSE if empty section detected in INF file // Return other error if no section detected in INF file //------------------------------------------------------------------------- FNMYINFGETEXTENSION myInfGetPolicyStatementExtension; HRESULT myInfGetPolicyStatementExtension( IN HINF hInf, OUT CERT_EXTENSION *pext) { HRESULT hr; if (NULL == hInf || INVALID_HANDLE_VALUE == hInf) { hr = E_HANDLE; _JumpError2(hr, error, "hInf", hr); } myInfClearError(); hr = infGetPolicyStatementExtensionSub( hInf, wszINFSECTION_POLICYSTATEMENT, szOID_CERT_POLICIES, pext); if (S_OK != hr) { HRESULT hr2; hr2 = infGetPolicyStatementExtensionSub( hInf, wszINFSECTION_CAPOLICY, szOID_CERT_POLICIES, pext); if (S_OK == hr2 || (S_FALSE == hr2 && (HRESULT) ERROR_LINE_NOT_FOUND == hr)) { hr = hr2; } } _JumpIfErrorStr3( hr, error, "infGetPolicyStatementExtensionSub", wszINFSECTION_POLICYSTATEMENT, ERROR_LINE_NOT_FOUND, S_FALSE); error: DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetPolicyStatementExtension hr=%x --> f=%d, cb=%x\n", hr, pext->fCritical, pext->Value.cbData)); return(hr); } //+------------------------------------------------------------------------ // myInfGetApplicationPolicyStatementExtension -- build application policy // extension from INF file // // [ApplicationPolicyStatementExtension] // Policies = LegalPolicy, LimitedUsePolicy, ExtraPolicy // ... // // Return S_OK if extension has been constructed from the INF file // Return S_FALSE if empty section detected in INF file // Return other error if no section detected in INF file //------------------------------------------------------------------------- FNMYINFGETEXTENSION myInfGetApplicationPolicyStatementExtension; HRESULT myInfGetApplicationPolicyStatementExtension( IN HINF hInf, OUT CERT_EXTENSION *pext) { HRESULT hr; if (NULL == hInf || INVALID_HANDLE_VALUE == hInf) { hr = E_HANDLE; _JumpError2(hr, error, "hInf", hr); } myInfClearError(); hr = infGetPolicyStatementExtensionSub( hInf, wszINFSECTION_APPLICATIONPOLICYSTATEMENT, szOID_APPLICATION_CERT_POLICIES, pext); _JumpIfErrorStr3( hr, error, "infGetPolicyStatementExtensionSub", wszINFSECTION_APPLICATIONPOLICYSTATEMENT, S_FALSE, (HRESULT) ERROR_LINE_NOT_FOUND); error: DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetApplicationPolicyStatementExtension hr=%x --> f=%d, cb=%x\n", hr, pext->fCritical, pext->Value.cbData)); return(hr); } HRESULT myInfGetCRLPublicationParams( IN HINF hInf, IN WCHAR const *pwszKeyCRLPeriodString, IN WCHAR const *pwszKeyCRLPeriodCount, OUT WCHAR **ppwszCRLPeriodString, OUT DWORD *pdwCRLPeriodCount) { HRESULT hr; WCHAR const *pwszKey; // Retrieve units count and string. If either fails, both are discarded. *ppwszCRLPeriodString = NULL; pwszKey = pwszKeyCRLPeriodCount; hr = myInfGetNumericKeyValue( hInf, TRUE, // fLog wszINFSECTION_CERTSERVER, pwszKey, 1, TRUE, // fLastValue pdwCRLPeriodCount); _JumpIfErrorStr2( hr, error, "myInfGetNumericKeyValue", pwszKeyCRLPeriodCount, ERROR_LINE_NOT_FOUND); pwszKey = pwszKeyCRLPeriodString; hr = myInfGetKeyValue( hInf, TRUE, // fLog wszINFSECTION_CERTSERVER, pwszKey, 1, TRUE, // fLastValue ppwszCRLPeriodString); _JumpIfErrorStr2( hr, error, "myInfGetKeyValue", pwszKeyCRLPeriodString, ERROR_LINE_NOT_FOUND); error: if (S_OK != hr) { INFSETERROR(hr, wszINFSECTION_CERTSERVER, pwszKey, NULL); } return(hr); } //+------------------------------------------------------------------------ // myInfGetKeyValue -- fetch a string value from INF file // // [pwszSection] // pwszKey = string // // Returns: allocated string key value //------------------------------------------------------------------------- HRESULT myInfGetKeyValue( IN HINF hInf, IN BOOL fLog, IN WCHAR const *pwszSection, IN WCHAR const *pwszKey, IN DWORD Index, IN BOOL fLastValue, OUT WCHAR **ppwszValue) { HRESULT hr; INFCONTEXT InfContext; WCHAR *pwszValue = NULL; *ppwszValue = NULL; myInfClearError(); CSASSERT(NULL != hInf && INVALID_HANDLE_VALUE != hInf); hr = infSetupFindFirstLine( hInf, pwszSection, pwszKey, TRUE, // fUniqueKey fLastValue? Index : 0, // cValueMax NULL, // apwszKeys FALSE, // fUniqueValidKeys &InfContext); if (S_OK != hr) { if (fLog) { CSILOG(hr, IDS_ILOG_CAPOLICY_NOKEY, pwszSection, pwszKey, NULL); } _JumpErrorStr2( hr, error, "infSetupFindFirstLine", pwszKey, ERROR_LINE_NOT_FOUND); } hr = infGetCurrentKeyValue( &InfContext, pwszSection, pwszKey, Index, fLastValue, &pwszValue); _JumpIfError(hr, error, "infGetCurrentKeyValue"); *ppwszValue = pwszValue; pwszValue = NULL; hr = S_OK; //wprintf(L"%ws = %ws\n", pwszKey, *ppwszValue); error: if (NULL != pwszValue) { LocalFree(pwszValue); } if (S_OK != hr && fLog) { INFSETERROR(hr, pwszSection, pwszKey, NULL); } return(hr); } //+------------------------------------------------------------------------ // myInfGetNumericKeyValue -- fetch a numeric value from INF file // // [pwszSection] // pwszKey = 2048 // // Returns: DWORD key value //------------------------------------------------------------------------- HRESULT myInfGetNumericKeyValue( IN HINF hInf, IN BOOL fLog, IN WCHAR const *pwszSection, IN WCHAR const *pwszKey, IN DWORD Index, IN BOOL fLastValue, OUT DWORD *pdwValue) { HRESULT hr; INFCONTEXT InfContext; WCHAR *pwszValue = NULL; INT Value; myInfClearError(); CSASSERT(NULL != hInf && INVALID_HANDLE_VALUE != hInf); hr = infSetupFindFirstLine( hInf, pwszSection, pwszKey, TRUE, // fUniqueKey fLastValue? Index : 0, // cValueMax NULL, // apwszKeys FALSE, // fUniqueValidKeys &InfContext); if (S_OK != hr) { if (fLog) { CSILOG(hr, IDS_ILOG_CAPOLICY_NOKEY, pwszSection, pwszKey, NULL); } _PrintErrorStr2( hr, "infSetupFindFirstLine", pwszKey, ERROR_LINE_NOT_FOUND); _JumpErrorStr2( hr, error, "infSetupFindFirstLine", pwszSection, ERROR_LINE_NOT_FOUND); } if (fLastValue) { hr = infGetCurrentKeyValue( &InfContext, pwszSection, pwszKey, Index, fLastValue, &pwszValue); _JumpIfError(hr, error, "infGetCurrentKeyValue"); } if (!SetupGetIntField(&InfContext, Index, &Value)) { hr = myHLastError(); if (fLog) { CSILOG(hr, IDS_ILOG_BAD_NUMERICFIELD, pwszSection, pwszKey, NULL); } _JumpErrorStr(hr, error, "SetupGetIntField", pwszKey); } *pdwValue = Value; DBGPRINT((DBG_SS_CERTLIBI, "%ws = %u\n", pwszKey, *pdwValue)); hr = S_OK; error: if (NULL != pwszValue) { LocalFree(pwszValue); } if (S_OK != hr && fLog) { INFSETERROR(hr, pwszSection, pwszKey, NULL); } return(hr); } //+------------------------------------------------------------------------ // myInfGetKeyLength -- fetch the renewal key length from CAPolicy.inf // // [certsrv_server] // RenewalKeyLength = 2048 // // Returns: DWORD key kength //------------------------------------------------------------------------- HRESULT myInfGetKeyLength( IN HINF hInf, OUT DWORD *pdwKeyLength) { HRESULT hr; if (NULL == hInf || INVALID_HANDLE_VALUE == hInf) { hr = E_HANDLE; _JumpError2(hr, error, "hInf", hr); } myInfClearError(); hr = myInfGetNumericKeyValue( hInf, TRUE, // fLog wszINFSECTION_CERTSERVER, wszINFKEY_RENEWALKEYLENGTH, 1, TRUE, // fLastValue pdwKeyLength); _JumpIfErrorStr2( hr, error, "myInfGetNumericKeyValue", wszINFKEY_RENEWALKEYLENGTH, ERROR_LINE_NOT_FOUND); error: DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetKeyLength hr=%x --> len=%x\n", hr, *pdwKeyLength)); return(hr); } //+------------------------------------------------------------------------ // infGetValidityPeriodSub -- fetch validity period & units from CAPolicy.inf // // [certsrv_server] // xxxxValidityPeriod = Years (string) // xxxxValidityPeriodUnits = 8 (count) // // Returns: validity period count and enum //------------------------------------------------------------------------- HRESULT infGetValidityPeriodSub( IN HINF hInf, IN BOOL fLog, IN WCHAR const *pwszInfKeyNameCount, IN WCHAR const *pwszInfKeyNameString, OPTIONAL IN WCHAR const *pwszValidityPeriodCount, OPTIONAL IN WCHAR const *pwszValidityPeriodString, OUT DWORD *pdwValidityPeriodCount, OUT ENUM_PERIOD *penumValidityPeriod) { HRESULT hr; WCHAR *pwszStringValue = NULL; BOOL fValidCount = TRUE; UINT idsLog = IDS_ILOG_BAD_VALIDITY_STRING; *pdwValidityPeriodCount = 0; *penumValidityPeriod = ENUM_PERIOD_INVALID; hr = S_OK; if (NULL == pwszValidityPeriodCount && NULL == pwszValidityPeriodString) { if (NULL == hInf || INVALID_HANDLE_VALUE == hInf) { hr = E_HANDLE; _JumpError2(hr, error, "hInf", hr); } hr = myInfGetNumericKeyValue( hInf, fLog, wszINFSECTION_CERTSERVER, pwszInfKeyNameCount, 1, TRUE, // fLastValue pdwValidityPeriodCount); _JumpIfErrorStr2( hr, error, "myInfGetNumericKeyValue", pwszInfKeyNameCount, ERROR_LINE_NOT_FOUND); hr = myInfGetKeyValue( hInf, fLog, wszINFSECTION_CERTSERVER, pwszInfKeyNameString, 1, TRUE, // fLastValue &pwszStringValue); if (S_OK != hr && fLog) { INFSETERROR(hr, wszINFSECTION_CERTSERVER, pwszInfKeyNameString, NULL); } _JumpIfErrorStr(hr, error, "myInfGetKeyValue", pwszInfKeyNameString); pwszValidityPeriodString = pwszStringValue; } else { if (NULL != pwszValidityPeriodCount) { *pdwValidityPeriodCount = myWtoI( pwszValidityPeriodCount, &fValidCount); } idsLog = IDS_ILOG_BAD_VALIDITY_STRING_UNATTEND; } DBGPRINT(( DBG_SS_CERTLIBI, "%ws = %u -- %ws = %ws\n", pwszInfKeyNameCount, *pdwValidityPeriodCount, pwszInfKeyNameString, pwszValidityPeriodString)); if (NULL != pwszValidityPeriodString) { if (0 == LSTRCMPIS(pwszValidityPeriodString, wszPERIODYEARS)) { *penumValidityPeriod = ENUM_PERIOD_YEARS; } else if (0 == LSTRCMPIS(pwszValidityPeriodString, wszPERIODMONTHS)) { *penumValidityPeriod = ENUM_PERIOD_MONTHS; } else if (0 == LSTRCMPIS(pwszValidityPeriodString, wszPERIODWEEKS)) { *penumValidityPeriod = ENUM_PERIOD_WEEKS; } else if (0 == LSTRCMPIS(pwszValidityPeriodString, wszPERIODDAYS)) { *penumValidityPeriod = ENUM_PERIOD_DAYS; } else if (fLog) { INFSETERROR( hr, wszINFSECTION_CERTSERVER, pwszInfKeyNameString, pwszValidityPeriodString); } } DBGPRINT(( DBG_SS_CERTLIBI, "%ws = %u (%ws)\n", pwszInfKeyNameString, *penumValidityPeriod, pwszValidityPeriodString)); if (!fValidCount || (ENUM_PERIOD_YEARS == *penumValidityPeriod && fValidCount && 9999 < *pdwValidityPeriodCount)) { hr = E_INVALIDARG; if (fLog) { WCHAR awcCount[cwcDWORDSPRINTF]; if (NULL == pwszValidityPeriodCount) { wsprintf(awcCount, L"%d", *pdwValidityPeriodCount); } INFSETERROR( hr, wszINFSECTION_CERTSERVER, pwszInfKeyNameCount, NULL != pwszValidityPeriodCount? pwszValidityPeriodCount : awcCount); CSILOG( hr, idsLog, wszINFSECTION_CERTSERVER, NULL == pwszValidityPeriodCount? pwszInfKeyNameCount : NULL, pdwValidityPeriodCount); } _JumpIfErrorStr( hr, error, "bad ValidityPeriod count value", pwszValidityPeriodCount); } hr = S_OK; error: if (NULL != pwszStringValue) { LocalFree(pwszStringValue); } return(hr); } //+------------------------------------------------------------------------ // myInfGetValidityPeriod -- fetch renewal period & units from CAPolicy.inf // // [certsrv_server] // xxxxValidityPeriod = Years (string) // xxxxValidityPeriodUnits = 8 (count) // // Returns: validity period count and enum //------------------------------------------------------------------------- HRESULT myInfGetValidityPeriod( IN HINF hInf, OPTIONAL IN WCHAR const *pwszValidityPeriodCount, OPTIONAL IN WCHAR const *pwszValidityPeriodString, OUT DWORD *pdwValidityPeriodCount, OUT ENUM_PERIOD *penumValidityPeriod, OPTIONAL OUT BOOL *pfSwap) { HRESULT hr; if ((NULL == hInf || INVALID_HANDLE_VALUE == hInf) && NULL == pwszValidityPeriodCount && NULL == pwszValidityPeriodString) { hr = E_HANDLE; _JumpError2(hr, error, "hInf", hr); } myInfClearError(); if (NULL != pfSwap) { *pfSwap = FALSE; } // Try correct order: // [certsrv_server] // xxxxValidityPeriod = Years (string) // xxxxValidityPeriodUnits = 8 (count) hr = infGetValidityPeriodSub( hInf, TRUE, wszINFKEY_RENEWALVALIDITYPERIODCOUNT, wszINFKEY_RENEWALVALIDITYPERIODSTRING, pwszValidityPeriodCount, pwszValidityPeriodString, pdwValidityPeriodCount, penumValidityPeriod); _PrintIfError2(hr, "infGetValidityPeriodSub", ERROR_LINE_NOT_FOUND); if (S_OK != hr) { // Try backwards: // [certsrv_server] // xxxxValidityPeriodUnits = Years (string) // xxxxValidityPeriod = 8 (count) hr = infGetValidityPeriodSub( hInf, FALSE, wszINFKEY_RENEWALVALIDITYPERIODSTRING, wszINFKEY_RENEWALVALIDITYPERIODCOUNT, pwszValidityPeriodString, pwszValidityPeriodCount, pdwValidityPeriodCount, penumValidityPeriod); _JumpIfError2( hr, error, "infGetValidityPeriodSub", ERROR_LINE_NOT_FOUND); if (NULL != pfSwap) { *pfSwap = TRUE; } } error: DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetValidityPeriod hr=%x --> c=%d, enum=%d\n", hr, *pdwValidityPeriodCount, *penumValidityPeriod)); return(hr); } //+------------------------------------------------------------------------ // myInfGetKeyList -- fetch a list of key values from CAPolicy.inf // // [pwszSection] // pwszKey = Value1 // pwszKey = Value2 // // Returns: double null terminated list of values //------------------------------------------------------------------------- HRESULT myInfGetKeyList( IN HINF hInf, IN WCHAR const *pwszSection, OPTIONAL IN WCHAR const *pwszKey, OPTIONAL WCHAR const * const *ppwszValidKeys, OPTIONAL OUT BOOL *pfCritical, OPTIONAL OUT WCHAR **ppwszz) { HRESULT hr; INFCONTEXT InfContext; DWORD iVal; DWORD cwc; WCHAR *pwsz; WCHAR **apwszVal = NULL; DWORD cVal; if (NULL != pfCritical) { *pfCritical = FALSE; } if (NULL != ppwszz) { *ppwszz = NULL; } myInfClearError(); cVal = 0; if (NULL == pwszKey) { if (NULL == ppwszValidKeys || NULL != pfCritical || NULL != ppwszz) { hr = E_INVALIDARG; _JumpErrorStr(hr, error, "NULL/non-NULL parms", pwszSection); } } else if (NULL == ppwszz) { hr = E_INVALIDARG; _JumpErrorStr(hr, error, "ppwszz parms", pwszKey); } if (NULL == hInf || INVALID_HANDLE_VALUE == hInf) { hr = E_HANDLE; _JumpError2(hr, error, "hInf", hr); } hr = infSetupFindFirstLine( hInf, pwszSection, pwszKey, FALSE, // fUniqueKey NULL == pwszKey? 0 : 1, // cValueMax ppwszValidKeys, FALSE, // fUniqueValidKeys &InfContext); if (S_OK != hr) { CSILOG(hr, IDS_ILOG_CAPOLICY_NOKEY, pwszSection, pwszKey, NULL); _JumpErrorStr3( hr, error, "infSetupFindFirstLine", pwszSection, S_FALSE, (HRESULT) ERROR_LINE_NOT_FOUND); } if (NULL != pwszKey) { for (;;) { cVal++; hr = infFindNextKey(pwszKey, &InfContext); if (S_FALSE == hr) { break; } _JumpIfErrorStr(hr, error, "infFindNextKey", pwszKey); } apwszVal = (WCHAR **) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, cVal * sizeof(apwszVal[0])); if (NULL == apwszVal) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } hr = infSetupFindFirstLine( hInf, pwszSection, pwszKey, FALSE, // fUniqueKey 1, // cValueMax NULL, // apwszKeys FALSE, // fUniqueValidKeys &InfContext); _JumpIfErrorStr(hr, error, "infSetupFindFirstLine", pwszSection); cwc = 1; iVal = 0; for (;;) { hr = infGetCurrentKeyValue( &InfContext, pwszSection, pwszKey, 1, TRUE, // fLastValue &apwszVal[iVal]); _JumpIfError(hr, error, "infGetCurrentKeyValue"); DBGPRINT((DBG_SS_CERTLIBI, "%ws = %ws\n", pwszKey, apwszVal[iVal])); cwc += wcslen(apwszVal[iVal]) + 1; iVal++; hr = infFindNextKey(pwszKey, &InfContext); if (S_FALSE == hr) { break; } _JumpIfErrorStr(hr, error, "infFindNextKey", pwszKey); } CSASSERT(iVal == cVal); *ppwszz = (WCHAR *) LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR)); if (NULL == *ppwszz) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } pwsz = *ppwszz; for (iVal = 0; iVal < cVal; iVal++) { wcscpy(pwsz, apwszVal[iVal]); pwsz += wcslen(pwsz) + 1; } *pwsz = L'\0'; CSASSERT(cwc == 1 + SAFE_SUBTRACT_POINTERS(pwsz, *ppwszz)); if (NULL != pfCritical) { hr = infGetCriticalFlag(hInf, pwszSection, FALSE, pfCritical); _JumpIfError(hr, error, "infGetCriticalFlag"); } } hr = S_OK; error: if (S_OK != hr) { INFSETERROR(hr, pwszSection, pwszKey, NULL); } if (NULL != apwszVal) { for (iVal = 0; iVal < cVal; iVal++) { if (NULL != apwszVal[iVal]) { LocalFree(apwszVal[iVal]); } } LocalFree(apwszVal); } return(hr); } //+------------------------------------------------------------------------ // myInfGetCRLDistributionPoints -- fetch CDP URLs from CAPolicy.inf // // [CRLDistributionPoint] // URL = http://CRLhttp.site.com/Public/MyCA.crl // URL = ftp://CRLftp.site.com/Public/MyCA.crl // // Returns: double null terminated list of CDP URLs //------------------------------------------------------------------------- HRESULT myInfGetCRLDistributionPoints( IN HINF hInf, OUT BOOL *pfCritical, OUT WCHAR **ppwszz) { HRESULT hr; static WCHAR const * const s_apwszKeys[] = { wszINFKEY_URL, wszINFKEY_CRITICAL, NULL }; hr = myInfGetKeyList( hInf, wszINFSECTION_CDP, wszINFKEY_URL, s_apwszKeys, pfCritical, ppwszz); _JumpIfErrorStr4( hr, error, "myInfGetKeyList", wszINFSECTION_CDP, ERROR_LINE_NOT_FOUND, S_FALSE, E_HANDLE); error: DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetCRLDistributionPoints hr=%x --> f=%d\n", hr, *pfCritical)); return(hr); } //+------------------------------------------------------------------------ // myInfGetAuthorityInformationAccess -- fetch AIA URLs from CAPolicy.inf // // [AuthorityInformationAccess] // URL = http://CRThttp.site.com/Public/MyCA.crt // URL = ftp://CRTftp.site.com/Public/MyCA.crt // // Returns: double null terminated list of AIA URLs //------------------------------------------------------------------------- HRESULT myInfGetAuthorityInformationAccess( IN HINF hInf, OUT BOOL *pfCritical, OUT WCHAR **ppwszz) { HRESULT hr; static WCHAR const * const s_apwszKeys[] = { wszINFKEY_URL, wszINFKEY_CRITICAL, NULL }; hr = myInfGetKeyList( hInf, wszINFSECTION_AIA, wszINFKEY_URL, s_apwszKeys, pfCritical, ppwszz); _JumpIfErrorStr4( hr, error, "myInfGetKeyList", wszINFSECTION_AIA, ERROR_LINE_NOT_FOUND, S_FALSE, E_HANDLE); error: DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetAuthorityInformationAccess hr=%x --> f=%d\n", hr, *pfCritical)); return(hr); } //+------------------------------------------------------------------------ // myInfGetEnhancedKeyUsage -- fetch EKU OIDS from CAPolicy.inf // // [EnhancedKeyUsage] // OID = 1.2.3.4.5 // OID = 1.2.3.4.6 // // Returns: double null terminated list of EKU OIDs //------------------------------------------------------------------------- HRESULT myInfGetEnhancedKeyUsage( IN HINF hInf, OUT BOOL *pfCritical, OUT WCHAR **ppwszz) { HRESULT hr; WCHAR *pwszz = NULL; WCHAR const *pwsz; static WCHAR const * const s_apwszKeys[] = { wszINFKEY_OID, wszINFKEY_CRITICAL, NULL }; *ppwszz = NULL; hr = myInfGetKeyList( hInf, wszINFSECTION_EKU, wszINFKEY_OID, s_apwszKeys, pfCritical, &pwszz); _JumpIfErrorStr4( hr, error, "myInfGetKeyList", wszINFSECTION_EKU, ERROR_LINE_NOT_FOUND, S_FALSE, E_HANDLE); for (pwsz = pwszz; L'\0' != *pwsz; pwsz += wcslen(pwsz) + 1) { hr = myVerifyObjId(pwsz); if (S_OK != hr) { INFSETERROR(hr, wszINFSECTION_EKU, wszINFKEY_OID, pwsz); _JumpErrorStr(hr, error, "myVerifyObjId", pwsz); } } *ppwszz = pwszz; pwszz = NULL; error: DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetEnhancedKeyUsage hr=%x --> f=%d\n", hr, *pfCritical)); if (NULL != pwszz) { LocalFree(pwszz); } return(hr); } //+-------------------------------------------------------------------------- // infGetBasicConstraints2CAExtension -- fetch basic constraints extension // from INF file, setting the SubjectType flag to CA // // If the INF handle is bad, or if the INF section does not exist, construct // a default extension only if fDefault is set, otherwise fail. // // [BasicConstraintsExtension] // ; Subject Type is not supported -- always set to CA // ; maximum subordinate CA path length // PathLength = 3 // // Return S_OK if extension has been constructed from INF file. // // Returns: encoded basic constraints extension //+-------------------------------------------------------------------------- HRESULT infGetBasicConstraints2CAExtension( IN BOOL fDefault, IN HINF hInf, OUT CERT_EXTENSION *pext) { HRESULT hr; CERT_BASIC_CONSTRAINTS2_INFO bc2i; INFCONTEXT InfContext; static WCHAR const * const s_apwszKeys[] = { wszINFKEY_PATHLENGTH, wszINFKEY_CRITICAL, NULL }; ZeroMemory(pext, sizeof(*pext)); ZeroMemory(&bc2i, sizeof(bc2i)); myInfClearError(); pext->fCritical = TRUE; // default value for both INF and default cases if (NULL == hInf || INVALID_HANDLE_VALUE == hInf) { if (!fDefault) { hr = E_HANDLE; _JumpError2(hr, error, "hInf", hr); } } else { hr = infSetupFindFirstLine( hInf, wszINFSECTION_BASICCONSTRAINTS, NULL, // pwszKey FALSE, // fUniqueKey 0, // cValueMax s_apwszKeys, TRUE, // fUniqueValidKeys &InfContext); if (S_OK != hr) { if (!fDefault) { _JumpErrorStr3( hr, error, "infSetupFindFirstLine", wszINFSECTION_BASICCONSTRAINTS, S_FALSE, (HRESULT) ERROR_LINE_NOT_FOUND); } } else { hr = infGetCriticalFlag( hInf, wszINFSECTION_BASICCONSTRAINTS, pext->fCritical, // fDefault &pext->fCritical); _JumpIfError(hr, error, "infGetCriticalFlag"); bc2i.fPathLenConstraint = TRUE; hr = myInfGetNumericKeyValue( hInf, TRUE, // fLog wszINFSECTION_BASICCONSTRAINTS, wszINFKEY_PATHLENGTH, 1, TRUE, // fLastValue &bc2i.dwPathLenConstraint); if (S_OK != hr) { _PrintErrorStr2( hr, "myInfGetNumericKeyValue", wszINFKEY_PATHLENGTH, ERROR_LINE_NOT_FOUND); if ((HRESULT) ERROR_LINE_NOT_FOUND != hr) { goto error; } bc2i.dwPathLenConstraint = 0; bc2i.fPathLenConstraint = FALSE; } } } bc2i.fCA = TRUE; if (!myEncodeObject( X509_ASN_ENCODING, X509_BASIC_CONSTRAINTS2, &bc2i, 0, CERTLIB_USE_LOCALALLOC, &pext->Value.pbData, &pext->Value.cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } hr = S_OK; error: if (S_OK != hr) { INFSETERROR(hr, wszINFSECTION_BASICCONSTRAINTS, NULL, NULL); } pext->pszObjId = szOID_BASIC_CONSTRAINTS2; // on error, too! DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetBasicConstraints2CAExtension hr=%x --> f=%d, cb=%x\n", hr, pext->fCritical, pext->Value.cbData)); return(hr); } //+-------------------------------------------------------------------------- // myInfGetBasicConstraints2CAExtension -- fetch basic constraints extension // from INF file, setting the SubjectType flag to CA // // [BasicConstraintsExtension] // ; Subject Type is not supported -- always set to CA // ; maximum subordinate CA path length // PathLength = 3 // // Return S_OK if extension has been constructed from INF file. // // Returns: encoded basic constraints extension //+-------------------------------------------------------------------------- FNMYINFGETEXTENSION myInfGetBasicConstraints2CAExtension; HRESULT myInfGetBasicConstraints2CAExtension( IN HINF hInf, OUT CERT_EXTENSION *pext) { HRESULT hr; hr = infGetBasicConstraints2CAExtension(FALSE, hInf, pext); _JumpIfError3( hr, error, "infGetBasicConstraints2CAExtension", S_FALSE, (HRESULT) ERROR_LINE_NOT_FOUND); error: return(hr); } FNMYINFGETEXTENSION myInfGetBasicConstraints2CAExtensionOrDefault; HRESULT myInfGetBasicConstraints2CAExtensionOrDefault( IN HINF hInf, OUT CERT_EXTENSION *pext) { HRESULT hr; hr = infGetBasicConstraints2CAExtension(TRUE, hInf, pext); _JumpIfError(hr, error, "infGetBasicConstraints2CAExtension"); error: return(hr); } //+-------------------------------------------------------------------------- // myInfGetEnhancedKeyUsageExtension -- fetch EKU extension from INF file // // [EnhancedKeyUsageExtension] // OID = 1.2.3.4.5 // OID = 1.2.3.4.6 // // Return S_OK if extension has been constructed from INF file. // Return S_FALSE if empty section detected in INF file // Return other error if no section detected in INF file // // Returns: encoded enhanced key usage extension //+-------------------------------------------------------------------------- FNMYINFGETEXTENSION myInfGetEnhancedKeyUsageExtension; HRESULT myInfGetEnhancedKeyUsageExtension( IN HINF hInf, OUT CERT_EXTENSION *pext) { HRESULT hr; DWORD i; DWORD cEKU = 0; WCHAR *pwszzEKU = NULL; WCHAR *pwszCurrentEKU; CERT_ENHKEY_USAGE ceku; ceku.rgpszUsageIdentifier = NULL; ceku.cUsageIdentifier = 0; ZeroMemory(pext, sizeof(*pext)); myInfClearError(); if (NULL == hInf || INVALID_HANDLE_VALUE == hInf) { hr = E_HANDLE; _JumpError2(hr, error, "hInf", hr); } hr = myInfGetEnhancedKeyUsage(hInf, &pext->fCritical, &pwszzEKU); _JumpIfError3( hr, error, "myInfGetEnhancedKeyUsage", S_FALSE, (HRESULT) ERROR_LINE_NOT_FOUND); pwszCurrentEKU = pwszzEKU; if (NULL != pwszCurrentEKU) { while (L'\0' != *pwszCurrentEKU) { cEKU++; pwszCurrentEKU += wcslen(pwszCurrentEKU) + 1; } } if (0 == cEKU) { hr = S_FALSE; goto error; } ceku.cUsageIdentifier = cEKU; ceku.rgpszUsageIdentifier = (char **) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, sizeof(ceku.rgpszUsageIdentifier[0]) * cEKU); if (NULL == ceku.rgpszUsageIdentifier) { hr = E_OUTOFMEMORY; _JumpIfError(hr, error, "LocalAlloc"); } cEKU = 0; pwszCurrentEKU = pwszzEKU; while (L'\0' != *pwszCurrentEKU) { if (!ConvertWszToSz(&ceku.rgpszUsageIdentifier[cEKU], pwszCurrentEKU, -1)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "ConvertWszToSz"); } cEKU++; pwszCurrentEKU += wcslen(pwszCurrentEKU) + 1; } CSASSERT(ceku.cUsageIdentifier == cEKU); if (!myEncodeObject( X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, &ceku, 0, CERTLIB_USE_LOCALALLOC, &pext->Value.pbData, &pext->Value.cbData)) { hr = myHLastError(); _JumpIfError(hr, error, "myEncodeObject"); } error: if (S_OK != hr && E_HANDLE != hr) { INFSETERROR(hr, wszINFSECTION_EKU, NULL, NULL); } pext->pszObjId = szOID_ENHANCED_KEY_USAGE; // on error, too! DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetEnhancedKeyUsageExtension hr=%x --> f=%d, cb=%x\n", hr, pext->fCritical, pext->Value.cbData)); if (NULL != ceku.rgpszUsageIdentifier) { for (i = 0; i < ceku.cUsageIdentifier; i++) { if (NULL != ceku.rgpszUsageIdentifier[i]) { LocalFree(ceku.rgpszUsageIdentifier[i]); } } LocalFree(ceku.rgpszUsageIdentifier); } if (NULL != pwszzEKU) { LocalFree(pwszzEKU); } return(hr); } HRESULT infAddAttribute( IN CRYPT_ATTR_BLOB const *pAttribute, IN OUT DWORD *pcAttribute, IN OUT CRYPT_ATTR_BLOB **ppaAttribute) { HRESULT hr; CRYPT_ATTR_BLOB *pAttribT; if (NULL == *ppaAttribute) { CSASSERT(0 == *pcAttribute); pAttribT = (CRYPT_ATTR_BLOB *) LocalAlloc( LMEM_FIXED, sizeof(**ppaAttribute)); } else { CSASSERT(0 != *pcAttribute); pAttribT = (CRYPT_ATTR_BLOB *) LocalReAlloc( *ppaAttribute, (*pcAttribute + 1) * sizeof(**ppaAttribute), LMEM_MOVEABLE); } if (NULL == pAttribT) { hr = E_OUTOFMEMORY; _JumpError( hr, error, NULL == *ppaAttribute? "LocalAlloc" : "LocalReAlloc"); } *ppaAttribute = pAttribT; pAttribT[(*pcAttribute)++] = *pAttribute; hr = S_OK; error: return(hr); } VOID myInfFreeRequestAttributes( IN DWORD cAttribute, IN OUT CRYPT_ATTR_BLOB *paAttribute) { if (NULL != paAttribute) { DWORD i; for (i = 0; i < cAttribute; i++) { if (NULL != paAttribute[i].pbData) { LocalFree(paAttribute[i].pbData); } } LocalFree(paAttribute); } } //+------------------------------------------------------------------------ // myInfGetRequestAttributes -- fetch request attributes from INF file // // [RequestAttributes] // AttributeName1 = AttributeValue1 // AttributeName2 = AttributeValue2 // ... // AttributeNameN = AttributeValueN // // Returns: array of encoded attribute blobs //------------------------------------------------------------------------- HRESULT myInfGetRequestAttributes( IN HINF hInf, OUT DWORD *pcAttribute, OUT CRYPT_ATTR_BLOB **ppaAttribute, OUT WCHAR **ppwszTemplateName) { HRESULT hr; INFCONTEXT InfContext; WCHAR *pwszName = NULL; WCHAR *pwszValue = NULL; DWORD cAttribute = 0; CRYPT_ATTR_BLOB Attribute; WCHAR *pwszTemplateName = NULL; *ppwszTemplateName = NULL; *pcAttribute = 0; *ppaAttribute = NULL; Attribute.pbData = NULL; myInfClearError(); if (NULL == hInf || INVALID_HANDLE_VALUE == hInf) { hr = E_HANDLE; _JumpError2(hr, error, "hInf", hr); } hr = infSetupFindFirstLine( hInf, wszINFSECTION_REQUESTATTRIBUTES, NULL, // pwszKey FALSE, // fUniqueKey 0, // cValueMax NULL, // apwszKeys FALSE, // fUniqueValidKeys &InfContext); _JumpIfErrorStr2( hr, error, "infSetupFindFirstLine", wszINFSECTION_REQUESTATTRIBUTES, ERROR_LINE_NOT_FOUND); for (;;) { CRYPT_ENROLLMENT_NAME_VALUE_PAIR NamePair; if (NULL != pwszName) { LocalFree(pwszName); pwszName = NULL; } if (NULL != pwszValue) { LocalFree(pwszValue); pwszValue = NULL; } hr = infGetCurrentKeyValue( &InfContext, wszINFSECTION_REQUESTATTRIBUTES, NULL, // pwszKey 0, FALSE, // fLastValue &pwszName); _JumpIfError(hr, error, "infGetCurrentKeyValue"); hr = infGetCurrentKeyValue( &InfContext, wszINFSECTION_REQUESTATTRIBUTES, pwszName, 1, TRUE, // fLastValue &pwszValue); _JumpIfError(hr, error, "infGetCurrentKeyValue"); //wprintf(L"%ws = %ws\n", pwszName, pwszValue); NamePair.pwszName = pwszName; NamePair.pwszValue = pwszValue; if (0 == LSTRCMPIS(pwszName, wszPROPCERTTEMPLATE)) { if (NULL != pwszTemplateName) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "Duplicate cert template"); } pwszTemplateName = pwszValue; pwszValue = NULL; } if (!myEncodeObject( X509_ASN_ENCODING, // X509__ENROLLMENT_NAME_VALUE_PAIR szOID_ENROLLMENT_NAME_VALUE_PAIR, &NamePair, 0, CERTLIB_USE_LOCALALLOC, &Attribute.pbData, &Attribute.cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } hr = infAddAttribute(&Attribute, &cAttribute, ppaAttribute); _JumpIfError(hr, error, "infAddAttribute"); Attribute.pbData = NULL; if (!SetupFindNextLine(&InfContext, &InfContext)) { hr = myHLastError(); _PrintError2(hr, "SetupFindNextLine(end)", hr); break; } } *pcAttribute = cAttribute; cAttribute = 0; *ppwszTemplateName = pwszTemplateName; pwszTemplateName = NULL; hr = S_OK; error: if (S_OK != hr) { INFSETERROR(hr, wszINFSECTION_REQUESTATTRIBUTES, pwszName, pwszValue); } if (NULL != pwszName) { LocalFree(pwszName); } if (NULL != pwszValue) { LocalFree(pwszValue); } if (NULL != pwszTemplateName) { LocalFree(pwszTemplateName); } if (NULL != Attribute.pbData) { LocalFree(Attribute.pbData); } if (0 != cAttribute) { myInfFreeRequestAttributes(cAttribute, *ppaAttribute); *ppaAttribute = NULL; } DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetRequestAttributes hr=%x --> c=%d\n", hr, *pcAttribute)); return(hr); } typedef struct _SUBTREEINFO { BOOL fEmptyDefault; DWORD dwInfMinMaxIndexBase; DWORD dwAltNameChoice; WCHAR const *pwszKey; } SUBTREEINFO; SUBTREEINFO g_aSubTreeInfo[] = { { TRUE, 2, CERT_ALT_NAME_OTHER_NAME, wszINFKEY_UPN }, { TRUE, 2, CERT_ALT_NAME_RFC822_NAME, wszINFKEY_EMAIL }, { TRUE, 2, CERT_ALT_NAME_DNS_NAME, wszINFKEY_DNS }, { TRUE, 2, CERT_ALT_NAME_DIRECTORY_NAME, wszINFKEY_DIRECTORYNAME }, { TRUE, 2, CERT_ALT_NAME_URL, wszINFKEY_URL }, { TRUE, 3, CERT_ALT_NAME_IP_ADDRESS, wszINFKEY_IPADDRESS }, { FALSE, 2, CERT_ALT_NAME_REGISTERED_ID, wszINFKEY_REGISTEREDID }, { FALSE, 3, CERT_ALT_NAME_OTHER_NAME, wszINFKEY_OTHERNAME }, }; #define CSUBTREEINFO ARRAYSIZE(g_aSubTreeInfo) #define STII_OTHERNAMEUPN 0 // CERT_ALT_NAME_OTHER_NAME pOtherName #define STII_RFC822NAME 1 // CERT_ALT_NAME_RFC822_NAME pwszRfc822Name #define STII_DNSNAME 2 // CERT_ALT_NAME_DNS_NAME pwszDNSName #define STII_DIRECTORYNAME 3 // CERT_ALT_NAME_DIRECTORY_NAME DirectoryName #define STII_URL 4 // CERT_ALT_NAME_URL pwszURL #define STII_IPADDRESS 5 // CERT_ALT_NAME_IP_ADDRESS IPAddress #define STII_REGISTEREDID 6 // CERT_ALT_NAME_REGISTERED_ID pszRegisteredID #define STII_OTHERNAMEOID 7 // CERT_ALT_NAME_OTHER_NAME pOtherName VOID infFreeGeneralSubTreeElement( IN OUT CERT_GENERAL_SUBTREE *pSubTree) { CERT_ALT_NAME_ENTRY *pName = &pSubTree->Base; VOID **ppv = NULL; DBGPRINT(( DBG_SS_CERTLIBI, "infFreeGeneralSubTreeElement: p=%x, choice=%x\n", pSubTree, pName->dwAltNameChoice)); switch (pName->dwAltNameChoice) { case CERT_ALT_NAME_OTHER_NAME: ppv = (VOID **) &pName->pOtherName; if (NULL != pName->pOtherName) { if (NULL != pName->pOtherName->Value.pbData) { DBGPRINT(( DBG_SS_CERTLIBI, "infFreeGeneralSubTreeElement: p=%x, Free(other.pbData=%x)\n", pSubTree, pName->pOtherName->Value.pbData)); LocalFree(pName->pOtherName->Value.pbData); } if (NULL != pName->pOtherName->pszObjId) { DBGPRINT(( DBG_SS_CERTLIBI, "infFreeGeneralSubTreeElement: p=%x, Free(other.pszObjId=%x)\n", pSubTree, pName->pOtherName->pszObjId)); LocalFree(pName->pOtherName->pszObjId); } } break; case CERT_ALT_NAME_RFC822_NAME: ppv = (VOID **) &pName->pwszRfc822Name; break; case CERT_ALT_NAME_DNS_NAME: ppv = (VOID **) &pName->pwszDNSName; break; case CERT_ALT_NAME_DIRECTORY_NAME: ppv = (VOID **) &pName->DirectoryName.pbData; break; case CERT_ALT_NAME_URL: ppv = (VOID **) &pName->pwszURL; break; case CERT_ALT_NAME_IP_ADDRESS: ppv = (VOID **) &pName->IPAddress.pbData; break; case CERT_ALT_NAME_REGISTERED_ID: ppv = (VOID **) &pName->pszRegisteredID; break; } if (NULL != ppv && NULL != *ppv) { DBGPRINT(( DBG_SS_CERTLIBI, "infFreeGeneralSubTreeElement: p=%x, Free(pv=%x)\n", pSubTree, *ppv)); LocalFree(*ppv); } } VOID infFreeGeneralSubTree( IN DWORD cSubTree, IN OUT CERT_GENERAL_SUBTREE *pSubTree) { if (NULL != pSubTree) { DWORD i; DBGPRINT(( DBG_SS_CERTLIBI, "infFreeGeneralSubTree: p=%x, c=%x\n", pSubTree, cSubTree)); for (i = 0; i < cSubTree; i++) { infFreeGeneralSubTreeElement(&pSubTree[i]); } LocalFree(pSubTree); } } HRESULT infParseIPAddressAndMask( IN WCHAR const *pwszIPAddress, IN WCHAR const *pwszIPAddressMask, OUT BYTE **ppbData, OUT DWORD *pcbData) { HRESULT hr; BYTE ab[2 * CB_IPV6ADDRESS]; DWORD cb; DWORD cbAddress; DWORD cbMask=0; WCHAR const *pwszValue = pwszIPAddress; *ppbData = NULL; *pcbData = 0; // if pwszValue is an empty string, encode zero length blob. cb = 0; if (L'\0' != *pwszIPAddress && L'\0' != *pwszIPAddressMask) { cbAddress = sizeof(ab) / 2; hr = myParseIPAddress(pwszIPAddress, ab, &cbAddress); _JumpIfError(hr, error, "myParseIPAddress"); if (L'\0' != *pwszIPAddressMask) { cbMask = sizeof(ab) / 2; pwszValue = pwszIPAddressMask; hr = myParseIPAddress(pwszIPAddressMask, &ab[cbAddress], &cbMask); _JumpIfError(hr, error, "infParseIPMask"); } if (cbAddress != cbMask) { hr = E_INVALIDARG; _JumpError(hr, error, "address and mask lengths differ"); } cb = cbAddress + cbMask; } else if (L'\0' != *pwszIPAddress || L'\0' != *pwszIPAddressMask) { hr = E_INVALIDARG; _JumpError(hr, error, "address or mask missing"); } *ppbData = (BYTE *) LocalAlloc(LMEM_FIXED, cb); if (NULL == *ppbData) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } *pcbData = cb; CopyMemory(*ppbData, ab, cb); DBGDUMPHEX(( DBG_SS_CERTLIBI, DH_NOADDRESS | DH_NOTABPREFIX | 8, *ppbData, *pcbData)); hr = S_OK; error: if (S_OK != hr) { INFSETERROR(hr, NULL, NULL, pwszValue); } return(hr); } HRESULT infVerifySubtreeElement( IN CERT_GENERAL_SUBTREE const *pSubTree) { HRESULT hr; BYTE *pb = NULL; DWORD cb; CERT_NAME_CONSTRAINTS_INFO *pnci = NULL; CERT_NAME_CONSTRAINTS_INFO nci; ZeroMemory(&nci, sizeof(nci)); nci.cPermittedSubtree = 1; nci.rgPermittedSubtree = const_cast(pSubTree); if (!myEncodeObject( X509_ASN_ENCODING, X509_NAME_CONSTRAINTS, &nci, 0, CERTLIB_USE_LOCALALLOC, &pb, &cb)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } if (!myDecodeObject( X509_ASN_ENCODING, X509_NAME_CONSTRAINTS, pb, cb, CERTLIB_USE_LOCALALLOC, (VOID **) &pnci, &cb)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeObject"); } hr = S_OK; error: if (NULL != pb) { LocalFree(pb); } if (NULL != pnci) { LocalFree(pnci); } return(hr); } // [NameConstraintsPermitted]/[NameConstraintsExcluded] // ; the numeric second and third arguments are optional // ; when present, the second argument is the minimum depth // ; when present, the third argument is the maximum depth // ; The IETF recommends against specifying dwMinimum & dwMaximum // DNS = foo@domain.com // DNS = domain1.domain.com, 3, 6 HRESULT infBuildSubTreeElement( IN OUT INFCONTEXT *pInfContext, OPTIONAL IN WCHAR const *pwszEmptyEntry, // NULL means read INF file IN DWORD iSubTreeInfo, OPTIONAL OUT CERT_GENERAL_SUBTREE *pSubTree) { HRESULT hr; SUBTREEINFO const *pSubTreeInfo = &g_aSubTreeInfo[iSubTreeInfo]; CERT_GENERAL_SUBTREE SubTree; WCHAR *pwszValueRead = NULL; WCHAR *pwszValueRead2 = NULL; WCHAR const *pwszValue = NULL; WCHAR const *pwszValue2; ZeroMemory(&SubTree, sizeof(SubTree)); if (NULL != pSubTree) { ZeroMemory(pSubTree, sizeof(*pSubTree)); } // If pwszEmptyEntry is NULL, read the value from the INF file. // Otherwise, encode the specified (empty string) value. if (NULL == pwszEmptyEntry) { INT Value; hr = infGetCurrentKeyValue( pInfContext, NULL, // pwszSection NULL, // pwszKey 1, 2 == pSubTreeInfo->dwInfMinMaxIndexBase, // fLastValue &pwszValueRead); _JumpIfError(hr, error, "infGetCurrentKeyValue"); pwszValue = pwszValueRead; if (!SetupGetIntField( pInfContext, pSubTreeInfo->dwInfMinMaxIndexBase, &Value)) { hr = myHLastError(); _PrintError2(hr, "SetupGetIntField:2", hr); Value = 0; } SubTree.dwMinimum = Value; SubTree.fMaximum = TRUE; if (!SetupGetIntField( pInfContext, pSubTreeInfo->dwInfMinMaxIndexBase + 1, &Value)) { hr = myHLastError(); _PrintError2(hr, "SetupGetIntField:3", hr); Value = 0; SubTree.fMaximum = FALSE; } SubTree.dwMaximum = Value; } else { pwszValue = pwszEmptyEntry; } SubTree.Base.dwAltNameChoice = pSubTreeInfo->dwAltNameChoice; if (NULL != pSubTree) { WCHAR **ppwsz = NULL; CSASSERT(CSUBTREEINFO > iSubTreeInfo); switch (iSubTreeInfo) { case STII_OTHERNAMEUPN: case STII_OTHERNAMEOID: { CERT_NAME_VALUE nameUpn; SubTree.Base.pOtherName = (CERT_OTHER_NAME *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, sizeof(*SubTree.Base.pOtherName)); if (NULL == SubTree.Base.pOtherName) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } if (STII_OTHERNAMEUPN == iSubTreeInfo) { hr = myDupStringA( szOID_NT_PRINCIPAL_NAME, &SubTree.Base.pOtherName->pszObjId); _JumpIfError(hr, error, "myDupStringA"); nameUpn.dwValueType = CERT_RDN_UTF8_STRING; nameUpn.Value.pbData = (BYTE *) pwszValue; nameUpn.Value.cbData = 0; if (!myEncodeObject( X509_ASN_ENCODING, X509_UNICODE_ANY_STRING, &nameUpn, 0, CERTLIB_USE_LOCALALLOC, &SubTree.Base.pOtherName->Value.pbData, &SubTree.Base.pOtherName->Value.cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } } else { hr = myVerifyObjId(pwszValue); _JumpIfErrorStr(hr, error, "myVerifyObjId", pwszValue); if (!myConvertWszToSz( &SubTree.Base.pOtherName->pszObjId, pwszValue, -1)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "ConvertWszToSz"); } if (NULL == pwszEmptyEntry) { hr = infGetCurrentKeyValue( pInfContext, NULL, // pwszSection NULL, // pwszKey 2, TRUE, // fLastValue &pwszValueRead2); _PrintIfError2(hr, "infGetCurrentKeyValue", hr); pwszValue2 = pwszValueRead2; } else { pwszValue2 = pwszEmptyEntry; } if (NULL == pwszValue2 || L'\0' == *pwszValue2) { pwszValue2 = wszPROPOCTETTAG; } hr = myEncodeOtherNameBinary( pwszValue2, &SubTree.Base.pOtherName->Value.pbData, &SubTree.Base.pOtherName->Value.cbData); _JumpIfError(hr, error, "myEncodeOtherNameBinary"); } DBGPRINT(( DBG_SS_CERTLIBI, "infBuildSubTreeElement: p=%x, OtherName=%x,%x,%x\n", pSubTree, SubTree.Base.pOtherName, SubTree.Base.pOtherName->pszObjId, SubTree.Base.pOtherName->Value.pbData)); break; } case STII_RFC822NAME: ppwsz = &SubTree.Base.pwszRfc822Name; break; case STII_DNSNAME: ppwsz = &SubTree.Base.pwszDNSName; break; case STII_DIRECTORYNAME: hr = myCertStrToName( X509_ASN_ENCODING, pwszValue, // pszX500 CERT_NAME_STR_REVERSE_FLAG, NULL, // pvReserved &SubTree.Base.DirectoryName.pbData, &SubTree.Base.DirectoryName.cbData, NULL); // ppszError _JumpIfError(hr, error, "myCertStrToName"); DBGPRINT(( DBG_SS_CERTLIBI, "infBuildSubTreeElement: p=%x, DirName=%x\n", pSubTree, SubTree.Base.DirectoryName.pbData)); break; case STII_URL: ppwsz = &SubTree.Base.pwszURL; break; case STII_IPADDRESS: // convert INF string value to binary IP Address if (NULL == pwszEmptyEntry) { hr = infGetCurrentKeyValue( pInfContext, NULL, // pwszSection NULL, // pwszKey 2, TRUE, // fLastValue &pwszValueRead2); _JumpIfError(hr, error, "infGetCurrentKeyValue"); pwszValue2 = pwszValueRead2; } else { pwszValue2 = pwszEmptyEntry; } hr = infParseIPAddressAndMask( pwszValue, pwszValue2, &SubTree.Base.IPAddress.pbData, &SubTree.Base.IPAddress.cbData); _JumpIfError(hr, error, "infParseIPAddressAndMask"); DBGPRINT(( DBG_SS_CERTLIBI, "infBuildSubTreeElement: p=%x, IPAddress=%x\n", pSubTree, SubTree.Base.IPAddress.pbData)); break; case STII_REGISTEREDID: if (!myConvertWszToSz( &SubTree.Base.pszRegisteredID, pwszValue, -1)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "ConvertWszToSz"); } DBGPRINT(( DBG_SS_CERTLIBI, "infBuildSubTreeElement: p=%x, psz=%x\n", pSubTree, SubTree.Base.pszRegisteredID)); break; } if (NULL != ppwsz) { hr = myDupString(pwszValue, ppwsz); _JumpIfError(hr, error, "myDupString"); DBGPRINT(( DBG_SS_CERTLIBI, "infBuildSubTreeElement: p=%x, pwsz=%x\n", pSubTree, *ppwsz)); } hr = infVerifySubtreeElement(&SubTree); _JumpIfErrorStr(hr, error, "infVerifySubTreeElement", pSubTreeInfo->pwszKey); *pSubTree = SubTree; ZeroMemory(&SubTree, sizeof(SubTree)); } hr = S_OK; error: if (S_OK != hr) { INFSETERROR(hr, NULL, NULL, pwszValue); } infFreeGeneralSubTreeElement(&SubTree); if (NULL != pwszValueRead) { LocalFree(pwszValueRead); } if (NULL != pwszValueRead2) { LocalFree(pwszValueRead2); } return(hr); } HRESULT infGetGeneralSubTreeByType( IN HINF hInf, IN WCHAR const *pwszSection, OPTIONAL IN WCHAR const *pwszEmptyEntry, IN DWORD iSubTreeInfo, IN OUT DWORD *pcName, OPTIONAL OUT CERT_GENERAL_SUBTREE *pSubTree) { HRESULT hr; SUBTREEINFO const *pSubTreeInfo = &g_aSubTreeInfo[iSubTreeInfo]; INFCONTEXT InfContext; DWORD iName; DWORD cName = MAXDWORD; BOOL fIgnore = FALSE; if (NULL == pSubTree) { *pcName = 0; } else { cName = *pcName; } // If pwszEmptyEntry is NULL, read the value from the INF file. // Otherwise, encode the specified (empty string) value. if (!SetupFindFirstLine( hInf, pwszSection, pSubTreeInfo->pwszKey, &InfContext)) { hr = myHLastError(); _PrintErrorStr2( hr, "SetupFindFirstLine", pwszSection, ERROR_LINE_NOT_FOUND); // INF file entry does not exist. Create an empty name constraints // entry only if asked to do so (if pwszEmptyEntry is non-NULL). if (NULL == pwszEmptyEntry) { fIgnore = (HRESULT) ERROR_LINE_NOT_FOUND == hr; goto error; } } else { // INF file entry exists; don't create an empty name constraints entry. pwszEmptyEntry = NULL; } for (iName = 0; ; ) { CSASSERT(NULL == pSubTree || iName < cName); hr = infBuildSubTreeElement( &InfContext, pwszEmptyEntry, iSubTreeInfo, pSubTree); _JumpIfErrorStr(hr, error, "infBuildSubTreeElement", pSubTreeInfo->pwszKey); DBGPRINT(( DBG_SS_CERTLIBI, "infBuildSubTreeElement: &p[%u]=%x, type=%ws\n", iName, pSubTree, pSubTreeInfo->pwszKey)); iName++; if (NULL != pSubTree) { pSubTree++; } if (NULL == pwszEmptyEntry) { hr = infFindNextKey(pSubTreeInfo->pwszKey, &InfContext); } else { hr = S_FALSE; } if (S_FALSE == hr) { break; } _JumpIfErrorStr(hr, error, "infFindNextKey", pSubTreeInfo->pwszKey); } DBGPRINT(( DBG_SS_CERTLIBI, "infGetGeneralSubTreeByType: i=%x, c=%x\n", iName, cName)); CSASSERT(NULL == pSubTree || iName <= cName); *pcName = iName; hr = S_OK; error: if (S_OK != hr && !fIgnore) { INFSETERROR(hr, pwszSection, pSubTreeInfo->pwszKey, NULL); } return(hr); } HRESULT infGetGeneralSubTree( IN HINF hInf, IN WCHAR const *pwszSection, IN WCHAR const *pwszKey, // key value is sub-section name OPTIONAL IN WCHAR const *pwszEmptyEntry, OUT DWORD *pcSubTree, OUT CERT_GENERAL_SUBTREE **ppSubTree) { HRESULT hr; WCHAR *pwszSubTreeSection = NULL; DWORD cSubTree = 0; CERT_GENERAL_SUBTREE *rgSubTree = NULL; CERT_GENERAL_SUBTREE *pSubTree; DWORD iSubTreeInfo; DWORD count; DWORD cRemain; SUBTREEINFO const *pSubTreeInfo; INFCONTEXT InfContext; static WCHAR const * const s_apwszKeys[] = { wszINFKEY_UPN, wszINFKEY_EMAIL, wszINFKEY_DNS, wszINFKEY_DIRECTORYNAME, wszINFKEY_URL, wszINFKEY_IPADDRESS, wszINFKEY_REGISTEREDID, wszINFKEY_OTHERNAME, NULL }; *pcSubTree = 0; *ppSubTree = NULL; CSASSERT(NULL != hInf && INVALID_HANDLE_VALUE != hInf); hr = myInfGetKeyValue( hInf, TRUE, // fLog pwszSection, pwszKey, 1, TRUE, // fLastValue &pwszSubTreeSection); _JumpIfErrorStr2( hr, error, "myInfGetKeyValue", pwszKey, ERROR_LINE_NOT_FOUND); hr = infSetupFindFirstLine( hInf, pwszSubTreeSection, NULL, // pwszKey FALSE, // fUniqueKey 0, // cValueMax s_apwszKeys, FALSE, // fUniqueValidKeys &InfContext); // if S_FALSE is returned if the section is empty. In this case, // allow execution to continue, to fill in the empty permitted names. if (S_OK != hr && S_FALSE != hr) { if ((HRESULT) ERROR_LINE_NOT_FOUND == hr) { hr = SPAPI_E_LINE_NOT_FOUND; // don't ignore this error } INFSETERROR(hr, pwszSubTreeSection, L"", L""); _JumpErrorStr(hr, error, "infSetupFindFirstLine", pwszSubTreeSection); } for (iSubTreeInfo = 0; iSubTreeInfo < CSUBTREEINFO; iSubTreeInfo++) { pSubTreeInfo = &g_aSubTreeInfo[iSubTreeInfo]; hr = infGetGeneralSubTreeByType( hInf, pwszSubTreeSection, pSubTreeInfo->fEmptyDefault? pwszEmptyEntry : NULL, iSubTreeInfo, &count, NULL); DBGPRINT(( DBG_SS_CERTLIBI, "infGetGeneralSubTreeByType(%ws, %ws, NULL) -> hr=%x, c=%x\n", pwszSubTreeSection, pSubTreeInfo->pwszKey, hr, count)); if (S_OK != hr) { _PrintErrorStr2( hr, "infGetGeneralSubTreeByType", pSubTreeInfo->pwszKey, ERROR_LINE_NOT_FOUND); if ((HRESULT) ERROR_LINE_NOT_FOUND != hr) { _JumpErrorStr( hr, error, "infGetGeneralSubTreeByType", pSubTreeInfo->pwszKey); } count = 0; } cSubTree += count; } if (0 == cSubTree) { hr = SPAPI_E_LINE_NOT_FOUND; // don't ignore this error INFSETERROR(hr, pwszSubTreeSection, L"", L""); _JumpErrorStr(hr, error, "infSetupFindFirstLine", pwszSubTreeSection); } rgSubTree = (CERT_GENERAL_SUBTREE *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, cSubTree * sizeof(rgSubTree[0])); if (NULL == rgSubTree) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } DBGPRINT(( DBG_SS_CERTLIBI, "infGetGeneralSubTree: rg=%x, total=%x\n", rgSubTree, cSubTree)); pSubTree = rgSubTree; cRemain = cSubTree; for (iSubTreeInfo = 0; iSubTreeInfo < CSUBTREEINFO; iSubTreeInfo++) { pSubTreeInfo = &g_aSubTreeInfo[iSubTreeInfo]; count = cRemain; hr = infGetGeneralSubTreeByType( hInf, pwszSubTreeSection, pSubTreeInfo->fEmptyDefault? pwszEmptyEntry : NULL, iSubTreeInfo, &count, pSubTree); DBGPRINT(( DBG_SS_CERTLIBI, "infGetGeneralSubTreeByType(%ws, %ws, &p[%x]=%x) -> hr=%x, c=%x\n", pwszSubTreeSection, pSubTreeInfo->pwszKey, SAFE_SUBTRACT_POINTERS(pSubTree, rgSubTree), pSubTree, hr, count)); if (S_OK != hr) { _PrintErrorStr2( hr, "infGetGeneralSubTreeByType", pSubTreeInfo->pwszKey, ERROR_LINE_NOT_FOUND); if ((HRESULT) ERROR_LINE_NOT_FOUND != hr) { _JumpErrorStr( hr, error, "infGetGeneralSubTreeByType", pSubTreeInfo->pwszKey); } if (0 < cRemain) { ZeroMemory(pSubTree, sizeof(*pSubTree)); } count = 0; } cRemain -= count; pSubTree += count; } CSASSERT(0 == cRemain); *pcSubTree = cSubTree; *ppSubTree = rgSubTree; rgSubTree = NULL; hr = S_OK; error: if (S_OK != hr) { INFSETERROR(hr, pwszSection, pwszKey, pwszSubTreeSection); } if (NULL != pwszSubTreeSection) { LocalFree(pwszSubTreeSection); } if (NULL != rgSubTree) { infFreeGeneralSubTree(cSubTree, rgSubTree); } return(hr); } //+------------------------------------------------------------------------ // myInfGetNameConstraintsExtension -- fetch name constraints extension from INF file // // [NameConstraintsExtension] // Include = NameConstraintsPermitted // Exclude = NameConstraintsExcluded // // [NameConstraintsPermitted] // ; the numeric second and third arguments are optional // ; when present, the second argument is the minimum depth // ; when present, the third argument is the maximum depth // ; The IETF recommends against specifying dwMinimum & dwMaximum // DNS = foo@domain.com // DNS = domain1.domain.com, 3, 6 // // [NameConstraintsExcluded] // DNS = domain.com // DNS = domain2.com // // Returns: encoded name constraints extension //------------------------------------------------------------------------- FNMYINFGETEXTENSION myInfGetNameConstraintsExtension; HRESULT myInfGetNameConstraintsExtension( IN HINF hInf, OUT CERT_EXTENSION *pext) { HRESULT hr; WCHAR const *pwszKey = NULL; CERT_NAME_CONSTRAINTS_INFO NameConstraints; INFCONTEXT InfContext; static WCHAR const * const s_apwszKeys[] = { wszINFKEY_EXCLUDE, wszINFKEY_INCLUDE, wszINFKEY_CRITICAL, NULL }; ZeroMemory(&NameConstraints, sizeof(NameConstraints)); ZeroMemory(pext, sizeof(*pext)); myInfClearError(); if (NULL == hInf || INVALID_HANDLE_VALUE == hInf) { hr = E_HANDLE; _JumpError2(hr, error, "hInf", hr); } hr = infSetupFindFirstLine( hInf, wszINFSECTION_NAMECONSTRAINTS, NULL, // pwszKey FALSE, // fUniqueKey 0, // cValueMax s_apwszKeys, TRUE, // fUniqueValidKeys &InfContext); _JumpIfErrorStr2( hr, error, "infSetupFindFirstLine", wszINFSECTION_NAMECONSTRAINTS, ERROR_LINE_NOT_FOUND); pwszKey = wszINFKEY_INCLUDE; hr = infGetGeneralSubTree( hInf, wszINFSECTION_NAMECONSTRAINTS, pwszKey, L"", &NameConstraints.cPermittedSubtree, &NameConstraints.rgPermittedSubtree); _PrintIfErrorStr2( hr, "infGetGeneralSubTree", pwszKey, ERROR_LINE_NOT_FOUND); if (S_OK != hr && (HRESULT) ERROR_LINE_NOT_FOUND != hr) { goto error; } pwszKey = wszINFKEY_EXCLUDE; hr = infGetGeneralSubTree( hInf, wszINFSECTION_NAMECONSTRAINTS, pwszKey, NULL, &NameConstraints.cExcludedSubtree, &NameConstraints.rgExcludedSubtree); _PrintIfErrorStr2( hr, "infGetGeneralSubTree", pwszKey, ERROR_LINE_NOT_FOUND); if (S_OK != hr && (HRESULT) ERROR_LINE_NOT_FOUND != hr) { goto error; } pwszKey = NULL; if (NULL == NameConstraints.rgPermittedSubtree && NULL == NameConstraints.rgExcludedSubtree) { hr = S_FALSE; _JumpError2(hr, error, "no data", hr); } hr = infGetCriticalFlag( hInf, wszINFSECTION_NAMECONSTRAINTS, FALSE, &pext->fCritical); _JumpIfError(hr, error, "infGetCriticalFlag"); if (!myEncodeObject( X509_ASN_ENCODING, X509_NAME_CONSTRAINTS, &NameConstraints, 0, CERTLIB_USE_LOCALALLOC, &pext->Value.pbData, &pext->Value.cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } error: if (S_OK != hr && S_FALSE != hr) { INFSETERROR(hr, wszINFSECTION_NAMECONSTRAINTS, pwszKey, NULL); } pext->pszObjId = szOID_NAME_CONSTRAINTS; // on error, too! if (NULL != NameConstraints.rgPermittedSubtree) { infFreeGeneralSubTree( NameConstraints.cPermittedSubtree, NameConstraints.rgPermittedSubtree); } if (NULL != NameConstraints.rgExcludedSubtree) { infFreeGeneralSubTree( NameConstraints.cExcludedSubtree, NameConstraints.rgExcludedSubtree); } DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetNameConstraintsExtension hr=%x --> f=%d, cb=%x\n", hr, pext->fCritical, pext->Value.cbData)); return(hr); } VOID infFreePolicyMappings( IN DWORD cPolicyMapping, IN OUT CERT_POLICY_MAPPING *pPolicyMapping) { if (NULL != pPolicyMapping) { DWORD i; for (i = 0; i < cPolicyMapping; i++) { CERT_POLICY_MAPPING *pMap = &pPolicyMapping[i]; if (NULL != pMap->pszIssuerDomainPolicy) { LocalFree(pMap->pszIssuerDomainPolicy); } if (NULL != pMap->pszSubjectDomainPolicy) { LocalFree(pMap->pszSubjectDomainPolicy); } } LocalFree(pPolicyMapping); } } HRESULT infGetPolicyMappingsSub( IN HINF hInf, IN WCHAR const *pwszSection, IN OUT DWORD *pcPolicyMapping, OPTIONAL OUT CERT_POLICY_MAPPING *pPolicyMapping) { HRESULT hr; INFCONTEXT InfContext; WCHAR *pwszIssuer = NULL; WCHAR *pwszSubject = NULL; DWORD cPolicyMappingIn; DWORD cPolicyMapping = 0; cPolicyMappingIn = MAXDWORD; if (NULL != pPolicyMapping) { cPolicyMappingIn = *pcPolicyMapping; } *pcPolicyMapping = 0; CSASSERT(NULL != hInf && INVALID_HANDLE_VALUE != hInf); cPolicyMapping = 0; hr = infSetupFindFirstLine( hInf, pwszSection, NULL, // pwszKey FALSE, // fUniqueKey 0, // cValueMax NULL, // apwszKeys FALSE, // fUniqueValidKeys &InfContext); _JumpIfErrorStr3( hr, error, "infSetupFindFirstLine", pwszSection, S_FALSE, (HRESULT) ERROR_LINE_NOT_FOUND); for (;;) { if (NULL != pwszIssuer) { LocalFree(pwszIssuer); pwszIssuer = NULL; } if (NULL != pwszSubject) { LocalFree(pwszSubject); pwszSubject = NULL; } hr = infGetCurrentKeyValue( &InfContext, pwszSection, NULL, // pwszKey 0, FALSE, // fLastValue &pwszIssuer); _JumpIfError(hr, error, "infGetCurrentKeyValue"); if (!iswdigit(pwszIssuer[0])) { if (0 != LSTRCMPIS(pwszIssuer, wszINFKEY_CRITICAL)) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpErrorStr(hr, error, "bad key", pwszIssuer); } } else { hr = myVerifyObjId(pwszIssuer); _JumpIfErrorStr(hr, error, "myVerifyObjId", pwszIssuer); hr = infGetCurrentKeyValue( &InfContext, pwszSection, pwszIssuer, 1, TRUE, // fLastValue &pwszSubject); _JumpIfError(hr, error, "infGetCurrentKeyValue"); hr = myVerifyObjId(pwszSubject); _JumpIfErrorStr(hr, error, "myVerifyObjId", pwszSubject); if (NULL != pPolicyMapping) { CERT_POLICY_MAPPING *pMap; if (cPolicyMappingIn <= cPolicyMapping) { hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); _JumpError(hr, error, "*pcPolicyMapping"); } pMap = &pPolicyMapping[cPolicyMapping]; if (!ConvertWszToSz(&pMap->pszIssuerDomainPolicy, pwszIssuer, -1) || !ConvertWszToSz(&pMap->pszSubjectDomainPolicy, pwszSubject, -1)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "ConvertWszToSz"); } } DBGPRINT(( DBG_SS_CERTLIBI, "Map[%u]: %ws = %ws\n", cPolicyMapping, pwszIssuer, pwszSubject)); cPolicyMapping++; } if (!SetupFindNextLine(&InfContext, &InfContext)) { hr = myHLastError(); _PrintError2(hr, "SetupFindNextLine", hr); break; } } *pcPolicyMapping = cPolicyMapping; hr = S_OK; error: if (S_OK != hr) { INFSETERROR(hr, pwszSection, pwszIssuer, pwszSubject); } if (NULL != pwszIssuer) { LocalFree(pwszIssuer); } if (NULL != pwszSubject) { LocalFree(pwszSubject); } return(hr); } HRESULT infGetPolicyMappings( IN HINF hInf, IN WCHAR const *pwszSection, OUT DWORD *pcPolicyMapping, OUT CERT_POLICY_MAPPING **ppPolicyMapping) { HRESULT hr; DWORD cPolicyMapping = 0; CERT_POLICY_MAPPING *pPolicyMapping = NULL; *pcPolicyMapping = 0; *ppPolicyMapping = NULL; CSASSERT(NULL != hInf && INVALID_HANDLE_VALUE != hInf); cPolicyMapping = 0; hr = infGetPolicyMappingsSub( hInf, pwszSection, &cPolicyMapping, NULL); _JumpIfError3( hr, error, "infGetPolicyMappingsSub", S_FALSE, (HRESULT) ERROR_LINE_NOT_FOUND); *pcPolicyMapping = cPolicyMapping; pPolicyMapping = (CERT_POLICY_MAPPING *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, cPolicyMapping * sizeof(*pPolicyMapping)); if (NULL == pPolicyMapping) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } hr = infGetPolicyMappingsSub( hInf, pwszSection, &cPolicyMapping, pPolicyMapping); _JumpIfError(hr, error, "infGetPolicyMappingsSub"); CSASSERT(*pcPolicyMapping == cPolicyMapping); *ppPolicyMapping = pPolicyMapping; pPolicyMapping = NULL; hr = S_OK; error: if (NULL != pPolicyMapping) { infFreePolicyMappings(*pcPolicyMapping, pPolicyMapping); } return(hr); } //+------------------------------------------------------------------------ // infGetPolicyMappingSub -- fetch policy mapping extension from INF file // // [pwszSection] // ; list of user defined policy mappings // ; The first OID is for the Issuer Domain Policy, the second is for the // ; Subject Domain Policy. Each entry maps one Issuer Domain policy OID // ; to a Subject Domain policy OID // // 1.3.6.1.4.1.311.21.53 = 1.2.3.4.87 // 1.3.6.1.4.1.311.21.53 = 1.2.3.4.89 // // Returns: encoded policy mapping extension //------------------------------------------------------------------------- HRESULT infGetPolicyMappingExtensionSub( IN HINF hInf, IN WCHAR const *pwszSection, IN char const *pszObjId, OUT CERT_EXTENSION *pext) { HRESULT hr; CERT_POLICY_MAPPINGS_INFO PolicyMappings; ZeroMemory(&PolicyMappings, sizeof(PolicyMappings)); ZeroMemory(pext, sizeof(*pext)); myInfClearError(); if (NULL == hInf || INVALID_HANDLE_VALUE == hInf) { hr = E_HANDLE; _JumpError2(hr, error, "hInf", hr); } hr = infGetPolicyMappings( hInf, pwszSection, &PolicyMappings.cPolicyMapping, &PolicyMappings.rgPolicyMapping); _JumpIfErrorStr3( hr, error, "infGetPolicyMappings", pwszSection, S_FALSE, (HRESULT) ERROR_LINE_NOT_FOUND); hr = infGetCriticalFlag( hInf, pwszSection, FALSE, &pext->fCritical); _JumpIfError(hr, error, "infGetCriticalFlag"); if (!myEncodeObject( X509_ASN_ENCODING, X509_POLICY_MAPPINGS, &PolicyMappings, 0, CERTLIB_USE_LOCALALLOC, &pext->Value.pbData, &pext->Value.cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } error: if (S_OK != hr && S_FALSE != hr) { INFSETERROR(hr, pwszSection, NULL, NULL); } pext->pszObjId = const_cast(pszObjId); // on error, too! if (NULL != PolicyMappings.rgPolicyMapping) { infFreePolicyMappings( PolicyMappings.cPolicyMapping, PolicyMappings.rgPolicyMapping); } return(hr); } //+------------------------------------------------------------------------ // myInfGetPolicyMapping -- fetch policy mapping extension from INF file // // [PolicyMappingExtension] // ; list of user defined policy mappings // ; The first OID is for the Issuer Domain Policy, the second is for the // ; Subject Domain Policy. Each entry maps one Issuer Domain policy OID // ; to a Subject Domain policy OID // // 1.3.6.1.4.1.311.21.53 = 1.2.3.4.87 // 1.3.6.1.4.1.311.21.53 = 1.2.3.4.89 // // Returns: encoded policy mapping extension //------------------------------------------------------------------------- FNMYINFGETEXTENSION myInfGetPolicyMappingExtension; HRESULT myInfGetPolicyMappingExtension( IN HINF hInf, OUT CERT_EXTENSION *pext) { HRESULT hr; hr = infGetPolicyMappingExtensionSub( hInf, wszINFSECTION_POLICYMAPPINGS, szOID_POLICY_MAPPINGS, pext); _JumpIfErrorStr3( hr, error, "infGetPolicyMappingExtensionSub", wszINFSECTION_POLICYMAPPINGS, S_FALSE, (HRESULT) ERROR_LINE_NOT_FOUND); error: DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetPolicyMappingExtension hr=%x --> f=%d, cb=%x\n", hr, pext->fCritical, pext->Value.cbData)); return(hr); } //+------------------------------------------------------------------------ // myInfGetApplicationPolicyMapping -- fetch application policy mapping // extension from INF file // // [ApplicationPolicyMappingExtension] // ; list of user defined policy mappings // ; The first OID is for the Issuer Domain Policy, the second is for the // ; Subject Domain Policy. Each entry maps one Issuer Domain policy OID // ; to a Subject Domain policy OID // // 1.3.6.1.4.1.311.21.53 = 1.2.3.4.87 // 1.3.6.1.4.1.311.21.53 = 1.2.3.4.89 // // Returns: encoded policy mapping extension //------------------------------------------------------------------------- FNMYINFGETEXTENSION myInfGetApplicationPolicyMappingExtension; HRESULT myInfGetApplicationPolicyMappingExtension( IN HINF hInf, OUT CERT_EXTENSION *pext) { HRESULT hr; hr = infGetPolicyMappingExtensionSub( hInf, wszINFSECTION_APPLICATIONPOLICYMAPPINGS, szOID_APPLICATION_POLICY_MAPPINGS, pext); _JumpIfErrorStr3( hr, error, "infGetPolicyMappingExtensionSub", wszINFSECTION_APPLICATIONPOLICYMAPPINGS, S_FALSE, (HRESULT) ERROR_LINE_NOT_FOUND); error: DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetApplicationPolicyMappingExtension hr=%x --> f=%d, cb=%x\n", hr, pext->fCritical, pext->Value.cbData)); return(hr); } //+------------------------------------------------------------------------ // infGetPolicyConstraintsExtensionSub -- get policy constraints ext from INF // // [pwszSection] // ; consists of two optional DWORDs // ; They refer to the depth of the CA hierarchy that requires and inhibits // ; Policy Mapping // RequireExplicitPolicy = 3 // InhibitPolicyMapping = 5 // // Returns: encoded policy constraints extension //------------------------------------------------------------------------- HRESULT infGetPolicyConstraintsExtensionSub( IN HINF hInf, IN WCHAR const *pwszSection, IN char const *pszObjId, OUT CERT_EXTENSION *pext) { HRESULT hr; CERT_POLICY_CONSTRAINTS_INFO PolicyConstraints; WCHAR const *pwszKey = NULL; INFCONTEXT InfContext; static WCHAR const * const s_apwszKeys[] = { wszINFKEY_REQUIREEXPLICITPOLICY, wszINFKEY_INHIBITPOLICYMAPPING, wszINFKEY_CRITICAL, NULL }; ZeroMemory(&PolicyConstraints, sizeof(PolicyConstraints)); ZeroMemory(pext, sizeof(*pext)); myInfClearError(); if (NULL == hInf || INVALID_HANDLE_VALUE == hInf) { hr = E_HANDLE; _JumpError2(hr, error, "hInf", hr); } hr = infSetupFindFirstLine( hInf, pwszSection, NULL, // pwszKey FALSE, // fUniqueKey 0, // cValueMax s_apwszKeys, TRUE, // fUniqueValidKeys &InfContext); _JumpIfErrorStr2( hr, error, "infSetupFindFirstLine", pwszSection, ERROR_LINE_NOT_FOUND); PolicyConstraints.fRequireExplicitPolicy = TRUE; pwszKey = wszINFKEY_REQUIREEXPLICITPOLICY; hr = myInfGetNumericKeyValue( hInf, TRUE, // fLog pwszSection, pwszKey, 1, TRUE, // fLastValue &PolicyConstraints.dwRequireExplicitPolicySkipCerts); if (S_OK != hr) { if ((HRESULT) ERROR_LINE_NOT_FOUND != hr) { _JumpError(hr, error, "myInfGetNumericKeyValue"); } _PrintErrorStr2( hr, "myInfGetNumericKeyValue", wszINFKEY_REQUIREEXPLICITPOLICY, hr); PolicyConstraints.dwRequireExplicitPolicySkipCerts = 0; PolicyConstraints.fRequireExplicitPolicy = FALSE; } PolicyConstraints.fInhibitPolicyMapping = TRUE; pwszKey = wszINFKEY_INHIBITPOLICYMAPPING; hr = myInfGetNumericKeyValue( hInf, TRUE, // fLog pwszSection, pwszKey, 1, TRUE, // fLastValue &PolicyConstraints.dwInhibitPolicyMappingSkipCerts); if (S_OK != hr) { if ((HRESULT) ERROR_LINE_NOT_FOUND != hr) { _JumpError(hr, error, "myInfGetNumericKeyValue"); } _PrintErrorStr2( hr, "myInfGetNumericKeyValue", wszINFKEY_INHIBITPOLICYMAPPING, hr); PolicyConstraints.dwInhibitPolicyMappingSkipCerts = 0; PolicyConstraints.fInhibitPolicyMapping = FALSE; } pwszKey = NULL; if (!PolicyConstraints.fRequireExplicitPolicy && !PolicyConstraints.fInhibitPolicyMapping) { hr = S_FALSE; _JumpError2(hr, error, "no policy constraints", hr); } hr = infGetCriticalFlag( hInf, pwszSection, FALSE, &pext->fCritical); _JumpIfError(hr, error, "infGetCriticalFlag"); if (!myEncodeObject( X509_ASN_ENCODING, X509_POLICY_CONSTRAINTS, &PolicyConstraints, 0, CERTLIB_USE_LOCALALLOC, &pext->Value.pbData, &pext->Value.cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } error: if (S_OK != hr && S_FALSE != hr) { INFSETERROR(hr, pwszSection, pwszKey, NULL); } pext->pszObjId = const_cast(pszObjId); // on error, too! return(hr); } //+------------------------------------------------------------------------ // myInfGetPolicyConstraintsExtension -- get policy constraints ext from INF // // [PolicyConstraintsExtension] // ; consists of two optional DWORDs // ; They refer to the depth of the CA hierarchy that requires and inhibits // ; Policy Mapping // RequireExplicitPolicy = 3 // InhibitPolicyMapping = 5 // // Returns: encoded policy constraints extension //------------------------------------------------------------------------- FNMYINFGETEXTENSION myInfGetPolicyConstraintsExtension; HRESULT myInfGetPolicyConstraintsExtension( IN HINF hInf, OUT CERT_EXTENSION *pext) { HRESULT hr; hr = infGetPolicyConstraintsExtensionSub( hInf, wszINFSECTION_POLICYCONSTRAINTS, szOID_POLICY_CONSTRAINTS, pext); _JumpIfErrorStr3( hr, error, "infGetPolicyConstraintsExtensionSub", wszINFSECTION_POLICYCONSTRAINTS, S_FALSE, (HRESULT) ERROR_LINE_NOT_FOUND); error: DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetPolicyConstraintsExtension hr=%x --> f=%d, cb=%x\n", hr, pext->fCritical, pext->Value.cbData)); return(hr); } //+------------------------------------------------------------------------ // myInfGetApplicationPolicyConstraintsExtension -- get application policy // constraints extension from INF // // [ApplicationPolicyConstraintsExtension] // ; consists of two optional DWORDs // ; They refer to the depth of the CA hierarchy that requires and inhibits // ; Policy Mapping // RequireExplicitPolicy = 3 // InhibitPolicyMapping = 5 // // Returns: encoded policy constraints extension //------------------------------------------------------------------------- FNMYINFGETEXTENSION myInfGetApplicationPolicyConstraintsExtension; HRESULT myInfGetApplicationPolicyConstraintsExtension( IN HINF hInf, OUT CERT_EXTENSION *pext) { HRESULT hr; hr = infGetPolicyConstraintsExtensionSub( hInf, wszINFSECTION_APPLICATIONPOLICYCONSTRAINTS, szOID_APPLICATION_POLICY_CONSTRAINTS, pext); _JumpIfErrorStr3( hr, error, "infGetPolicyConstraintsExtensionSub", wszINFSECTION_APPLICATIONPOLICYCONSTRAINTS, S_FALSE, (HRESULT) ERROR_LINE_NOT_FOUND); error: DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetApplicationPolicyConstraintsExtension hr=%x --> f=%d, cb=%x\n", hr, pext->fCritical, pext->Value.cbData)); return(hr); } //+------------------------------------------------------------------------ // myInfGetCrossCertDistributionPointsExtension -- fetch Cross CertDist Point // URLs from CAPolicy.inf // // [CrossCertificateDistributionPointsExtension] // SyncDeltaTime = 24 // URL = http://CRLhttp.site.com/Public/MyCA.crt // URL = ftp://CRLftp.site.com/Public/MyCA.crt // // Returns: encoded cross cert dist points extension //------------------------------------------------------------------------- FNMYINFGETEXTENSION myInfGetCrossCertDistributionPointsExtension; HRESULT myInfGetCrossCertDistributionPointsExtension( IN HINF hInf, OUT CERT_EXTENSION *pext) { HRESULT hr; INFCONTEXT InfContext; CROSS_CERT_DIST_POINTS_INFO ccdpi; CERT_ALT_NAME_INFO AltNameInfo; CERT_ALT_NAME_ENTRY *rgAltEntry = NULL; WCHAR const *pwsz; WCHAR *pwszzURL = NULL; DWORD i; WCHAR const *pwszKey = NULL; static WCHAR const * const s_apwszKeys[] = { wszINFKEY_CCDPSYNCDELTATIME, wszINFKEY_URL, wszINFKEY_CRITICAL, NULL }; ZeroMemory(&ccdpi, sizeof(ccdpi)); ZeroMemory(&AltNameInfo, sizeof(AltNameInfo)); ZeroMemory(pext, sizeof(*pext)); myInfClearError(); if (NULL == hInf || INVALID_HANDLE_VALUE == hInf) { hr = E_HANDLE; _JumpError2(hr, error, "hInf", hr); } hr = infSetupFindFirstLine( hInf, wszINFSECTION_CCDP, NULL, // pwszKey FALSE, // fUniqueKey 0, // cValueMax s_apwszKeys, FALSE, // fUniqueValidKeys &InfContext); _JumpIfErrorStr3( hr, error, "infSetupFindFirstLine", wszINFSECTION_CCDP, S_FALSE, (HRESULT) ERROR_LINE_NOT_FOUND); pwszKey = wszINFKEY_CCDPSYNCDELTATIME; hr = myInfGetNumericKeyValue( hInf, TRUE, // fLog wszINFSECTION_CCDP, pwszKey, 1, TRUE, // fLastValue &ccdpi.dwSyncDeltaTime); if (S_OK != hr) { _PrintErrorStr2( hr, "myInfGetNumericKeyValue", pwszKey, ERROR_LINE_NOT_FOUND); ccdpi.dwSyncDeltaTime = 0; } pwszKey = wszINFKEY_URL; hr = myInfGetKeyList( hInf, wszINFSECTION_CCDP, pwszKey, NULL, // apwszKeys &pext->fCritical, &pwszzURL); _JumpIfErrorStr3( hr, error, "myInfGetKeyList", pwszKey, ERROR_LINE_NOT_FOUND, S_FALSE); pwszKey = NULL; if (NULL != pwszzURL) { for (pwsz = pwszzURL; L'\0' != *pwsz; pwsz += wcslen(pwsz) + 1) { AltNameInfo.cAltEntry++; } } if (0 != AltNameInfo.cAltEntry) { ccdpi.cDistPoint = 1; ccdpi.rgDistPoint = &AltNameInfo; AltNameInfo.rgAltEntry = (CERT_ALT_NAME_ENTRY *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, AltNameInfo.cAltEntry * sizeof(AltNameInfo.rgAltEntry[0])); if (NULL == AltNameInfo.rgAltEntry) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } i = 0; for (pwsz = pwszzURL; L'\0' != *pwsz; pwsz += wcslen(pwsz) + 1) { AltNameInfo.rgAltEntry[i].pwszURL = const_cast(pwsz); AltNameInfo.rgAltEntry[i].dwAltNameChoice = CERT_ALT_NAME_URL; i++; } CSASSERT(i == AltNameInfo.cAltEntry); } if (!myEncodeObject( X509_ASN_ENCODING, X509_CROSS_CERT_DIST_POINTS, &ccdpi, 0, CERTLIB_USE_LOCALALLOC, &pext->Value.pbData, &pext->Value.cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } error: if (S_OK != hr && S_FALSE != hr) { INFSETERROR(hr, wszINFSECTION_CCDP, pwszKey, NULL); } pext->pszObjId = szOID_CROSS_CERT_DIST_POINTS; // on error, too! DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetCrossCertDistributionPointsExtension hr=%x --> f=%d, cb=%x\n", hr, pext->fCritical, pext->Value.cbData)); if (NULL != AltNameInfo.rgAltEntry) { LocalFree(AltNameInfo.rgAltEntry); } if (NULL != pwszzURL) { LocalFree(pwszzURL); } if (NULL != rgAltEntry) { LocalFree(rgAltEntry); } return(hr); } HRESULT infAddKey( IN WCHAR const *pwszName, IN OUT DWORD *pcValues, IN OUT INFVALUES **prgInfValues, OUT INFVALUES **ppInfValues) { HRESULT hr; INFVALUES *rgInfValues; WCHAR *pwszKeyT = NULL; hr = myDupString(pwszName, &pwszKeyT); _JumpIfError(hr, error, "myDupString"); if (NULL == *prgInfValues) { CSASSERT(0 == *pcValues); rgInfValues = (INFVALUES *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, sizeof(**prgInfValues)); } else { CSASSERT(0 != *pcValues); rgInfValues = (INFVALUES *) LocalReAlloc( *prgInfValues, (*pcValues + 1) * sizeof(**prgInfValues), LMEM_MOVEABLE | LMEM_ZEROINIT); } if (NULL == rgInfValues) { hr = E_OUTOFMEMORY; _JumpError( hr, error, NULL == *prgInfValues? "LocalAlloc" : "LocalReAlloc"); } *prgInfValues = rgInfValues; *ppInfValues = &rgInfValues[*pcValues]; (*pcValues)++; (*ppInfValues)->pwszKey = pwszKeyT; pwszKeyT = NULL; hr = S_OK; error: if (NULL != pwszKeyT) { LocalFree(pwszKeyT); } return(hr); } HRESULT infAddValue( IN WCHAR const *pwszValue, IN OUT INFVALUES *pInfValues) { HRESULT hr; WCHAR *pwszValueT = NULL; WCHAR **rgpwszValues = NULL; hr = myDupString(pwszValue, &pwszValueT); _JumpIfError(hr, error, "myDupString"); if (NULL == pInfValues->rgpwszValues) { CSASSERT(0 == pInfValues->cValues); rgpwszValues = (WCHAR **) LocalAlloc( LMEM_FIXED, sizeof(*pInfValues->rgpwszValues)); } else { CSASSERT(0 != pInfValues->cValues); rgpwszValues = (WCHAR **) LocalReAlloc( pInfValues->rgpwszValues, (pInfValues->cValues + 1) * sizeof(*pInfValues->rgpwszValues), LMEM_MOVEABLE); } if (NULL == rgpwszValues) { hr = E_OUTOFMEMORY; _JumpError( hr, error, NULL == pInfValues->rgpwszValues? "LocalAlloc" : "LocalReAlloc"); } pInfValues->rgpwszValues = rgpwszValues; pInfValues->rgpwszValues[pInfValues->cValues] = pwszValueT; pInfValues->cValues++; pwszValueT = NULL; hr = S_OK; error: if (NULL != pwszValueT) { LocalFree(pwszValueT); } return(hr); } VOID myInfFreeSectionValues( IN DWORD cInfValues, IN OUT INFVALUES *rgInfValues) { DWORD i; DWORD ival; INFVALUES *pInfValues; if (NULL != rgInfValues) { for (i = 0; i < cInfValues; i++) { pInfValues = &rgInfValues[i]; if (NULL != pInfValues->pwszKey) { LocalFree(pInfValues->pwszKey); } if (NULL != pInfValues->rgpwszValues) { for (ival = 0; ival < pInfValues->cValues; ival++) { if (NULL != pInfValues->rgpwszValues[ival]) { LocalFree(pInfValues->rgpwszValues[ival]); } } LocalFree(pInfValues->rgpwszValues); } } LocalFree(rgInfValues); } } //+------------------------------------------------------------------------ // myInfGetSectionValues -- fetch all section values from INF file // // [pwszSection] // KeyName1 = KeyValue1a, KeyValue1b, ... // KeyName2 = KeyValue2a, KeyValue2b, ... // ... // KeyNameN = KeyValueNa, KeyValueNb, ... // // Returns: array of key names and values //------------------------------------------------------------------------- HRESULT myInfGetSectionValues( IN HINF hInf, IN WCHAR const *pwszSection, OUT DWORD *pcInfValues, OUT INFVALUES **prgInfValues) { HRESULT hr; INFCONTEXT InfContext; WCHAR *pwszName = NULL; WCHAR *pwszValue = NULL; DWORD i; DWORD cInfValues = 0; INFVALUES *rgInfValues = NULL; INFVALUES *pInfValues; *pcInfValues = 0; *prgInfValues = NULL; myInfClearError(); if (NULL == hInf || INVALID_HANDLE_VALUE == hInf) { hr = E_HANDLE; _JumpError2(hr, error, "hInf", hr); } hr = infSetupFindFirstLine( hInf, pwszSection, NULL, // pwszKey FALSE, // fUniqueKey 0, // cValueMax NULL, // apwszKeys FALSE, // fUniqueValidKeys &InfContext); _JumpIfErrorStr(hr, error, "infSetupFindFirstLine", pwszSection); for (;;) { DWORD cValue; if (NULL != pwszName) { LocalFree(pwszName); pwszName = NULL; } hr = infGetCurrentKeyValue( &InfContext, pwszSection, NULL, // pwszKey 0, FALSE, // fLastValue &pwszName); _JumpIfError(hr, error, "infGetCurrentKeyValue"); //wprintf(L"%ws[0]:\n", pwszName); hr = infAddKey(pwszName, &cInfValues, &rgInfValues, &pInfValues); _JumpIfError(hr, error, "infAddKey"); cValue = SetupGetFieldCount(&InfContext); if (0 == cValue) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); INFSETERROR(hr, pwszSection, pwszName, L""); _JumpErrorStr(hr, error, "SetupGetFieldCount", pwszName); } for (i = 0; i < cValue; i++) { if (NULL != pwszValue) { LocalFree(pwszValue); pwszValue = NULL; } hr = infGetCurrentKeyValue( &InfContext, pwszSection, pwszName, i + 1, FALSE, // fLastValue &pwszValue); _JumpIfError(hr, error, "infGetCurrentKeyValue"); //wprintf(L"%ws[%u] = %ws\n", pwszName, i, pwszValue); hr = infAddValue(pwszValue, pInfValues); _JumpIfError(hr, error, "infAddValue"); } if (!SetupFindNextLine(&InfContext, &InfContext)) { hr = myHLastError(); _PrintError2(hr, "SetupFindNextLine(end)", hr); break; } } *pcInfValues = cInfValues; *prgInfValues = rgInfValues; rgInfValues = NULL; hr = S_OK; error: if (S_OK != hr && S_FALSE != hr) { INFSETERROR(hr, pwszSection, pwszName, pwszValue); } if (NULL != rgInfValues) { myInfFreeSectionValues(cInfValues, rgInfValues); } if (NULL != pwszName) { LocalFree(pwszName); } if (NULL != pwszValue) { LocalFree(pwszValue); } DBGPRINT(( DBG_SS_CERTLIBI, "myInfGetSectionValues hr=%x --> c=%d\n", hr, *pcInfValues)); return(hr); } HRESULT myInfGetEnableKeyCounting( IN HINF hInf, OUT BOOL *pfValue) { HRESULT hr; *pfValue = FALSE; if (NULL == hInf || INVALID_HANDLE_VALUE == hInf) { hr = E_HANDLE; _JumpError2(hr, error, "hInf", hr); } myInfClearError(); hr = myInfGetBooleanValue( hInf, wszINFSECTION_CERTSERVER, wszINFKEY_ENABLEKEYCOUNTING, TRUE, pfValue); _JumpIfError2(hr, error, "myDupString", ERROR_LINE_NOT_FOUND); error: return(hr); } VOID myInfFreeExtensions( IN DWORD cExt, IN CERT_EXTENSION *rgExt) { if (NULL != rgExt) { DWORD i; for (i = 0; i < cExt; i++) { if (NULL != rgExt[i].pszObjId) { LocalFree(rgExt[i].pszObjId); } if (NULL != rgExt[i].Value.pbData) { LocalFree(rgExt[i].Value.pbData); } } LocalFree(rgExt); } } HRESULT infBuildExtension( IN OUT INFCONTEXT *pInfContext, OPTIONAL OUT CERT_EXTENSION *pExt) { HRESULT hr; WCHAR *pwszKey = NULL; char *pszObjId = NULL; WCHAR *pwszValue = NULL; BYTE *pbData = NULL; DWORD cbData; hr = infGetCurrentKeyValue( pInfContext, NULL, // pwszSection NULL, // pwszKey 0, FALSE, // fLastValue &pwszKey); _JumpIfError(hr, error, "infGetCurrentKeyValue"); DBGPRINT((DBG_SS_CERTLIBI, "Element = %ws\n", pwszKey)); if (0 == LSTRCMPIS(pwszKey, wszINFKEY_CRITICAL) || 0 == LSTRCMPIS(pwszKey, wszINFKEY_CONTINUE)) { hr = S_FALSE; // Skip this key _JumpError2(hr, error, "skip Critical/_continue_ key", hr); } if (!myConvertWszToSz(&pszObjId, pwszKey, -1)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "myConvertWszToSz"); } hr = myVerifyObjIdA(pszObjId); _JumpIfErrorStr(hr, error, "myVerifyObjIdA", pwszKey); DBGPRINT((DBG_SS_CERTLIBI, "OID = %hs\n", pszObjId)); hr = infGetCurrentKeyValue( pInfContext, NULL, // pwszSection pwszKey, 1, TRUE, // fLastValue &pwszValue); _JumpIfErrorStr(hr, error, "infGetCurrentKeyValue", pwszKey); DBGPRINT((DBG_SS_CERTLIBI, "%ws = %ws\n", pwszKey, pwszValue)); cbData = 0; if (L'\0' != *pwszValue) // allow empty values { hr = myCryptStringToBinary( pwszValue, wcslen(pwszValue), CRYPT_STRING_BASE64, &pbData, &cbData, NULL, NULL); _JumpIfErrorStr(hr, error, "myCryptStringToBinary", pwszKey); } if (NULL != pExt) { pExt->pszObjId = pszObjId; pExt->Value.pbData = pbData; pExt->Value.cbData = cbData; pszObjId = NULL; pbData = NULL; } hr = S_OK; error: if (S_OK != hr && S_FALSE != hr) { INFSETERROR(hr, NULL, pwszKey, pwszValue); } if (NULL != pwszKey) { LocalFree(pwszKey); } if (NULL != pszObjId) { LocalFree(pszObjId); } if (NULL != pwszValue) { LocalFree(pwszValue); } if (NULL != pbData) { LocalFree(pbData); } return(hr); } HRESULT myInfGetExtensions( IN HINF hInf, OUT DWORD *pcExt, OUT CERT_EXTENSION **ppExt) { HRESULT hr; DWORD i; DWORD cExt; CERT_EXTENSION *rgExt = NULL; DWORD cCritical; INFCONTEXT InfContext; WCHAR *pwszValue = NULL; char *pszObjId = NULL; WCHAR *pwszObjIdKey = NULL; WCHAR const *pwszSection = wszINFSECTION_EXTENSIONS; WCHAR const *pwszKey = wszINFKEY_CRITICAL; DWORD j; *pcExt = 0; *ppExt = NULL; cExt = 0; if (NULL == hInf || INVALID_HANDLE_VALUE == hInf) { hr = E_HANDLE; _JumpError2(hr, error, "hInf", hr); } myInfClearError(); hr = infSetupFindFirstLine( hInf, pwszSection, NULL, // pwszKey FALSE, // fUniqueKey 0, // cValueMax NULL, // apwszKeys FALSE, // fUniqueValidKeys &InfContext); _JumpIfErrorStr2( hr, error, "infSetupFindFirstLine", pwszSection, ERROR_LINE_NOT_FOUND); for (i = 0; ; ) { hr = infBuildExtension(&InfContext, NULL); if (S_FALSE != hr) { _JumpIfErrorStr(hr, error, "infBuildExtension", pwszSection); i++; } if (!SetupFindNextLine(&InfContext, &InfContext)) { hr = myHLastError(); _PrintErrorStr2(hr, "SetupFindNextLine", pwszSection, hr); break; } } cExt = i; rgExt = (CERT_EXTENSION *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, cExt * sizeof(rgExt[0])); if (NULL == rgExt) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } hr = infSetupFindFirstLine( hInf, pwszSection, NULL, // pwszKey FALSE, // fUniqueKey 0, // cValueMax NULL, // apwszKeys FALSE, // fUniqueValidKeys &InfContext); _JumpIfErrorStr(hr, error, "infSetupFindFirstLine", pwszSection); for (i = 0; ; ) { // handle one URL or text message hr = infBuildExtension(&InfContext, &rgExt[i]); if (S_FALSE != hr) { _JumpIfErrorStr(hr, error, "infBuildExtension", pwszSection); for (j = 0; j < i; j++) { if (0 == strcmp(rgExt[j].pszObjId, rgExt[i].pszObjId)) { if (!myConvertSzToWsz(&pwszObjIdKey, rgExt[i].pszObjId, -1)) { _PrintError(E_OUTOFMEMORY, "myConvertSzToWsz"); } hr = HRESULT_FROM_WIN32(RPC_S_ENTRY_ALREADY_EXISTS); INFSETERROR(hr, pwszSection, pwszObjIdKey, NULL); _JumpErrorStr(hr, error, "infBuildExtension", pwszObjIdKey); } } i++; } if (!SetupFindNextLine(&InfContext, &InfContext)) { hr = myHLastError(); _PrintErrorStr2(hr, "SetupFindNextLine", pwszSection, hr); break; } } CSASSERT(i == cExt); hr = infSetupFindFirstLine( hInf, pwszSection, pwszKey, TRUE, // fUniqueKey 0, // cValueMax NULL, // apwszKeys FALSE, // fUniqueValidKeys &InfContext); _PrintIfErrorStr2( hr, "infSetupFindFirstLine", pwszKey, ERROR_LINE_NOT_FOUND); if (S_OK != hr) { if ((HRESULT) ERROR_LINE_NOT_FOUND != hr) { INFSETERROR(hr, pwszSection, pwszKey, NULL); _JumpErrorStr(hr, error, "infSetupFindFirstLine", pwszKey); } } else { cCritical = SetupGetFieldCount(&InfContext); for (i = 1; i <= cCritical; i++) { if (NULL != pwszValue) { LocalFree(pwszValue); pwszValue = NULL; } if (NULL != pszObjId) { LocalFree(pszObjId); pszObjId = NULL; } hr = infGetCurrentKeyValue( &InfContext, NULL, // pwszSection pwszKey, i, FALSE, // fLastValue &pwszValue); _JumpIfErrorStr(hr, error, "infGetCurrentKeyValue", pwszKey); if (!myConvertWszToSz(&pszObjId, pwszValue, -1)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "myConvertWszToSz"); } for (j = 0; j < cExt; j++) { if (NULL != rgExt[j].Value.pbData && 0 != rgExt[j].Value.cbData && 0 == strcmp(rgExt[j].pszObjId, pszObjId)) { if (rgExt[j].fCritical) { hr = HRESULT_FROM_WIN32(RPC_S_ENTRY_ALREADY_EXISTS); INFSETERROR(hr, pwszSection, pwszKey, pwszValue); _JumpErrorStr(hr, error, "duplicate OID", pwszValue); } rgExt[j].fCritical = TRUE; break; } } if (j == cExt) { hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); INFSETERROR(hr, pwszSection, pwszKey, pwszValue); _JumpErrorStr(hr, error, "extraneous OID", pwszValue); } } } *pcExt = cExt; *ppExt = rgExt; rgExt = NULL; hr = S_OK; error: if (S_OK != hr && (HRESULT) ERROR_LINE_NOT_FOUND != hr) { INFSETERROR(hr, pwszSection, NULL, NULL); } if (NULL != pwszValue) { LocalFree(pwszValue); } if (NULL != pszObjId) { LocalFree(pszObjId); } if (NULL != pwszObjIdKey) { LocalFree(pwszObjIdKey); } myInfFreeExtensions(cExt, rgExt); return(hr); }