Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1885 lines
58 KiB

  1. /*++
  2. Copyright (c) 1997-1998 Microsoft Corporation
  3. Module Name:
  4. ds.c
  5. Abstract:
  6. This module contains the code to process OS Chooser message
  7. for the BINL server.
  8. Author:
  9. Adam Barr (adamba) 9-Jul-1997
  10. Geoff Pease (gpease) 10-Nov-1997
  11. Environment:
  12. User Mode - Win32
  13. Revision History:
  14. --*/
  15. #include "binl.h"
  16. #pragma hdrstop
  17. #include <math.h> // pow() function
  18. #include <riname.h>
  19. #include <riname.c>
  20. DWORD
  21. OscGetUserDetails (
  22. PCLIENT_STATE clientState
  23. )
  24. //
  25. // This function fills in USERDOMAIN, USERFIRSTNAME, USERLASTNAME, USEROU in
  26. // the client state. Also fills in ROOTDOMAIN for root of enterprise.
  27. //
  28. {
  29. DWORD Error = ERROR_SUCCESS;
  30. DWORD Count;
  31. LPWSTR pszUserName = OscFindVariableW( clientState, "USERNAME" );
  32. LPWSTR pUserDomain = OscFindVariableW( clientState, "USERDOMAIN" );
  33. LPWSTR pUserOU = OscFindVariableW( clientState, "USEROU" );
  34. LPWSTR pUserFullName = OscFindVariableW( clientState, "USERFULLNAME" );
  35. PLDAP LdapHandle;
  36. PLDAPMessage LdapMessage = NULL;
  37. WCHAR Filter[256];
  38. PWCHAR ldapAttributes[5];
  39. BOOLEAN impersonating = FALSE;
  40. PLDAPMessage ldapEntry;
  41. PWCHAR *ldapConfigContainer = NULL;
  42. PWCHAR *ldapDomain = NULL;
  43. PWCHAR *ldapFirstName = NULL;
  44. PWCHAR *ldapLastName = NULL;
  45. PWCHAR *ldapDisplayName = NULL;
  46. PWCHAR *ldapAccountName = NULL;
  47. BOOLEAN allocatedContainer = FALSE;
  48. PWCHAR configContainer = NULL;
  49. BOOLEAN firstNameValid = FALSE;
  50. BOOLEAN lastNameValid = FALSE;
  51. BOOLEAN userFullNameSet = FALSE;
  52. PLDAPControlW controlArray[2];
  53. LDAPControlW controlNoReferrals;
  54. ULONG noReferralsPlease;
  55. PWCHAR ldapUserDN = NULL;
  56. PWCHAR *explodedDN = NULL;
  57. PWCHAR dnUsersOU = NULL;
  58. TraceFunc( "OscGetUserDetails( )\n" );
  59. if ( pszUserName[0] == L'\0' ) {
  60. OscAddVariableA( clientState, "SUBERROR", "USERNAME" );
  61. return ERROR_BINL_MISSING_VARIABLE;
  62. }
  63. //
  64. // If the USERFULLNAME variable already exists, we won't change it below.
  65. // But if it came back as an empty string, that might actually mean
  66. // that the variable doesn't exist. In such a case, when SearchAndReplace
  67. // processes the .SIF file for the client, it will leave occurrences of
  68. // "%USERFULLNAME%" alone -- it won't replace them with "". We don't want
  69. // "%USERFULLNAME% to hang around, so we explicitly set it to an empty
  70. // string if it doesn't already exist or is an empty string. We do the
  71. // same thing with USERFIRSTNAME, USERLASTNAME, and USERDISPLAYNAME.
  72. //
  73. if (pUserFullName[0] != L'\0') {
  74. userFullNameSet = TRUE;
  75. } else {
  76. OscAddVariableW( clientState, "USERFULLNAME", L"" );
  77. }
  78. {
  79. LPWSTR name;
  80. name = OscFindVariableW( clientState, "USERFIRSTNAME" );
  81. if (name[0] == L'\0') {
  82. OscAddVariableW( clientState, "USERFIRSTNAME", L"" );
  83. }
  84. name = OscFindVariableW( clientState, "USERLASTNAME" );
  85. if (name[0] == L'\0') {
  86. OscAddVariableW( clientState, "USERLASTNAME", L"" );
  87. }
  88. name = OscFindVariableW( clientState, "USERDISPLAYNAME" );
  89. if (name[0] == L'\0') {
  90. OscAddVariableW( clientState, "USERDISPLAYNAME", L"" );
  91. }
  92. }
  93. if ( pUserOU[0] != L'\0' ) {
  94. //
  95. // if we've already found this user's info, bail here with success.
  96. //
  97. return ERROR_SUCCESS;
  98. }
  99. //
  100. // if the users domain and the servers domain don't match,
  101. // then try connecting to the DC for the new domain. If we
  102. // don't do this, then we won't necessarily be able to get
  103. // the correct information about the user. By connecting to
  104. // the new DC, we get the clientState to cache some information
  105. // about the new domain.
  106. //
  107. if (pUserDomain[0] != L'\0' ) {
  108. PWSTR CrossDC = OscFindVariableW( clientState, "DCNAME" );
  109. if ( (CrossDC[0] == L'\0') &&
  110. (_wcsicmp(pUserDomain, BinlGlobalOurDomainName) != 0)) {
  111. HANDLE hDC;
  112. PSTR pUserDomainA = OscFindVariableA( clientState, "USERDOMAIN" );
  113. Error = MyGetDcHandle(clientState, pUserDomainA,&hDC);
  114. if (Error == ERROR_SUCCESS) {
  115. DsUnBindA(&hDC);
  116. }
  117. }
  118. }
  119. Error = OscImpersonate(clientState);
  120. if (Error != ERROR_SUCCESS) {
  121. BinlPrintDbg((DEBUG_ERRORS,
  122. "OscGetUserDetails: OscImpersonate failed %lx\n", Error));
  123. return Error;
  124. }
  125. impersonating = TRUE;
  126. BinlAssert( clientState->AuthenticatedDCLdapHandle != NULL );
  127. LdapHandle = clientState->AuthenticatedDCLdapHandle;
  128. //
  129. // we first look up the configuration and default container, we'll need
  130. // one or the other, based on whether we have a domain name or not.
  131. //
  132. ldapAttributes[0] = L"configurationNamingContext";
  133. ldapAttributes[1] = L"rootDomainNamingContext";
  134. ldapAttributes[2] = NULL;
  135. Error = ldap_search_ext_sW(LdapHandle,
  136. NULL,
  137. LDAP_SCOPE_BASE,
  138. L"(objectClass=*)",
  139. ldapAttributes,
  140. FALSE,
  141. NULL,
  142. NULL,
  143. 0,
  144. 0,
  145. &LdapMessage);
  146. Count = ldap_count_entries( LdapHandle, LdapMessage );
  147. if (Count > 0) {
  148. ldapEntry = ldap_first_entry( LdapHandle, LdapMessage );
  149. if (ldapEntry != NULL) {
  150. ldapConfigContainer = ldap_get_valuesW( LdapHandle,
  151. ldapEntry,
  152. L"configurationNamingContext" );
  153. ldapDomain = ldap_get_valuesW( LdapHandle,
  154. ldapEntry,
  155. L"rootDomainNamingContext" );
  156. if (ldapDomain != NULL &&
  157. *ldapDomain != NULL &&
  158. **ldapDomain != L'\0') {
  159. OscAddVariableW( clientState, "ROOTDOMAIN", *ldapDomain );
  160. }
  161. }
  162. } else {
  163. LogLdapError( EVENT_WARNING_LDAP_SEARCH_ERROR,
  164. LdapGetLastError(),
  165. LdapHandle
  166. );
  167. }
  168. ldap_msgfree( LdapMessage );
  169. //
  170. // we either have the config container or the default domain DN. If
  171. // we only have the config container, go get the correct domain DN.
  172. //
  173. if ( pUserDomain[0] != L'\0' ) {
  174. //
  175. // Since the user specified a domain, remove the defaulting to the same domain
  176. // as the RIS server.
  177. //
  178. ldapDomain = NULL;
  179. //
  180. // if a domain was specified, then we look it up to find the baseDN
  181. //
  182. // we fail if we didn't get the config container
  183. //
  184. if (ldapConfigContainer == NULL ||
  185. *ldapConfigContainer == NULL ||
  186. **ldapConfigContainer == L'\0') {
  187. if (Error == LDAP_SUCCESS) {
  188. Error = LDAP_NO_SUCH_ATTRIBUTE;
  189. }
  190. BinlPrintDbg((DEBUG_ERRORS,
  191. "OscGetUserDetails: get config container failed %lx\n", Error));
  192. Error = LdapMapErrorToWin32( Error );
  193. goto exitGetUserDetails;
  194. }
  195. //
  196. // we then tack on "CN=Partitions," to search the partitions container
  197. //
  198. Count = lstrlenW( *ldapConfigContainer ) + lstrlenW( L"CN=Partitions," ) + 1;
  199. configContainer = BinlAllocateMemory( Count * sizeof(WCHAR) );
  200. if (configContainer == NULL) {
  201. Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
  202. goto exitGetUserDetails;
  203. }
  204. lstrcpyW( configContainer, L"CN=Partitions," );
  205. lstrcatW( configContainer, *ldapConfigContainer );
  206. //
  207. // then we find the correct partition, we ignore the enterprise and
  208. // enterprise schema entries by specifying a non-empty netbios name.
  209. //
  210. ldapAttributes[0] = L"NCName";
  211. ldapAttributes[1] = NULL;
  212. wsprintf( Filter, L"(&(objectClass=CrossRef)(netbiosName=*)(|(dnsRoot=%s)(cn=%s)))",
  213. pUserDomain, pUserDomain );
  214. Error = ldap_search_ext_sW(LdapHandle,
  215. configContainer,
  216. LDAP_SCOPE_ONELEVEL,
  217. Filter,
  218. ldapAttributes,
  219. FALSE,
  220. NULL,
  221. NULL,
  222. 0,
  223. 0,
  224. &LdapMessage);
  225. Count = ldap_count_entries( LdapHandle, LdapMessage );
  226. if (Count > 0) {
  227. PWCHAR *ldapDomainFromPartition = NULL;
  228. ldapEntry = ldap_first_entry( LdapHandle,
  229. LdapMessage );
  230. if (ldapEntry != NULL) {
  231. ldapDomainFromPartition = ldap_get_valuesW( LdapHandle,
  232. ldapEntry,
  233. L"NCName" );
  234. if (ldapDomainFromPartition != NULL) {
  235. //
  236. // if we read a valid DN from the partitions container,
  237. // we free the default one and switch over to the
  238. // one we just found.
  239. //
  240. if (*ldapDomainFromPartition != NULL &&
  241. **ldapDomainFromPartition != L'\0') {
  242. ldap_value_free( ldapDomain );
  243. ldapDomain = ldapDomainFromPartition;
  244. } else {
  245. ldap_value_free( ldapDomainFromPartition );
  246. }
  247. }
  248. }
  249. } else {
  250. LogLdapError( EVENT_WARNING_LDAP_SEARCH_ERROR, LdapGetLastError(), LdapHandle);
  251. }
  252. ldap_msgfree( LdapMessage );
  253. } else if ((ldapDomain != NULL) && (*ldapDomain != NULL) && (**ldapDomain != L'\0')) {
  254. //
  255. // Add the user's domain as a variable to the client state.
  256. //
  257. OscAddVariableW( clientState, "USERDOMAIN", *ldapDomain );
  258. pUserDomain = OscFindVariableW( clientState, "USERDOMAIN" );
  259. }
  260. if (ldapDomain == NULL ||
  261. *ldapDomain == NULL ||
  262. **ldapDomain == L'\0') {
  263. if (Error == LDAP_SUCCESS) {
  264. Error = LDAP_NO_SUCH_ATTRIBUTE;
  265. }
  266. BinlPrintDbg((DEBUG_ERRORS,
  267. "OscGetUserDetails: get default domain failed %lx\n", Error));
  268. Error = LdapMapErrorToWin32( Error );
  269. goto exitGetUserDetails;
  270. }
  271. //
  272. // go find the user's first name, last name, display name,
  273. // and account name from the DS.
  274. //
  275. ldapAttributes[0] = &L"givenName";
  276. ldapAttributes[1] = &L"sn";
  277. ldapAttributes[2] = &L"displayName";
  278. ldapAttributes[3] = &L"cn";
  279. ldapAttributes[4] = NULL;
  280. wsprintf( Filter, L"(&(objectClass=user)(samAccountName=%s))", pszUserName );
  281. //
  282. // we really don't want it to go chasing referrals over the entire
  283. // enterprise since we know what the domain is but we do want to chase
  284. // externals.
  285. //
  286. noReferralsPlease = (ULONG)((ULONG_PTR)LDAP_CHASE_EXTERNAL_REFERRALS);
  287. controlNoReferrals.ldctl_oid = LDAP_CONTROL_REFERRALS_W;
  288. controlNoReferrals.ldctl_value.bv_len = sizeof(ULONG);
  289. controlNoReferrals.ldctl_value.bv_val = (PCHAR) &noReferralsPlease;
  290. controlNoReferrals.ldctl_iscritical = FALSE;
  291. controlArray[0] = &controlNoReferrals;
  292. controlArray[1] = NULL;
  293. Error = ldap_search_ext_sW(LdapHandle,
  294. *ldapDomain,
  295. LDAP_SCOPE_SUBTREE,
  296. Filter,
  297. ldapAttributes,
  298. FALSE,
  299. NULL,
  300. &controlArray[0],
  301. 0,
  302. 1,
  303. &LdapMessage);
  304. Count = ldap_count_entries( LdapHandle, LdapMessage );
  305. if (Count > 0) {
  306. ldapEntry = ldap_first_entry( LdapHandle, LdapMessage );
  307. if (ldapEntry != NULL) {
  308. ldapFirstName = ldap_get_valuesW( LdapHandle,
  309. ldapEntry,
  310. L"givenName" );
  311. if (ldapFirstName != NULL &&
  312. *ldapFirstName != NULL &&
  313. **ldapFirstName != L'\0') {
  314. OscAddVariableW( clientState, "USERFIRSTNAME", *ldapFirstName );
  315. firstNameValid = TRUE;
  316. }
  317. ldapLastName = ldap_get_valuesW( LdapHandle,
  318. ldapEntry,
  319. L"sn" );
  320. if (ldapLastName != NULL &&
  321. *ldapLastName != NULL &&
  322. **ldapLastName != L'\0') {
  323. OscAddVariableW( clientState, "USERLASTNAME", *ldapLastName );
  324. lastNameValid = TRUE;
  325. }
  326. //
  327. // Now that we have first and last name, set the USERFULLNAME
  328. // if either is not empty.
  329. //
  330. if ((firstNameValid || lastNameValid) && (userFullNameSet == FALSE)) {
  331. ULONG userFullNameLength = 0;
  332. PWCHAR userFullName;
  333. if (firstNameValid) {
  334. userFullNameLength = (wcslen(*ldapFirstName) + 1) * sizeof(WCHAR);
  335. }
  336. if (lastNameValid) {
  337. if (firstNameValid) {
  338. userFullNameLength += sizeof(WCHAR); // for the space
  339. }
  340. userFullNameLength += (wcslen(*ldapLastName) + 1) * sizeof(WCHAR);
  341. }
  342. userFullName = BinlAllocateMemory(userFullNameLength);
  343. if (userFullName != NULL) {
  344. userFullName[0] = L'\0';
  345. if (firstNameValid) {
  346. wcscat(userFullName, *ldapFirstName);
  347. }
  348. if (lastNameValid) {
  349. if (firstNameValid) {
  350. wcscat(userFullName, L" ");
  351. }
  352. wcscat(userFullName, *ldapLastName);
  353. }
  354. OscAddVariableW( clientState, "USERFULLNAME", userFullName);
  355. BinlFreeMemory(userFullName);
  356. userFullNameSet = TRUE;
  357. }
  358. }
  359. ldapDisplayName = ldap_get_valuesW( LdapHandle,
  360. ldapEntry,
  361. L"displayName" );
  362. if (ldapDisplayName != NULL &&
  363. *ldapDisplayName != NULL &&
  364. **ldapDisplayName != L'\0') {
  365. OscAddVariableW( clientState, "USERDISPLAYNAME", *ldapDisplayName );
  366. if (!userFullNameSet) {
  367. OscAddVariableW( clientState, "USERFULLNAME", *ldapDisplayName );
  368. userFullNameSet = TRUE;
  369. }
  370. }
  371. ldapAccountName = ldap_get_valuesW( LdapHandle,
  372. ldapEntry,
  373. L"cn" );
  374. if (ldapAccountName != NULL &&
  375. *ldapAccountName != NULL &&
  376. **ldapAccountName != L'\0') {
  377. OscAddVariableW( clientState, "USERACCOUNTNAME", *ldapAccountName );
  378. if (!userFullNameSet) {
  379. OscAddVariableW( clientState, "USERFULLNAME", *ldapAccountName );
  380. userFullNameSet = TRUE;
  381. }
  382. }
  383. ldapUserDN = ldap_get_dnW( LdapHandle, ldapEntry );
  384. if (ldapUserDN != NULL) {
  385. PWCHAR *explodedDN = ldap_explode_dnW( ldapUserDN, 0);
  386. if (explodedDN != NULL &&
  387. *explodedDN != NULL &&
  388. *(explodedDN+1) != NULL ) {
  389. //
  390. // if there's less than two components, we can't do
  391. // anything with this DN.
  392. //
  393. PWCHAR component;
  394. ULONG requiredSize = 1; // 1 for null terminator
  395. //
  396. // we now have an array of strings, each of which
  397. // is a component of the DN. This is the safe and
  398. // correct way to chop off the first element.
  399. //
  400. Count = 1;
  401. while ((component = explodedDN[Count++]) != NULL) {
  402. requiredSize += lstrlenW( component ) + 1;
  403. }
  404. dnUsersOU = BinlAllocateMemory( requiredSize * sizeof(WCHAR) );
  405. if (dnUsersOU != NULL) {
  406. lstrcpyW( dnUsersOU, explodedDN[1] );
  407. Count = 2;
  408. while ((component = explodedDN[Count++]) != NULL) {
  409. lstrcatW( dnUsersOU, L"," );
  410. lstrcatW( dnUsersOU, component );
  411. }
  412. OscAddVariableW( clientState, "USEROU", dnUsersOU );
  413. } else {
  414. BinlPrintDbg((DEBUG_ERRORS,
  415. "OscGetUserDetails: unable to allocate %lx for user OU\n",
  416. requiredSize * sizeof(WCHAR)));
  417. }
  418. }
  419. }
  420. }
  421. } else {
  422. LogLdapError( EVENT_WARNING_LDAP_SEARCH_ERROR,
  423. LdapGetLastError(),
  424. LdapHandle
  425. );
  426. }
  427. ldap_msgfree( LdapMessage );
  428. Error = ERROR_SUCCESS;
  429. exitGetUserDetails:
  430. if (dnUsersOU != NULL) {
  431. BinlFreeMemory( dnUsersOU );
  432. }
  433. if (explodedDN != NULL) {
  434. ldap_value_free( explodedDN );
  435. }
  436. if (ldapUserDN != NULL) {
  437. ldap_memfree( ldapUserDN );
  438. }
  439. if (ldapConfigContainer != NULL) {
  440. ldap_value_free( ldapConfigContainer );
  441. }
  442. if (ldapDomain != NULL) {
  443. ldap_value_free( ldapDomain );
  444. }
  445. if (ldapFirstName != NULL) {
  446. ldap_value_free( ldapFirstName );
  447. }
  448. if (ldapLastName != NULL) {
  449. ldap_value_free( ldapLastName );
  450. }
  451. if (ldapDisplayName != NULL) {
  452. ldap_value_free( ldapDisplayName );
  453. }
  454. if (ldapAccountName != NULL) {
  455. ldap_value_free( ldapAccountName );
  456. }
  457. if (impersonating) {
  458. OscRevert( clientState );
  459. }
  460. if (configContainer != NULL) {
  461. BinlFreeMemory( configContainer );
  462. }
  463. return Error;
  464. }
  465. DWORD
  466. OscCreateAccount(
  467. PCLIENT_STATE clientState,
  468. PCREATE_DATA CreateData
  469. )
  470. /*++
  471. Routine Description:
  472. This function creates an account for the client specified by
  473. RequestContext and writes the response in CreateData, which
  474. will be sent down to the client.
  475. It also creates the client's base image directory.
  476. Arguments:
  477. clientState - client state information
  478. CreateData - The block of data that will be sent down to the
  479. client if the account is successfully created.
  480. Return Value:
  481. None.
  482. --*/
  483. {
  484. DWORD Error;
  485. PWCHAR pMachineName;
  486. PWCHAR pMachineDN = NULL;
  487. PWCHAR pMachineOU;
  488. WCHAR SetupPath[MAX_PATH];
  489. PWCHAR pNameDollarSign;
  490. ULONG HostNameSize;
  491. UINT uSize;
  492. LPSTR pGuid;
  493. PWCHAR pStrings[3];
  494. MACHINE_INFO MachineInfo = { 0 };
  495. TraceFunc("OscCreateAccount( )\n");
  496. pMachineName = OscFindVariableW( clientState, "MACHINENAME" );
  497. pNameDollarSign = OscFindVariableW( clientState, "NETBIOSNAME" );
  498. //
  499. // Convert the GUID
  500. //
  501. pGuid = OscFindVariableA( clientState, "GUID" );
  502. Error = OscGuidToBytes( pGuid, MachineInfo.Guid );
  503. if ( Error != ERROR_SUCCESS )
  504. goto e0;
  505. if (clientState->fCreateNewAccount) {
  506. //
  507. // Create client's FQDN(DS)
  508. //
  509. pMachineOU = OscFindVariableW( clientState, "MACHINEOU" );
  510. BinlAssert( pMachineOU[0] != L'\0' );
  511. uSize = wcslen( pMachineName ) * sizeof(WCHAR)
  512. + wcslen( pMachineOU ) * sizeof(WCHAR)
  513. + sizeof(L"CN=,"); // includes terminating NULL char
  514. pMachineDN = (PWCHAR) BinlAllocateMemory( uSize );
  515. if ( !pMachineDN ) {
  516. Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
  517. goto e0;
  518. }
  519. wsprintf( pMachineDN, L"CN=%ws,%ws", pMachineName, pMachineOU );
  520. OscAddVariableW( clientState, "MACHINEDN", pMachineDN );
  521. } else {
  522. pMachineDN = OscFindVariableW( clientState, "MACHINEDN" );
  523. }
  524. //
  525. // Create the full setup path
  526. //
  527. wsprintf( SetupPath,
  528. L"\\\\%ws\\REMINST\\%ws",
  529. OscFindVariableW( clientState, "SERVERNAME" ),
  530. OscFindVariableW( clientState, "INSTALLPATH" ) );
  531. EnterCriticalSection( &gcsParameters );
  532. if ( BinlGlobalOurDnsName == NULL ) {
  533. LeaveCriticalSection( &gcsParameters );
  534. Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
  535. goto e0;
  536. }
  537. MachineInfo.HostName = (PWCHAR) BinlAllocateMemory( ( lstrlenW( BinlGlobalOurDnsName ) + 1 ) * sizeof(WCHAR) );
  538. if ( !MachineInfo.HostName ) {
  539. LeaveCriticalSection( &gcsParameters );
  540. Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
  541. goto e0;
  542. }
  543. lstrcpyW( MachineInfo.HostName, BinlGlobalOurDnsName );
  544. LeaveCriticalSection( &gcsParameters );
  545. //
  546. // Fill in the rest of the MachineInfo structure
  547. //
  548. MachineInfo.Name = pMachineName;
  549. MachineInfo.MachineDN = pMachineDN;
  550. #if 1
  551. //
  552. // Don't store BOOTFILE in the cache/DS, since BOOTFILE points to setupldr
  553. // and we want the cache entry to point to oschooser. If we store an
  554. // empty string in the cache/DS, then GetBootParametersExt() will replace
  555. // that with the path to oschooser.
  556. //
  557. MachineInfo.BootFileName = L"";
  558. #else
  559. MachineInfo.BootFileName = OscFindVariableW( clientState, "BOOTFILE" );
  560. #endif
  561. MachineInfo.SetupPath = SetupPath;
  562. MachineInfo.SamName = pNameDollarSign;
  563. MachineInfo.Password = clientState->MachineAccountPassword;
  564. MachineInfo.PasswordLength = clientState->MachineAccountPasswordLength;
  565. MachineInfo.dwFlags = MI_NAME
  566. | MI_HOSTNAME
  567. | MI_BOOTFILENAME
  568. | MI_SETUPPATH
  569. | MI_SAMNAME
  570. | MI_PASSWORD
  571. | MI_MACHINEDN
  572. | MI_GUID;
  573. //
  574. // Create the MAO in the DS
  575. //
  576. Error = UpdateAccount( clientState,
  577. &MachineInfo,
  578. clientState->fCreateNewAccount ); // create it
  579. if ( Error ) {
  580. goto e0;
  581. }
  582. //
  583. // Create the response to the client
  584. //
  585. Error = OscConstructSecret(
  586. clientState,
  587. clientState->MachineAccountPassword,
  588. clientState->MachineAccountPasswordLength,
  589. CreateData );
  590. if ( Error != ERROR_SUCCESS ) {
  591. OscCreateWin32SubError( clientState, Error );
  592. Error = ERROR_BINL_FAILED_TO_INITIALIZE_CLIENT;
  593. goto e0;
  594. }
  595. BinlPrint(( DEBUG_OSC, "Successfully created account for <%ws>\n", pMachineName ));
  596. pStrings[0] = pMachineName;
  597. pStrings[1] = OscFindVariableW( clientState, "USERNAME" );
  598. BinlReportEventW( EVENT_COMPUTER_ACCOUNT_CREATED_SUCCESSFULLY,
  599. EVENTLOG_INFORMATION_TYPE,
  600. 2,
  601. 0,
  602. pStrings,
  603. 0 );
  604. e0:
  605. // No need to call FreeMachineInfo() since all the information
  606. // in it is either allocated on the stack or is referenced
  607. // by the clientState, but we do need to free the HostName
  608. // since it is allocated above.
  609. if ( MachineInfo.HostName ) {
  610. BinlFreeMemory( MachineInfo.HostName );
  611. }
  612. if ( pMachineDN && clientState->fCreateNewAccount ) {
  613. BinlFreeMemory( pMachineDN );
  614. }
  615. return Error;
  616. }
  617. //
  618. // CheckForDuplicateMachineName( )
  619. //
  620. DWORD
  621. CheckForDuplicateMachineName(
  622. PCLIENT_STATE clientState,
  623. LPWSTR pszMachineName )
  624. {
  625. DWORD Error = ERROR_SUCCESS;
  626. PLDAPMessage LdapMessage = NULL;
  627. WCHAR Filter[128];
  628. DWORD count;
  629. PWCHAR ComputerAttrs[2];
  630. LPWSTR pDomain = OscFindVariableW( clientState, "MACHINEOU" );
  631. PWCHAR BaseDN;
  632. PLDAP LdapHandle;
  633. ULONG ldapRetryLimit = 0;
  634. PWCHAR *gcBase;
  635. PLDAPControlW controlArray[2];
  636. LDAPControlW controlNoReferrals;
  637. ULONG noReferralsPlease;
  638. ComputerAttrs[0] = &L"cn";
  639. ComputerAttrs[1] = NULL;
  640. TraceFunc( "CheckForDuplicateMachineName( )\n" );
  641. if (pDomain[0] == L'\0') {
  642. pDomain = OscFindVariableW( clientState, "USERDOMAIN" );
  643. BinlPrintDbg((DEBUG_ERRORS, "CheckforDupMachine: couldn't find root domain, using user's domain %ws\n.", pDomain));
  644. }
  645. BaseDN = StrStrIW( pDomain, L"DC=" );
  646. if (BaseDN == NULL) {
  647. BaseDN = pDomain;
  648. }
  649. LdapHandle = clientState->AuthenticatedDCLdapHandle;
  650. BinlAssert( LdapHandle != NULL );
  651. //
  652. // According to the DS guys, it's not necessarily the case that CN is
  653. // equal to SamAccountName and the latter is the important one. It has
  654. // a dollar sign at the end, so we'll tack that on.
  655. //
  656. wsprintf( Filter, L"(&(objectClass=Computer)(samAccountName=%s", pszMachineName );
  657. lstrcatW( Filter, L"$))" );
  658. //
  659. // we really don't want it to go chasing subordinate referrals over
  660. // the entire enterprise since we know what the domain is, therefore
  661. // limit it to only external referrals (for child domains).
  662. //
  663. noReferralsPlease = (ULONG)((ULONG_PTR) LDAP_CHASE_EXTERNAL_REFERRALS);
  664. controlNoReferrals.ldctl_oid = LDAP_CONTROL_REFERRALS_W;
  665. controlNoReferrals.ldctl_value.bv_len = sizeof(ULONG);
  666. controlNoReferrals.ldctl_value.bv_val = (PCHAR) &noReferralsPlease;
  667. controlNoReferrals.ldctl_iscritical = FALSE;
  668. controlArray[0] = &controlNoReferrals;
  669. controlArray[1] = NULL;
  670. Retry:
  671. Error = ldap_search_ext_s(LdapHandle,
  672. BaseDN,
  673. LDAP_SCOPE_SUBTREE,
  674. Filter,
  675. ComputerAttrs,
  676. FALSE,
  677. NULL,
  678. &controlArray[0],
  679. 0,
  680. 1,
  681. &LdapMessage);
  682. switch ( Error )
  683. {
  684. case LDAP_SUCCESS:
  685. break;
  686. case LDAP_BUSY:
  687. if (++ldapRetryLimit < LDAP_BUSY_LIMIT) {
  688. Sleep( LDAP_BUSY_DELAY );
  689. goto Retry;
  690. }
  691. // lack of break is on purpose.
  692. default:
  693. OscCreateLDAPSubError( clientState, Error );
  694. LogLdapError( EVENT_WARNING_LDAP_SEARCH_ERROR,
  695. Error,
  696. LdapHandle
  697. );
  698. BinlPrintDbg(( DEBUG_OSC_ERROR, "!!LdapError 0x%08x - Failed search to create machine name.\n", Error ));
  699. goto exitCheck;
  700. }
  701. count = ldap_count_entries( LdapHandle, LdapMessage );
  702. if ( count != 0 ) {
  703. Error = -1; // signal multiple accounts
  704. goto exitCheck;
  705. }
  706. ldap_msgfree( LdapMessage );
  707. LdapMessage = NULL;
  708. //
  709. // now we go check the GC.
  710. //
  711. gcBase = NULL;
  712. Error = InitializeConnection( TRUE, &LdapHandle, &gcBase );
  713. if ( Error != ERROR_SUCCESS ) {
  714. //
  715. // if no GC is present or available, we'll let this call succeed.
  716. // Reasoning here is GCs can be flaky creatures.
  717. //
  718. Error = ERROR_SUCCESS;
  719. goto exitCheck;
  720. }
  721. ldapRetryLimit = 0;
  722. RetryGC:
  723. Error = ldap_search_ext_s(LdapHandle,
  724. *gcBase,
  725. LDAP_SCOPE_SUBTREE,
  726. Filter,
  727. ComputerAttrs,
  728. FALSE,
  729. NULL,
  730. NULL,
  731. 0,
  732. 1,
  733. &LdapMessage);
  734. switch ( Error )
  735. {
  736. case LDAP_SUCCESS:
  737. break;
  738. case LDAP_BUSY:
  739. if (++ldapRetryLimit < LDAP_BUSY_LIMIT) {
  740. Sleep( LDAP_BUSY_DELAY );
  741. goto RetryGC;
  742. }
  743. // lack of break is on purpose.
  744. default:
  745. OscCreateLDAPSubError( clientState, Error );
  746. LogLdapError( EVENT_WARNING_LDAP_SEARCH_ERROR,
  747. Error,
  748. LdapHandle
  749. );
  750. BinlPrintDbg(( DEBUG_OSC_ERROR, "!!LdapError 0x%08x - Failed search to create machine name.\n", Error ));
  751. goto exitCheck;
  752. }
  753. count = ldap_count_entries( LdapHandle, LdapMessage );
  754. if ( count != 0 ) {
  755. Error = -1; // signal multiple accounts
  756. } else {
  757. Error = ERROR_SUCCESS;
  758. }
  759. exitCheck:
  760. if (LdapMessage) {
  761. ldap_msgfree( LdapMessage );
  762. }
  763. return Error;
  764. }
  765. //
  766. // GenerateMachineName( )
  767. //
  768. DWORD
  769. GenerateMachineName(
  770. PCLIENT_STATE clientState
  771. )
  772. {
  773. DWORD Error = ERROR_SUCCESS;
  774. GENNAME_VARIABLES variables;
  775. WCHAR szMachineName[ DNS_MAX_LABEL_BUFFER_LENGTH ];
  776. DWORD Count = 1;
  777. LPWSTR missingVariable;
  778. BOOL usedCounter;
  779. LPWSTR pszUserName;
  780. LPWSTR pszFirstName;
  781. LPWSTR pszLastName;
  782. LPWSTR pUserOU;
  783. LPWSTR pszMAC;
  784. TraceFunc( "GenerateMachineName( )\n" );
  785. pszUserName = OscFindVariableW( clientState, "USERNAME" );
  786. if ( pszUserName[0] == L'\0' ) {
  787. OscAddVariableA( clientState, "SUBERROR", "USERNAME" );
  788. return ERROR_BINL_MISSING_VARIABLE;
  789. }
  790. Error = OscGetUserDetails( clientState );
  791. if (Error != ERROR_SUCCESS) {
  792. BinlPrintDbg((DEBUG_OSC_ERROR,
  793. "GenerateMachineName: OscGetUserDetails failed %lx\n", Error));
  794. return Error;
  795. }
  796. pszFirstName = OscFindVariableW( clientState, "USERFIRSTNAME" );
  797. pszLastName = OscFindVariableW( clientState, "USERLASTNAME" );
  798. pUserOU = OscFindVariableW( clientState, "USEROU" );
  799. pszMAC = OscFindVariableW( clientState, "MAC" );
  800. variables.UserName = pszUserName;
  801. variables.FirstName = pszFirstName;
  802. variables.LastName = pszLastName;
  803. variables.MacAddress = pszMAC;
  804. variables.AllowCounterTruncation = FALSE;
  805. TryAgain:
  806. variables.Counter = ++clientState->nCreateAccountCounter;
  807. EnterCriticalSection( &gcsParameters );
  808. Error = GenerateNameFromTemplate(
  809. NewMachineNamingPolicy,
  810. &variables,
  811. szMachineName,
  812. DNS_MAX_LABEL_BUFFER_LENGTH,
  813. &missingVariable,
  814. &usedCounter,
  815. NULL
  816. );
  817. LeaveCriticalSection( &gcsParameters );
  818. if ( (Error != GENNAME_NO_ERROR) && (Error != GENNAME_NAME_TOO_LONG) ) {
  819. if ( Error == GENNAME_VARIABLE_MISSING ) {
  820. OscAddVariableW( clientState, "SUBERROR", missingVariable );
  821. clientState->nCreateAccountCounter = 0;
  822. return ERROR_BINL_MISSING_VARIABLE;
  823. }
  824. BinlAssert( (Error == GENNAME_COUNTER_TOO_HIGH) || (Error = GENNAME_TEMPLATE_INVALID) );
  825. clientState->nCreateAccountCounter = 0;
  826. return ERROR_BINL_UNABLE_TO_GENERATE_MACHINE_NAME;
  827. }
  828. BinlPrint(( DEBUG_OSC, "Generated MachineName = %ws\n", szMachineName ));
  829. Error = CheckForDuplicateMachineName( clientState, szMachineName );
  830. if ( Error == -1 ) {
  831. if ( usedCounter ) {
  832. goto TryAgain;
  833. }
  834. Error = ERROR_BINL_DUPLICATE_MACHINE_NAME_FOUND;
  835. } else if ( Error == LDAP_SIZELIMIT_EXCEEDED ) {
  836. BinlPrint(( DEBUG_OSC, "MachineName '%s' has mutliple accounts already.\n", szMachineName ));
  837. if ( usedCounter ) {
  838. goto TryAgain;
  839. }
  840. } else if ( Error != LDAP_SUCCESS ) {
  841. Error = ERROR_BINL_UNABLE_TO_GENERATE_MACHINE_NAME;
  842. } else {
  843. BinlPrintDbg(( DEBUG_OSC, "MachineName: '%ws'\n", szMachineName ));
  844. Error = OscAddVariableW( clientState, "MACHINENAME", szMachineName );
  845. if ( Error == ERROR_SUCCESS ) {
  846. WCHAR NameDollarSign[17]; // MACHINENAME(15)+'$'+'\0'
  847. UINT uSize;
  848. clientState->fAutomaticMachineName = TRUE;
  849. uSize = sizeof(NameDollarSign);
  850. // DnsHostnameToComputerNameW takes BYTEs in and returns the # of WCHARs out.
  851. if ( !DnsHostnameToComputerNameW( szMachineName, NameDollarSign, &uSize ) ) {
  852. // if this fails(?), default to truncating machine name and
  853. // add '$' to the end
  854. BinlPrintDbg((DEBUG_OSC_ERROR, "!! Error 0x%08x - DnsHostnameToComputerNameW failed.\n", GetLastError() ));
  855. BinlPrintDbg((DEBUG_OSC, "WARNING: Truncating machine name to 15 characters to generated NETBIOS name.\n" ));
  856. memset( NameDollarSign, 0, sizeof(NameDollarSign) );
  857. wcsncpy( NameDollarSign, szMachineName, 15 );
  858. }
  859. wcscat( NameDollarSign, L"$");
  860. Error = OscAddVariableW( clientState, "NETBIOSNAME", NameDollarSign );
  861. }
  862. }
  863. clientState->nCreateAccountCounter = 0;
  864. return Error;
  865. }
  866. DWORD
  867. OscCheckMachineDN(
  868. PCLIENT_STATE clientState
  869. )
  870. //
  871. // Ensure that the client name, OU, and domain are setup correctly. If there
  872. // are duplicate records in the DS with this same guid, we'll return
  873. // ERROR_BINL_DUPLICATE_MACHINE_NAME_FOUND and set %SUBERROR% string to
  874. // those DNs and return an error.
  875. //
  876. {
  877. DWORD dwErr = ERROR_SUCCESS;
  878. PWCHAR pwc; // parsing pointer
  879. WCHAR wch; // temp wide char
  880. PWCHAR pMachineName; // Pointer to Machine Name variable value
  881. PWCHAR pMachineOU; // Pointer to where the MAO will be created
  882. PWCHAR pDomain; // Pointer to Domain variable name
  883. PCHAR pGuid; // Pointer to Guid variable name
  884. WCHAR NameDollarSign[17]; // MACHINENAME(15)+'$'+'\0'
  885. WCHAR Path[MAX_PATH]; // general purpose path buffer
  886. ULONG i; // general counter
  887. BOOL b; // general purpose BOOLean.
  888. UINT uSize;
  889. UCHAR Guid[ BINL_GUID_LENGTH ];
  890. PMACHINE_INFO pMachineInfo = NULL;
  891. USHORT SystemArchitecture;
  892. DWORD DupRecordCount;
  893. TraceFunc("OscCheckMachineDN( )\n");
  894. if ( clientState->fHaveSetupMachineDN ) {
  895. // we've been through this logic before, just exit here with success.
  896. dwErr = ERROR_SUCCESS;
  897. goto e0;
  898. }
  899. dwErr = OscGetUserDetails( clientState );
  900. if (dwErr != ERROR_SUCCESS) {
  901. BinlPrintDbg((DEBUG_OSC_ERROR,
  902. "OscCheckMachineDN: OscGetUserDetails failed %lx\n", dwErr));
  903. goto e0;
  904. }
  905. pGuid = OscFindVariableA( clientState, "GUID" );
  906. if ( pGuid[0] == '\0' ) {
  907. OscAddVariableA( clientState, "SUBERROR", "GUID" );
  908. dwErr = ERROR_BINL_MISSING_VARIABLE;
  909. goto e0;
  910. }
  911. dwErr = OscGuidToBytes( pGuid, Guid );
  912. if ( dwErr != ERROR_SUCCESS ) {
  913. goto e0;
  914. }
  915. // Do we have a machine name yet?
  916. clientState->fCreateNewAccount = TRUE;
  917. pMachineName = OscFindVariableW( clientState, "MACHINENAME" );
  918. if ( pMachineName[0] == L'\0' ) {
  919. clientState->CustomInstall = FALSE;
  920. } else {
  921. clientState->CustomInstall = TRUE;
  922. }
  923. clientState->fHaveSetupMachineDN = TRUE;
  924. SystemArchitecture = OscPlatformToArchitecture(clientState);
  925. //
  926. // See if the client already has an account with a matching GUID
  927. //
  928. dwErr = GetBootParameters( Guid,
  929. &pMachineInfo,
  930. MI_NAME | MI_DOMAIN | MI_MACHINEDN,
  931. SystemArchitecture,
  932. FALSE );
  933. if (( dwErr == ERROR_SUCCESS ) &&
  934. ( !clientState->CustomInstall )) {
  935. PWCHAR pszOU;
  936. //
  937. // Since we asked for these, they should be set.
  938. //
  939. ASSERT ( pMachineInfo->dwFlags & MI_NAME );
  940. ASSERT ( pMachineInfo->dwFlags & MI_MACHINEDN );
  941. //
  942. // if this is an automatic install, then we simply set the
  943. // account info to the account we found.
  944. //
  945. // skip the comma
  946. pszOU = wcschr( pMachineInfo->MachineDN, L',' );
  947. if (pszOU) {
  948. pszOU++;
  949. OscAddVariableW( clientState, "MACHINEOU", pszOU );
  950. }
  951. OscAddVariableW( clientState, "MACHINEDN", pMachineInfo->MachineDN );
  952. dwErr = OscAddVariableW( clientState, "MACHINENAME", pMachineInfo->Name );
  953. if ( dwErr != ERROR_SUCCESS ) {
  954. BinlPrintDbg((DEBUG_OSC_ERROR,
  955. "!!Error 0x%08x - OscCheckMachineDN: Unable to add MACHINENAME variable\n", dwErr ));
  956. goto e0;
  957. }
  958. clientState->fCreateNewAccount = FALSE;
  959. if ( pMachineInfo->dwFlags & MI_DOMAIN ) {
  960. OscAddVariableW( clientState, "MACHINEDOMAIN", pMachineInfo->Domain );
  961. }
  962. }
  963. //
  964. // Do we have an OU yet?
  965. //
  966. pMachineOU = OscFindVariableW( clientState, "MACHINEOU" );
  967. if ( pMachineOU[0] == L'\0' ) {
  968. //
  969. // Here's how we determine the OU...
  970. //
  971. // if this is an auto, then MACHINEOU shouldn't already have
  972. // been set by now. If it's custom, then MACHINEOU may be empty
  973. // or it may be set to what the user wants it set to.
  974. //
  975. // if it's not already set, then we look at BinlGlobalDefaultContainer.
  976. //
  977. // if this value is equal to the server's DN, then we set it to the
  978. // default for this domain.
  979. //
  980. // if BinlGlobalDefaultContainer is empty, then we set it to the
  981. // user's OU.
  982. //
  983. if ( BinlGlobalServerDN == NULL ) {
  984. dwErr = ERROR_BINL_NO_DN_AVAILABLE_FOR_SERVER;
  985. BinlPrintDbg((DEBUG_OSC_ERROR,
  986. "!!Error - OscCheckMachineDN: BinlGlobalServerDN is null\n", dwErr ));
  987. goto e0;
  988. }
  989. EnterCriticalSection( &gcsParameters );
  990. if ( BinlGlobalServerDN &&
  991. StrCmpI( BinlGlobalDefaultContainer, BinlGlobalServerDN ) == 0) {
  992. //
  993. // If the machine's OU is the same as this server's OU, then we set
  994. // it to the default for this server's domain.
  995. //
  996. PWCHAR pDomain = StrStrIW( BinlGlobalServerDN, L"DC=" );
  997. ULONG dwErr;
  998. if ( pDomain ) {
  999. dwErr = OscGetDefaultContainerForDomain( clientState, pDomain );
  1000. if (dwErr != ERROR_SUCCESS) {
  1001. BinlPrintDbg(( DEBUG_OSC_ERROR, "Could not get default MACHINEOU, 0x%x\n",dwErr));
  1002. }
  1003. }
  1004. } else {
  1005. dwErr = OscAddVariableW( clientState, "MACHINEOU", BinlGlobalDefaultContainer );
  1006. if ( dwErr != ERROR_SUCCESS ) {
  1007. LeaveCriticalSection( &gcsParameters );
  1008. BinlPrintDbg(( DEBUG_OSC_ERROR, "!!Error 0x%08x - Could not add MACHINEOU\n", dwErr ));
  1009. goto e0;
  1010. }
  1011. }
  1012. LeaveCriticalSection( &gcsParameters );
  1013. pMachineOU = OscFindVariableW( clientState, "MACHINEOU" );
  1014. if ( pMachineOU[0] == L'\0' ) {
  1015. LPWSTR pUserOU = OscFindVariableW( clientState, "USEROU" );
  1016. //
  1017. // the machine OU isn't already specified, that means we set it to
  1018. // the same as the user's OU.
  1019. //
  1020. if ( pUserOU[0] == L'\0' ) {
  1021. BinlPrintDbg(( DEBUG_OSC_ERROR, "Missing UserOU variable\n" ));
  1022. OscAddVariableA( clientState, "SUBERROR", "USEROU" );
  1023. dwErr = ERROR_BINL_MISSING_VARIABLE;
  1024. goto e0;
  1025. }
  1026. dwErr = OscAddVariableW( clientState, "MACHINEOU", pUserOU );
  1027. if ( dwErr != ERROR_SUCCESS ) {
  1028. BinlPrintDbg(( DEBUG_OSC_ERROR, "!!Error 0x%08x - Could not add MACHINEOU\n", dwErr ));
  1029. goto e0;
  1030. }
  1031. pMachineOU = OscFindVariableW( clientState, "MACHINEOU" );
  1032. }
  1033. }
  1034. //
  1035. // We need to generate the MACHINENAME after MACHINEOU because we need
  1036. // to know MACHINEOU to know which domain to check for duplicate
  1037. // machine names.
  1038. //
  1039. pMachineName = OscFindVariableW( clientState, "MACHINENAME" );
  1040. if ( pMachineName[0] == L'\0' ) {
  1041. dwErr = GenerateMachineName( clientState );
  1042. if ( dwErr != ERROR_SUCCESS ) {
  1043. BinlPrintDbg(( DEBUG_OSC_ERROR, "!!Error 0x%08x - Failed to generate machine name\n" ));
  1044. goto e0;
  1045. }
  1046. // now we should have one
  1047. pMachineName = OscFindVariableW( clientState, "MACHINENAME" );
  1048. }
  1049. BinlAssertMsg( pMachineName[0] != L'\0', "Missing MACHINENAME" );
  1050. uSize = sizeof(NameDollarSign);
  1051. // DnsHostnameToComputerNameW takes BYTEs in and returns the # of WCHARs out.
  1052. if ( !DnsHostnameToComputerNameW( pMachineName, NameDollarSign, &uSize ) )
  1053. {
  1054. // if this fails(?), default to truncating machine name and
  1055. // add '$' to the end
  1056. BinlPrintDbg((DEBUG_OSC_ERROR, "!! Error 0x%08x - DnsHostnameToComputerNameW failed.\n", GetLastError( ) ));
  1057. BinlPrintDbg((DEBUG_OSC, "WARNING: Truncating machine name to 15 characters to generated NETBIOS name.\n" ));
  1058. memset( NameDollarSign, 0, sizeof(NameDollarSign) );
  1059. wcsncpy( NameDollarSign, pMachineName, 15 );
  1060. // don't return the error...
  1061. }
  1062. wcscat( NameDollarSign, L"$");
  1063. OscAddVariableW( clientState, "NETBIOSNAME", NameDollarSign );
  1064. // Do we have a domain yet?
  1065. pDomain = OscFindVariableW( clientState, "MACHINEDOMAIN" );
  1066. if ( pDomain[0] == L'\0' ) {
  1067. // skip to the first "DC="
  1068. pDomain = StrStrIW( pMachineOU, L"DC=" );
  1069. if ( pDomain ) {
  1070. PDS_NAME_RESULTW pResults;
  1071. dwErr = DsCrackNames( INVALID_HANDLE_VALUE,
  1072. DS_NAME_FLAG_SYNTACTICAL_ONLY,
  1073. DS_FQDN_1779_NAME,
  1074. DS_CANONICAL_NAME,
  1075. 1,
  1076. &pDomain,
  1077. &pResults );
  1078. BinlAssertMsg( dwErr == ERROR_SUCCESS, "Error in DsCrackNames" );
  1079. if ( dwErr == ERROR_SUCCESS ) {
  1080. if ( pResults->cItems == 1
  1081. && pResults->rItems[0].status == DS_NAME_NO_ERROR
  1082. && pResults->rItems[0].pName ) { // paranoid
  1083. pResults->rItems[0].pName[wcslen(pResults->rItems[0].pName)-1] = L'\0';
  1084. OscAddVariableW( clientState, "MACHINEDOMAIN", pResults->rItems[0].pName );
  1085. }
  1086. DsFreeNameResult( pResults );
  1087. pDomain = OscFindVariableW( clientState, "MACHINEDOMAIN" );
  1088. } else {
  1089. pDomain = NULL;
  1090. }
  1091. }
  1092. }
  1093. // All else fails default to the servers
  1094. if ( !pDomain || pDomain[0] == '\0' )
  1095. {
  1096. OscAddVariableW( clientState,
  1097. "MACHINEDOMAIN",
  1098. OscFindVariableW( clientState, "SERVERDOMAIN" ) );
  1099. }
  1100. //
  1101. // check for duplicate accounts in the ds. fail if we find any, though
  1102. // we only fail after we have everything setup in case the user on
  1103. // custom install wants to ignore the error. For automatic, it's
  1104. // currently a fatal error but this could be changed in the osc screens.
  1105. //
  1106. if (( pMachineInfo != NULL ) &&
  1107. ( pMachineInfo->dwFlags & MI_MACHINEDN )) {
  1108. PDUP_GUID_DN dupDN;
  1109. PLIST_ENTRY listEntry;
  1110. if (( pMachineInfo->dwFlags & MI_NAME ) &&
  1111. ( clientState->CustomInstall )) {
  1112. //
  1113. // if this is a custom install, then we compare the account
  1114. // the user entered with all the existing accounts we found.
  1115. // We want to match both machine namd and OU (this is really
  1116. // just the DN but we have not necessarily constructed that
  1117. // yet).
  1118. //
  1119. // First we try the main entry in the cache, then all of
  1120. // the rest in the DNsWithSameGuid list.
  1121. //
  1122. // skip the comma
  1123. ULONG err;
  1124. PWCHAR MachineDNToUse;
  1125. PWCHAR pszOU = wcschr( pMachineInfo->MachineDN, L',' );
  1126. if (pszOU) {
  1127. pszOU++;
  1128. }
  1129. //
  1130. // See if the main machine name and OU in the cache
  1131. // entry match.
  1132. //
  1133. if ((CompareStringW(
  1134. LOCALE_SYSTEM_DEFAULT,
  1135. NORM_IGNORECASE,
  1136. pMachineName,
  1137. -1,
  1138. pMachineInfo->Name,
  1139. -1
  1140. ) != 2)
  1141. ||
  1142. ((pszOU == NULL) && (pMachineOU[0] != L'\0'))
  1143. ||
  1144. ((pszOU != NULL) &&
  1145. (CompareStringW(
  1146. LOCALE_SYSTEM_DEFAULT,
  1147. NORM_IGNORECASE,
  1148. pMachineOU,
  1149. -1,
  1150. pszOU,
  1151. -1
  1152. ) != 2))) {
  1153. //
  1154. // We did not match the main entry in the cache, so
  1155. // keep looking.
  1156. //
  1157. for (listEntry = pMachineInfo->DNsWithSameGuid.Flink;
  1158. listEntry != &pMachineInfo->DNsWithSameGuid;
  1159. listEntry = listEntry->Flink) {
  1160. dupDN = CONTAINING_RECORD(listEntry, DUP_GUID_DN, ListEntry);
  1161. pszOU = wcschr( &dupDN->DuplicateName[dupDN->DuplicateDNOffset], L',' );
  1162. if (pszOU) {
  1163. pszOU++;
  1164. }
  1165. if ((CompareStringW(
  1166. LOCALE_SYSTEM_DEFAULT,
  1167. NORM_IGNORECASE,
  1168. pMachineName,
  1169. -1,
  1170. dupDN->DuplicateName,
  1171. -1
  1172. ) != 2)
  1173. ||
  1174. ((pszOU == NULL) && (pMachineOU[0] != L'\0'))
  1175. ||
  1176. ((pszOU != NULL) &&
  1177. (CompareStringW(
  1178. LOCALE_SYSTEM_DEFAULT,
  1179. NORM_IGNORECASE,
  1180. pMachineOU,
  1181. -1,
  1182. pszOU,
  1183. -1
  1184. ) != 2))) {
  1185. //
  1186. // No match on this one.
  1187. //
  1188. continue;
  1189. } else {
  1190. //
  1191. // We found a match. Note which DN to use for
  1192. // this account.
  1193. //
  1194. MachineDNToUse = &dupDN->DuplicateName[dupDN->DuplicateDNOffset];
  1195. break;
  1196. }
  1197. }
  1198. //
  1199. // If we got to the end of our list with no match, jump to
  1200. // the error case.
  1201. //
  1202. if (listEntry == &pMachineInfo->DNsWithSameGuid) {
  1203. goto exitWithDupError;
  1204. }
  1205. } else {
  1206. //
  1207. // The main cache entry matched.
  1208. //
  1209. MachineDNToUse = pMachineInfo->MachineDN;
  1210. }
  1211. //
  1212. // We didn't jump to exitWithDupError above, so we found a match.
  1213. // we know that the client is using an existing account, let's
  1214. // mark the client state as such. this is the custom case.
  1215. //
  1216. clientState->fCreateNewAccount = FALSE;
  1217. OscAddVariableW( clientState, "MACHINEDN", MachineDNToUse );
  1218. if ( pMachineInfo->dwFlags & MI_DOMAIN ) {
  1219. OscAddVariableW( clientState, "MACHINEDOMAIN", pMachineInfo->Domain );
  1220. }
  1221. }
  1222. if (!IsListEmpty(&pMachineInfo->DNsWithSameGuid)) {
  1223. //
  1224. // if there's more than one account, we fill in SUBERROR
  1225. // with a list of the duplicates and return an error.
  1226. //
  1227. PWCHAR dnList;
  1228. ULONG requiredSize;
  1229. exitWithDupError:
  1230. //
  1231. // since we tack a <BR> to the end of each string, we'll account
  1232. // for it when we allocate the string as +4 from what we need.
  1233. //
  1234. #define MAX_DUPLICATE_RECORDS_TO_DISPLAY 4
  1235. requiredSize = lstrlenW( pMachineInfo->Name ) + sizeof("<BR>");
  1236. listEntry = pMachineInfo->DNsWithSameGuid.Flink;
  1237. DupRecordCount = 0;
  1238. while (listEntry != &pMachineInfo->DNsWithSameGuid) {
  1239. dupDN = CONTAINING_RECORD(listEntry, DUP_GUID_DN, ListEntry);
  1240. listEntry = listEntry->Flink;
  1241. DupRecordCount += 1;
  1242. if (DupRecordCount <= MAX_DUPLICATE_RECORDS_TO_DISPLAY) {
  1243. requiredSize += lstrlenW( &dupDN->DuplicateName[0] ) + sizeof("<BR>");
  1244. } else if (DupRecordCount == MAX_DUPLICATE_RECORDS_TO_DISPLAY+1) {
  1245. requiredSize += lstrlenW( L"..." ) + sizeof("<BR>");
  1246. }
  1247. }
  1248. dnList = BinlAllocateMemory( requiredSize * sizeof(WCHAR) );
  1249. DupRecordCount = 0;
  1250. if (dnList != NULL) {
  1251. ULONG nameLength;
  1252. nameLength = lstrlenW(pMachineInfo->Name);
  1253. //
  1254. // The Name field should not end in a '$'.
  1255. //
  1256. ASSERT (!((nameLength > 1) && (pMachineInfo->Name[nameLength-1] == L'$')));
  1257. lstrcpyW( dnList, pMachineInfo->Name );
  1258. lstrcatW( dnList, L"<BR>" );
  1259. listEntry = pMachineInfo->DNsWithSameGuid.Flink;
  1260. while (listEntry != &pMachineInfo->DNsWithSameGuid) {
  1261. dupDN = CONTAINING_RECORD(listEntry, DUP_GUID_DN, ListEntry);
  1262. listEntry = listEntry->Flink;
  1263. DupRecordCount += 1;
  1264. if (DupRecordCount <= MAX_DUPLICATE_RECORDS_TO_DISPLAY) {
  1265. nameLength = lstrlenW(dupDN->DuplicateName);
  1266. //
  1267. // The DuplicateName field should not have the '$' either
  1268. //
  1269. ASSERT (!((nameLength > 1) && (dupDN->DuplicateName[nameLength-1] == L'$')));
  1270. lstrcatW( dnList, dupDN->DuplicateName );
  1271. lstrcatW( dnList, L"<BR>" );
  1272. } else if (DupRecordCount == MAX_DUPLICATE_RECORDS_TO_DISPLAY + 1) {
  1273. lstrcatW( dnList, L"..." );
  1274. lstrcatW( dnList, L"<BR>" );
  1275. }
  1276. }
  1277. } else {
  1278. dnList = pMachineInfo->MachineDN;
  1279. }
  1280. OscAddVariableW( clientState, "SUBERROR", dnList );
  1281. dwErr = ERROR_BINL_DUPLICATE_MACHINE_NAME_FOUND;
  1282. }
  1283. } else {
  1284. //
  1285. // We must not exist in the DS yet so there cannot be a duplicate.
  1286. // set the error to successand return.
  1287. //
  1288. dwErr = ERROR_SUCCESS;
  1289. }
  1290. e0:
  1291. if ( pMachineInfo ) {
  1292. BinlDoneWithCacheEntry( pMachineInfo, FALSE );
  1293. }
  1294. return dwErr;
  1295. }
  1296. DWORD
  1297. OscGetDefaultContainerForDomain (
  1298. PCLIENT_STATE clientState,
  1299. PWCHAR DomainDN
  1300. )
  1301. {
  1302. PLDAP LdapHandle;
  1303. PLDAPMessage LdapMessage = NULL;
  1304. PWCHAR ldapAttributes[2];
  1305. BOOLEAN impersonating = FALSE;
  1306. PLDAPMessage ldapEntry;
  1307. PWCHAR *ldapWellKnownObjectValues = NULL;
  1308. PWCHAR objectValue;
  1309. PWCHAR guidEnd;
  1310. WCHAR savedChar;
  1311. ULONG Error = LDAP_NO_SUCH_ATTRIBUTE;
  1312. ULONG Count;
  1313. if (clientState->AuthenticatedDCLdapHandle == NULL) {
  1314. Error = OscImpersonate(clientState);
  1315. if (Error != ERROR_SUCCESS) {
  1316. BinlPrintDbg((DEBUG_ERRORS,
  1317. "OscGetDefaultContainer: OscImpersonate failed %lx\n", Error));
  1318. return Error;
  1319. }
  1320. impersonating = TRUE;
  1321. BinlAssert( clientState->AuthenticatedDCLdapHandle != NULL );
  1322. }
  1323. LdapHandle = clientState->AuthenticatedDCLdapHandle;
  1324. //
  1325. // we look up the wellKnownObjects in the root of the domain
  1326. //
  1327. ldapAttributes[0] = L"wellKnownObjects";
  1328. ldapAttributes[1] = NULL;
  1329. Error = ldap_search_ext_sW(LdapHandle,
  1330. DomainDN,
  1331. LDAP_SCOPE_BASE,
  1332. L"objectclass=*",
  1333. ldapAttributes,
  1334. FALSE,
  1335. NULL,
  1336. NULL,
  1337. 0,
  1338. 0,
  1339. &LdapMessage);
  1340. Count = ldap_count_entries( LdapHandle, LdapMessage );
  1341. Error = LDAP_NO_SUCH_ATTRIBUTE;
  1342. if (Count == 0) {
  1343. BinlPrintDbg((DEBUG_ERRORS,
  1344. "OscGetDefaultContainer: get default domain failed with no records found\n"));
  1345. LogLdapError( EVENT_WARNING_LDAP_SEARCH_ERROR,
  1346. Error,
  1347. LdapHandle
  1348. );
  1349. goto exitGetDefaultContainer;
  1350. }
  1351. ldapEntry = ldap_first_entry( LdapHandle, LdapMessage );
  1352. if (ldapEntry == NULL) {
  1353. BinlPrintDbg((DEBUG_ERRORS,
  1354. "OscGetDefaultContainer: get first entry failed\n"));
  1355. goto exitGetDefaultContainer;
  1356. }
  1357. ldapWellKnownObjectValues = ldap_get_valuesW( LdapHandle,
  1358. ldapEntry,
  1359. L"wellKnownObjects" );
  1360. if (ldapWellKnownObjectValues == NULL) {
  1361. BinlPrintDbg((DEBUG_ERRORS,"OscGetDefaultContainer: get value failed\n"));
  1362. goto exitGetDefaultContainer;
  1363. }
  1364. Count = 0;
  1365. objectValue = NULL;
  1366. while (1) {
  1367. objectValue = ldapWellKnownObjectValues[Count++];
  1368. if (objectValue == NULL) {
  1369. break;
  1370. }
  1371. //
  1372. // the structure of this particular field is :
  1373. // L"B:32:GUID:DN" where GUID is AA312825768811D1ADED00C04FD8D5CD
  1374. //
  1375. if (lstrlenW( objectValue ) <
  1376. lstrlenW( COMPUTER_DEFAULT_CONTAINER_IN_B32_FORM )) {
  1377. continue;
  1378. }
  1379. //
  1380. // see if it matches "B:32:specialGuid:" then DN follows
  1381. //
  1382. guidEnd = objectValue + lstrlenW( COMPUTER_DEFAULT_CONTAINER_IN_B32_FORM );
  1383. savedChar = *guidEnd;
  1384. *guidEnd = L'\0';
  1385. if (lstrcmpiW( objectValue, COMPUTER_DEFAULT_CONTAINER_IN_B32_FORM) != 0) {
  1386. *guidEnd = savedChar;
  1387. continue;
  1388. }
  1389. *guidEnd = savedChar; // this is the first character of the DN
  1390. //
  1391. // we have our value, now copy it off.
  1392. //
  1393. OscAddVariableW( clientState, "MACHINEOU", guidEnd );
  1394. Error = ERROR_SUCCESS;
  1395. break;
  1396. }
  1397. exitGetDefaultContainer:
  1398. if (ldapWellKnownObjectValues) {
  1399. ldap_value_free( ldapWellKnownObjectValues );
  1400. }
  1401. if (LdapMessage) {
  1402. ldap_msgfree( LdapMessage );
  1403. }
  1404. if (impersonating) {
  1405. OscRevert( clientState );
  1406. }
  1407. return Error;
  1408. }
  1409. VOID
  1410. LogLdapError (
  1411. ULONG LdapEvent,
  1412. ULONG LdapError,
  1413. PLDAP LdapHandle OPTIONAL
  1414. )
  1415. {
  1416. PWCHAR Server = NULL;
  1417. if (LdapError != LDAP_SUCCESS) {
  1418. if (LdapHandle != NULL) {
  1419. ldap_get_option( LdapHandle, LDAP_OPT_HOST_NAME, &Server );
  1420. }
  1421. if (++BinlGlobalLdapErrorCount <= BinlGlobalMaxLdapErrorsLogged) {
  1422. PWCHAR strings[2];
  1423. if (Server) {
  1424. strings[0] = Server;
  1425. } else {
  1426. strings[0] = L"?";
  1427. }
  1428. strings[1] = NULL;
  1429. BinlReportEventW( LdapEvent,
  1430. EVENTLOG_WARNING_TYPE,
  1431. (Server != NULL) ? 1 : 0,
  1432. sizeof(LdapError),
  1433. (Server != NULL) ? strings : NULL,
  1434. &LdapError
  1435. );
  1436. }
  1437. }
  1438. return;
  1439. }
  1440. DWORD
  1441. MyGetDcHandle(
  1442. PCLIENT_STATE clientState,
  1443. PCSTR DomainName,
  1444. PHANDLE Handle
  1445. )
  1446. {
  1447. DWORD Error;
  1448. HANDLE hDC;
  1449. PDOMAIN_CONTROLLER_INFOA DCI = NULL;
  1450. DWORD impersonateError = ERROR_NOT_ENOUGH_SERVER_MEMORY;
  1451. BinlPrintDbg((
  1452. DEBUG_OSC,
  1453. "Attempting discovery of DC in %s domain.\n",
  1454. DomainName ));
  1455. Error = DsGetDcNameA(
  1456. NULL,
  1457. DomainName,
  1458. NULL,
  1459. NULL,
  1460. DS_IS_DNS_NAME | DS_RETURN_DNS_NAME,
  1461. &DCI);
  1462. if (Error == ERROR_SUCCESS) {
  1463. BinlPrintDbg((
  1464. DEBUG_OSC,
  1465. "DC is %s, attempting bind.\n",
  1466. DCI->DomainControllerName ));
  1467. impersonateError = OscImpersonate(clientState);
  1468. Error = DsBindA(DCI->DomainControllerName, NULL, &hDC);
  1469. if (Error != ERROR_SUCCESS) {
  1470. BinlPrintDbg((
  1471. DEBUG_OSC_ERROR,
  1472. "DsBind failed, ec = %d.\n",
  1473. Error ));
  1474. } else {
  1475. PSTR p = DCI->DomainControllerName;
  1476. *Handle = hDC;
  1477. //
  1478. // if it's got '\\' in the front, then strip those
  1479. // off because ldap_init hates them
  1480. //
  1481. while (*p == '\\') {
  1482. p = p + 1;
  1483. }
  1484. OscAddVariableA( clientState, "DCNAME", p );
  1485. }
  1486. NetApiBufferFree(DCI);
  1487. } else {
  1488. BinlPrintDbg((
  1489. DEBUG_OSC_ERROR,
  1490. "DsGetDcNameA failed, ec = %d.\n",
  1491. Error ));
  1492. }
  1493. if (impersonateError == ERROR_SUCCESS) {
  1494. OscRevert(clientState);
  1495. }
  1496. return(Error);
  1497. }