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.

652 lines
19 KiB

  1. /*--
  2. Copyright (c) 1996-1997 Microsoft Corporation
  3. Module Name:
  4. nlldap.cxx
  5. Abstract:
  6. Routines the use the DS's ldap filter.
  7. This is actually in a .cxx file by itself to work around a compiler bug
  8. where .c files cannot include ldap.h
  9. Author:
  10. Environment:
  11. User mode only.
  12. Contains NT-specific code.
  13. Requires ANSI C extensions: slash-slash comments, long external names.
  14. Revision History:
  15. --*/
  16. #ifdef __cplusplus
  17. extern "C" {
  18. #endif /* __cplusplus */
  19. #include <nt.h> // LARGE_INTEGER definition
  20. #include <ntrtl.h> // LARGE_INTEGER definition
  21. #include <nturtl.h> // LARGE_INTEGER definition
  22. #include <ntlsa.h> // Needed by lsrvdata.h
  23. #include <ntddbrow.h> // Needed by nlcommon.h
  24. #define NOMINMAX // Avoid redefinition of min and max in stdlib.h
  25. #include <rpc.h> // Needed by logon_s.h
  26. #include <logon_s.h> // includes lmcons.h, lmaccess.h, netlogon.h, ssi.h, windef.h
  27. //
  28. #include <windows.h>
  29. #include <winsock2.h> // Winsock support
  30. #include <lmapibuf.h> // NetApiBufferFree
  31. #include <logonp.h> // NetpLogon routines
  32. #include <lsarpc.h> // Needed by lsrvdata.h and logonsrv.h
  33. #include <lsaisrv.h> // Needed by changelg.h
  34. #include <wincrypt.h> // CryptoAPI, needed by lsrvdata.h
  35. #ifdef __cplusplus
  36. } /* extern "C" */
  37. #endif /* __cplusplus */
  38. #include <netlib.h> // Needed by nlcommon.h
  39. #ifdef __cplusplus
  40. extern "C" {
  41. #endif /* __cplusplus */
  42. #include <samrpc.h> // Needed by lsrvdata.h and logonsrv.h
  43. #include <ntdsa.h> // THSave/THRestore
  44. #include <wmistr.h> // Needed by lsrvdata.h
  45. #include <evntrace.h> // Needed by lsrvdata.h
  46. #include <sspi.h> // Needed by ssiinit.h
  47. //
  48. // Netlogon specific header files.
  49. //
  50. #define _AVOID_REPL_API 1
  51. #include <nlrepl.h> // I_Net*
  52. #include "worker.h" // Worker routines
  53. #include "nlcommon.h" // Routines shared with logonsrv\common
  54. #include "domain.h" // Hosted domain definitions
  55. #include "changelg.h" // Change Log support
  56. #include "chutil.h" // Change Log utilities
  57. #include "iniparm.h" // registry parameters
  58. #include "ssiinit.h" // Misc global definitions
  59. #include "nldebug.h" // Netlogon debugging
  60. #include "lsrvdata.h" // Globals
  61. // #include <string.h> // strnicmp ...
  62. #include <ldap.h> // Filter for LDAP query
  63. #define NL_MAXIMUM_USER_NAME_LENGTH 20 // The max SAM allows
  64. BOOLEAN
  65. IsFilterName(
  66. IN Filter *CurrentFilter,
  67. IN LPSTR NameToMatch
  68. )
  69. /*++
  70. Routine Description:
  71. If the CurrentFilter is for NameToMatch, return TRUE
  72. Arguments:
  73. Filter - Pointer to an equalityMatch Filter element.
  74. NameToMatch - Specifies the name to compare with the filter element.
  75. Return Value:
  76. TRUE name matches.
  77. --*/
  78. {
  79. ULONG NameLength;
  80. NameLength = strlen( NameToMatch );
  81. if ( NameLength != CurrentFilter->u.equalityMatch.attributeDesc.length ) {
  82. return FALSE;
  83. }
  84. if ( _strnicmp( NameToMatch,
  85. (LPSTR)CurrentFilter->u.equalityMatch.attributeDesc.value,
  86. NameLength ) != 0 ) {
  87. return FALSE;
  88. }
  89. return TRUE;
  90. }
  91. NET_API_STATUS
  92. FilterString(
  93. IN Filter *CurrentFilter,
  94. IN ULONG UnicodeStringBufferSize,
  95. OUT LPWSTR UnicodeStringBuffer OPTIONAL,
  96. OUT LPWSTR *UnicodeString
  97. )
  98. /*++
  99. Routine Description:
  100. Return a zero terminated unicode string containing the value of the
  101. current equalityMatch filter element.
  102. The value of the element is expected to be in UTF-8.
  103. Arguments:
  104. Filter - Pointer to an equalityMatch Filter element.
  105. UnicodeStringBuffer -- Buffer to copy the covnverted string to.
  106. If NULL, the function will allocate the needed memory and
  107. return it in UnicodeString.
  108. UnicodeStringSize - Size in wide charactes of UnicodeStringBuffer.
  109. If this size is less than what's needed to store the resulting
  110. NULL terminated unicode string, the function will allocate the
  111. needed memory and return it in UnicodeString.
  112. UnicodeString - Returns a pointer to the resulting string.
  113. If the passed in buffer for the resulting unicode string isn't
  114. large enough, the function will allocate the needed memory and
  115. the pointer to the allocated memory will be returned in this
  116. parameter. If NULL and the passed in buffer isn't large enough
  117. to store the resulting NULL terminated string, the function
  118. returns ERROR_INSUFFICIENT_BUFFER. On successful return, the
  119. caller should check whether UnicodeString != UnicodeStringBuffer
  120. and, if so, free the allocated buffer using NetApiBufferFree.
  121. Return Value:
  122. Error returned by NetpAllocWStrFromUtf8StrAsRequired.
  123. --*/
  124. {
  125. NET_API_STATUS NetStatus;
  126. LPWSTR AllocatedUnicodeString = NULL;
  127. //
  128. // Call the worker routine
  129. //
  130. NetStatus = NetpAllocWStrFromUtf8StrAsRequired(
  131. (LPSTR)CurrentFilter->u.equalityMatch.assertionValue.value,
  132. CurrentFilter->u.equalityMatch.assertionValue.length,
  133. UnicodeStringBufferSize,
  134. UnicodeStringBuffer,
  135. &AllocatedUnicodeString );
  136. //
  137. // Set the return pointer to point to the appropriate buffer
  138. //
  139. if ( NetStatus == NO_ERROR ) {
  140. if ( AllocatedUnicodeString != NULL ) {
  141. NlAssert( AllocatedUnicodeString != UnicodeStringBuffer );
  142. *UnicodeString = AllocatedUnicodeString;
  143. } else {
  144. *UnicodeString = UnicodeStringBuffer;
  145. }
  146. }
  147. return NetStatus;
  148. }
  149. NET_API_STATUS
  150. FilterBinary(
  151. IN Filter *CurrentFilter,
  152. IN ULONG BufferSize,
  153. OUT PVOID Buffer,
  154. OUT PULONG DataSize
  155. )
  156. /*++
  157. Routine Description:
  158. Returns a properly aligned pointer to the binary data.
  159. Arguments:
  160. Filter - Pointer to an equalityMatch Filter element.
  161. Buffer - Buffer to copy the data into.
  162. BufferSize - The size of the passed buffer. If the
  163. buffer isn't large enough to store the data,
  164. ERROR_INSUFFICIENT_BUFFER is returned.
  165. DataSize - Size of the data copied
  166. Return Value:
  167. NO_ERROR - The data has been successfully coppied.
  168. ERROR_INSUFFICIENT_BUFFER - The passed in buffer is too small.
  169. --*/
  170. {
  171. if ( BufferSize < CurrentFilter->u.equalityMatch.assertionValue.length ) {
  172. return ERROR_INSUFFICIENT_BUFFER;
  173. }
  174. RtlCopyMemory( Buffer,
  175. CurrentFilter->u.equalityMatch.assertionValue.value,
  176. CurrentFilter->u.equalityMatch.assertionValue.length );
  177. *DataSize = CurrentFilter->u.equalityMatch.assertionValue.length;
  178. return NO_ERROR;
  179. }
  180. NTSTATUS
  181. I_NetLogonLdapLookup(
  182. IN PVOID FilterParam,
  183. OUT PVOID *Response,
  184. OUT PULONG ResponseSize
  185. )
  186. /*++
  187. Routine Description:
  188. See I_NetLogonLdapLookupEx.
  189. Arguments:
  190. See I_NetLogonLdapLookupEx.
  191. Return Value:
  192. See I_NetLogonLdapLookupEx.
  193. --*/
  194. {
  195. return I_NetLogonLdapLookupEx( FilterParam,
  196. NULL, // No Ip Address from client,
  197. Response,
  198. ResponseSize );
  199. }
  200. NTSTATUS
  201. I_NetLogonLdapLookupEx(
  202. IN PVOID FilterParam,
  203. IN PVOID ClientSockAddr,
  204. OUT PVOID *Response,
  205. OUT PULONG ResponseSize
  206. )
  207. /*++
  208. Routine Description:
  209. This routine builds a response to an LDAP ping of a DC. DsGetDcName does
  210. such a ping to ensure the DC is functional and still meets the requirements
  211. of the DsGetDcName. DsGetDcName does an LDAP lookup of the NULL DN asking
  212. for attribute "Netlogon". The DS turns that into a call to this routine
  213. passing in the filter parameter.
  214. The DS is expected to save the DS thread state before calling. This routine
  215. calls SAM which expects to have its own DS thread state.
  216. Arguments:
  217. Filter - Filter describing the query. The filter is built by the DsGetDcName
  218. client, so we can limit the flexibility significantly.
  219. SockAddr - Socket Address of the client this request came in on.
  220. Response - Returns a pointer to an allocated buffer containing
  221. the response to return to the caller. This response is a binary blob
  222. which should be returned to the caller bit-for-bit intact.
  223. The buffer should be freed be calling I_NetLogonFree.
  224. ResponseSize - Size (in bytes) of the returned message.
  225. Return Value:
  226. STATUS_SUCCESS -- The response was returned in the supplied buffer.
  227. STATUS_INVALID_PARAMETER -- The filter was invalid.
  228. --*/
  229. {
  230. NTSTATUS Status = STATUS_SUCCESS;
  231. NET_API_STATUS NetStatus = NO_ERROR;
  232. Filter *LdapFilter = (Filter *) FilterParam;
  233. Filter *CurrentFilter;
  234. struct _setof3_ *CurrentAnd;
  235. LPSTR DnsDomainName = NULL;
  236. GUID DomainGuid;
  237. GUID *DomainGuidPtr = NULL;
  238. // The domain SID buffer must be DWORD alligned. Avoid any buffer overflow problem
  239. // due to truncation in case SECURITY_MAX_SID_SIZE isn't evenly divisible by 4
  240. ULONG DomainSid[SECURITY_MAX_SID_SIZE/sizeof(ULONG)+1];
  241. PSID DomainSidPtr = NULL;
  242. WCHAR UnicodeComputerNameBuffer[MAX_COMPUTERNAME_LENGTH+1];
  243. LPWSTR UnicodeComputerName = NULL;
  244. WCHAR UnicodeUserNameBuffer[NL_MAXIMUM_USER_NAME_LENGTH+1];
  245. LPWSTR UnicodeUserName = NULL;
  246. ULONG AllowableAccountControlBits = 0;
  247. DWORD NtVersion = LMNT_MESSAGE;
  248. DWORD NtVersionFlags = NETLOGON_NT_VERSION_5;
  249. ULONG Length = 0;
  250. SOCKADDR_IN SockAddrIn;
  251. //
  252. // Initialization.
  253. //
  254. *Response = NULL;
  255. *ResponseSize = 0;
  256. //
  257. // If caller is calling when the netlogon service isn't running,
  258. // tell it so.
  259. //
  260. if ( !NlStartNetlogonCall() ) {
  261. return STATUS_INVALID_PARAMETER;
  262. }
  263. //
  264. // Validate the client socket address.
  265. //
  266. if ( ClientSockAddr != NULL ) {
  267. if ( ((PSOCKADDR)ClientSockAddr)->sa_family != AF_INET ) {
  268. NlPrint((NL_CRITICAL,
  269. "I_NetlogonLdapLookupEx: DS passed us a SOCKADDR that wasn't AF_INET (ignoring it)\n" ));
  270. ClientSockAddr = NULL;
  271. } else {
  272. //
  273. // Force the port number to zero to avoid confusion later.
  274. SockAddrIn = *((PSOCKADDR_IN)ClientSockAddr);
  275. SockAddrIn.sin_port = 0;
  276. ClientSockAddr = &SockAddrIn;
  277. }
  278. }
  279. //
  280. // Parse the filter.
  281. //
  282. if ( LdapFilter != NULL ) {
  283. //
  284. // The first element of the filter must be an AND.
  285. //
  286. if ( LdapFilter->choice != and_chosen ) {
  287. NlPrint((NL_CRITICAL,
  288. "I_NetlogonLdapLookup: Filter doesn't have AND %ld\n",
  289. LdapFilter->choice ));
  290. Status = STATUS_INVALID_PARAMETER;
  291. goto Cleanup;
  292. }
  293. //
  294. // Loop through the AND'ed attributes.
  295. //
  296. //
  297. for ( CurrentAnd = LdapFilter->u.and;
  298. CurrentAnd != NULL;
  299. CurrentAnd = CurrentAnd->next ) {
  300. CurrentFilter = &CurrentAnd->value;
  301. if ( CurrentFilter->choice != equalityMatch_chosen ) {
  302. NlPrint((NL_CRITICAL,
  303. "I_NetlogonLdapLookup: Filter doesn't have EqualityMatch %ld\n",
  304. LdapFilter->choice ));
  305. Status = STATUS_INVALID_PARAMETER;
  306. goto Cleanup;
  307. }
  308. //
  309. // Handle DnsDomainName parameter.
  310. //
  311. if ( IsFilterName( CurrentFilter, NL_FILTER_DNS_DOMAIN_NAME ) ) {
  312. if ( CurrentFilter->u.equalityMatch.assertionValue.length == 0 ) {
  313. NlPrint((NL_CRITICAL,
  314. "I_NetlogonLdapLookup: Bad DnsDomainName\n" ));
  315. Status = STATUS_INVALID_PARAMETER;
  316. goto Cleanup;
  317. }
  318. NetStatus = NetApiBufferAllocate(
  319. CurrentFilter->u.equalityMatch.assertionValue.length + sizeof(CHAR),
  320. (LPVOID *) &DnsDomainName );
  321. if ( NetStatus != NO_ERROR ) {
  322. Status = STATUS_NO_MEMORY;
  323. goto Cleanup;
  324. }
  325. RtlCopyMemory( DnsDomainName,
  326. CurrentFilter->u.equalityMatch.assertionValue.value,
  327. CurrentFilter->u.equalityMatch.assertionValue.length );
  328. DnsDomainName[ CurrentFilter->u.equalityMatch.assertionValue.length ] = '\0';
  329. //
  330. // Handle Host parameter.
  331. //
  332. } else if ( IsFilterName( CurrentFilter, NL_FILTER_HOST_NAME ) ) {
  333. NetStatus = FilterString( CurrentFilter,
  334. MAX_COMPUTERNAME_LENGTH+1,
  335. UnicodeComputerNameBuffer,
  336. &UnicodeComputerName );
  337. if ( NetStatus != NO_ERROR ) {
  338. NlPrint((NL_CRITICAL,
  339. "I_NetlogonLdapLookup: Bad UnicodeComputerName 0x%lx\n",
  340. NetStatus ));
  341. Status = STATUS_INVALID_PARAMETER;
  342. goto Cleanup;
  343. }
  344. //
  345. // Handle User parameter.
  346. //
  347. } else if ( IsFilterName( CurrentFilter, NL_FILTER_USER_NAME ) ) {
  348. NetStatus = FilterString( CurrentFilter,
  349. NL_MAXIMUM_USER_NAME_LENGTH+1,
  350. UnicodeUserNameBuffer,
  351. &UnicodeUserName );
  352. if ( NetStatus != NO_ERROR ) {
  353. NlPrint((NL_CRITICAL,
  354. "I_NetlogonLdapLookup: Bad UnicodeUserName 0x%lx\n",
  355. NetStatus ));
  356. Status = STATUS_INVALID_PARAMETER;
  357. goto Cleanup;
  358. }
  359. //
  360. // Handle AccountControlBits parameter.
  361. //
  362. } else if ( IsFilterName( CurrentFilter, NL_FILTER_ALLOWABLE_ACCOUNT_CONTROL ) ) {
  363. NetStatus = FilterBinary( CurrentFilter,
  364. sizeof(AllowableAccountControlBits),
  365. &AllowableAccountControlBits,
  366. &Length );
  367. if ( NetStatus != NO_ERROR || Length != sizeof(AllowableAccountControlBits) ) {
  368. NlPrint((NL_CRITICAL,
  369. "I_NetlogonLdapLookup: Bad AllowableAccountControl 0x%lx %lu\n",
  370. NetStatus,
  371. Length ));
  372. Status = STATUS_INVALID_PARAMETER;
  373. goto Cleanup;
  374. }
  375. //
  376. // Handle DomainSid parameter.
  377. //
  378. } else if ( IsFilterName( CurrentFilter, NL_FILTER_DOMAIN_SID ) ) {
  379. NetStatus = FilterBinary( CurrentFilter,
  380. sizeof(DomainSid),
  381. DomainSid,
  382. &Length );
  383. //
  384. // Check the length
  385. //
  386. if ( NetStatus != NO_ERROR ||
  387. !RtlValidSid(DomainSid) ||
  388. RtlLengthSid(DomainSid) != Length ) {
  389. NlPrint((NL_CRITICAL,
  390. "I_NetlogonLdapLookup: Bad DomainSid 0x%lx %ld\n",
  391. NetStatus,
  392. Length ));
  393. Status = STATUS_INVALID_PARAMETER;
  394. goto Cleanup;
  395. }
  396. DomainSidPtr = DomainSid;
  397. //
  398. // Handle DomainGuid parameter.
  399. //
  400. } else if ( IsFilterName( CurrentFilter, NL_FILTER_DOMAIN_GUID ) ) {
  401. NetStatus = FilterBinary( CurrentFilter,
  402. sizeof(DomainGuid),
  403. &DomainGuid,
  404. &Length );
  405. //
  406. // Check the length
  407. //
  408. if ( NetStatus != NO_ERROR ||
  409. Length != sizeof(GUID) ) {
  410. NlPrint((NL_CRITICAL,
  411. "I_NetlogonLdapLookup: Bad DomainGuid 0x%lx %ld\n",
  412. NetStatus,
  413. Length ));
  414. Status = STATUS_INVALID_PARAMETER;
  415. goto Cleanup;
  416. }
  417. DomainGuidPtr = &DomainGuid;
  418. //
  419. // Handle NtVersion parameter.
  420. //
  421. } else if ( IsFilterName( CurrentFilter, NL_FILTER_NT_VERSION ) ) {
  422. NetStatus = FilterBinary( CurrentFilter,
  423. sizeof(NtVersionFlags),
  424. &NtVersionFlags,
  425. &Length );
  426. if ( NetStatus != NO_ERROR || Length != sizeof(NtVersionFlags) ) {
  427. NlPrint((NL_CRITICAL,
  428. "I_NetlogonLdapLookup: Bad NtVersionFlags 0x%lx %ld\n",
  429. NetStatus,
  430. Length ));
  431. Status = STATUS_INVALID_PARAMETER;
  432. goto Cleanup;
  433. }
  434. //
  435. // Attributes we don't understand are ignored. That way clients
  436. // that are newer than this version can pass additional information
  437. // to newer DCs and not worry about breaking older DCs.
  438. //
  439. } else {
  440. NlPrint((NL_CRITICAL,
  441. "I_NetlogonLdapLookup: unrecognized parameter %.*s\n",
  442. CurrentFilter->u.equalityMatch.attributeDesc.length,
  443. CurrentFilter->u.equalityMatch.attributeDesc.value ));
  444. }
  445. }
  446. }
  447. //
  448. // Build the ping based on the queried information.
  449. //
  450. NetStatus = NlGetLocalPingResponse(
  451. L"UDP LDAP",
  452. TRUE, // LDAP ping
  453. NULL, // No Netbios domain name
  454. DnsDomainName,
  455. DomainGuidPtr,
  456. DomainSidPtr,
  457. FALSE, // Not PDC only
  458. UnicodeComputerName,
  459. UnicodeUserName,
  460. AllowableAccountControlBits,
  461. NtVersion,
  462. NtVersionFlags,
  463. (PSOCKADDR)ClientSockAddr,
  464. Response,
  465. ResponseSize );
  466. if ( NetStatus != NO_ERROR ) {
  467. Status = STATUS_INVALID_PARAMETER;
  468. goto Cleanup;
  469. }
  470. #if NETLOGONDBG
  471. NlpDumpBuffer( NL_MAILSLOT_TEXT, *Response, *ResponseSize );
  472. #endif // NETLOGONDBG
  473. Status = STATUS_SUCCESS;
  474. Cleanup:
  475. if ( DnsDomainName != NULL ) {
  476. NetApiBufferFree( DnsDomainName );
  477. }
  478. if ( UnicodeComputerName != NULL &&
  479. UnicodeComputerName != UnicodeComputerNameBuffer ) {
  480. NetApiBufferFree( UnicodeComputerName );
  481. }
  482. if ( UnicodeUserName != NULL &&
  483. UnicodeUserName != UnicodeUserNameBuffer ) {
  484. NetApiBufferFree( UnicodeUserName );
  485. }
  486. // Let netlogon service exit.
  487. NlEndNetlogonCall();
  488. return Status;
  489. }
  490. #ifdef __cplusplus
  491. } /* extern "C" */
  492. #endif /* __cplusplus */