//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 2002 // // File: simutil.cpp // //-------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////////// // SimUtil.cpp // // Utilities routines specific to the Security Identity Mapping project. // // HISTORY // 25-Jun-97 t-danm Creation. ///////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "common.h" const TCHAR szDefaultCertificateExtension[] = _T("cer"); // Not subject to localization ///////////////////////////////////////////////////////////////////// // UiGetCertificateFile() // // Invoke the common dialog to get a certificate file. // // Return FALSE if the user clicked on cancel button. // BOOL UiGetCertificateFile( CString * pstrCertificateFilename) // OUT: Name of the certificate file { CThemeContextActivator activator; ASSERT(pstrCertificateFilename != NULL); BOOL bResult = FALSE; CString strFilter; VERIFY( strFilter.LoadString(IDS_SIM_CERTIFICATE_FILTER) ); CFileDialog* pDlg = new CFileDialog ( TRUE, // Open File szDefaultCertificateExtension, // lpszDefExt NULL, // lpszFileName OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST, strFilter); // lpszFilter if ( pDlg ) { CString strCaption; VERIFY( strCaption.LoadString(IDS_SIM_ADD_CERTIFICATE) ); pDlg->m_ofn.lpstrTitle = (LPCTSTR)strCaption; if (pDlg->DoModal() == IDOK) { // Copy the string *pstrCertificateFilename = pDlg->GetPathName(); bResult = TRUE; } delete pDlg; } return bResult; } // UiGetCertificateFile() ///////////////////////////////////////////////////////////////////// // strSimToUi() // // Convert a SIM string into a format the user understand. // // The routine will remove any quotes and expand any escape characters // to a format friendly to the user. // void strSimToUi( LPCTSTR pszSIM, // IN: CString * pstrUI) // OUT: { ASSERT(pszSIM != NULL); ASSERT(pstrUI != NULL); // Find out if the string contains a quote if (!wcschr(pszSIM, '"')) { // No quote found, so return the original string *pstrUI = pszSIM; return; } pstrUI->Empty(); while (TRUE) { if (*pszSIM == '"') { pszSIM++; } if (*pszSIM == '\0') break; *pstrUI += *pszSIM++; } // while } // strSimToUi() ///////////////////////////////////////////////////////////////////// // strUiToSim() // // Convert a string typed by the user to a valid SIM format. // // If the UI string contains special characters, the routine // will add quotes and other escape characters wherever necessary. // // BACKGROUND INFORMATION // From the CryptoAPI SDK // Quote the RDN value if it contains leading or trailing // white space or one of the following characters: // ",", "+", "=", """, "\n", "<", ">", "#" or ";". // The quoting character is ". If the RDN Value contains a " it is double quoted (""). // void strUiToSim( LPCTSTR pszUI, // IN: CString * pstrSIM) // OUT: { ASSERT(pszUI != NULL); ASSERT(pstrSIM != NULL); // // String containing special characters // static const TCHAR szSpecialCharacters[] = _T(",+=<>#;\"\n"); // // Skip the leading spaces // while (*pszUI == _T(' ')) { pszUI++; } const TCHAR * pszDataString = pszUI; // // Find out wherever the string needs to be surrounded by quotes // const TCHAR * pchFirstSpecialToken = wcspbrk(pszUI, szSpecialCharacters); if (pchFirstSpecialToken != NULL && *pchFirstSpecialToken == '=') { pszDataString = pchFirstSpecialToken; pchFirstSpecialToken = wcspbrk(pchFirstSpecialToken + 1, szSpecialCharacters); } BOOL fNeedQuotes = (pchFirstSpecialToken != NULL) || (pszDataString[0] == _T(' ')) || (pszDataString[lstrlen(pszDataString)] == _T(' ')); if (!fNeedQuotes) { *pstrSIM = pszUI; return; } pstrSIM->Empty(); const TCHAR * pchSrc = pszUI; ASSERT(pszDataString != NULL); if (*pszDataString == '=') { // Copy string until the equal '=' sign ASSERT(pszDataString >= pchSrc); for (int cch = (int)((pszDataString - pchSrc) + 1); cch > 0; cch--) { ASSERT(*pchSrc != '\0' && "Unexpected end of string"); *pstrSIM += *pchSrc++; } } // if // Add the openning quote *pstrSIM += '"'; for ( ; *pchSrc != '\0'; pchSrc++) { if (*pchSrc == '"') *pstrSIM += '"'; // Add one more quote for each quote *pstrSIM += *pchSrc; } // while // Add the tailing quote *pstrSIM += '"'; } // strUiToSim() // Macro to make pointer 'DWORD aligned' #define ALIGN_NEXT_DWORD_PTR(pv) (( ((INT_PTR)(pv)) + 3) & ~3) ///////////////////////////////////////////////////////////////////// // ParseSimString() // // Parse a SIM string into an array of zero-terminated string. // // RETURN // Return a pointer to an allocated array of pointers to strings. // The array of pointers allocated with the new() operator, // therefore the caller must call ONCE delete() to free the memory. // The routine may return NULL if the input string has a syntax error. // // INTERFACE NOTES // The format returned is the same as the API CommandLineToArgvW() // which is the same as main(int argc, char * argv[]). // - This routine will handle special characters such as quotes and // commas that are embedded into strings. // // EXTRA INFO // See strSimToUi() and strUiToSim(). // // EXAMPLE // LPTSTR * pargzpsz; // Pointer to allocated array of pointer to strings // pargzpsz = ParseSimString("X509:L=InternetC=US, O=Microsoft, OU=DBSD, CN=Bob Bitter"); // ... The output will be // "X509:" // "" // "L=Internet" // "" // "C=US" // "O=Microsoft" // "OU=DBSD" // "CN=Bob Bitter" // delete pargzpsz; // LPTSTR * ParseSimString( LPCTSTR szSimString, // IN: String to parse int * pArgc) // OUT: OPTIONAL: Argument count { ASSERT(szSimString != NULL); Endorse(pArgc == NULL); // Compute how much memory is needed for allocation. // The computation may allocate more memory than necessary depending // on the input string. CONST TCHAR * pchSrc; int cch = 0; int cStringCount = 2; // Estimate of the number of strings for (pchSrc = szSimString; *pchSrc != _T('\0'); pchSrc++, cch++) { // Search for any character that will make a new string switch (*pchSrc) { case _T(':'): // Colon case _T('<'): // Angle bracket case _T('>'): // Angle bracket case _T(','): // Comma cStringCount++; break; } // switch } // for // Extra space for pointers and DWORD alignment cch += cStringCount * (2 * sizeof(TCHAR *)) + 16; // Allocate a single block of memory for all the data LPTSTR * pargzpsz = (LPTSTR *)new TCHAR[cch]; ASSERT(pargzpsz != NULL && "new() should throw"); TCHAR * pchDst = (TCHAR *)&pargzpsz[cStringCount+1]; #ifdef DEBUG DebugCode( int cStringCountT = cStringCount; ) #endif pargzpsz[0] = pchDst; pchSrc = szSimString; cStringCount = 0; int cchString = 0; // Scan the rest of the string TCHAR chSpecial = 0; while (TRUE) { // Make a new string *pchDst = '\0'; if (cchString > 0) { pchDst++; pchDst = (TCHAR *)ALIGN_NEXT_DWORD_PTR(pchDst); pargzpsz[++cStringCount] = pchDst; cchString = 0; } *pchDst = '\0'; if (chSpecial) { switch (chSpecial) { case _T('<'): for ( ; ; pchSrc++) { if (*pchSrc == '\0') goto Error; // Unexpected end of string *pchDst++ = *pchSrc; cchString++; if (*pchSrc == _T('>')) { pchSrc++; break; } } // for break; case _T(','): while (*++pchSrc == _T(' ')) ; // Skip the blanks break; // Make a new string } // switch chSpecial = 0; continue; } // if while (chSpecial == '\0') { switch (*pchSrc) { case _T('\0'): goto Done; case _T('<'): case _T(','): chSpecial = *pchSrc; break; case _T(':'): *pchDst++ = *pchSrc++; cchString++; if (cStringCount == 0) chSpecial = _T(':'); break; case _T('"'): // The string contains quotes cchString++; *pchDst++ = *pchSrc++; // Copy the first quiote if (*pchDst == _T('"')) { // Two consecutive quotes *pchDst++ = *pchSrc++; break; } // Skip until the next quote while (TRUE) { if (*pchSrc == _T('\0')) goto Error; // Unexpected end of string if (*pchSrc == _T('"')) { *pchDst++ = *pchSrc++; break; } *pchDst++ = *pchSrc++; } break; default: *pchDst++ = *pchSrc++; cchString++; } // switch } // while } // while Done: *pchDst = '\0'; if (cchString > 0) cStringCount++; #ifdef DEBUG ASSERT(cStringCount <= cStringCountT); #endif pargzpsz[cStringCount] = NULL; if (pArgc != NULL) *pArgc = cStringCount; return pargzpsz; Error: TRACE1("ParseSimString() - Error parsing string %s.\n", szSimString); delete [] pargzpsz; return NULL; } // ParseSimString() ///////////////////////////////////////////////////////////////////// // UnparseSimString() // // This is the opposite of ParseSimString(). This routine // will concacenate an array of strings to produce // a single long SIM string. // // INTERFACE NOTES // This toutine will concatenate the array of strings to the // existing CString object. // void UnparseSimString( CString * pstrOut, // INOUT: Pointer to concatenated string const LPCTSTR rgzpsz[]) // IN: Array of pointer to strings { ASSERT(rgzpsz != NULL); ASSERT(pstrOut != NULL); for (int i = 0; rgzpsz[i] != NULL; i++) { if (i > 0) *pstrOut += ","; *pstrOut += rgzpsz[i]; } // for } // UnparseSimString() ///////////////////////////////////////////////////////////////////// // ParseSimSeparators() // // Break up an array of pointer to string to sub-array // of pointer to string for Issuer, Subject and AltSubject. // // INTERFACE NOTES // The output parameters must have enough storage to hold // the substrings. // void ParseSimSeparators( const LPCTSTR rgzpszIn[], // IN: Array of pointer to string LPCTSTR rgzpszIssuer[], // OUT: Substrings for Issuer LPCTSTR rgzpszSubject[], // OUT: Substrings for Subject LPCTSTR rgzpszAltSubject[]) // OUT: Substrings for AltSubject { ASSERT(rgzpszIn != NULL); Endorse(rgzpszIssuer == NULL); Endorse(rgzpszSubject == NULL); Endorse(rgzpszAltSubject == NULL); if (rgzpszIssuer != NULL) { // Get the substrings for Issuer (void)FindSimAttributes(szSimIssuer, IN rgzpszIn, OUT rgzpszIssuer); } if (rgzpszSubject != NULL) { // Get the substrings for Subject (void)FindSimAttributes(szSimSubject, IN rgzpszIn, OUT rgzpszSubject); } if (rgzpszAltSubject != NULL) { // Get the substrings for AltSubject (void)FindSimAttributes(szSimAltSubject, IN rgzpszIn, OUT rgzpszAltSubject); } } // ParseSimSeparators() ///////////////////////////////////////////////////////////////////// // UnparseSimSeparators() // // Append the strings for Issuer, Subject and AltSubject into // a single string. // // INTERFACE NOTES // The routine will append to the existing CString object. // int UnparseSimSeparators( CString * pstrOut, // INOUT: Pointer to contatenated string const LPCTSTR rgzpszIssuer[], const LPCTSTR rgzpszSubject[], const LPCTSTR rgzpszAltSubject[]) { ASSERT(pstrOut != NULL); int cSeparators = 0; // Number of separators added to the contatenated string if (rgzpszIssuer != NULL && rgzpszIssuer[0] != NULL) { cSeparators++; *pstrOut += szSimIssuer; UnparseSimString(OUT pstrOut, rgzpszIssuer); } if (rgzpszSubject != NULL && rgzpszSubject[0] != NULL) { cSeparators++; *pstrOut += szSimSubject; UnparseSimString(OUT pstrOut, rgzpszSubject); } if (rgzpszAltSubject != NULL && rgzpszAltSubject[0] != NULL) { cSeparators++; *pstrOut += szSimAltSubject; UnparseSimString(OUT pstrOut, rgzpszAltSubject); } return cSeparators; } // UnparseSimSeparators() ///////////////////////////////////////////////////////////////////// // PchFindSimAttribute() // // Search an array of strings for a given tag and an attribute. // // Return pointer to string containing the attribute. Routine // may return NULL if attribute is not found within the tags. // // INTERFACE NOTES // The routine assumes that all tags start with an openning bracket '<'. // // EXAMPLE // LPCTSTR pszIssuer = PchFindSimAttribute(pargzpsz, "", "OU="); // LPCTSTR PchFindSimAttribute( const LPCTSTR rgzpsz[], // IN: Array of pointer to strings LPCTSTR pszSeparatorTag, // IN: Tag to search. eg: "", "" and "" LPCTSTR pszAttributeTag) // IN: Attribute to searc for. eg: "CN=", "OU=" { ASSERT(rgzpsz != NULL); ASSERT(pszSeparatorTag != NULL); ASSERT(pszAttributeTag != NULL); size_t nLenAttrTag = wcslen (pszAttributeTag); PTSTR pszPossibleResult = 0; // hold a possible result of pszAttributeTag, // but continue to check for others further // out in the name for (int i = 0; rgzpsz[i] != NULL; i++) { if (_wcsicmp(pszSeparatorTag, rgzpsz[i]) != 0) continue; for (++i; ; i++) { if (rgzpsz[i] == NULL) { if ( pszPossibleResult ) return pszPossibleResult; // return, ran out of strings else return NULL; } if (rgzpsz[i][0] == _T('<')) { // We have found another separator tag if ( pszPossibleResult ) return pszPossibleResult; // return, last element before new separator else break; } if (_wcsnicmp(pszAttributeTag, rgzpsz[i], nLenAttrTag) == 0) { pszPossibleResult = const_cast (rgzpsz[i]); continue; } } // for } // for return NULL; } // PchFindSimAttribute() ///////////////////////////////////////////////////////////////////// // FindSimAttributes() // // Search an array of strings for a given tag. Fill an array of // strings that belongs to the tag. // // Return number of strings belonging to the tab (which is // the length of rgzpszOut). // // INTERFACE NOTES // This routine assumes parameter rgzpszOut has enough storage // to hold all the strings for the tag. It is recommended to make // rgzpszOut the same length as rgzpszIn (for safety). // - The output array does not include the tag. // int FindSimAttributes( LPCTSTR pszSeparatorTag, // IN: Tag to search. eg: "", "" and "" const LPCTSTR rgzpszIn[], // IN: Array of pointer to strings LPCTSTR rgzpszOut[]) // OUT: Output array of pointer to strings for tag { ASSERT(pszSeparatorTag != NULL); ASSERT(rgzpszIn != NULL); ASSERT(rgzpszOut != NULL); BOOL fTagFound = FALSE; int iOut = 0; // Index for the output array for (int iIn = 0; rgzpszIn[iIn] != NULL; iIn++) { const LPCTSTR pszT = rgzpszIn[iIn]; if (pszT[0] == '<') { fTagFound = (_wcsicmp(pszSeparatorTag, pszT) == 0) ? TRUE : FALSE; } else if (fTagFound) { rgzpszOut[iOut++] = pszT; } } // for rgzpszOut[iOut] = NULL; return iOut; } // FindSimAttributes() ///////////////////////////////////////////////////////////////////// // SplitX509String() // // Split a X509 string into its Issuer, Subject and AltSubject. // // Return a pointer to an allocated array of pointers to strings allocated // by ParseSimString(). // // INTERFACE NOTES // As the hungarian name implies, the output parameters // are pointers to allcated arrays of substrings for the // Issuer, Subject and AltSubject respectively. // - The caller is responsible of freeing the memory for // both the return value and all the output parameters. // // LPTSTR * SplitX509String( LPCTSTR pszX509, // IN: String to split LPCTSTR * ppargzpszIssuer[], // OUT: Pointer to allocated array of Substrings for Issuer LPCTSTR * ppargzpszSubject[], // OUT: Pointer to allocated array of Substrings for Subject LPCTSTR * ppargzpszAltSubject[]) // OUT: Pointer to allocated array of Substrings for AltSubject { ASSERT(pszX509 != NULL); Endorse(ppargzpszIssuer == NULL); Endorse(ppargzpszSubject == NULL); Endorse(ppargzpszAltSubject == NULL); LPTSTR * pargzpsz; // Pointer to allocated array of pointer to strings int cNumStr; // Number of strings pargzpsz = ParseSimString(IN pszX509, OUT &cNumStr); if (pargzpsz == NULL) { TRACE1("SplitX509String() - Error parsing string %s.\n", pszX509); return NULL; } ASSERT(cNumStr > 0); if (ppargzpszIssuer != NULL) { *ppargzpszIssuer = new LPCTSTR[cNumStr]; // Get the substrings for Issuer (void)FindSimAttributes(szSimIssuer, IN pargzpsz, OUT *ppargzpszIssuer); } if (ppargzpszSubject != NULL) { *ppargzpszSubject = new LPCTSTR[cNumStr]; // Get the substrings for Subject (void)FindSimAttributes(szSimSubject, IN pargzpsz, OUT *ppargzpszSubject); } if (ppargzpszAltSubject != NULL) { *ppargzpszAltSubject = new LPCTSTR[cNumStr]; // Get the substrings for AltSubject (void)FindSimAttributes(szSimAltSubject, IN pargzpsz, OUT *ppargzpszAltSubject); } return pargzpsz; } // SplitX509String() ///////////////////////////////////////////////////////////////////// int UnsplitX509String( CString * pstrX509, // OUT: Concatenated string const LPCTSTR rgzpszIssuer[], // IN: const LPCTSTR rgzpszSubject[], // IN: const LPCTSTR rgzpszAltSubject[]) // IN: { ASSERT(pstrX509 != NULL); *pstrX509 = szX509; return UnparseSimSeparators( INOUT pstrX509, IN rgzpszIssuer, IN rgzpszSubject, IN rgzpszAltSubject); } // UnsplitX509String()