#define INC_OLE2 #define UNICODE 1 #define _WIN32_DCOM #include #include #include #include #include #include #include #include #include #include "CreateUserHelpers.h" #include "UserProps.h" //#include "AttributesList.h" #define MAX_ATTRIBS 4096 //////////////////////////////////////////////////////////////////////////////////////////////////// // String for usage information WCHAR * pwszUsage = L"Creates new User under the passed container.\n" L"Usage:\n" L"CreateUser\n" L" <*> Simple New User Information <*>\n" L" /LDAP \n" L" ADsPath of the container for placing the new group\n" L" /UNAME \n" L" This is the name for the new group\n" L" /SAMNAME \n" L" Cannot exceed 20 characters and must be globally unique\n" L"\n" L" <*> Detailed New User information <*>\n" L" /FILE < Info File >\n" L" Filename for file to contain new user information\n" L"\n" L" <*> OPTIONAL Binding Information <*>\n" L" /USER \n" L" /PASS \n" L" (If these are passed, binding is done with ADsOpenObject())\n" L"\n" L"\n"; // Sample command line: // /LDAP "LDAP://nttest.microsoft.com/CN=UGExercise Sample Container,dc=nttest,dc=microsoft,dc=com" /UNAME BOB /SAMNAME BOB /USER Administrator /PASS "" //////////////////////////////////////////////////////////////////////////////////////////////////// // Module level global variables BSTR bsLDAP = NULL; // ADsPath of the container for placing the new group BSTR bsUNAME = NULL; // This is the name for the new group BSTR bsSAMNAME = NULL; // Cannot exceed 20 characters and must be globally unique BSTR bsFILE = NULL; // < Info File > Filename for file to contain new user information BSTR bsUSER = NULL; // BSTR bsPASS = NULL; // //////////////////////////////////////////////////////////////////////////////////////////////////// // Forward Declarations BOOL ParseCommandLine( int argc, wchar_t *argv[ ]); HRESULT CreateUser(IDirectoryObject *pDirObject, LPWSTR pwCommonName,LPWSTR pwSamAcctName,IDirectoryObject ** ppDirObjRet); void Trim(LPWSTR pwszData); HRESULT CreateUserFromFile(IDirectoryObject *pDirObject, LPWSTR pwCommonName,LPWSTR pwSamAcctName,IDirectoryObject ** ppDirObjRet,LPWSTR pwszFileName); BOOL GetNextLine(FILE* fp,LPWSTR pwszDest,int iSize); void RemoveSpecialChars(LPWSTR pwszLine); //////////////////////////////////////////////////////////////////////////////////////////////////// // main() /* Note: Using the UNICODE version of main(). this removes the need for the sample to include UNICODE-ANSI conversion routines */ void wmain( int argc, wchar_t *argv[ ]) { WCHAR pwszTemp[4096]; // We have now scanned PAST whitespace- so copy the string: wcscpy_s(pwszTemp,4096,L" A String"); Trim(pwszTemp); HRESULT hr; IDirectoryObject * pDirObjectContainer = NULL; IDirectoryObject * pDirObjRet = NULL; if (!ParseCommandLine(argc,argv)) return; // Initialize COM CoInitialize(0); // Bind to the container passed // If USER and PASS passed in, use ADsOpenObject() if (bsUSER) hr = ADsOpenObject(bsLDAP, bsUSER, bsPASS, ADS_SECURE_AUTHENTICATION,IID_IDirectoryObject, (void**) &pDirObjectContainer); else hr = ADsGetObject( bsLDAP, IID_IDirectoryObject,(void **)&pDirObjectContainer); if (SUCCEEDED(hr)) { // if a file is NOT passed in- Do the simple version if (!bsFILE) { // Call the helper funtion to create the User hr = CreateUser(pDirObjectContainer, bsUNAME,bsSAMNAME, &pDirObjRet); } else // file was passed in { // Call the helper funtion to create the User hr = CreateUserFromFile(pDirObjectContainer, bsUNAME,bsSAMNAME, &pDirObjRet,bsFILE); } if (SUCCEEDED(hr)) { _putws(L"\n\n New User created with the following properties:\n"); IADs * pIADsNewGoup = NULL; // User succeeded- now get an IADs interface to it // and print some properties hr = pDirObjRet->QueryInterface(IID_IADs,(void**)&pIADsNewGoup); if (SUCCEEDED(hr)) { PrintIADSObject(pIADsNewGoup); pIADsNewGoup->Release(); pIADsNewGoup = NULL; } else CheckADHRESULT(hr,L"QueryInterface() - New User for IADs"); pDirObjRet->Release(); pDirObjRet = NULL; } else CheckADHRESULT(hr,L"CreateUser()"); pDirObjectContainer->Release(); pDirObjectContainer = NULL; } else if (bsUSER) CheckADHRESULT(hr,L"ADsOpenObject()"); else CheckADHRESULT(hr,L"ADsGetObject()"); if ( bsLDAP ) ::SysFreeString(bsLDAP); if ( bsUNAME ) ::SysFreeString(bsUNAME); if ( bsSAMNAME ) ::SysFreeString(bsSAMNAME); if ( bsFILE ) ::SysFreeString(bsFILE); if ( bsUSER ) ::SysFreeString(bsUSER); if ( bsPASS ) ::SysFreeString(bsPASS); CoUninitialize(); } //////////////////////////////////////////////////////////////////////////////////////////////////// /* ParseCommandLine()- Parses command line and sets module level globals Parameters: int argc - Number of Arguments wchar_t *argv[ ] - Array of arguments Returns: TRUE if Command line was successfully parsed Affectets global variables: BSTR bsLDAP BSTR bsUNAME BSTR bsSAMNAME BSTR bsFILE BSTR bsUSER BSTR bsPASS */ BOOL ParseCommandLine( int argc, wchar_t *argv[ ]) { /* ** PARSE the FOLLOWING ARGUMENTS: /LDAP ADsPath of the container for placing the new group /UNAME This is the name for the new group /SAMNAME Cannot exceed 20 characters and must be globally unique <*> Detailed New User information <*> /FILE < Info File > Filename for file to contain new user information <*> OPTIONAL Binding Information <*> /USER /PASS (If these are passed, binding is done with ADsOpenObject()) */ if (argc == 1) { _putws(pwszUsage); return FALSE; } for (int x= 1; x < argc; x++) { if (_wcsicmp(argv[x],L"/LDAP")==0) { if (argc == x) // Make sure the parameter was passed { wprintf(L"\nERROR: %s Missing parameter!!!!\n\n",argv[x]); _putws(pwszUsage); return FALSE; } // Go to the next argument and save it in module level variable x++; bsLDAP = SysAllocString(argv[x]); } else if (_wcsicmp(argv[x],L"/UNAME")==0) { if (argc == x) // Make sure the parameter was passed { wprintf(L"\nERROR: %s Missing parameter!!!!\n\n",argv[x]); _putws(pwszUsage); return FALSE; } // Go to the next argument and save it in module level variable x++; bsUNAME = SysAllocString(argv[x]); } else if (_wcsicmp(argv[x],L"/SAMNAME")==0) { if (argc == x) // Make sure the parameter was passed { wprintf(L"\nERROR: %s Missing parameter!!!!\n\n",argv[x]); _putws(pwszUsage); return FALSE; } // Go to the next argument and save it in module level variable x++; bsSAMNAME = SysAllocString(argv[x]); } else if (_wcsicmp(argv[x],L"/FILE")==0) { if (argc == x) // Make sure the parameter was passed { wprintf(L"\nERROR: %s Missing parameter!!!!\n\n",argv[x]); _putws(pwszUsage); return FALSE; } // Go to the next argument and save it in module level variable x++; bsFILE = SysAllocString(argv[x]); } else if (_wcsicmp(argv[x],L"/USER")==0) { if (argc == x) // Make sure the parameter was passed { wprintf(L"\nERROR: %s Missing parameter!!!!\n\n",argv[x]); _putws(pwszUsage); return FALSE; } // Go to the next argument and save it in module level variable x++; bsUSER = SysAllocString(argv[x]); } else if (_wcsicmp(argv[x],L"/PASS")==0) { if (argc == x) // Make sure the parameter was passed { wprintf(L"\nERROR: %s Missing parameter!!!!\n\n",argv[x]); _putws(pwszUsage); return FALSE; } // Go to the next argument and save it in module level variable x++; bsPASS = SysAllocString(argv[x]); } else { wprintf(L"\nERROR: %s UNknown Argument\n\n",argv[x]); _putws(pwszUsage); return FALSE; } } // Check if User is poassed, then password is required: if (bsUSER || bsPASS) if (!bsUSER || !bsPASS) // if either one is missing complain { _putws(L"\nERROR: If /USER is specified /PASS is required!"); return FALSE; } return TRUE; } HRESULT ConvertUTCFromStringToUTCTime( LPWSTR pwszTime, SYSTEMTIME *pst) { FILETIME ft; TCHAR sz[3]; LPWSTR pszSrc = pwszTime; SYSTEMTIME st; // Year sz[0] = pszSrc[0]; sz[1] = pszSrc[1]; sz[2] = TEXT('\0'); st.wYear = (WORD)_wtoi(sz); if (st.wYear < 50) { st.wYear += 2000; } else { st.wYear += 1900; } // Month sz[0] = pszSrc[2]; sz[1] = pszSrc[3]; st.wMonth = (WORD)_wtoi(sz); // Day sz[0] = pszSrc[4]; sz[1] = pszSrc[5]; st.wDay = (WORD)_wtoi(sz); // Hour sz[0] = pszSrc[6]; sz[1] = pszSrc[7]; st.wHour = (WORD)_wtoi(sz); // Minute sz[0] = pszSrc[8]; sz[1] = pszSrc[9]; st.wMinute = (WORD)_wtoi(sz); // Second sz[0] = pszSrc[10]; sz[1] = pszSrc[11]; st.wSecond = (WORD)_wtoi(sz); st.wMilliseconds = 0; // This gets us the day of week SystemTimeToFileTime(&st, &ft); FileTimeToSystemTime(&ft, &st); *pst = st; return S_OK; } //////////////////////////////////////////////////////////////////////////////////////////////////// /* GetNextLine() - Retrives a line from the passed file pointer Parameters FILE* fp - File Pointer LPWSTR pwszDest - Destination Buffer int iSize - Size of Destination Buffer */ BOOL GetNextLine(FILE* fp,LPWSTR pwszDest,int iSize) { //BOOL bRet; WCHAR c =0 ; int x = 0; do { if (x < iSize ) { // Get a character c = getwc(fp); // Handle the end if (x ==0 && ((char)c) == EOF ) return FALSE; // If it is NOT a carriage return - save the character // (note the cast to (char): as EOF is a char -1. // with a WIDE character this becomes 65535) // so we cast DOWN the char to test for the potential -1 if (c!= '\r' && ((char)c) != EOF && c != '\n') pwszDest[x++] = c; else break; } } while (c != '\r' && c != EOF && c != '\n' && c !=65535); pwszDest[x] = NULL; return TRUE; } //////////////////////////////////////////////////////////////////////////////////////////////////// /* Trim() - VERY simple function for removing left and right whitespace from a unicode string Parameters LPWSTR pwszData - Data passed for trimming- Function will modify */ void Trim(LPWSTR pwszData) { int iscan=0; int iput=0; int iLenData = wcslen(pwszData); LPWSTR pwszTemp = new WCHAR[iLenData+1]; int iLenTemp; // Trim left: while (pwszData[iscan] != NULL && (pwszData[iscan] == ' ' || pwszData[iscan] == '\t' )) iscan++; // We have now scanned PAST whitespace- so copy the string: wcscpy_s(pwszTemp,iLenData+1,&pwszData[iscan]); // Now we need to do the TrimRight iLenTemp = wcslen(pwszTemp); for (iscan = iLenTemp-1; iscan; iscan--) { if (!(pwszTemp[iscan] == ' ' || pwszTemp[iscan] == '\t' ) ) break; } pwszTemp[iscan+1] =NULL; wcscpy_s(pwszData,iLenData,pwszTemp); } //////////////////////////////////////////////////////////////////////////////////////////////////// /* RemoveSpecialChars() - Removes Tabs from passed string Parameters LPWSTR pwszLine - Target String */ void RemoveSpecialChars(LPWSTR pwszLine) { int iLen = wcslen(pwszLine); for (int x = 0; x < iLen; x++) { switch (pwszLine[x]) { case '\t': pwszLine[x] = ' '; // convert tabs to spaces.. } } } //////////////////////////////////////////////////////////////////////////////////////////////////// /* ReadDataFromLine() - Reads the Attribute and the Value from a passed line of text Parameters LPWSTR pwszLine - Line to read BSTR * pbsAttrib - Attribute NAME to return BSTR * pbsValue - Value of the attribute */ HRESULT ReadDataFromLine(LPWSTR pwszLine,BSTR * pbsAttrib, BSTR * pbsValue) { int x; int iLen = wcslen(pwszLine); *pbsAttrib = NULL; *pbsValue = NULL; // Remove any tabs still in the line: RemoveSpecialChars(pwszLine); // If this line is a comment or blank, bail.. if (pwszLine[0] == L'/' || iLen == 0) return S_FALSE; // Make a temp string to save our string LPWSTR pwszTempAttrib = new WCHAR [iLen +1]; if ( !pwszTempAttrib ) return E_FAIL; pwszTempAttrib[0]= NULL; for (x = 0; x< iLen; x++) switch(pwszLine[x]) { case L' ': case L'\t': { // NULL terminate the copy we are building pwszTempAttrib[x] = L'\0'; x++; goto DoneReadDataFromLine; break; } case NULL: // if we hit the end of the string- there is NOT a value there { wprintf(L"\n!!!Attribute missing value [%s]\n",pwszLine); delete [] pwszTempAttrib; return E_FAIL; } default: { pwszTempAttrib[x] = pwszLine[x]; } } DoneReadDataFromLine: // Trim off any preceding spaces.. Trim((LPWSTR)pwszLine +x); // Grab the END of the passed line, and put it in the // Value BSTR *pbsValue = SysAllocString((LPWSTR)pwszLine +x); // Alloc a new BSTR to return for this attribute *pbsAttrib= SysAllocString(pwszTempAttrib); if (iLen) delete [] pwszTempAttrib ; return S_OK; } //////////////////////////////////////////////////////////////////////////////////////////////////// /* MakeOctetADSVALUE() - Reads the passed octet string into the passed ADSVALUE, Sample String: x00 x00 x00 x00 xe0 xff x03 xe0 xff x03 xe0 xff x03 xe0 xff x03 xe0 xff x03 x00 x00 The "x"'s are required and the values must be in HEX format Parameters ADSVALUE * pAdsValue - Returned ADSVALUE of Type OctetString LPWSTR pwszOctet - String representing OCTET */ HRESULT MakeOctetADSVALUE(ADSVALUE * pAdsValue,LPWSTR pwszOctet) { int iNumBytes = 0; int iStrLen = wcslen(pwszOctet); // Count the number of BYTES in the String for (int x =0;x OctetString.lpValue = new BYTE [iNumBytes+1]; pAdsValue->OctetString.dwLength = iNumBytes; for (int x =0;x OctetString.lpValue[iCurrByte++]= iVal; // scan past the rest of the string for (x =x;xpszAttrName = new WCHAR [attrNameLen]; wcscpy_s(pattrInfo->pszAttrName,attrNameLen,pwszAttrib); pattrInfo->dwControlCode = ADS_ATTR_UPDATE; pattrInfo->dwADsType = ADSTYPE_CASE_IGNORE_STRING; pattrInfo->pADsValues = new ADSVALUE; pattrInfo->pADsValues->dwType = adType; pattrInfo->dwNumValues = 1; // Fill in the VALUE: switch (adType ) { case ADSTYPE_DN_STRING: case ADSTYPE_CASE_EXACT_STRING: case ADSTYPE_CASE_IGNORE_STRING: case ADSTYPE_PRINTABLE_STRING: case ADSTYPE_NUMERIC_STRING: case ADSTYPE_TYPEDNAME: case ADSTYPE_FAXNUMBER: case ADSTYPE_PATH: case ADSTYPE_OBJECT_CLASS: { pattrInfo->pADsValues->CaseIgnoreString = new WCHAR [wcslen(pwszValue)+1]; wcscpy_s(pattrInfo->pADsValues->CaseIgnoreString,wcslen(pwszValue)+1,pwszValue); break; } case ADSTYPE_BOOLEAN: if (_wcsicmp(pwszValue,L"TRUE")==0) pattrInfo->pADsValues->Boolean = 1; else if (_wcsicmp(pwszValue,L"FALSE")==0) pattrInfo->pADsValues->Boolean = 0; else if (_wtoi(pwszValue)>0) pattrInfo->pADsValues->Boolean = 1; else pattrInfo->pADsValues->Boolean = 0; break; case ADSTYPE_INTEGER: { pattrInfo->pADsValues->Integer = _wtoi(pwszValue); break; } case ADSTYPE_OCTET_STRING: { // Read the string into an actualy binary blob HRESULT hr = MakeOctetADSVALUE(pattrInfo->pADsValues ,pwszValue); if (FAILED(hr)) return FALSE; break; } case ADSTYPE_UTC_TIME: { SYSTEMTIME pst; HRESULT hr = ConvertUTCFromStringToUTCTime( pwszValue,&pst ); if (FAILED(hr)) return FALSE; pattrInfo->pADsValues->UTCTime = pst; break; } case ADSTYPE_LARGE_INTEGER: { pattrInfo->pADsValues->dwType = ADSTYPE_LARGE_INTEGER; swscanf_s (pwszValue, L"%I64d", &pattrInfo->pADsValues->LargeInteger); break; } default: { wprintf(L"\n\n!!!ERROR Unrecognized Type for atrib: %s",pwszAttrib); return FALSE; } } return TRUE; } //////////////////////////////////////////////////////////////////////////////////////////////////// /* PopulateAttrInfoFromFile() - Populates the passed ADS_ATTR_INFO from the passed filename Parameters LPWSTR pwszFileName - file from which info is read ADS_ATTR_INFO * pattrInfo - Pointer to Elements to be added DWORD &rdwNumPopulated - Returned number populated */ BOOL PopulateAttrInfoFromFile(LPWSTR pwszFileName,ADS_ATTR_INFO * pattrInfo,DWORD &rdwNumPopulated ) { HRESULT hr = S_OK; BOOL bRet = FALSE; FILE * fpInput = NULL; WCHAR pwszLine[4096]; rdwNumPopulated = 0; errno_t status = 0; if( (status = _wfopen_s( &fpInput, pwszFileName, L"r" )) == NULL ) { wprintf(L"\nError Opening Input File:%s",pwszFileName); return FALSE; } while (GetNextLine(fpInput,pwszLine,4096) && SUCCEEDED(hr)) { Trim(pwszLine); BSTR bsAttrib; BSTR bsValue; // Read the line into our BSTRS // if S_FALSE is returned, then we do not have a line // if E_FAIL is returned, then we have an error condition hr = ReadDataFromLine(pwszLine,&bsAttrib,&bsValue); if (hr == S_OK) { wprintf(L" attrib:%s value:%s\n",bsAttrib,bsValue); // Take the attribute and value and add them to the Array if (!PopulateADSAttrInfo(&pattrInfo[rdwNumPopulated],bsAttrib,bsValue)) { hr = E_FAIL; break; } SysFreeString(bsAttrib); SysFreeString(bsValue); rdwNumPopulated++; } } fclose(fpInput); return (SUCCEEDED(hr)); } //////////////////////////////////////////////////////////////////////////////////////////////////// /* CreateUserFromFile() - Function for creating a basic User Parameters IDirectoryObject *pDirObject - Parent Directory Object for the new User LPWSTR pwCommonName - Common Name for the new User IDirectoryObject ** ppDirObjRet - Pointer to the Pointer which will receive the new User int iUserType - Bitflags for new User: ADS_User_TYPE_GLOBAL_User, ADS_User_TYPE_DOMAIN_LOCAL_User, ADS_User_TYPE_UNIVERSAL_User, ADS_User_TYPE_SECURITY_ENABLED */ HRESULT CreateUserFromFile(IDirectoryObject *pDirObject, LPWSTR pwCommonName,LPWSTR pwSamAcctName,IDirectoryObject ** ppDirObjRet,LPWSTR pwszFileName) { // To simply things, allocate a fixed array: ADS_ATTR_INFO attrInfo [MAX_ATTRIBS+1]; HRESULT hr = S_OK; DWORD dwNumPopulated; LPDISPATCH pDisp; *ppDirObjRet = NULL; WCHAR pwCommonNameFull[MAX_PATH*2]; BOOL bRet = FALSE; FILE * fpInput = NULL; WCHAR pwszLine[4096]; dwNumPopulated = 0; errno_t status = 0; if( (status = _wfopen_s( &fpInput, pwszFileName, L"r" )) == NULL ) { wprintf(L"\nError Opening Input File:%s",pwszFileName); return FALSE; } while (GetNextLine(fpInput,pwszLine,4096) && SUCCEEDED(hr)) { Trim(pwszLine); BSTR bsAttrib; BSTR bsValue; // Read the line into our BSTRS // if S_FALSE is returned, then we do not have a line // if E_FAIL is returned, then we have an error condition hr = ReadDataFromLine(pwszLine,&bsAttrib,&bsValue); if (hr == S_OK) { wprintf(L" attrib:%s value:%s\n",bsAttrib,bsValue); // Take the attribute and value and add them to the Array if (!PopulateADSAttrInfo(&attrInfo[dwNumPopulated],bsAttrib,bsValue)) { hr = E_FAIL; break; } SysFreeString(bsAttrib); SysFreeString(bsValue); dwNumPopulated++; } } fclose(fpInput); wsprintfW(pwCommonNameFull,L"CN=%s",pwCommonName); hr = pDirObject->CreateDSObject( pwCommonNameFull, attrInfo, dwNumPopulated, &pDisp ); if (SUCCEEDED(hr)) { hr = pDisp->QueryInterface(IID_IDirectoryObject,(void**) ppDirObjRet); pDisp->Release(); pDisp = NULL; } //FreeAttrInfo(attrInfo); return hr; } //////////////////////////////////////////////////////////////////////////////////////////////////// /* CreateUser() - Function for creating a basic User Parameters IDirectoryObject *pDirObject - Parent Directory Object for the new User LPWSTR pwCommonName - Common Name for the new User IDirectoryObject ** ppDirObjRet - Pointer to the Pointer which will receive the new User int iUserType - Bitflags for new User: ADS_User_TYPE_GLOBAL_User, ADS_User_TYPE_DOMAIN_LOCAL_User, ADS_User_TYPE_UNIVERSAL_User, ADS_User_TYPE_SECURITY_ENABLED */ HRESULT CreateUser(IDirectoryObject *pDirObject, LPWSTR pwCommonName,LPWSTR pwSamAcctName,IDirectoryObject ** ppDirObjRet) { assert(pDirObject); if (wcslen(pwSamAcctName) >20) { MessageBox(NULL,L"SamAccountName CANNOT be bigger than 20 characters",L"Error: CreateSimpleUser()",MB_ICONSTOP); assert(0); return E_FAIL; } HRESULT hr; ADSVALUE sAMValue; ADSVALUE classValue; LPDISPATCH pDisp; WCHAR pwCommonNameFull[1024]; ADS_ATTR_INFO attrInfo[] = { { L"objectClass", ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING, &classValue, 1 }, {L"sAMAccountName", ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING, &sAMValue, 1}, }; DWORD dwAttrs = sizeof(attrInfo)/sizeof(ADS_ATTR_INFO); classValue.dwType = ADSTYPE_CASE_IGNORE_STRING; classValue.CaseIgnoreString = L"User"; sAMValue.dwType=ADSTYPE_CASE_IGNORE_STRING; sAMValue.CaseIgnoreString = pwSamAcctName; wsprintfW(pwCommonNameFull,L"CN=%s",pwCommonName); hr = pDirObject->CreateDSObject( pwCommonNameFull, attrInfo, dwAttrs, &pDisp ); if (SUCCEEDED(hr)) { hr = pDisp->QueryInterface(IID_IDirectoryObject,(void**) ppDirObjRet); pDisp->Release(); pDisp = NULL; } return hr; }