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.

920 lines
23 KiB

  1. /*++
  2. Copyright (c) 1998 - 1998 Microsoft Corporation
  3. Module Name:
  4. refresh.c
  5. Abstract:
  6. This Module implements the delegation tool, which allows for the management
  7. of access to DS objects
  8. Author:
  9. Mac McLain (MacM) 10-15-96
  10. Environment:
  11. User Mode
  12. Revision History:
  13. --*/
  14. #include "stdafx.h"
  15. #include "utils.h"
  16. #include "dsace.h"
  17. #include "dsacls.h"
  18. typedef struct _DEFAULT_SD_NODE {
  19. PWSTR ObjectClass;
  20. PSECURITY_DESCRIPTOR DefaultSd;
  21. struct _DEFAULT_SD_NODE *Next;
  22. } DEFAULT_SD_NODE, *PDEFAULT_SD_NODE;
  23. typedef struct _DEFAULT_SD_INFO {
  24. LDAP *Ldap;
  25. PWSTR SchemaPath;
  26. PSID DomainSid;
  27. PDEFAULT_SD_NODE SdList;
  28. } DEFAULT_SD_INFO, *PDEFAULT_SD_INFO;
  29. #define DSACL_ALL_FILTER L"(ObjectClass=*)"
  30. #define DSACL_SCHEMA_NC L"schemaNamingContext"
  31. #define DSACL_OBJECT_CLASS L"objectClass"
  32. #define DSACL_LDAP_DN L"(ldapDisplayName="
  33. #define DSACL_LDAP_DN_CLOSE L")"
  34. #define DSACL_DEFAULT_SD L"defaultSecurityDescriptor"
  35. DWORD
  36. FindDefaultSdForClass(
  37. IN PWSTR ClassId,
  38. IN PDEFAULT_SD_INFO SdInfo,
  39. IN OUT PDEFAULT_SD_NODE *DefaultSdNode
  40. )
  41. /*++
  42. Routine Description:
  43. This routine will search the SD_INFO list for an existing entry that matches the current
  44. class type. If no such entry is found, one will be created from information from the schema
  45. Arguments:
  46. ClassId - ClassId to find the default SD node for
  47. SdInfo - Current list of default SDs and associated information
  48. DefaultSdNode - Where the locted node is returned
  49. Returns:
  50. ERROR_SUCCESS - Success
  51. ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
  52. --*/
  53. {
  54. DWORD Win32Err = ERROR_SUCCESS;
  55. PWSTR Attributes[] = {
  56. NULL,
  57. NULL
  58. };
  59. LDAPMessage *Message = NULL, *Entry;
  60. PWSTR Filter = NULL, SchemaObjectDn = NULL, DefaultSd = NULL, *DefaultSdList = NULL;
  61. PDEFAULT_SD_NODE Node;
  62. *DefaultSdNode = NULL;
  63. Node = SdInfo->SdList;
  64. while ( Node ) {
  65. if ( !_wcsicmp( Node->ObjectClass, ClassId ) ) {
  66. *DefaultSdNode = Node;
  67. break;
  68. }
  69. Node = Node->Next;
  70. }
  71. //
  72. // If it wasn't found, we'll have to go out and load it out of the Ds.
  73. //
  74. if ( !Node ) {
  75. Filter = (LPWSTR)LocalAlloc( LMEM_FIXED,
  76. sizeof( DSACL_LDAP_DN ) - sizeof( WCHAR ) +
  77. ( wcslen( ClassId ) * sizeof( WCHAR ) ) +
  78. sizeof( DSACL_LDAP_DN_CLOSE ) );
  79. if ( !Filter ) {
  80. Win32Err = ERROR_NOT_ENOUGH_MEMORY;
  81. goto FindDefaultExit;
  82. }
  83. swprintf( Filter,
  84. L"%ws%ws%ws",
  85. DSACL_LDAP_DN,
  86. ClassId,
  87. DSACL_LDAP_DN_CLOSE );
  88. //
  89. // Now, do the search
  90. //
  91. Win32Err = LdapMapErrorToWin32( ldap_search_s( SdInfo->Ldap,
  92. SdInfo->SchemaPath,
  93. LDAP_SCOPE_SUBTREE,
  94. Filter,
  95. Attributes,
  96. 0,
  97. &Message ) );
  98. if ( Win32Err != ERROR_SUCCESS ) {
  99. goto FindDefaultExit;
  100. }
  101. Entry = ldap_first_entry( SdInfo->Ldap, Message );
  102. if ( Entry ) {
  103. SchemaObjectDn = ldap_get_dn( SdInfo->Ldap, Entry );
  104. ldap_msgfree( Message );
  105. if ( !SchemaObjectDn ) {
  106. Win32Err = ERROR_NOT_ENOUGH_MEMORY;
  107. goto FindDefaultExit;
  108. }
  109. } else {
  110. Win32Err = LdapMapErrorToWin32( SdInfo->Ldap->ld_errno );
  111. goto FindDefaultExit;
  112. }
  113. //
  114. // Ok, now we can read the default security descriptor
  115. //
  116. Attributes[ 0 ] = DSACL_DEFAULT_SD;
  117. Win32Err = LdapMapErrorToWin32( ldap_search_s( SdInfo->Ldap,
  118. SchemaObjectDn,
  119. LDAP_SCOPE_BASE,
  120. DSACL_ALL_FILTER,
  121. Attributes,
  122. 0,
  123. &Message ) );
  124. Entry = ldap_first_entry( SdInfo->Ldap, Message );
  125. if ( Entry ) {
  126. //
  127. // Now, we'll have to get the values
  128. //
  129. DefaultSdList = ldap_get_values( SdInfo->Ldap, Entry, Attributes[ 0 ] );
  130. if ( DefaultSdList ) {
  131. DefaultSd = DefaultSdList[ 0 ];
  132. } else {
  133. Win32Err = LdapMapErrorToWin32( SdInfo->Ldap->ld_errno );
  134. goto FindDefaultExit;
  135. }
  136. ldap_msgfree( Message );
  137. }
  138. //
  139. // Find a new node and insert it
  140. //
  141. Node = (DEFAULT_SD_NODE*)LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
  142. sizeof( DEFAULT_SD_NODE ) );
  143. if ( !Node ) {
  144. Win32Err = ERROR_NOT_ENOUGH_MEMORY;
  145. goto FindDefaultExit;
  146. }
  147. if ( !ConvertStringSDToSDRootDomain( SdInfo->DomainSid,
  148. DefaultSd,
  149. SDDL_REVISION,
  150. &Node->DefaultSd,
  151. NULL ) ) {
  152. Win32Err = GetLastError();
  153. }
  154. if ( Win32Err == ERROR_SUCCESS ) {
  155. Node->ObjectClass =(LPWSTR) LocalAlloc( LMEM_FIXED,
  156. ( wcslen( ClassId ) + 1 ) * sizeof( WCHAR ) );
  157. if ( Node->ObjectClass == NULL ) {
  158. Win32Err = ERROR_NOT_ENOUGH_MEMORY;
  159. } else {
  160. wcscpy( Node->ObjectClass, ClassId );
  161. Node->Next = SdInfo->SdList;
  162. SdInfo->SdList = Node;
  163. }
  164. }
  165. if ( Win32Err != ERROR_SUCCESS ) {
  166. LocalFree( Node->DefaultSd );
  167. LocalFree( Node->ObjectClass );
  168. LocalFree( Node );
  169. } else {
  170. *DefaultSdNode = Node;
  171. }
  172. }
  173. FindDefaultExit:
  174. LocalFree( Filter );
  175. if ( SchemaObjectDn ) {
  176. ldap_memfree( SchemaObjectDn );
  177. }
  178. if ( DefaultSdList ) {
  179. ldap_value_free( DefaultSdList );
  180. }
  181. return( Win32Err );
  182. }
  183. DWORD
  184. SetDefaultSdForObject(
  185. IN LDAP *Ldap,
  186. IN PWSTR ObjectPath,
  187. IN PDEFAULT_SD_INFO SdInfo,
  188. IN SECURITY_INFORMATION Protection
  189. )
  190. /*++
  191. Routine Description:
  192. This routine set the default security descriptor on the indicated object
  193. Arguments:
  194. Ldap - Ldap connect to the server holding the object
  195. ObjectPath - 1779 style path to the object
  196. SdInfo - Current list of default SDs and associated information
  197. Returns:
  198. ERROR_SUCCESS - Success
  199. ERROR_DS_NAME_TYPE_UNKNOWN - Unable to determine the class id of the object
  200. --*/
  201. {
  202. DWORD Win32Err = ERROR_SUCCESS;
  203. PWSTR Attributes[] = {
  204. DSACL_OBJECT_CLASS,
  205. NULL
  206. };
  207. LDAPMessage *Message = NULL, *Entry;
  208. PWSTR ClassId = NULL;
  209. PWSTR *ClassList = NULL;
  210. ULONG i;
  211. PDEFAULT_SD_NODE DefaultSdNode = NULL;
  212. PACTRL_ACCESS NewAccess = NULL;
  213. PACTRL_AUDIT NewAudit = NULL;
  214. //
  215. // First, get the class id off of the object
  216. //
  217. Win32Err = LdapMapErrorToWin32( ldap_search_s( Ldap,
  218. ObjectPath,
  219. LDAP_SCOPE_BASE,
  220. DSACL_ALL_FILTER,
  221. Attributes,
  222. 0,
  223. &Message ) );
  224. if ( Win32Err != ERROR_SUCCESS ) {
  225. goto SetDefaultExit;
  226. }
  227. Entry = ldap_first_entry( Ldap, Message );
  228. if ( Entry ) {
  229. //
  230. // Now, we'll have to get the values
  231. //
  232. ClassList = ldap_get_values( Ldap, Entry, Attributes[ 0 ] );
  233. if ( ClassList ) {
  234. //
  235. // Get the class id
  236. //
  237. i = 0;
  238. while ( TRUE ) {
  239. if ( ClassList[ i ] ) {
  240. i++;
  241. } else {
  242. break;
  243. }
  244. }
  245. // ASSERT( i > 0 );
  246. if ( i == 0 ) {
  247. Win32Err = ERROR_DS_NAME_TYPE_UNKNOWN;
  248. goto SetDefaultExit;
  249. }
  250. ClassId = ClassList[ i - 1 ];
  251. } else {
  252. Win32Err = LdapMapErrorToWin32( Ldap->ld_errno );
  253. goto SetDefaultExit;
  254. }
  255. ldap_msgfree( Message );
  256. Message = NULL;
  257. }
  258. if ( !ClassId ) {
  259. Win32Err = ERROR_DS_NAME_TYPE_UNKNOWN;
  260. goto SetDefaultExit;
  261. }
  262. //
  263. // Now, see if we have a cache entry for that...
  264. //
  265. Win32Err = FindDefaultSdForClass( ClassId,
  266. SdInfo,
  267. &DefaultSdNode );
  268. if ( Win32Err != ERROR_SUCCESS ) {
  269. goto SetDefaultExit;
  270. }
  271. //
  272. // Ok, we have everything we need, so let's go ahead and set it all
  273. //
  274. /* Win32Err = ConvertSecurityDescriptorToAccessNamed( ObjectPath,
  275. SE_DS_OBJECT_ALL,
  276. DefaultSdNode->DefaultSd,
  277. &NewAccess,
  278. &NewAudit,
  279. NULL,
  280. NULL );
  281. */
  282. if ( Win32Err == ERROR_SUCCESS ) {
  283. Win32Err = WriteObjectSecurity(ObjectPath,
  284. DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | Protection,
  285. DefaultSdNode->DefaultSd
  286. );
  287. }
  288. if ( Win32Err == ERROR_SUCCESS ) {
  289. DisplayMessageEx( 0, MSG_DSACLS_PROCESSED, ObjectPath );
  290. }
  291. SetDefaultExit:
  292. if ( ClassList ) {
  293. ldap_value_free( ClassList );
  294. }
  295. if ( Message ) {
  296. ldap_msgfree( Message );
  297. }
  298. LocalFree( NewAccess );
  299. LocalFree( NewAudit );
  300. return( Win32Err );
  301. }
  302. DWORD
  303. SetDefaultSdForObjectAndChildren(
  304. IN LDAP *Ldap,
  305. IN PWSTR ObjectPath,
  306. IN PDEFAULT_SD_INFO SdInfo,
  307. IN BOOLEAN Propagate,
  308. IN SECURITY_INFORMATION Protection
  309. )
  310. /*++
  311. Routine Description:
  312. This routine will set the security descriptor on the object and potentially all of its
  313. children to the default security as obtained from the schema
  314. Arguments:
  315. Ldap - Ldap connect to the server holding the object
  316. ObjectPath - 1779 style path to the object
  317. SdInfo - Current list of default SDs and associated information
  318. Propagate - If TRUE, reset the security on the children as well
  319. Returns:
  320. ERROR_SUCCESS - Success
  321. ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
  322. --*/
  323. {
  324. DWORD Win32Err = ERROR_SUCCESS;
  325. PWSTR Attributes[] = {
  326. NULL
  327. };
  328. LDAPMessage *Message = NULL, *Entry;
  329. PWSTR ChildName = NULL;
  330. PLDAPSearch SearchHandle = NULL;
  331. ULONG Count;
  332. //
  333. // First, get the class id off of the object
  334. //
  335. SearchHandle = ldap_search_init_pageW( Ldap,
  336. ObjectPath,
  337. Propagate ? LDAP_SCOPE_SUBTREE : LDAP_SCOPE_BASE,
  338. DSACL_ALL_FILTER,
  339. Attributes,
  340. FALSE,
  341. NULL,
  342. NULL,
  343. 0,
  344. 2000,
  345. NULL );
  346. if ( SearchHandle == NULL ) {
  347. Win32Err = LdapMapErrorToWin32( LdapGetLastError( ) );
  348. } else {
  349. while ( Win32Err == ERROR_SUCCESS ) {
  350. Count = 0;
  351. //
  352. // Get the next page
  353. //
  354. Win32Err = ldap_get_next_page_s( Ldap,
  355. SearchHandle,
  356. NULL,
  357. 100,
  358. &Count,
  359. &Message );
  360. if ( Message ) {
  361. Entry = ldap_first_entry( Ldap, Message );
  362. while ( Entry ) {
  363. ChildName = ldap_get_dn( SdInfo->Ldap, Entry );
  364. if ( !ChildName ) {
  365. Win32Err = ERROR_NOT_ENOUGH_MEMORY;
  366. break;
  367. }
  368. Win32Err = SetDefaultSdForObject( Ldap,
  369. ChildName,
  370. SdInfo,
  371. Protection);
  372. ldap_memfree( ChildName );
  373. if ( Win32Err != ERROR_SUCCESS ) {
  374. break;
  375. }
  376. Entry = ldap_next_entry( Ldap, Entry );
  377. }
  378. Win32Err = Ldap->ld_errno;
  379. ldap_msgfree( Message );
  380. Message = NULL;
  381. }
  382. if ( Win32Err == LDAP_NO_RESULTS_RETURNED ) {
  383. Win32Err = ERROR_SUCCESS;
  384. break;
  385. }
  386. }
  387. ldap_search_abandon_page( Ldap,
  388. SearchHandle );
  389. }
  390. return( Win32Err );
  391. }
  392. DWORD
  393. BindToDsObject(
  394. IN PWSTR ObjectPath,
  395. OUT PLDAP *Ldap,
  396. OUT PSID *DomainSid OPTIONAL
  397. )
  398. /*++
  399. Routine Description:
  400. This routine will bind to the ldap server on a domain controller that holds the specified
  401. object path. Optionally, the sid of the domain hosted by that domain controller is returned
  402. Arguments:
  403. ObjectPath - 1779 style path to the object
  404. Ldap - Where the ldap connection handle is returned
  405. DomainSid - Sid of the domain hosted by the domain controller.
  406. Returns:
  407. ERROR_SUCCESS - Success
  408. ERROR_PATH_NOT_FOUND - A domain controller for this path could not be located
  409. ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
  410. --*/
  411. {
  412. DWORD Win32Err = ERROR_SUCCESS;
  413. PWSTR ServerName = NULL;
  414. PWSTR Separator = NULL;
  415. PDOMAIN_CONTROLLER_INFO DcInfo = NULL;
  416. PWSTR Path = NULL;
  417. HANDLE DsHandle = NULL;
  418. PDS_NAME_RESULT NameRes = NULL;
  419. BOOLEAN NamedServer = FALSE;
  420. UNICODE_STRING ServerNameU;
  421. OBJECT_ATTRIBUTES ObjectAttributes;
  422. LSA_HANDLE LsaHandle;
  423. PPOLICY_PRIMARY_DOMAIN_INFO PolicyPDI = NULL;
  424. NTSTATUS Status;
  425. //
  426. // Get a server name
  427. //
  428. /* if ( wcslen( ObjectPath ) > 2 && *ObjectPath == L'\\' && *( ObjectPath + 1 ) == L'\\' ) {
  429. Separator = wcschr( ObjectPath + 2, L'\\' );
  430. if ( Separator ) {
  431. *Separator = L'\0';
  432. Path = Separator + 1;
  433. }
  434. ServerName = ObjectPath + 2;
  435. NamedServer = TRUE;
  436. } else {
  437. Path = ObjectPath;
  438. Win32Err = DsGetDcName( NULL,
  439. NULL,
  440. NULL,
  441. NULL,
  442. DS_IP_REQUIRED |
  443. DS_DIRECTORY_SERVICE_REQUIRED,
  444. &DcInfo );
  445. if ( Win32Err == ERROR_SUCCESS ) {
  446. ServerName = DcInfo[ 0 ].DomainControllerName + 2;
  447. }
  448. }
  449. //
  450. // Do the bind and crack
  451. //
  452. if ( Win32Err == ERROR_SUCCESS ) {
  453. Win32Err = DsBind( ServerName,
  454. NULL,
  455. &DsHandle );
  456. if ( Win32Err == ERROR_SUCCESS ) {
  457. Win32Err = DsCrackNames( DsHandle,
  458. DS_NAME_NO_FLAGS,
  459. DS_FQDN_1779_NAME,
  460. DS_FQDN_1779_NAME,
  461. 1,
  462. &Path,
  463. &NameRes );
  464. if ( Win32Err == ERROR_SUCCESS ) {
  465. if ( NameRes->cItems != 0 && !NamedServer &&
  466. NameRes->rItems[ 0 ].status == DS_NAME_ERROR_DOMAIN_ONLY ) {
  467. NetApiBufferFree( DcInfo );
  468. DcInfo = NULL;
  469. Win32Err = DsGetDcNameW( NULL,
  470. NameRes->rItems[ 0 ].pDomain,
  471. NULL,
  472. NULL,
  473. DS_IP_REQUIRED |
  474. DS_DIRECTORY_SERVICE_REQUIRED,
  475. &DcInfo );
  476. if ( Win32Err == ERROR_SUCCESS ) {
  477. DsUnBindW( &DsHandle );
  478. DsHandle = NULL;
  479. ServerName = DcInfo->DomainControllerName + 2;
  480. //Win32Err = DsBind( DcInfo->DomainControllerAddress,
  481. // NULL,
  482. // &DsHandle );
  483. //
  484. Win32Err = DsBind( ServerName,
  485. NULL,
  486. &DsHandle );
  487. if ( Win32Err == ERROR_SUCCESS ) {
  488. Win32Err = DsCrackNames( DsHandle,
  489. DS_NAME_NO_FLAGS,
  490. DS_FQDN_1779_NAME,
  491. DS_FQDN_1779_NAME,
  492. 1,
  493. &Path,
  494. &NameRes);
  495. }
  496. }
  497. }
  498. }
  499. }
  500. }
  501. */
  502. //
  503. // Now, do the bind
  504. //
  505. *Ldap = ldap_open( g_szServerName,
  506. LDAP_PORT );
  507. if ( *Ldap == NULL ) {
  508. Win32Err = ERROR_PATH_NOT_FOUND;
  509. } else {
  510. Win32Err = LdapMapErrorToWin32( ldap_bind_s( *Ldap,
  511. NULL,
  512. NULL,
  513. LDAP_AUTH_SSPI ) );
  514. }
  515. //
  516. // If specified, get the sid for the domain
  517. //
  518. if ( DomainSid ) {
  519. RtlInitUnicodeString( &ServerNameU, g_szServerName );
  520. InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL );
  521. //
  522. // Get the sid of the domain
  523. //
  524. Status = LsaOpenPolicy( &ServerNameU,
  525. &ObjectAttributes,
  526. POLICY_VIEW_LOCAL_INFORMATION,
  527. &LsaHandle );
  528. if ( NT_SUCCESS( Status ) ) {
  529. Status = LsaQueryInformationPolicy( LsaHandle,
  530. PolicyPrimaryDomainInformation,
  531. ( PVOID * )&PolicyPDI );
  532. if ( NT_SUCCESS( Status ) ) {
  533. *DomainSid = (PSID)LocalAlloc( LMEM_FIXED,
  534. RtlLengthSid( PolicyPDI->Sid ) );
  535. if ( *DomainSid == NULL ) {
  536. Status = STATUS_INSUFFICIENT_RESOURCES;
  537. } else {
  538. RtlCopySid( RtlLengthSid( PolicyPDI->Sid ), *DomainSid, PolicyPDI->Sid );
  539. }
  540. LsaFreeMemory( PolicyPDI );
  541. }
  542. LsaClose( LsaHandle );
  543. }
  544. if ( !NT_SUCCESS( Status ) ) {
  545. Win32Err = RtlNtStatusToDosError( Status );
  546. ldap_unbind( *Ldap );
  547. *Ldap = NULL;
  548. }
  549. }
  550. return( Win32Err );
  551. }
  552. DWORD
  553. SetDefaultSecurityOnObjectTree(
  554. IN PWSTR ObjectPath,
  555. IN BOOLEAN Propagate,
  556. IN SECURITY_INFORMATION Protection
  557. )
  558. /*++
  559. Routine Description:
  560. This routine will set the security descriptor on the object and potentially all of its
  561. children to the default security as obtained from the schema
  562. Arguments:
  563. ObjectPath - 1779 style path to the object
  564. Propagate - If TRUE, reset the security on the children as well
  565. Returns:
  566. ERROR_SUCCESS - Success
  567. --*/
  568. {
  569. DWORD Win32Err = ERROR_SUCCESS;
  570. PWSTR Attributes[] = {
  571. DSACL_SCHEMA_NC,
  572. NULL
  573. };
  574. LDAPMessage *Message, *Entry;
  575. PWSTR *PathList = NULL;
  576. DEFAULT_SD_INFO SdInfo = {
  577. NULL,
  578. NULL,
  579. NULL,
  580. NULL
  581. };
  582. PDEFAULT_SD_NODE CleanupNode;
  583. //
  584. // Bind to the ds object
  585. //
  586. Win32Err = BindToDsObject( ObjectPath,
  587. &SdInfo.Ldap,
  588. &SdInfo.DomainSid );
  589. if ( Win32Err != ERROR_SUCCESS ) {
  590. goto SetDefaultExit;
  591. }
  592. //
  593. // Get the schema path
  594. //
  595. Win32Err = LdapMapErrorToWin32( ldap_search_s( SdInfo.Ldap,
  596. NULL,
  597. LDAP_SCOPE_BASE,
  598. DSACL_ALL_FILTER,
  599. Attributes,
  600. 0,
  601. &Message ) );
  602. if ( Win32Err == ERROR_SUCCESS ) {
  603. Entry = ldap_first_entry( SdInfo.Ldap, Message );
  604. if ( Entry ) {
  605. //
  606. // Now, we'll have to get the values
  607. //
  608. PathList = ldap_get_values( SdInfo.Ldap, Entry, Attributes[ 0 ] );
  609. if ( PathList ) {
  610. SdInfo.SchemaPath = PathList[ 0 ];
  611. } else {
  612. Win32Err = LdapMapErrorToWin32( SdInfo.Ldap->ld_errno );
  613. }
  614. ldap_msgfree( Message );
  615. }
  616. }
  617. if( SdInfo.Ldap )
  618. {
  619. Win32Err = SetDefaultSdForObjectAndChildren( SdInfo.Ldap,
  620. ObjectPath,
  621. &SdInfo,
  622. Propagate,
  623. Protection);
  624. }
  625. SetDefaultExit:
  626. //
  627. // Unbind from the DS
  628. //
  629. if ( SdInfo.Ldap ) {
  630. ldap_unbind( SdInfo.Ldap );
  631. }
  632. if ( PathList ) {
  633. ldap_value_free( PathList );
  634. }
  635. //
  636. // Clean up the Default SD Info list
  637. //
  638. LocalFree( SdInfo.DomainSid );
  639. while ( SdInfo.SdList ) {
  640. CleanupNode = SdInfo.SdList;
  641. LocalFree( CleanupNode->ObjectClass );
  642. LocalFree( CleanupNode->DefaultSd );
  643. SdInfo.SdList = SdInfo.SdList->Next;
  644. LocalFree( CleanupNode );
  645. }
  646. return( Win32Err );
  647. }