windows-nt/Source/XPSP1/NT/admin/snapin/dsadmin/simutil.cpp
2020-09-26 16:20:57 +08:00

645 lines
17 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1997 - 1998
//
// 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
{
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:<I>L=Internet<S>C=US, O=Microsoft, OU=DBSD, CN=Bob Bitter");
// ... The output will be
// "X509:"
// "<I>"
// "L=Internet"
// "<S>"
// "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, "<I>", "OU=");
//
LPCTSTR PchFindSimAttribute(
const LPCTSTR rgzpsz[], // IN: Array of pointer to strings
LPCTSTR pszSeparatorTag, // IN: Tag to search. eg: "<I>", "<S>" and "<AS>"
LPCTSTR pszAttributeTag) // IN: Attribute to searc for. eg: "CN=", "OU="
{
ASSERT(rgzpsz != NULL);
ASSERT(pszSeparatorTag != NULL);
ASSERT(pszAttributeTag != NULL);
size_t nLenAttrTag = wcslen (pszAttributeTag);
for (int i = 0; rgzpsz[i] != NULL; i++)
{
if (_wcsicmp(pszSeparatorTag, rgzpsz[i]) != 0)
continue;
for (++i; ; i++)
{
if (rgzpsz[i] == NULL)
return NULL;
if (rgzpsz[i][0] == _T('<'))
{
// We have found another separator tag
break;
}
if (_wcsnicmp(pszAttributeTag, rgzpsz[i], nLenAttrTag) == 0)
{
return rgzpsz[i];
}
} // 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: "<I>", "<S>" and "<AS>"
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()