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.

7512 lines
179 KiB

  1. /*++
  2. Copyright (c) 1998-2001 Microsoft Corporation
  3. Module Name:
  4. security.c
  5. Abstract:
  6. Domain Name System (DNS) Library
  7. DNS secure update API.
  8. Author:
  9. Jim Gilroy (jamesg) January, 1998
  10. Revision History:
  11. --*/
  12. #include "local.h"
  13. #include "time.h" // time() function
  14. // security headers
  15. #define SECURITY_WIN32
  16. #include "sspi.h"
  17. #include "issperr.h"
  18. #include "rpc.h"
  19. #include "rpcndr.h"
  20. #include "ntdsapi.h"
  21. // security definitions
  22. #define SIG_LEN 33
  23. #define NAME_OWNER "." // root node
  24. #define SEC_SUCCESS(Status) ((Status) >= 0)
  25. #define PACKAGE_NAME L"negotiate"
  26. #define NT_DLL_NAME "security.dll"
  27. //
  28. // Maximum length of data signed
  29. // - full packet, length, and sig
  30. //
  31. // If a problem can use packet buffer length and sig length and allocate that
  32. //
  33. #define MAX_SIGNING_SIZE (0x11000)
  34. //
  35. // Global Sspi credentials handle
  36. //
  37. SECURITY_INTEGER g_SspiCredentialsLifetime = { 0, 0 };
  38. CredHandle g_hSspiCredentials;
  39. TimeStamp g_SspiCredentialsLifetime;
  40. #define SSPI_INVALID_HANDLE(x) \
  41. ( ((PSecHandle) (x))->dwLower == (ULONG_PTR) -1 && \
  42. ((PSecHandle) (x))->dwUpper == (ULONG_PTR) -1 )
  43. //
  44. // DEV_NOTE: Security ticket expiration
  45. //
  46. // Security team is yet unsure about how to use the expiration time &
  47. // currently tix are valid forever. If it becomes invalid accept/init context
  48. // will re-nego a new one for us underneath so we should concern ourselves
  49. // at this point. Still, in principal they say we may need to worry about it
  50. // in the future...
  51. //
  52. #define SSPI_EXPIRED_HANDLE( x ) ( FALSE )
  53. //
  54. // Currently only negotiate kerberos
  55. //
  56. // DCR: tie this to regkey, then set in init function
  57. //
  58. BOOL g_NegoKerberosOnly = TRUE;
  59. //
  60. // Context "key" for TKEYs
  61. //
  62. typedef struct _DNS_SECCTXT_KEY
  63. {
  64. IP4_ADDRESS IpRemote;
  65. PSTR pszTkeyName;
  66. PSTR pszClientContext;
  67. PWSTR pwsCredKey;
  68. }
  69. DNS_SECCTXT_KEY, *PDNS_SECCTXT_KEY;
  70. //
  71. // Context name uniqueness
  72. //
  73. // Tick helps insure uniqueness of context name
  74. LONG g_ContextCount = 0;
  75. // UUID insures uniqueness across IP reuse
  76. CHAR g_ContextUuid[ GUID_STRING_BUFFER_LENGTH ] = {0};
  77. //
  78. // Security context request blob
  79. //
  80. typedef struct _DNS_SECCTXT_REQUEST
  81. {
  82. LPSTR pszServerName;
  83. PCHAR pCredentials;
  84. LPSTR pszContext;
  85. DWORD dwFlag;
  86. IP_ADDRESS ipServer;
  87. PIP_ARRAY aipServer;
  88. }
  89. DNS_SECCTXT_REQUEST, *PDNS_SECCTXT_REQUEST;
  90. //
  91. // Security context
  92. //
  93. typedef struct _DnsSecurityContext
  94. {
  95. struct _DnsSecurityContext * pNext;
  96. struct _SecHandle hSecHandle;
  97. DNS_SECCTXT_KEY Key;
  98. CredHandle CredHandle;
  99. // context info
  100. DWORD Version;
  101. WORD TkeySize;
  102. // context state
  103. BOOL fNewConversation;
  104. BOOL fNegoComplete;
  105. BOOL fEchoToken;
  106. BOOL fHaveSecHandle;
  107. BOOL fHaveCredHandle;
  108. BOOL fClient;
  109. // timeout
  110. DWORD dwCreateTime;
  111. DWORD dwCleanupTime;
  112. DWORD dwExpireTime;
  113. }
  114. SEC_CNTXT, *PSEC_CNTXT;
  115. //
  116. // Security session info.
  117. // Held only during interaction, not cached
  118. //
  119. typedef struct _SecPacketInfo
  120. {
  121. PSEC_CNTXT pSecContext;
  122. SecBuffer RemoteBuf;
  123. SecBuffer LocalBuf;
  124. PDNS_HEADER pMsgHead;
  125. PCHAR pMsgEnd;
  126. PDNS_RECORD pTsigRR;
  127. PDNS_RECORD pTkeyRR;
  128. PCHAR pszContextName;
  129. DNS_PARSED_RR ParsedRR;
  130. // client must save signature of query to verify sig on response
  131. PCHAR pQuerySig;
  132. WORD QuerySigLength;
  133. WORD ExtendedRcode;
  134. // version on TKEY \ TSIG
  135. DWORD TkeyVersion;
  136. }
  137. SECPACK, *PSECPACK;
  138. //
  139. // DNS API context
  140. //
  141. typedef struct _DnsAPIContext
  142. {
  143. DWORD Flags;
  144. PVOID Credentials;
  145. PSEC_CNTXT pSecurityContext;
  146. }
  147. DNS_API_CONTEXT, *PDNS_API_CONTEXT;
  148. //
  149. // TCP timeout
  150. //
  151. #define DEFAULT_TCP_TIMEOUT 10
  152. #define SECURE_UPDATE_TCP_TIMEOUT (15)
  153. //
  154. // Public security globals (exposed in dnslib.h)
  155. //
  156. BOOL g_fSecurityPackageInitialized = FALSE;
  157. //
  158. // Private security globals
  159. //
  160. HINSTANCE g_hLibSecurity;
  161. PSecurityFunctionTableW g_pSecurityFunctionTable;
  162. DWORD g_SecurityTokenMaxLength = 0;
  163. DWORD g_SignatureMaxLength = 0;
  164. //
  165. // Security context caching
  166. //
  167. PSEC_CNTXT SecurityContextListHead = NULL;
  168. CRITICAL_SECTION SecurityContextListCS;
  169. DWORD SecContextCreate = 0;
  170. DWORD SecContextFree = 0;
  171. DWORD SecContextQueue = 0;
  172. DWORD SecContextQueueInNego = 0;
  173. DWORD SecContextDequeue = 0;
  174. DWORD SecContextTimeout = 0;
  175. //
  176. // Security packet info memory tracking
  177. //
  178. DWORD SecPackAlloc = 0;
  179. DWORD SecPackFree = 0;
  180. //
  181. // Security packet verifications
  182. //
  183. DWORD SecTkeyInvalid = 0;
  184. DWORD SecTkeyBadTime = 0;
  185. DWORD SecTsigFormerr = 0;
  186. DWORD SecTsigEcho = 0;
  187. DWORD SecTsigBadKey = 0;
  188. DWORD SecTsigVerifySuccess = 0;
  189. DWORD SecTsigVerifyFailed = 0;
  190. //
  191. // Hacks
  192. //
  193. // Allowing old TSIG off by default, server can turn on.
  194. BOOL SecAllowOldTsig = 0; // 1 to allow old sigs, 2 any sig
  195. DWORD SecTsigVerifyOldSig = 0;
  196. DWORD SecTsigVerifyOldFailed = 0;
  197. //
  198. // TIME values
  199. //
  200. // (in seconds)
  201. #define TIME_WEEK_S 604800
  202. #define TIME_DAY_S 86400
  203. #define TIME_10_HOUR_S 36000
  204. #define TIME_8_HOUR_S 28800
  205. #define TIME_4_HOUR_S 14400
  206. #define TIME_HOUR_S 3600
  207. #define TIME_10_MINUTE_S 600
  208. #define TIME_5_MINUTE_S 300
  209. #define TIME_3_MINUTE_S 160
  210. #define TIME_MINUTE_S 60
  211. // Big Time skew on by default
  212. DWORD SecBigTimeSkew = TIME_DAY_S;
  213. DWORD SecBigTimeSkewBypass = 0;
  214. //
  215. // TSIG - GSS alogrithm
  216. //
  217. #define W2K_GSS_ALGORITHM_NAME_PACKET ("\03gss\011microsoft\03com")
  218. #define W2K_GSS_ALGORITHM_NAME_PACKET_LENGTH (sizeof(W2K_GSS_ALGORITHM_NAME_PACKET))
  219. #define GSS_ALGORITHM_NAME_PACKET ("\010gss-tsig")
  220. #define GSS_ALGORITHM_NAME_PACKET_LENGTH (sizeof(GSS_ALGORITHM_NAME_PACKET))
  221. PCHAR g_pAlgorithmNameW2K = W2K_GSS_ALGORITHM_NAME_PACKET;
  222. PCHAR g_pAlgorithmNameCurrent = GSS_ALGORITHM_NAME_PACKET;
  223. //
  224. // TKEY context name
  225. //
  226. #define MAX_CONTEXT_NAME_LENGTH DNS_MAX_NAME_BUFFER_LENGTH
  227. //
  228. // TKEY/TSIG versioning
  229. //
  230. // Win2K shipped with some deviations from current the GSS-TSIG RFC.
  231. // Specifically
  232. // - client sent TKEY query in Answer section instead of addtional
  233. // - alg name was "gss.microsoft.com", new name is "gss-tsig"
  234. // - client would reuse context based on process id, rather than
  235. // forcing unique context
  236. // - signing didn't include length when including previous sig
  237. //
  238. // Defining versioning -- strictly internal to this module
  239. //
  240. #define TKEY_VERSION_W2K 3
  241. #define TKEY_VERSION_WHISTLER_BETA 4
  242. #define TKEY_VERSION_XP_BAD_SIG 5
  243. #define TKEY_VERSION_XP_RC1 6
  244. #define TKEY_VERSION_XP 7
  245. #define TKEY_VERSION_CURRENT TKEY_VERSION_XP
  246. //
  247. // TKEY expiration
  248. // - cleanup if inactive for 3 minutes
  249. // - max kept alive four hours then must renego
  250. //
  251. #define TKEY_CLEANUP_INTERVAL (TIME_3_MINUTE_S)
  252. //
  253. // DCR_FIX: Nego time issue (GM vs local time)
  254. //
  255. // Currently netlogon seems to run in GM time, so we limit our time
  256. // check to one day. Later on, we should move it back to 1 hour.
  257. //
  258. #define TKEY_EXPIRE_INTERVAL (TIME_DAY_S)
  259. #define TSIG_EXPIRE_INTERVAL (TIME_10_HOUR_S)
  260. #define TKEY_MAX_EXPIRE_INTERVAL (TIME_4_HOUR_S)
  261. #define MAX_TIME_SKEW (TIME_DAY_S)
  262. //
  263. // ntdsapi.dll loading
  264. // - for making SPN for DNS server
  265. //
  266. #define NTDSAPI_DLL_NAMEW L"ntdsapi.dll"
  267. #define MAKE_SPN_FUNC "DsClientMakeSpnForTargetServerW"
  268. FARPROC g_pfnMakeSpn = NULL;
  269. HMODULE g_hLibNtdsa = NULL;
  270. //
  271. // Private protos
  272. //
  273. VOID
  274. DnsPrint_SecurityContextList(
  275. IN PRINT_ROUTINE PrintRoutine,
  276. IN OUT PPRINT_CONTEXT pPrintContext,
  277. IN LPSTR pszHeader,
  278. IN PSEC_CNTXT pListHead
  279. );
  280. VOID
  281. DnsPrint_SecurityContext(
  282. IN PRINT_ROUTINE PrintRoutine,
  283. IN OUT PPRINT_CONTEXT pPrintContext,
  284. IN LPSTR pszHeader,
  285. IN PSEC_CNTXT pSecCtxt
  286. );
  287. VOID
  288. DnsPrint_SecurityPacketInfo(
  289. IN PRINT_ROUTINE PrintRoutine,
  290. IN OUT PPRINT_CONTEXT pPrintContext,
  291. IN LPSTR pszHeader,
  292. IN PSECPACK pSecPack
  293. );
  294. #if DBG
  295. #define DnsDbg_SecurityContextList(a,b) DnsPrint_SecurityContextList(DnsPR,NULL,a,b)
  296. #define DnsDbg_SecurityContext(a,b) DnsPrint_SecurityContext(DnsPR,NULL,a,b)
  297. #define DnsDbg_SecurityPacketInfo(a,b) DnsPrint_SecurityPacketInfo(DnsPR,NULL,a,b)
  298. #else
  299. #define DnsDbg_SecurityContextList(a,b)
  300. #define DnsDbg_SecurityContext(a,b)
  301. #define DnsDbg_SecurityPacketInfo(a,b)
  302. #endif
  303. #define Dns_FreeSecurityPacketInfo(p) Dns_CleanupSecurityPacketInfoEx((p),TRUE)
  304. #define Dns_ResetSecurityPacketInfo(p) Dns_CleanupSecurityPacketInfoEx((p),FALSE)
  305. DNS_STATUS
  306. Dns_LoadNtdsapiProcs(
  307. VOID
  308. );
  309. PWSTR
  310. MakeCredKey(
  311. IN PCHAR pCreds
  312. );
  313. BOOL
  314. CompareCredKeys(
  315. IN PWSTR pwsCredKey1,
  316. IN PWSTR pwsCredKey2
  317. );
  318. DNS_STATUS
  319. Dns_AcquireCredHandle(
  320. OUT PCredHandle pCredHandle,
  321. IN BOOL fDnsServer,
  322. IN PCHAR pCreds
  323. );
  324. //
  325. // Security session packet info
  326. //
  327. PSECPACK
  328. Dns_CreateSecurityPacketInfo(
  329. VOID
  330. )
  331. /*++
  332. Routine Description:
  333. Create security packet info structure.
  334. Arguments:
  335. None.
  336. Return Value:
  337. Ptr to new zeroed security packet info.
  338. --*/
  339. {
  340. PSECPACK psecPack;
  341. psecPack = (PSECPACK) ALLOCATE_HEAP_ZERO( sizeof(SECPACK) );
  342. if ( !psecPack )
  343. {
  344. return( NULL );
  345. }
  346. SecPackAlloc++;
  347. return( psecPack );
  348. }
  349. VOID
  350. Dns_InitSecurityPacketInfo(
  351. OUT PSECPACK pSecPack,
  352. IN PSEC_CNTXT pSecCtxt
  353. )
  354. /*++
  355. Routine Description:
  356. Init security packet info for given context
  357. Arguments:
  358. Return Value:
  359. None.
  360. --*/
  361. {
  362. // clear previous info
  363. RtlZeroMemory(
  364. pSecPack,
  365. sizeof(SECPACK) );
  366. // set context ptr
  367. pSecPack->pSecContext = pSecCtxt;
  368. }
  369. VOID
  370. Dns_CleanupSecurityPacketInfoEx(
  371. IN OUT PSECPACK pSecPack,
  372. IN BOOL fFree
  373. )
  374. /*++
  375. Routine Description:
  376. Cleans up security packet info.
  377. Arguments:
  378. pSecPack -- ptr to security packet info to clean up
  379. Return Value:
  380. None.
  381. --*/
  382. {
  383. if ( !pSecPack )
  384. {
  385. return;
  386. }
  387. if ( pSecPack->pszContextName )
  388. {
  389. FREE_HEAP( pSecPack->pszContextName );
  390. }
  391. if ( pSecPack->pTsigRR )
  392. {
  393. FREE_HEAP( pSecPack->pTsigRR );
  394. //Dns_RecordFree( pSecPack->pTsigRR );
  395. }
  396. if ( pSecPack->pTkeyRR )
  397. {
  398. FREE_HEAP( pSecPack->pTkeyRR );
  399. //Dns_RecordFree( pSecPack->pTkeyRR );
  400. }
  401. if ( pSecPack->pQuerySig )
  402. {
  403. FREE_HEAP( pSecPack->pQuerySig );
  404. }
  405. if ( pSecPack->LocalBuf.pvBuffer )
  406. {
  407. FREE_HEAP( pSecPack->LocalBuf.pvBuffer );
  408. }
  409. if ( fFree )
  410. {
  411. FREE_HEAP( pSecPack );
  412. SecPackFree++;
  413. }
  414. else
  415. {
  416. RtlZeroMemory(
  417. pSecPack,
  418. sizeof(SECPACK) );
  419. }
  420. }
  421. VOID
  422. DnsPrint_SecurityPacketInfo(
  423. IN PRINT_ROUTINE PrintRoutine,
  424. IN OUT PPRINT_CONTEXT pPrintContext,
  425. IN LPSTR pszHeader,
  426. IN PSECPACK pSecPack
  427. )
  428. {
  429. if ( !pSecPack )
  430. {
  431. PrintRoutine(
  432. pPrintContext,
  433. "%s NULL security context\n",
  434. pszHeader ? pszHeader : "" );
  435. return;
  436. }
  437. DnsPrint_Lock();
  438. PrintRoutine(
  439. pPrintContext,
  440. "%s\n"
  441. "\tptr = %p\n"
  442. "\tpSec Context = %p\n"
  443. "\tContext Name = %s\n"
  444. "\tVersion = %d\n"
  445. "\tpTsigRR = %p\n"
  446. "\tpTkeyRR = %p\n"
  447. "\tExt RCODE = %d\n"
  448. "\tremote buf = %p\n"
  449. "\t length = %d\n"
  450. "\tlocal buf = %p\n"
  451. "\t length = %d\n",
  452. pszHeader ? pszHeader : "Security packet info:",
  453. pSecPack,
  454. pSecPack->pSecContext,
  455. pSecPack->pszContextName,
  456. pSecPack->TkeyVersion,
  457. pSecPack->pTsigRR,
  458. pSecPack->pTkeyRR,
  459. pSecPack->ExtendedRcode,
  460. pSecPack->RemoteBuf.pvBuffer,
  461. pSecPack->RemoteBuf.cbBuffer,
  462. pSecPack->LocalBuf.pvBuffer,
  463. pSecPack->LocalBuf.cbBuffer
  464. );
  465. DnsPrint_ParsedRecord(
  466. PrintRoutine,
  467. pPrintContext,
  468. "Parsed Security RR",
  469. & pSecPack->ParsedRR
  470. );
  471. if ( pSecPack->pTsigRR )
  472. {
  473. DnsPrint_Record(
  474. PrintRoutine,
  475. pPrintContext,
  476. "TSIG RR",
  477. pSecPack->pTsigRR,
  478. NULL // no previous record
  479. );
  480. }
  481. if ( pSecPack->pTkeyRR )
  482. {
  483. DnsPrint_Record(
  484. PrintRoutine,
  485. pPrintContext,
  486. "TKEY RR",
  487. pSecPack->pTkeyRR,
  488. NULL // no previous record
  489. );
  490. }
  491. if ( pSecPack->pSecContext )
  492. {
  493. DnsPrint_SecurityContext(
  494. PrintRoutine,
  495. pPrintContext,
  496. "Associated Security Context",
  497. pSecPack->pSecContext
  498. );
  499. }
  500. DnsPrint_Unlock();
  501. }
  502. //
  503. // Security context routines
  504. //
  505. PSEC_CNTXT
  506. Dns_CreateSecurityContext(
  507. VOID
  508. )
  509. /*++
  510. Routine Description:
  511. Allocate a new security context blob.
  512. Arguments:
  513. None.
  514. Return Value:
  515. Ptr to new context.
  516. NULL on alloc failure.
  517. --*/
  518. {
  519. PSEC_CNTXT psecCtxt;
  520. psecCtxt = (PSEC_CNTXT) ALLOCATE_HEAP_ZERO( sizeof(SEC_CNTXT) );
  521. if ( !psecCtxt )
  522. {
  523. return( NULL );
  524. }
  525. psecCtxt->fNewConversation = TRUE;
  526. SecContextCreate++;
  527. return( psecCtxt );
  528. }
  529. VOID
  530. Dns_FreeSecurityContext(
  531. IN OUT PSEC_CNTXT pSecCtxt
  532. )
  533. /*++
  534. Routine Description:
  535. Cleans up security session data.
  536. Arguments:
  537. pSecCtxt -- handle to context to clean up
  538. Return Value:
  539. TRUE if successful
  540. FALSE otherwise
  541. --*/
  542. {
  543. PSEC_CNTXT psecCtxt = (PSEC_CNTXT)pSecCtxt;
  544. if ( !psecCtxt )
  545. {
  546. return;
  547. }
  548. if ( psecCtxt->Key.pszTkeyName )
  549. {
  550. FREE_HEAP( psecCtxt->Key.pszTkeyName );
  551. }
  552. if ( psecCtxt->Key.pszClientContext )
  553. {
  554. FREE_HEAP( psecCtxt->Key.pszClientContext );
  555. }
  556. if ( psecCtxt->Key.pwsCredKey )
  557. {
  558. FREE_HEAP( psecCtxt->Key.pwsCredKey );
  559. }
  560. if ( psecCtxt->fHaveSecHandle )
  561. {
  562. g_pSecurityFunctionTable->DeleteSecurityContext( &psecCtxt->hSecHandle );
  563. }
  564. if ( psecCtxt->fHaveCredHandle )
  565. {
  566. g_pSecurityFunctionTable->FreeCredentialsHandle( &psecCtxt->CredHandle );
  567. }
  568. FREE_HEAP( psecCtxt );
  569. SecContextFree++;
  570. }
  571. //
  572. // Security context list routines
  573. //
  574. // Server side may have multiple security sessions active and does
  575. // not maintain client state on a thread's stack, so must have
  576. // a list to hold previous session info.
  577. //
  578. PSEC_CNTXT
  579. Dns_DequeueSecurityContextByKey(
  580. IN DNS_SECCTXT_KEY Key,
  581. IN BOOL fComplete
  582. )
  583. /*++
  584. Routine Description:
  585. Get security session context from session list based on key.
  586. Arguments:
  587. Key -- session key
  588. fComplete -- TRUE if need fully negotiated context
  589. FALSE if still in negotiation
  590. Return Value:
  591. Handle to security session context, if found.
  592. NULL if no context for key.
  593. --*/
  594. {
  595. PSEC_CNTXT pcur;
  596. PSEC_CNTXT pback;
  597. DWORD currentTime = Dns_GetCurrentTimeInSeconds();
  598. DNSDBG( SECURITY, (
  599. "DequeueSecurityContext()\n"
  600. "\tIP = %s\n"
  601. "\tTKEY name = %s\n"
  602. "\tcontext name = %s\n"
  603. "\tcred string = %S\n",
  604. IP_STRING( Key.IpRemote ),
  605. Key.pszTkeyName,
  606. Key.pszClientContext,
  607. Key.pwsCredKey ));
  608. EnterCriticalSection( &SecurityContextListCS );
  609. IF_DNSDBG( SECURITY )
  610. {
  611. DnsDbg_SecurityContextList(
  612. "Before Get",
  613. SecurityContextListHead );
  614. }
  615. pback = (PSEC_CNTXT) &SecurityContextListHead;
  616. while ( pcur = pback->pNext )
  617. {
  618. // if context is stale -- delete it
  619. if ( pcur->dwCleanupTime < currentTime )
  620. {
  621. pback->pNext = pcur->pNext;
  622. SecContextTimeout++;
  623. Dns_FreeSecurityContext( pcur );
  624. continue;
  625. }
  626. // match context to key
  627. // - must match IP
  628. // - server side must match TKEY name
  629. // - client side must match context key
  630. if ( Key.IpRemote == pcur->Key.IpRemote
  631. &&
  632. ( ( Key.pszTkeyName &&
  633. Dns_NameCompare_UTF8(
  634. Key.pszTkeyName,
  635. pcur->Key.pszTkeyName ))
  636. ||
  637. ( Key.pszClientContext &&
  638. Dns_NameCompare_UTF8(
  639. Key.pszClientContext,
  640. pcur->Key.pszClientContext )) )
  641. &&
  642. CompareCredKeys(
  643. Key.pwsCredKey,
  644. pcur->Key.pwsCredKey ) )
  645. {
  646. // if expect completed context, ignore incomplete
  647. //
  648. // DCR: should dump once RFC compliant
  649. if ( fComplete && !pcur->fNegoComplete )
  650. {
  651. DNSDBG( ANY, (
  652. "WARNING: Requested dequeue security context still in nego!\n"
  653. "\tmatching key %s %s\n"
  654. "\tcontext complete = %d\n"
  655. "\trequest fComplete = %d\n",
  656. Key.pszTkeyName,
  657. IP_STRING( Key.IpRemote ),
  658. pcur->fNegoComplete,
  659. fComplete ));
  660. pback = pcur;
  661. continue;
  662. }
  663. // detach context
  664. // DCR: could ref count context and leave in
  665. // not sure this adds much -- how many process do MT
  666. // updates in same security context
  667. pback->pNext = pcur->pNext;
  668. SecContextDequeue++;
  669. break;
  670. }
  671. // not found -- continue search
  672. pback = pcur;
  673. }
  674. IF_DNSDBG( SECURITY )
  675. {
  676. DnsDbg_SecurityContextList(
  677. "After Dequeue",
  678. SecurityContextListHead );
  679. }
  680. LeaveCriticalSection( &SecurityContextListCS);
  681. return( pcur );
  682. }
  683. PSEC_CNTXT
  684. Dns_FindOrCreateSecurityContext(
  685. IN DNS_SECCTXT_KEY Key
  686. )
  687. /*++
  688. Routine Description:
  689. Find and extract existing security context from list,
  690. OR
  691. create a new one.
  692. Arguments:
  693. Key -- key for context
  694. Return Value:
  695. Ptr to security context.
  696. --*/
  697. {
  698. PSEC_CNTXT psecCtxt;
  699. DNSDBG( SECURITY, (
  700. "Dns_FindOrCreateSecurityContext()\n" ));
  701. // find existing context
  702. psecCtxt = Dns_DequeueSecurityContextByKey( Key, FALSE );
  703. if ( psecCtxt )
  704. {
  705. return psecCtxt;
  706. }
  707. //
  708. // create context
  709. //
  710. // server's will come with complete TKEY name from packet
  711. // client's will come with specific context name, we must
  712. // generate globally unique name
  713. // - context count
  714. // - tick count
  715. // - UUID
  716. //
  717. // implementation notes:
  718. // - UUID to make sure we're unique across IP reuse
  719. //
  720. // - UUID and timer enforce uniqueness across process shutdown
  721. // and restart (even if generation UUID fails, you'll be at
  722. // a different tick count)
  723. //
  724. // - context count enforces uniqueness within process
  725. // - interlock allows us to eliminate thread id
  726. // - even with thread id, we'd still need this anyway
  727. // (without interlock) to back up timer since GetTickCount()
  728. // is "chunky" and a thread could concievably not "tick"
  729. // between contexts on the same thread if they were dropped
  730. // before going to the wire
  731. //
  732. //
  733. psecCtxt = Dns_CreateSecurityContext();
  734. if ( psecCtxt )
  735. {
  736. PSTR pstr;
  737. PSTR pnameTkey;
  738. CHAR nameBuf[ DNS_MAX_NAME_BUFFER_LENGTH ];
  739. pnameTkey = Key.pszTkeyName;
  740. if ( Key.pszClientContext )
  741. {
  742. LONG count = InterlockedIncrement( &g_ContextCount );
  743. //
  744. // Note: it is important that this string is in canonical
  745. // form as per RFC 2535 section 8.1 - basically this means
  746. // lower case.
  747. //
  748. _snprintf(
  749. nameBuf,
  750. MAX_CONTEXT_NAME_LENGTH,
  751. "%s.%d-%x.%s",
  752. Key.pszClientContext,
  753. count,
  754. GetTickCount(),
  755. g_ContextUuid );
  756. nameBuf[ DNS_MAX_NAME_BUFFER_LENGTH ] = 0;
  757. pnameTkey = nameBuf;
  758. pstr = Dns_CreateStringCopy_A( Key.pszClientContext );
  759. if ( !pstr )
  760. {
  761. goto Failed;
  762. }
  763. psecCtxt->Key.pszClientContext = pstr;
  764. }
  765. // remote IP
  766. psecCtxt->Key.IpRemote = Key.IpRemote;
  767. // TKEY name
  768. pstr = Dns_CreateStringCopy_A( pnameTkey );
  769. if ( !pstr )
  770. {
  771. goto Failed;
  772. }
  773. psecCtxt->Key.pszTkeyName = pstr;
  774. // cred key
  775. if ( Key.pwsCredKey )
  776. {
  777. pstr = (PSTR) Dns_CreateStringCopy_W( Key.pwsCredKey );
  778. if ( !pstr )
  779. {
  780. goto Failed;
  781. }
  782. psecCtxt->Key.pwsCredKey = (PWSTR) pstr;
  783. }
  784. }
  785. IF_DNSDBG( SECURITY )
  786. {
  787. DnsDbg_SecurityContextList(
  788. "New security context:",
  789. psecCtxt );
  790. }
  791. return( psecCtxt );
  792. Failed:
  793. // memory allocation failure
  794. Dns_FreeSecurityContext( psecCtxt );
  795. return NULL;
  796. }
  797. VOID
  798. Dns_EnlistSecurityContext(
  799. IN OUT PSEC_CNTXT pSecCtxt
  800. )
  801. /*++
  802. Routine Description:
  803. Enlist a security context.
  804. Note this does NOT create the context it simply enlists a current one.
  805. Arguments:
  806. Key -- key for context
  807. Return Value:
  808. Handle to security context.
  809. --*/
  810. {
  811. PSEC_CNTXT pnew = (PSEC_CNTXT)pSecCtxt;
  812. DWORD currentTime;
  813. //
  814. // catch queuing up some bogus blob
  815. //
  816. ASSERT( pnew->fNewConversation == TRUE || pnew->fNewConversation == FALSE );
  817. ASSERT( pnew->dwCreateTime < pnew->dwCleanupTime || pnew->dwCleanupTime == 0 );
  818. ASSERT( pnew->Key.pszTkeyName );
  819. ASSERT( pnew->Key.IpRemote );
  820. //
  821. // reset expire time so keep context active if in use
  822. //
  823. // DCR_FIX: need expire time to use min of TKEY and fixed hard timeout
  824. //
  825. currentTime = Dns_GetCurrentTimeInSeconds();
  826. if ( !pnew->dwCreateTime )
  827. {
  828. pnew->dwCreateTime = currentTime;
  829. }
  830. if ( !pnew->dwExpireTime )
  831. {
  832. pnew->dwExpireTime = currentTime + TKEY_MAX_EXPIRE_INTERVAL;
  833. }
  834. //
  835. // cleanup after interval not used
  836. // unconditionally maximum of cleanup interval.
  837. //
  838. pnew->dwCleanupTime = currentTime + TKEY_CLEANUP_INTERVAL;
  839. EnterCriticalSection( &SecurityContextListCS );
  840. pnew->pNext = SecurityContextListHead;
  841. SecurityContextListHead = pnew;
  842. SecContextQueue++;
  843. if ( !pnew->fNegoComplete )
  844. {
  845. SecContextQueueInNego++;
  846. }
  847. IF_DNSDBG( SECURITY )
  848. {
  849. DnsDbg_SecurityContextList(
  850. "After add",
  851. SecurityContextListHead );
  852. }
  853. LeaveCriticalSection( &SecurityContextListCS );
  854. }
  855. VOID
  856. Dns_TimeoutSecurityContextList(
  857. IN BOOL fClearList
  858. )
  859. /*++
  860. Routine Description:
  861. Eliminate old session data.
  862. Arguments:
  863. fClearList -- TRUE to delete all, FALSE to timeout
  864. Return Value:
  865. None.
  866. --*/
  867. {
  868. PSEC_CNTXT pcur;
  869. PSEC_CNTXT pback;
  870. DWORD currentTime;
  871. if ( fClearList )
  872. {
  873. currentTime = MAXDWORD;
  874. }
  875. else
  876. {
  877. currentTime = Dns_GetCurrentTimeInSeconds();
  878. }
  879. EnterCriticalSection( &SecurityContextListCS );
  880. pback = (PSEC_CNTXT) &SecurityContextListHead;
  881. while ( pcur = pback->pNext )
  882. {
  883. // if haven't reached cleanup time, keep in list
  884. if ( pcur->dwCleanupTime > currentTime )
  885. {
  886. pback = pcur;
  887. continue;
  888. }
  889. // entry has expired
  890. // - cut from list
  891. // - free the session context
  892. pback->pNext = pcur->pNext;
  893. SecContextTimeout++;
  894. Dns_FreeSecurityContext( pcur );
  895. }
  896. ASSERT( !fClearList || SecurityContextListHead==NULL );
  897. LeaveCriticalSection( &SecurityContextListCS );
  898. }
  899. VOID
  900. Dns_FreeSecurityContextList(
  901. VOID
  902. )
  903. /*++
  904. Routine Description ():
  905. Free all security contexts in global list
  906. Arguments:
  907. None
  908. Return Value:
  909. None
  910. --*/
  911. {
  912. PSEC_CNTXT pcur;
  913. PSEC_CNTXT ptmp;
  914. INT countDelete = 0;
  915. DNSDBG( SECURITY, (
  916. "Dns_FreeSecurityContextList()\n" ));
  917. EnterCriticalSection( &SecurityContextListCS );
  918. IF_DNSDBG( SECURITY )
  919. {
  920. DnsDbg_SecurityContextList(
  921. "Before Get",
  922. SecurityContextListHead );
  923. }
  924. // if empty list -- done
  925. if ( !SecurityContextListHead )
  926. {
  927. DNSDBG( SECURITY, (
  928. "Attempt to free empty SecurityCOntextList.\n" ));
  929. goto Done;
  930. }
  931. //
  932. // Cycle through list & free all entries
  933. //
  934. pcur = SecurityContextListHead->pNext;
  935. while( pcur )
  936. {
  937. ptmp = pcur;
  938. pcur = pcur->pNext;
  939. Dns_FreeSecurityContext( ptmp );
  940. countDelete++;
  941. }
  942. Done:
  943. SecContextDequeue += countDelete;
  944. LeaveCriticalSection( &SecurityContextListCS );
  945. DNSDBG( SECURITY, (
  946. "Dns_FreeSecurityContextList emptied %d entries\n",
  947. countDelete ));
  948. }
  949. VOID
  950. DnsPrint_SecurityContext(
  951. IN PRINT_ROUTINE PrintRoutine,
  952. IN OUT PPRINT_CONTEXT pPrintContext,
  953. IN LPSTR pszHeader,
  954. IN PSEC_CNTXT pSecCtxt
  955. )
  956. {
  957. PSEC_CNTXT pctxt = (PSEC_CNTXT)pSecCtxt;
  958. if ( !pSecCtxt )
  959. {
  960. PrintRoutine(
  961. pPrintContext,
  962. "%s NULL security context\n",
  963. pszHeader ? pszHeader : "" );
  964. return;
  965. }
  966. DnsPrint_Lock();
  967. PrintRoutine(
  968. pPrintContext,
  969. "%s\n"
  970. "\tptr = %p\n"
  971. "\tpnext = %p\n"
  972. "\tkey = %s %s %s\n"
  973. "\tversion = %d\n"
  974. "\tCred Handle = %p %p\n"
  975. "\tSec Handle = %p %p\n"
  976. "\tcreate time = %d\n"
  977. "\texpire time = %d\n"
  978. "\tcleanup time = %d\n"
  979. "\thave cred = %d\n"
  980. "\thave sec = %d\n"
  981. "\tnew con = %d\n"
  982. "\tinitialized = %d\n"
  983. "\techo token = %d\n",
  984. pszHeader ? pszHeader : "Security context:",
  985. pctxt,
  986. pctxt->pNext,
  987. IP_STRING(pctxt->Key.IpRemote),
  988. pctxt->Key.pszTkeyName,
  989. pctxt->Key.pszClientContext,
  990. pctxt->Version,
  991. pctxt->CredHandle.dwUpper,
  992. pctxt->CredHandle.dwLower,
  993. pctxt->hSecHandle.dwUpper,
  994. pctxt->hSecHandle.dwLower,
  995. pctxt->dwCreateTime,
  996. pctxt->dwExpireTime,
  997. pctxt->dwCleanupTime,
  998. pctxt->fHaveCredHandle,
  999. pctxt->fHaveSecHandle,
  1000. pctxt->fNewConversation,
  1001. pctxt->fNegoComplete,
  1002. pctxt->fEchoToken
  1003. );
  1004. if ( !pctxt->fHaveCredHandle )
  1005. {
  1006. PrintRoutine(
  1007. pPrintContext,
  1008. "Global cred handle\n"
  1009. "\tCred Handle = %p %p\n",
  1010. g_hSspiCredentials.dwUpper,
  1011. g_hSspiCredentials.dwLower );
  1012. }
  1013. DnsPrint_Unlock();
  1014. }
  1015. VOID
  1016. DnsPrint_SecurityContextList(
  1017. IN PRINT_ROUTINE PrintRoutine,
  1018. IN OUT PPRINT_CONTEXT pPrintContext,
  1019. IN LPSTR pszHeader,
  1020. IN PSEC_CNTXT pList
  1021. )
  1022. {
  1023. PSEC_CNTXT pcur;
  1024. EnterCriticalSection( &SecurityContextListCS );
  1025. DnsPrint_Lock();
  1026. pcur = pList;
  1027. PrintRoutine(
  1028. pPrintContext,
  1029. "Security context list %s\n"
  1030. "\tList ptr = %p\n"
  1031. "%s",
  1032. pszHeader,
  1033. pList,
  1034. pcur ? "" : "\tList EMPTY\n" );
  1035. while ( pcur != NULL )
  1036. {
  1037. DnsPrint_SecurityContext(
  1038. PrintRoutine,
  1039. pPrintContext,
  1040. NULL,
  1041. pcur );
  1042. pcur = pcur->pNext;
  1043. }
  1044. PrintRoutine(
  1045. pPrintContext,
  1046. "*** End security context list ***\n" );
  1047. DnsPrint_Unlock();
  1048. LeaveCriticalSection( &SecurityContextListCS );
  1049. }
  1050. //
  1051. // Security utils
  1052. //
  1053. DNS_STATUS
  1054. MakeKerberosName(
  1055. OUT PWSTR pwsKerberosName,
  1056. IN PSTR pszDnsName,
  1057. IN BOOL fTrySpn
  1058. )
  1059. /*++
  1060. Routine Description:
  1061. Map DNS name to kerberos name for security lookup.
  1062. Arguments:
  1063. pszDnsName -- DNS name
  1064. pwsKerberosName -- buffer to recv kerb name
  1065. fSPNFormat -- use SPN format
  1066. Return Value:
  1067. ERROR_SUCCESS successful.
  1068. ErrorCode on failure.
  1069. --*/
  1070. {
  1071. DNS_STATUS status = ERROR_SUCCESS;
  1072. WCHAR nameBuf[ DNS_MAX_NAME_BUFFER_LENGTH ];
  1073. INT nameLength;
  1074. PWCHAR pwMachine;
  1075. PWCHAR pwDomain;
  1076. PWCHAR pwTmp;
  1077. DNSDBG( SECURITY, (
  1078. "MakeKerberosName(%s, %p, %d)\n",
  1079. pszDnsName,
  1080. pwsKerberosName,
  1081. fTrySpn
  1082. ));
  1083. if ( !pszDnsName || !pwsKerberosName )
  1084. {
  1085. DNS_ASSERT( FALSE );
  1086. return ERROR_INVALID_PARAMETER;
  1087. }
  1088. //
  1089. // convert to wide char
  1090. // - note, function returns byte count, not status
  1091. //
  1092. if ( ! Dns_NameCopyWireToUnicode(
  1093. nameBuf,
  1094. pszDnsName ) )
  1095. {
  1096. status = GetLastError();
  1097. DNSDBG( SECURITY, (
  1098. "ERROR: Bad DNS name %s failed conversion to unicode\n",
  1099. pszDnsName ));
  1100. DNS_ASSERT( FALSE );
  1101. goto Cleanup;
  1102. }
  1103. //
  1104. // build SPN name
  1105. //
  1106. if ( fTrySpn && g_pfnMakeSpn )
  1107. {
  1108. nameLength = MAX_PATH;
  1109. status = (DNS_STATUS) g_pfnMakeSpn(
  1110. DNS_SPN_SERVICE_CLASS_W,
  1111. nameBuf,
  1112. & nameLength,
  1113. pwsKerberosName );
  1114. DNSDBG( SECURITY, (
  1115. "Translated (via DsSpn) %s into Kerberos name: %S\n",
  1116. pszDnsName,
  1117. pwsKerberosName ));
  1118. goto Cleanup;
  1119. }
  1120. //
  1121. // no SPN -- build kerberos name
  1122. // - convert FQDN to domain\machine$
  1123. // compatible with old servers that did not register SPNs.
  1124. //
  1125. {
  1126. PWSTR pdomain;
  1127. PWSTR pdump;
  1128. //
  1129. // break into host\domain name pieces
  1130. //
  1131. pdomain = Dns_GetDomainName_W( nameBuf );
  1132. if ( !pdomain )
  1133. {
  1134. status = ERROR_INVALID_DATA;
  1135. goto Cleanup;
  1136. }
  1137. *(pdomain-1) = 0;
  1138. // break off single label domain name
  1139. pdump = Dns_GetDomainName_W( pdomain );
  1140. if ( !pdump )
  1141. {
  1142. status = ERROR_INVALID_DATA;
  1143. goto Cleanup;
  1144. }
  1145. *(pdump-1) = 0;
  1146. // format as <domain>\<machine>$
  1147. wcscpy( pwsKerberosName, pdomain );
  1148. wcscat( pwsKerberosName, L"\\" );
  1149. wcscat( pwsKerberosName, nameBuf );
  1150. wcscat( pwsKerberosName, L"$" );
  1151. //
  1152. // note: tried this and got linker error
  1153. //
  1154. wsprintfW(
  1155. pwsKerberosName,
  1156. L"%S\\%ws$",
  1157. pdomain,
  1158. nameBuf );
  1159. }
  1160. DNSDBG( SECURITY, (
  1161. "Translated %s into Kerberos name: %S\n",
  1162. pszDnsName,
  1163. pwsKerberosName ));
  1164. Cleanup:
  1165. return status;
  1166. }
  1167. DNS_STATUS
  1168. Dns_LoadNtdsapiProcs(
  1169. VOID
  1170. )
  1171. /*++
  1172. Routine Description:
  1173. Dynamically loads SPN function from Ntdsapi.dll
  1174. Arguments:
  1175. None
  1176. Return Value:
  1177. ERROR_SUCCESS if successful.
  1178. ErrorCode on failure.
  1179. --*/
  1180. {
  1181. HMODULE hlib = NULL;
  1182. DNS_STATUS status = ERROR_SUCCESS;
  1183. //
  1184. // Note, function assumes MT safe.
  1185. // At single thread startup or protected by CS
  1186. //
  1187. //
  1188. // return if module already loaded
  1189. //
  1190. if ( g_hLibNtdsa )
  1191. {
  1192. ASSERT( g_pfnMakeSpn );
  1193. return ERROR_SUCCESS;
  1194. }
  1195. //
  1196. // load ntdsapi.dll -- for getting SPNs
  1197. //
  1198. hlib = LoadLibraryExW(
  1199. NTDSAPI_DLL_NAMEW,
  1200. NULL,
  1201. 0 ); // Previously used: DONT_RESOLVE_DLL_REFERENCES
  1202. if ( !hlib )
  1203. {
  1204. return GetLastError();
  1205. }
  1206. //
  1207. // get SPN function
  1208. //
  1209. g_pfnMakeSpn = GetProcAddress( hlib, MAKE_SPN_FUNC );
  1210. if ( !g_pfnMakeSpn )
  1211. {
  1212. status = GetLastError();
  1213. FreeLibrary( hlib );
  1214. }
  1215. else
  1216. {
  1217. g_hLibNtdsa = hlib;
  1218. }
  1219. return ERROR_SUCCESS;
  1220. }
  1221. DNS_STATUS
  1222. Dns_StartSecurity(
  1223. IN BOOL fProcessAttach
  1224. )
  1225. /*++
  1226. Routine Description:
  1227. Initialize the security package for dynamic update.
  1228. Note, this function is self-initializing, BUT is not
  1229. MT safe, unless called at process attach.
  1230. Parameters:
  1231. fProcessAttach - TRUE if called during process attach
  1232. in that case we initialize only the CS
  1233. otherwise we initialize completely
  1234. Return Value:
  1235. TRUE if successful.
  1236. FALSE otherwise, error code available from GetLastError().
  1237. --*/
  1238. {
  1239. DNS_STATUS status = ERROR_SUCCESS;
  1240. static BOOL fcsInitialized = FALSE;
  1241. //
  1242. // DCR_PERF: ought to have one CS for dnslib, initialized on a DnsLib
  1243. // init function; then it is always valid and can be used
  1244. // whenever necessary
  1245. //
  1246. if ( fProcessAttach || !fcsInitialized )
  1247. {
  1248. fcsInitialized = TRUE;
  1249. InitializeCriticalSection( &SecurityContextListCS );
  1250. SecInvalidateHandle( &g_hSspiCredentials );
  1251. g_fSecurityPackageInitialized = FALSE;
  1252. }
  1253. //
  1254. // do full security package init
  1255. //
  1256. if ( !fProcessAttach )
  1257. {
  1258. EnterCriticalSection( &SecurityContextListCS );
  1259. if ( !g_fSecurityPackageInitialized )
  1260. {
  1261. status = Dns_InitializeSecurityPackage(
  1262. &g_SecurityTokenMaxLength,
  1263. FALSE // client, not DNS server
  1264. );
  1265. if ( status == ERROR_SUCCESS )
  1266. {
  1267. g_fSecurityPackageInitialized = TRUE;
  1268. // load ntdsapi.dll for SPN building
  1269. status = Dns_LoadNtdsapiProcs();
  1270. ASSERT( ERROR_SUCCESS == status );
  1271. }
  1272. }
  1273. LeaveCriticalSection( &SecurityContextListCS );
  1274. }
  1275. return( status );
  1276. }
  1277. DNS_STATUS
  1278. Dns_StartServerSecurity(
  1279. VOID
  1280. )
  1281. /*++
  1282. Routine Description:
  1283. Startup server security.
  1284. Note this function is NOT MT-safe.
  1285. Call it once on load, or protect call with a CS.
  1286. Arguments:
  1287. None.
  1288. Return Value:
  1289. TRUE if security is initialized.
  1290. FALSE if security initialization failure.
  1291. --*/
  1292. {
  1293. DNS_STATUS status;
  1294. if ( g_fSecurityPackageInitialized )
  1295. {
  1296. return( ERROR_SUCCESS );
  1297. }
  1298. //
  1299. // init globals
  1300. // - this protects us on server restart
  1301. //
  1302. g_SecurityTokenMaxLength = 0;
  1303. g_SignatureMaxLength = 0;
  1304. SecurityContextListHead = NULL;
  1305. g_pfnMakeSpn = NULL;
  1306. //
  1307. // CS is initialized before init sec pak in order to
  1308. // have it done similarly to the client code.
  1309. //
  1310. InitializeCriticalSection( &SecurityContextListCS );
  1311. status = Dns_InitializeSecurityPackage(
  1312. &g_SecurityTokenMaxLength,
  1313. TRUE
  1314. );
  1315. if ( status == ERROR_SUCCESS )
  1316. {
  1317. g_fSecurityPackageInitialized = TRUE;
  1318. }
  1319. else
  1320. {
  1321. ASSERT ( g_fSecurityPackageInitialized == FALSE );
  1322. DeleteCriticalSection( &SecurityContextListCS );
  1323. }
  1324. return( status );
  1325. }
  1326. DNS_STATUS
  1327. Dns_InitializeSecurityPackage(
  1328. OUT PDWORD pdwMaxMessage,
  1329. IN BOOL fDnsServer
  1330. )
  1331. /*++
  1332. Routine Description:
  1333. Load and initialize the security package.
  1334. Note, call this function at first UPDATE.
  1335. MUST NOT call this function at DLL init, this becomes possibly cyclic.
  1336. Parameters:
  1337. pdwMaxMessage - addr to recv max security token length
  1338. Return Value:
  1339. ERROR_SUCCESS if successful.
  1340. ErrorCode on failure.
  1341. --*/
  1342. {
  1343. SECURITY_STATUS status;
  1344. FARPROC psecurityEntry;
  1345. PSecPkgInfoW pkgInfo;
  1346. UUID uuid;
  1347. //
  1348. // init SSPI credentials handle (regardless of package state)
  1349. //
  1350. SecInvalidateHandle( &g_hSspiCredentials );
  1351. //
  1352. // load and initialize the appropriate SSP
  1353. //
  1354. g_hLibSecurity = LoadLibrary( NT_DLL_NAME );
  1355. if ( !g_hLibSecurity )
  1356. {
  1357. status = GetLastError();
  1358. DNS_PRINT(( "Couldn't load dll: %u\n", status ));
  1359. goto Failed;
  1360. }
  1361. psecurityEntry = GetProcAddress( g_hLibSecurity, SECURITY_ENTRYPOINTW );
  1362. if ( !psecurityEntry )
  1363. {
  1364. status = GetLastError();
  1365. DNS_PRINT(( "Couldn't get sec init routine: %u\n", status ));
  1366. goto Failed;
  1367. }
  1368. g_pSecurityFunctionTable = (PSecurityFunctionTableW) psecurityEntry();
  1369. if ( !g_pSecurityFunctionTable )
  1370. {
  1371. status = ERROR_DLL_INIT_FAILED;
  1372. DNS_PRINT(( "ERROR: unable to get security function table.\n"));
  1373. goto Failed;
  1374. }
  1375. // Get info for security package (negotiate)
  1376. // - need max size of tokens
  1377. status = g_pSecurityFunctionTable->QuerySecurityPackageInfoW( PACKAGE_NAME, &pkgInfo );
  1378. if ( !SEC_SUCCESS(status) )
  1379. {
  1380. DNS_PRINT((
  1381. "Couldn't query package info for %s, error %u\n",
  1382. PACKAGE_NAME,
  1383. status ));
  1384. goto Failed;
  1385. }
  1386. g_SecurityTokenMaxLength = pkgInfo->cbMaxToken;
  1387. g_pSecurityFunctionTable->FreeContextBuffer( pkgInfo );
  1388. //
  1389. // Note: This is the maximum addition to the size of the
  1390. // DNS update packet. (excluding the signature)
  1391. //
  1392. // DCR_CLEANUP: what is the point of this? as we have set a global
  1393. //
  1394. if ( pdwMaxMessage)
  1395. {
  1396. *pdwMaxMessage = g_SecurityTokenMaxLength;
  1397. }
  1398. //
  1399. // Acquire process credentials handle from SSPI
  1400. //
  1401. status = Dns_RefreshSSpiCredentialsHandle(
  1402. fDnsServer,
  1403. NULL );
  1404. if ( !SEC_SUCCESS(status) )
  1405. {
  1406. DNSDBG( SECURITY, (
  1407. "Error 0xX: Cannot acquire credentials handle\n",
  1408. status ));
  1409. ASSERT ( FALSE );
  1410. goto Failed;
  1411. }
  1412. //
  1413. // Get a unique id
  1414. // - even if call fails, just take what's in stack
  1415. // and make a string out of it -- we just want the string
  1416. //
  1417. UuidCreateSequential( &uuid );
  1418. DnsStringPrint_Guid(
  1419. g_ContextUuid,
  1420. & uuid );
  1421. DNSDBG( SECURITY, (
  1422. "Started security package (%S)\n"
  1423. "\tmax token = %d\n",
  1424. PACKAGE_NAME,
  1425. g_SecurityTokenMaxLength ));
  1426. return( ERROR_SUCCESS );
  1427. Failed:
  1428. if ( status == ERROR_SUCCESS )
  1429. {
  1430. status = ERROR_DLL_INIT_FAILED;
  1431. }
  1432. return( status );
  1433. }
  1434. VOID
  1435. Dns_TerminateSecurityPackage(
  1436. VOID
  1437. )
  1438. /*++
  1439. Routine Description:
  1440. Terminate security package on shutdown.
  1441. Arguments:
  1442. None.
  1443. Return Value:
  1444. None.
  1445. --*/
  1446. {
  1447. DWORD status=ERROR_SUCCESS;
  1448. if ( g_fSecurityPackageInitialized )
  1449. {
  1450. #if 0
  1451. //
  1452. // it turns out that the security lib get unloaded before in some cases
  1453. // us for some reason (alhtough we explicity tells it to unload
  1454. // after us).
  1455. // We will never alloc over ourselves anyway (see startup).
  1456. //
  1457. if ( !SSPI_INVALID_HANDLE ( &g_hSspiCredentials ) )
  1458. {
  1459. //
  1460. // Free previously allocated handle
  1461. //
  1462. status = g_pSecurityFunctionTable->FreeCredentialsHandle(
  1463. &g_hSspiCredentials );
  1464. if ( !SEC_SUCCESS(status) )
  1465. {
  1466. DNSDBG( SECURITY, (
  1467. "Error <0x%x>: Cannot free credentials handle\n",
  1468. status ));
  1469. }
  1470. }
  1471. // continue regardless.
  1472. SecInvalidateHandle( &g_hSspiCredentials );
  1473. Dns_FreeSecurityContextList();
  1474. #endif
  1475. if ( g_hLibSecurity )
  1476. {
  1477. FreeLibrary( g_hLibSecurity );
  1478. }
  1479. if ( g_hLibNtdsa )
  1480. {
  1481. FreeLibrary( g_hLibNtdsa );
  1482. }
  1483. }
  1484. DeleteCriticalSection( &SecurityContextListCS );
  1485. }
  1486. DNS_STATUS
  1487. Dns_InitClientSecurityContext(
  1488. IN OUT PSECPACK pSecPack,
  1489. IN LPSTR pszNameServer,
  1490. OUT PBOOL pfDoneNegotiate
  1491. )
  1492. /*++
  1493. Routine Description:
  1494. Initialize client security context building security token to send.
  1495. On first pass, creates context blob (and returns handle).
  1496. On second pass, uses server context to rebuild negotiated token.
  1497. Arguments:
  1498. pSecPack -- ptr to security info for packet
  1499. pszNameServer -- DNS server to nego with
  1500. pCreds -- credentials (if given)
  1501. pfDoneNegotiate -- addr to set if done with negotiation
  1502. TRUE if done with nego
  1503. FALSE if continuing
  1504. Return Value:
  1505. ERROR_SUCCESS -- if done
  1506. DNS_STATUS_CONTINUE_NEEDED -- if continue respone to client is needed
  1507. ErrorCode on failure.
  1508. --*/
  1509. {
  1510. //PSECPACK pSecPack = (PSECPACK)hSecPack;
  1511. SECURITY_STATUS status;
  1512. PSEC_CNTXT psecCtxt;
  1513. BOOL fcreatedContext = FALSE;
  1514. TimeStamp lifetime;
  1515. SecBufferDesc outBufDesc;
  1516. SecBufferDesc inBufDesc;
  1517. ULONG contextAttributes = 0;
  1518. WCHAR wszKerberosName[ MAX_PATH ];
  1519. PCredHandle pcredHandle;
  1520. DNSDBG( SECURITY, ( "Enter InitClientSecurityContext()\n" ));
  1521. IF_DNSDBG( SECURITY )
  1522. {
  1523. DnsDbg_SecurityPacketInfo(
  1524. "InitClientSecurityContext() at top.\n",
  1525. pSecPack );
  1526. }
  1527. //
  1528. // if not existing context, create new one
  1529. //
  1530. // note: if want to create new here, then need context key
  1531. //
  1532. psecCtxt = pSecPack->pSecContext;
  1533. if ( !psecCtxt )
  1534. {
  1535. DNSDBG( SECURITY, (
  1536. "ERROR: Called into Dns_InitClientSecurityContext w/ no security context!!\n" ));
  1537. ASSERT ( FALSE );
  1538. return( DNS_ERROR_NO_MEMORY );
  1539. }
  1540. //
  1541. // client completed initialization
  1542. // - if server sent back token, should be echo of client's token
  1543. //
  1544. if ( psecCtxt->fNegoComplete )
  1545. {
  1546. if ( pSecPack->LocalBuf.pvBuffer &&
  1547. pSecPack->LocalBuf.cbBuffer == pSecPack->RemoteBuf.cbBuffer &&
  1548. RtlEqualMemory(
  1549. pSecPack->LocalBuf.pvBuffer,
  1550. pSecPack->RemoteBuf.pvBuffer,
  1551. pSecPack->LocalBuf.cbBuffer
  1552. ) )
  1553. {
  1554. return( ERROR_SUCCESS );
  1555. }
  1556. DNSDBG( ANY, (
  1557. "InitClientSecurityContext() on already negotiated context %p\n"
  1558. "\tserver buffer is NOT echo of buffer sent!\n",
  1559. psecCtxt ));
  1560. return( DNS_ERROR_RCODE_BADKEY );
  1561. }
  1562. //
  1563. // prepare output buffer, allocate if necessary
  1564. // - security token will be written to this buffer
  1565. //
  1566. if ( !pSecPack->LocalBuf.pvBuffer )
  1567. {
  1568. PCHAR pbuf;
  1569. ASSERT( g_SecurityTokenMaxLength );
  1570. pbuf = (PVOID) ALLOCATE_HEAP( g_SecurityTokenMaxLength );
  1571. if ( !pbuf )
  1572. {
  1573. status = DNS_ERROR_NO_MEMORY;
  1574. goto Failed;
  1575. }
  1576. pSecPack->LocalBuf.pvBuffer = pbuf;
  1577. pSecPack->LocalBuf.BufferType = SECBUFFER_TOKEN;
  1578. //pSecPack->LocalBuf.cbBuffer = g_SecurityTokenMaxLength;
  1579. }
  1580. // set\reset buffer length
  1581. pSecPack->LocalBuf.cbBuffer = g_SecurityTokenMaxLength;
  1582. outBufDesc.ulVersion = 0;
  1583. outBufDesc.cBuffers = 1;
  1584. outBufDesc.pBuffers = &pSecPack->LocalBuf;
  1585. // DCR_PERF: zeroing buffer is unnecessary -- remove
  1586. RtlZeroMemory(
  1587. pSecPack->LocalBuf.pvBuffer,
  1588. pSecPack->LocalBuf.cbBuffer );
  1589. //
  1590. // if have response from server, then send as input buffer
  1591. //
  1592. if ( pSecPack->RemoteBuf.pvBuffer )
  1593. {
  1594. ASSERT( !psecCtxt->fNewConversation );
  1595. ASSERT( pSecPack->RemoteBuf.cbBuffer );
  1596. ASSERT( pSecPack->RemoteBuf.BufferType == SECBUFFER_TOKEN );
  1597. inBufDesc.ulVersion = 0;
  1598. inBufDesc.cBuffers = 1;
  1599. inBufDesc.pBuffers = & pSecPack->RemoteBuf;
  1600. }
  1601. ELSE_ASSERT( psecCtxt->fNewConversation );
  1602. //
  1603. // get server in SPN format
  1604. //
  1605. // DCR_PERF: SPN name lookup duplicated on second pass
  1606. // - if know we are synchronous could keep
  1607. // - or could save to packet stuct (but then would have to alloc)
  1608. status = MakeKerberosName(
  1609. wszKerberosName,
  1610. pszNameServer,
  1611. TRUE
  1612. );
  1613. if ( status != ERROR_SUCCESS )
  1614. {
  1615. status = ERROR_INVALID_DATA;
  1616. goto Failed;
  1617. }
  1618. IF_DNSDBG( SECURITY )
  1619. {
  1620. DNS_PRINT((
  1621. "Before InitClientSecurityContextW().\n"
  1622. "\ttime (ms) = %d\n"
  1623. "\tkerb name = %S\n",
  1624. GetCurrentTime(),
  1625. wszKerberosName ));
  1626. DnsDbg_SecurityPacketInfo(
  1627. "Before call to InitClientSecurityContextW().\n",
  1628. pSecPack );
  1629. }
  1630. //
  1631. // cred handle
  1632. //
  1633. pcredHandle = &g_hSspiCredentials;
  1634. if ( psecCtxt->fHaveCredHandle )
  1635. {
  1636. pcredHandle = &psecCtxt->CredHandle;
  1637. }
  1638. status = g_pSecurityFunctionTable->InitializeSecurityContextW(
  1639. pcredHandle,
  1640. psecCtxt->fNewConversation
  1641. ? NULL
  1642. : &psecCtxt->hSecHandle,
  1643. wszKerberosName,
  1644. ISC_REQ_REPLAY_DETECT |
  1645. ISC_REQ_DELEGATE |
  1646. ISC_REQ_MUTUAL_AUTH, // context requirements
  1647. 0, // reserved1
  1648. SECURITY_NATIVE_DREP,
  1649. psecCtxt->fNewConversation
  1650. ? NULL
  1651. : &inBufDesc,
  1652. 0, // reserved2
  1653. & psecCtxt->hSecHandle,
  1654. & outBufDesc,
  1655. & contextAttributes,
  1656. & lifetime
  1657. );
  1658. DNSDBG( SECURITY, (
  1659. "After InitClientSecurityContextW().\n"
  1660. "\ttime (ms) = %d\n"
  1661. "\tkerb name = %S\n"
  1662. "\tcontext attr = %08x\n"
  1663. "\tstatus = %d (%08x)\n",
  1664. GetCurrentTime(),
  1665. wszKerberosName,
  1666. contextAttributes,
  1667. status, status ));
  1668. //
  1669. // failed?
  1670. // - if unable to get kerberos (mutual auth) then bail
  1671. // this eliminates trying to do nego when in workgroup
  1672. //
  1673. if ( !SEC_SUCCESS(status) ||
  1674. ( status == SEC_E_OK &&
  1675. !(contextAttributes & ISC_REQ_MUTUAL_AUTH) ) )
  1676. {
  1677. DNS_PRINT((
  1678. "InitializeSecurityContextW() failed: %08x %u\n"
  1679. "\tContext Attributes = %p\n"
  1680. "\tTokenMaxLength = %d\n"
  1681. "\tSigMaxLength = %d\n"
  1682. "\tPackageInitialized = %d\n"
  1683. "\tlifetime = %d\n",
  1684. status, status,
  1685. contextAttributes,
  1686. g_SecurityTokenMaxLength,
  1687. g_SignatureMaxLength,
  1688. g_fSecurityPackageInitialized,
  1689. lifetime
  1690. ));
  1691. //
  1692. // DCR: security error codes on local function failures:
  1693. // - key's no good
  1694. // - sigs no good
  1695. // RCODE errors are fine for sending back to remote, but don't
  1696. // convey the correct info locally
  1697. //
  1698. status = DNS_ERROR_RCODE_BADKEY;
  1699. goto Failed;
  1700. }
  1701. psecCtxt->fHaveSecHandle = TRUE;
  1702. DNSDBG( SECURITY, (
  1703. "Finished InitializeSecurityContext():\n"
  1704. "\tstatus = %08x (%d)\n"
  1705. "\thandle = %p\n"
  1706. "\toutput buffers\n"
  1707. "\t\tcBuffers = %d\n"
  1708. "\t\tpBuffers = %p\n"
  1709. "\tlocal buffer\n"
  1710. "\t\tptr = %p\n"
  1711. "\t\tlength = %d\n",
  1712. status, status,
  1713. & psecCtxt->hSecHandle,
  1714. outBufDesc.cBuffers,
  1715. outBufDesc.pBuffers,
  1716. pSecPack->LocalBuf.pvBuffer,
  1717. pSecPack->LocalBuf.cbBuffer
  1718. ));
  1719. ASSERT( status == SEC_E_OK ||
  1720. status == SEC_I_CONTINUE_NEEDED ||
  1721. status == SEC_I_COMPLETE_AND_CONTINUE );
  1722. //
  1723. // determine signature length
  1724. //
  1725. // note: not safe to do just once on start of process, as can fail
  1726. // to locate DC and end up ntlm on first pass then locate
  1727. // DC later and need a larger sig; so many potential client's
  1728. // under services, it is dangerous not to calculate each time
  1729. //
  1730. if ( status == SEC_E_OK )
  1731. {
  1732. SecPkgContext_Sizes Sizes;
  1733. status = g_pSecurityFunctionTable->QueryContextAttributesW(
  1734. & psecCtxt->hSecHandle,
  1735. SECPKG_ATTR_SIZES,
  1736. (PVOID) &Sizes
  1737. );
  1738. if ( !SEC_SUCCESS(status) )
  1739. {
  1740. // DEVNOTE: this will leave us will valid return but
  1741. // potentially unset sig max length
  1742. goto Failed;
  1743. }
  1744. if ( Sizes.cbMaxSignature > g_SignatureMaxLength )
  1745. {
  1746. g_SignatureMaxLength = Sizes.cbMaxSignature;
  1747. }
  1748. DNSDBG( SECURITY, (
  1749. "Signature max length = %d\n",
  1750. g_SignatureMaxLength
  1751. ));
  1752. }
  1753. //
  1754. // now have context, flag for next pass
  1755. //
  1756. psecCtxt->fNewConversation = FALSE;
  1757. //
  1758. // completed -- have key
  1759. // - if just created, then need to send back to server
  1760. // - otherwise done
  1761. //
  1762. if ( status == ERROR_SUCCESS )
  1763. {
  1764. psecCtxt->fNegoComplete = TRUE;
  1765. ASSERT( pSecPack->LocalBuf.pvBuffer );
  1766. if ( pSecPack->LocalBuf.cbBuffer )
  1767. {
  1768. //ASSERT( pSecPack->LocalBuf.cbBuffer != pSecPack->RemoteBuf.cbBuffer );
  1769. status = DNS_STATUS_CONTINUE_NEEDED;
  1770. }
  1771. }
  1772. //
  1773. // continue needed? -- use single return code
  1774. //
  1775. else
  1776. {
  1777. ASSERT( status == SEC_I_CONTINUE_NEEDED ||
  1778. status == SEC_I_COMPLETE_AND_CONTINUE );
  1779. DNSDBG( SECURITY, (
  1780. "Initializing client context continue needed.\n"
  1781. "\tlocal complete = %d\n",
  1782. ( status == SEC_I_COMPLETE_AND_CONTINUE )
  1783. ));
  1784. //psecCtxt->State = DNSGSS_STATE_CONTINUE;
  1785. status = DNS_STATUS_CONTINUE_NEEDED;
  1786. psecCtxt->fNegoComplete = FALSE;
  1787. }
  1788. *pfDoneNegotiate = psecCtxt->fNegoComplete;
  1789. ASSERT( status == ERROR_SUCCESS || status == DNS_STATUS_CONTINUE_NEEDED );
  1790. Failed:
  1791. IF_DNSDBG( SECURITY )
  1792. {
  1793. DnsPrint_Lock();
  1794. DNSDBG( SECURITY, (
  1795. "Leaving InitClientSecurityContext().\n"
  1796. "\tstatus = %08x (%d)\n",
  1797. status, status ));
  1798. DnsDbg_SecurityContext(
  1799. "Security Context",
  1800. psecCtxt );
  1801. DnsDbg_SecurityPacketInfo(
  1802. "Security Session Packet Info",
  1803. pSecPack );
  1804. DnsPrint_Unlock();
  1805. }
  1806. #if 0
  1807. //
  1808. // security context (the struct) is NEVER created in this function
  1809. // so no need to determine cleanup issue on failure
  1810. // caller determines action if
  1811. //
  1812. if ( status == ERROR_SUCCESS || status == DNS_STATUS_CONTINUE_NEEDED )
  1813. {
  1814. return( status );
  1815. }
  1816. //
  1817. // DEVNOTE: should we attempt to preserve a context on failure?
  1818. // - could be a potential security attack to crash negotiation contexts,
  1819. // by sending garbage
  1820. // - however don't want bad context to stay around and block all future
  1821. // attempts to renegotiate
  1822. //
  1823. // delete any locally create context
  1824. // caller will be responsible for making determination about recaching or
  1825. // deleting context for passed in context
  1826. //
  1827. if ( fcreatedContext )
  1828. {
  1829. Dns_FreeSecurityContext( psecCtxt );
  1830. pSecPack->pSecContext = NULL;
  1831. }
  1832. else
  1833. {
  1834. Dns_EnlistSecurityContext( (PSEC_CNTXT)psecCtxt );
  1835. }
  1836. #endif
  1837. return( status );
  1838. }
  1839. DNS_STATUS
  1840. Dns_ServerAcceptSecurityContext(
  1841. IN OUT PSECPACK pSecPack,
  1842. IN BOOL fBreakOnAscFailure
  1843. )
  1844. /*++
  1845. Routine Description:
  1846. Initialized server's security context for session with client.
  1847. This is called with newly created context on first client packet,
  1848. then called again with previously initialized context, after client
  1849. responds to negotiation.
  1850. Arguments:
  1851. pSecPack -- security context info for server's session with client
  1852. Return Value:
  1853. ERROR_SUCCESS -- if done
  1854. DNS_STATUS_CONTINUE_NEEDED -- if continue respone to client is needed
  1855. ErrorCode on failure.
  1856. --*/
  1857. {
  1858. PSEC_CNTXT psecCtxt;
  1859. SECURITY_STATUS status;
  1860. TimeStamp lifetime;
  1861. SecBufferDesc outBufDesc;
  1862. SecBufferDesc inBufDesc;
  1863. ULONG contextAttributes = 0;
  1864. DNSDBG( SECURITY, (
  1865. "ServerAcceptSecurityContext(%p, fBreak=%d)\n",
  1866. pSecPack,
  1867. fBreakOnAscFailure ));
  1868. IF_DNSDBG( SECURITY )
  1869. {
  1870. DnsDbg_SecurityPacketInfo(
  1871. "Entering ServerAcceptSecurityContext()",
  1872. pSecPack );
  1873. }
  1874. //
  1875. // get context
  1876. //
  1877. psecCtxt = pSecPack->pSecContext;
  1878. if ( !psecCtxt )
  1879. {
  1880. DNSDBG( SECURITY, (
  1881. "ERROR: ServerAcceptSecurityContext called with no security context\n" ));
  1882. ASSERT( FALSE );
  1883. return( DNS_ERROR_NO_MEMORY );
  1884. }
  1885. //
  1886. // already initialized
  1887. // - echo of previous token is legitimate
  1888. // - if client still thinks it's negotiating => problem
  1889. //
  1890. // DCR_CLEAN: need clear story here on how to handle this -- do these
  1891. // "mistaken" clients cause context to be scrapped from cache?
  1892. //
  1893. if ( psecCtxt->fNegoComplete )
  1894. {
  1895. if ( psecCtxt->TkeySize == pSecPack->RemoteBuf.cbBuffer )
  1896. {
  1897. return( ERROR_SUCCESS );
  1898. }
  1899. #if 0
  1900. // DCR_FIX:
  1901. // NOTE: couldn't do buf compare as not MT
  1902. // safe when allow context\buffer cleanup
  1903. // QUESTION: how can this be dumped while in use
  1904. if ( pSecPack->LocalBuf.pvBuffer &&
  1905. psecCtxt->TkeySize == pSecPack->RemoteBuf.cbBuffer &&
  1906. pSecPack->LocalBuf.cbBuffer == pSecPack->RemoteBuf.cbBuffer &&
  1907. RtlEqualMemory(
  1908. pSecPack->LocalBuf.pvBuffer,
  1909. pSecPack->RemoteBuf.pvBuffer,
  1910. pSecPack->LocalBuf.cbBuffer
  1911. ) )
  1912. {
  1913. return( ERROR_SUCCESS );
  1914. }
  1915. #endif
  1916. DNSDBG( ANY, (
  1917. "WARNING: Server receiving new or incorrect TKEY on already\n"
  1918. "\tnegotiated context %p;\n"
  1919. "\tserver buffer is NOT echo of buffer sent!\n",
  1920. psecCtxt ));
  1921. return( DNS_ERROR_RCODE_BADKEY );
  1922. }
  1923. // refresh SSPI credentials if expired
  1924. if ( SSPI_EXPIRED_HANDLE( g_SspiCredentialsLifetime ) )
  1925. {
  1926. status = Dns_RefreshSSpiCredentialsHandle( TRUE, NULL );
  1927. if ( !SEC_SUCCESS(status) )
  1928. {
  1929. DNS_PRINT((
  1930. "Error <0x%x>: Cannot refresh Sspi Credentials Handle\n",
  1931. status ));
  1932. }
  1933. }
  1934. //
  1935. // accept security context
  1936. //
  1937. // allocate local token buffer if doesn't exists
  1938. // note, the reason I do this is so I won't have the memory of
  1939. // a large buffer sitting around during a two pass security session
  1940. // and hence tied up until I time out
  1941. //
  1942. // DCR_PERF: security token buffer allocation
  1943. // since context will be verified before queued, is this approach
  1944. // sensible?
  1945. // if can delete when TCP connection fails, or on short timeout, then
  1946. // ok to append to SEC_CNTXT and save an allocation
  1947. //
  1948. if ( !pSecPack->LocalBuf.pvBuffer )
  1949. {
  1950. PCHAR pbuf;
  1951. pbuf = (PVOID) ALLOCATE_HEAP( g_SecurityTokenMaxLength );
  1952. if ( !pbuf )
  1953. {
  1954. status = DNS_ERROR_NO_MEMORY;
  1955. goto Failed;
  1956. }
  1957. pSecPack->LocalBuf.pvBuffer = pbuf;
  1958. pSecPack->LocalBuf.cbBuffer = g_SecurityTokenMaxLength;
  1959. pSecPack->LocalBuf.BufferType = SECBUFFER_TOKEN;
  1960. }
  1961. pSecPack->LocalBuf.cbBuffer = g_SecurityTokenMaxLength;
  1962. outBufDesc.ulVersion = 0;
  1963. outBufDesc.cBuffers = 1;
  1964. outBufDesc.pBuffers = &pSecPack->LocalBuf;
  1965. // DCR_PERF: zeroing nego buffer is unnecessary
  1966. RtlZeroMemory(
  1967. pSecPack->LocalBuf.pvBuffer,
  1968. pSecPack->LocalBuf.cbBuffer );
  1969. // prepare input buffer with client token
  1970. inBufDesc.ulVersion = 0;
  1971. inBufDesc.cBuffers = 1;
  1972. inBufDesc.pBuffers = & pSecPack->RemoteBuf;
  1973. status = g_pSecurityFunctionTable->AcceptSecurityContext(
  1974. & g_hSspiCredentials,
  1975. psecCtxt->fNewConversation
  1976. ? NULL
  1977. : & psecCtxt->hSecHandle,
  1978. & inBufDesc,
  1979. ASC_REQ_REPLAY_DETECT
  1980. | ASC_REQ_DELEGATE
  1981. | ASC_REQ_MUTUAL_AUTH, // context requirements
  1982. SECURITY_NATIVE_DREP,
  1983. & psecCtxt->hSecHandle,
  1984. & outBufDesc,
  1985. & contextAttributes,
  1986. & lifetime
  1987. );
  1988. if ( fBreakOnAscFailure &&
  1989. ( status != SEC_E_OK &&
  1990. status != SEC_I_CONTINUE_NEEDED &&
  1991. status != SEC_I_COMPLETE_AND_CONTINUE ) )
  1992. {
  1993. DNS_PRINT(( "HARD BREAK: BreakOnAscFailure status=%d\n",
  1994. status ));
  1995. DebugBreak();
  1996. }
  1997. if ( !SEC_SUCCESS(status) )
  1998. {
  1999. DNS_PRINT((
  2000. "ERROR: Accept security context failed status = %d (%08x)\n",
  2001. status, status ));
  2002. goto Failed;
  2003. }
  2004. psecCtxt->fHaveSecHandle = TRUE;
  2005. DNSDBG( SECURITY, (
  2006. "Finished AcceptSecurityContext():\n"
  2007. "\tstatus = %08x (%d)\n"
  2008. "\thandle = %p\n"
  2009. "\toutput buffers\n"
  2010. "\t\tcBuffers = %d\n"
  2011. "\t\tpBuffers = %p\n"
  2012. "\tlocal buffer\n"
  2013. "\t\tptr = %p\n"
  2014. "\t\tlength = %d\n"
  2015. "\tlifetime = %ld %ld\n"
  2016. "\tcontext flag = 0x%lx\n",
  2017. status, status,
  2018. & psecCtxt->hSecHandle,
  2019. outBufDesc.cBuffers,
  2020. outBufDesc.pBuffers,
  2021. pSecPack->LocalBuf.pvBuffer,
  2022. pSecPack->LocalBuf.cbBuffer,
  2023. lifetime.HighPart,
  2024. lifetime.LowPart,
  2025. contextAttributes
  2026. ));
  2027. ASSERT( status == SEC_E_OK ||
  2028. status == SEC_I_CONTINUE_NEEDED ||
  2029. status == SEC_I_COMPLETE_AND_CONTINUE );
  2030. //
  2031. // compute the size of signature if you are done with initializing
  2032. // the security context and haven't done it before
  2033. //
  2034. if ( status == SEC_E_OK )
  2035. {
  2036. SecPkgContext_Sizes Sizes;
  2037. //
  2038. // reject NULL sessions
  2039. // NTLM security will establish NULL sessions to non-domain clients,
  2040. // even if ASC_REQ_ALLOW_NULL_SESSION is not set
  2041. // note, context has been created, but will be cleaned up in normal
  2042. // failure path
  2043. //
  2044. if ( contextAttributes & ASC_RET_NULL_SESSION )
  2045. {
  2046. DNSDBG( SECURITY, (
  2047. "Rejecting NULL session from AcceptSecurityContext()\n" ));
  2048. status = DNS_ERROR_RCODE_BADKEY;
  2049. goto Failed;
  2050. }
  2051. status = g_pSecurityFunctionTable->QueryContextAttributesW(
  2052. &psecCtxt->hSecHandle,
  2053. SECPKG_ATTR_SIZES,
  2054. (PVOID)& Sizes
  2055. );
  2056. if ( !SEC_SUCCESS(status) )
  2057. {
  2058. DNS_PRINT(( "Query context attribtues failed\n" ));
  2059. ASSERT( FALSE );
  2060. goto Failed;
  2061. }
  2062. //
  2063. // we should use the largest signature there is among all
  2064. // packages
  2065. //
  2066. // DCR_FIX: signature length stuff bogus???
  2067. //
  2068. // when packet is signed, the length is assumed to be g_SignatureMaxLength
  2069. // if this is not the signature length for the desired package, does
  2070. // this still work properly???
  2071. //
  2072. // DCR_FIX: potential very small timing window where two clients
  2073. // getting different packages could cause this to miss highest
  2074. // value -- potential causing a signing failure?
  2075. //
  2076. if ( Sizes.cbMaxSignature > g_SignatureMaxLength )
  2077. {
  2078. g_SignatureMaxLength = Sizes.cbMaxSignature;
  2079. }
  2080. //
  2081. // finished negotiation
  2082. // - set flag
  2083. // - save final TKEY data length, so can recognize response
  2084. //
  2085. // this is valid only on new conversation, shouldn't have
  2086. // no sig second time through
  2087. //
  2088. psecCtxt->fNegoComplete = TRUE;
  2089. psecCtxt->TkeySize = (WORD) pSecPack->LocalBuf.cbBuffer;
  2090. //
  2091. // need token response from server
  2092. // some protocols (kerberos) complete in one pass, but hence require
  2093. // non-echo response from server for mutual-authentication
  2094. //
  2095. if ( psecCtxt->TkeySize )
  2096. {
  2097. DNSDBG( SECURITY, (
  2098. "Successful security context accept, but need server reponse\n"
  2099. "\t-- doing continue.\n" ));
  2100. status = DNS_STATUS_CONTINUE_NEEDED;
  2101. }
  2102. #if 0
  2103. if ( !psecCtxt->pTsigRR && psecCtxt->fNewConversation )
  2104. {
  2105. DNSDBG( SECURITY, (
  2106. "Successful security context accept, without sig, doing continue\n" ));
  2107. status = DNS_STATUS_CONTINUE_NEEDED;
  2108. }
  2109. #endif
  2110. }
  2111. //
  2112. // continue needed?
  2113. // - single status code returned for continue needed
  2114. //
  2115. else if ( status == SEC_I_CONTINUE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE )
  2116. {
  2117. DNSDBG( SECURITY, (
  2118. "Initializing server context, continue needed.\n"
  2119. "\tlocal complete = %d\n",
  2120. ( status == SEC_I_COMPLETE_AND_CONTINUE )
  2121. ));
  2122. psecCtxt->fNegoComplete = FALSE;
  2123. status = DNS_STATUS_CONTINUE_NEEDED;
  2124. }
  2125. psecCtxt->fNewConversation = FALSE;
  2126. Failed:
  2127. IF_DNSDBG( SECURITY )
  2128. {
  2129. DNSDBG( SECURITY, (
  2130. "Leaving ServerAcceptSecurityContext().\n"
  2131. "\tstatus = %d %08x\n",
  2132. status, status ));
  2133. DnsDbg_SecurityContext(
  2134. "Security Session Context leaving ServerAcceptSecurityContext()",
  2135. psecCtxt );
  2136. }
  2137. return( status );
  2138. }
  2139. DNS_STATUS
  2140. Dns_SrvImpersonateClient(
  2141. IN HANDLE hSecPack
  2142. )
  2143. /*++
  2144. Routine Description:
  2145. Make server impersonate client.
  2146. Parameters:
  2147. hSecPack -- session context handle
  2148. Return Value:
  2149. ERROR_SUCCESS if successful impersonation.
  2150. ErrorCode on failue.
  2151. --*/
  2152. {
  2153. PSEC_CNTXT psecCtxt;
  2154. // get security context
  2155. psecCtxt = ((PSECPACK)hSecPack)->pSecContext;
  2156. if ( !psecCtxt )
  2157. {
  2158. DNS_PRINT(( "ERROR: Dns_SrvImpersonateClient without context!!!\n" ));
  2159. ASSERT( FALSE );
  2160. return( DNS_ERROR_RCODE_BADKEY );
  2161. }
  2162. return g_pSecurityFunctionTable->ImpersonateSecurityContext( &psecCtxt->hSecHandle );
  2163. }
  2164. DNS_STATUS
  2165. Dns_SrvRevertToSelf(
  2166. IN HANDLE hSecPack
  2167. )
  2168. /*++
  2169. Routine Description:
  2170. Return server context to itself.
  2171. Parameters:
  2172. hSecPack -- session context handle
  2173. Return Value:
  2174. ERROR_SUCCESS if successful impersonation.
  2175. ErrorCode on failue.
  2176. --*/
  2177. {
  2178. PSEC_CNTXT psecCtxt;
  2179. // get security context
  2180. psecCtxt = ((PSECPACK)hSecPack)->pSecContext;
  2181. if ( !psecCtxt )
  2182. {
  2183. DNS_PRINT(( "ERROR: Dns_SrvRevertToSelf without context!!!\n" ));
  2184. ASSERT( FALSE );
  2185. return( DNS_ERROR_RCODE_BADKEY );
  2186. }
  2187. return g_pSecurityFunctionTable->RevertSecurityContext( &psecCtxt->hSecHandle );
  2188. }
  2189. //
  2190. // Security record packet write
  2191. //
  2192. DNS_STATUS
  2193. Dns_WriteGssTkeyToMessage(
  2194. IN PSECPACK pSecPack,
  2195. IN PDNS_HEADER pMsgHead,
  2196. IN PCHAR pMsgBufEnd,
  2197. IN OUT PCHAR * ppCurrent,
  2198. IN BOOL fIsServer
  2199. )
  2200. /*++
  2201. Routine Description:
  2202. Write security record into packet, and optionally sign.
  2203. Arguments:
  2204. hSecPack -- security session handle
  2205. pMsgHead -- ptr to start of DNS message
  2206. pMsgEnd -- ptr to end of message buffer
  2207. ppCurrent -- addr to recv ptr to end of message
  2208. fIsServer -- performing this operation as DNS server?
  2209. Return Value:
  2210. ERROR_SUCCESS on success
  2211. ErrorCode of failure to accomodate or sign message.
  2212. --*/
  2213. {
  2214. DNS_STATUS status = ERROR_INVALID_DATA;
  2215. PSEC_CNTXT psecCtxt;
  2216. PCHAR pch;
  2217. DWORD expireTime;
  2218. WORD keyLength;
  2219. WORD keyRecordDataLength;
  2220. PCHAR precordData;
  2221. PCHAR pnameAlg;
  2222. WORD lengthAlg;
  2223. DNSDBG( SECURITY, ( "Dns_WriteGssTkeyToMessage( %p )\n", pSecPack ));
  2224. //
  2225. // get security context
  2226. //
  2227. psecCtxt = pSecPack->pSecContext;
  2228. if ( !psecCtxt )
  2229. {
  2230. DNS_PRINT(( "ERROR: attempted signing without security context!!!\n" ));
  2231. ASSERT( FALSE );
  2232. return( DNS_ERROR_RCODE_BADKEY );
  2233. }
  2234. //
  2235. // peal packet back to question section
  2236. //
  2237. pMsgHead->AnswerCount = 0;
  2238. pMsgHead->NameServerCount = 0;
  2239. pMsgHead->AdditionalCount = 0;
  2240. // go to end of packet to insert TKEY record
  2241. pch = Dns_SkipToRecord(
  2242. pMsgHead,
  2243. pMsgBufEnd,
  2244. 0 // go to end of packet
  2245. );
  2246. if ( !pch )
  2247. {
  2248. DNS_ASSERT( FALSE );
  2249. DNS_PRINT(("Dns_SkipToSecurityRecord failed!\n" ));
  2250. goto Exit;
  2251. }
  2252. //
  2253. // reset section count where the TKEY RR will be written
  2254. //
  2255. // for client section depends on version
  2256. // W2K -> answer
  2257. // later -> additional
  2258. //
  2259. if ( fIsServer )
  2260. {
  2261. pMsgHead->AnswerCount = 1;
  2262. // for server set client TKEY version in context
  2263. // - if not learned on previous pass
  2264. if ( psecCtxt->Version == 0 )
  2265. {
  2266. psecCtxt->Version = pSecPack->TkeyVersion;
  2267. }
  2268. }
  2269. else
  2270. {
  2271. if ( psecCtxt->Version == TKEY_VERSION_W2K )
  2272. {
  2273. pMsgHead->AnswerCount = 1;
  2274. }
  2275. else
  2276. {
  2277. pMsgHead->AdditionalCount = 1;
  2278. }
  2279. }
  2280. //
  2281. // write TKEY owner
  2282. // - this is context "name"
  2283. //
  2284. pch = Dns_WriteDottedNameToPacket(
  2285. pch,
  2286. pMsgBufEnd,
  2287. psecCtxt->Key.pszTkeyName,
  2288. NULL, // FQDN, no domain
  2289. 0, // no domain offset
  2290. FALSE // not unicode
  2291. );
  2292. if ( !pch )
  2293. {
  2294. goto Exit;
  2295. }
  2296. //
  2297. // TKEY record
  2298. // - algorithm owner
  2299. // - time
  2300. // - expire time
  2301. // - key length
  2302. // - key
  2303. //
  2304. if ( psecCtxt->Version == TKEY_VERSION_W2K )
  2305. {
  2306. pnameAlg = g_pAlgorithmNameW2K;
  2307. lengthAlg = W2K_GSS_ALGORITHM_NAME_PACKET_LENGTH;
  2308. }
  2309. else
  2310. {
  2311. //DNS_ASSERT( psecCtxt->Version == TKEY_VERSION_CURRENT );
  2312. pnameAlg = g_pAlgorithmNameCurrent;
  2313. lengthAlg = GSS_ALGORITHM_NAME_PACKET_LENGTH;
  2314. }
  2315. keyLength = (WORD) pSecPack->LocalBuf.cbBuffer;
  2316. keyRecordDataLength = keyLength + SIZEOF_TKEY_FIXED_DATA + lengthAlg;
  2317. if ( pch + sizeof(DNS_WIRE_RECORD) + keyRecordDataLength > pMsgBufEnd )
  2318. {
  2319. DNS_PRINT(( "Dns_WriteGssTkeyToMessage() failed! -- insufficient length\n" ));
  2320. DNS_ASSERT( FALSE );
  2321. status = ERROR_INVALID_PARAMETER;
  2322. goto Exit;
  2323. }
  2324. pch = Dns_WriteRecordStructureToPacketEx(
  2325. pch,
  2326. DNS_TYPE_TKEY,
  2327. DNS_CLASS_ANY,
  2328. 0,
  2329. keyRecordDataLength );
  2330. // write algorithm name
  2331. precordData = pch;
  2332. RtlCopyMemory(
  2333. pch,
  2334. pnameAlg,
  2335. lengthAlg );
  2336. pch += lengthAlg;
  2337. // time signed and expire time
  2338. // give ten minutes before expiration
  2339. expireTime = (DWORD) time( NULL );
  2340. INLINE_WRITE_FLIPPED_DWORD( pch, expireTime );
  2341. pch += sizeof(DWORD);
  2342. expireTime += TKEY_EXPIRE_INTERVAL;
  2343. INLINE_WRITE_FLIPPED_DWORD( pch, expireTime );
  2344. pch += sizeof(DWORD);
  2345. // mode
  2346. INLINE_WRITE_FLIPPED_WORD( pch, DNS_TKEY_MODE_GSS );
  2347. pch += sizeof(WORD);
  2348. // extended RCODE -- report back to caller
  2349. INLINE_WRITE_FLIPPED_WORD( pch, pSecPack->ExtendedRcode );
  2350. pch += sizeof(WORD);
  2351. // key length
  2352. INLINE_WRITE_FLIPPED_WORD( pch, keyLength );
  2353. pch += sizeof(WORD);
  2354. // write key token
  2355. RtlCopyMemory(
  2356. pch,
  2357. pSecPack->LocalBuf.pvBuffer,
  2358. keyLength );
  2359. pch += keyLength;
  2360. DNSDBG( SECURITY, (
  2361. "Wrote TKEY to packet at %p\n"
  2362. "\tlength = %d\n"
  2363. "\tpacket end = %p\n",
  2364. pMsgHead,
  2365. keyLength,
  2366. pch ));
  2367. ASSERT( pch < pMsgBufEnd );
  2368. // other length
  2369. WRITE_UNALIGNED_WORD( pch, 0 );
  2370. pch += sizeof(WORD);
  2371. ASSERT( pch < pMsgBufEnd );
  2372. ASSERT( pch - precordData == keyRecordDataLength );
  2373. *ppCurrent = pch;
  2374. status = ERROR_SUCCESS;
  2375. Exit:
  2376. return( status );
  2377. }
  2378. DNS_STATUS
  2379. Dns_SignMessageWithGssTsig(
  2380. IN HANDLE hSecPackCtxt,
  2381. IN PDNS_HEADER pMsgHead,
  2382. IN PCHAR pMsgBufEnd,
  2383. IN OUT PCHAR * ppCurrent
  2384. )
  2385. /*++
  2386. Routine Description:
  2387. Write GSS TSIG record to packet.
  2388. Arguments:
  2389. hSecPackCtxt -- packet security context
  2390. pMsgHead -- ptr to start of DNS message
  2391. pMsgEnd -- ptr to end of message buffer
  2392. ppCurrent -- addr to recv ptr to end of message
  2393. Return Value:
  2394. ERROR_SUCCESS on success
  2395. ErrorCode of failure to accomodate or sign message.
  2396. --*/
  2397. {
  2398. PSECPACK pSecPack = (PSECPACK) hSecPackCtxt;
  2399. PSEC_CNTXT psecCtxt;
  2400. DNS_STATUS status = ERROR_INVALID_DATA;
  2401. PCHAR pch; // ptr to walk through TSIG record during build
  2402. PCHAR ptsigRRHead;
  2403. PCHAR ptsigRdataBegin;
  2404. PCHAR ptsigRdataEnd;
  2405. PCHAR pbufStart = NULL; // signing buf
  2406. PCHAR pbuf; // ptr to walk through signing buf
  2407. PCHAR psig = NULL; // query signature
  2408. WORD sigLength;
  2409. DWORD length;
  2410. DWORD createTime;
  2411. SecBufferDesc outBufDesc;
  2412. SecBuffer outBuffs[2];
  2413. WORD netXid;
  2414. PCHAR pnameAlg;
  2415. DWORD lengthAlg;
  2416. DNSDBG( SECURITY, (
  2417. "Dns_SignMessageWithGssTsig( %p )\n",
  2418. pMsgHead ));
  2419. //
  2420. // get security context
  2421. //
  2422. psecCtxt = pSecPack->pSecContext;
  2423. if ( !psecCtxt )
  2424. {
  2425. DNS_PRINT(( "ERROR: attempted signing without security context!!!\n" ));
  2426. ASSERT( FALSE );
  2427. return( DNS_ERROR_RCODE_BADKEY );
  2428. }
  2429. //
  2430. // peal off existing TSIG (if any)
  2431. //
  2432. if ( pMsgHead->AdditionalCount )
  2433. {
  2434. DNS_PARSED_RR parsedRR;
  2435. pch = Dns_SkipToRecord(
  2436. pMsgHead,
  2437. pMsgBufEnd,
  2438. (-1) // go to last record
  2439. );
  2440. if ( !pch )
  2441. {
  2442. DNS_ASSERT( FALSE );
  2443. DNS_PRINT(("Dns_SkipToRecord() failed!\n" ));
  2444. goto Exit;
  2445. }
  2446. pch = Dns_ParsePacketRecord(
  2447. pch,
  2448. pMsgBufEnd,
  2449. &parsedRR );
  2450. if ( !pch )
  2451. {
  2452. DNS_ASSERT( FALSE );
  2453. DNS_PRINT(("Dns_ParsePacketRecord failed!\n" ));
  2454. goto Exit;
  2455. }
  2456. if ( parsedRR.Type == DNS_TYPE_TSIG )
  2457. {
  2458. DNSDBG( SECURITY, (
  2459. "Erasing existing TSIG before resigning packet %p\n",
  2460. pMsgHead ));
  2461. pMsgHead->AdditionalCount--;
  2462. }
  2463. // note could save end-of-message here (pch)
  2464. // for non-TSIG case instead of redoing skip
  2465. }
  2466. // go to end of packet to insert TSIG record
  2467. pch = Dns_SkipToRecord(
  2468. pMsgHead,
  2469. pMsgBufEnd,
  2470. 0 // go to end of packet
  2471. );
  2472. if ( !pch )
  2473. {
  2474. DNS_ASSERT( FALSE );
  2475. DNS_PRINT(("Dns_SkipToSecurityRecord failed!\n" ));
  2476. goto Exit;
  2477. }
  2478. //
  2479. // write TSIG owner
  2480. // - this is context "name"
  2481. //
  2482. pch = Dns_WriteDottedNameToPacket(
  2483. pch,
  2484. pMsgBufEnd,
  2485. psecCtxt->Key.pszTkeyName,
  2486. NULL, // FQDN, no domain
  2487. 0, // no domain offset
  2488. FALSE // not unicode
  2489. );
  2490. if ( !pch )
  2491. {
  2492. goto Exit;
  2493. }
  2494. //
  2495. // TSIG record
  2496. // - algorithm owner
  2497. // - time
  2498. // - expire time
  2499. // - original XID
  2500. // - sig length
  2501. // - sig
  2502. //
  2503. if ( psecCtxt->Version == TKEY_VERSION_W2K )
  2504. {
  2505. pnameAlg = g_pAlgorithmNameW2K;
  2506. lengthAlg = W2K_GSS_ALGORITHM_NAME_PACKET_LENGTH;
  2507. }
  2508. else
  2509. {
  2510. //DNS_ASSERT( psecCtxt->Version == TKEY_VERSION_CURRENT );
  2511. pnameAlg = g_pAlgorithmNameCurrent;
  2512. lengthAlg = GSS_ALGORITHM_NAME_PACKET_LENGTH;
  2513. }
  2514. if ( pch +
  2515. sizeof(DNS_WIRE_RECORD) +
  2516. SIZEOF_TSIG_FIXED_DATA +
  2517. lengthAlg +
  2518. g_SignatureMaxLength > pMsgBufEnd )
  2519. {
  2520. DNS_PRINT(( "Dns_WriteTsigToMessage() failed! -- insufficient length\n" ));
  2521. DNS_ASSERT( FALSE );
  2522. status = ERROR_INVALID_PARAMETER;
  2523. goto Exit;
  2524. }
  2525. // write record structure
  2526. ptsigRRHead = pch;
  2527. pch = Dns_WriteRecordStructureToPacketEx(
  2528. pch,
  2529. DNS_TYPE_TSIG,
  2530. DNS_CLASS_ANY, // per TSIG-04 draft
  2531. 0,
  2532. 0 );
  2533. // write algorithm name
  2534. // - save ptr to RDATA as all is directly signable in packet
  2535. // format up to SigLength field
  2536. ptsigRdataBegin = pch;
  2537. RtlCopyMemory(
  2538. pch,
  2539. pnameAlg,
  2540. lengthAlg );
  2541. pch += lengthAlg;
  2542. //
  2543. // set time fields
  2544. // - signing time seconds since 1970 in 48 bit
  2545. // - expire time
  2546. //
  2547. // DCR_FIX: not 2107 safe
  2548. // have 48 bits on wire, but setting with 32 bit time
  2549. //
  2550. RtlZeroMemory( pch, sizeof(WORD) );
  2551. pch += sizeof(WORD);
  2552. createTime = (DWORD) time( NULL );
  2553. INLINE_WRITE_FLIPPED_DWORD( pch, createTime );
  2554. pch += sizeof(DWORD);
  2555. INLINE_WRITE_FLIPPED_WORD( pch, TSIG_EXPIRE_INTERVAL );
  2556. pch += sizeof(WORD);
  2557. ptsigRdataEnd = pch;
  2558. //
  2559. // create signing buffer
  2560. // - everything signed must fit into message
  2561. //
  2562. pbuf = ALLOCATE_HEAP( MAX_SIGNING_SIZE );
  2563. if ( !pbuf )
  2564. {
  2565. status = DNS_ERROR_NO_MEMORY;
  2566. goto Exit;
  2567. }
  2568. pbufStart = pbuf;
  2569. //
  2570. // sign
  2571. // - query signature (if exists)
  2572. // (note, W2K improperly left out query sig length)
  2573. // - message up to TSIG
  2574. // - TSIG owner name
  2575. // - TSIG header
  2576. // - class
  2577. // - TTL
  2578. // - TSIG RDATA
  2579. // - everything before SigLength
  2580. // - original id
  2581. // - other data length and other data
  2582. //
  2583. if ( pMsgHead->IsResponse )
  2584. {
  2585. if ( pSecPack->pQuerySig )
  2586. {
  2587. WORD sigLength = pSecPack->QuerySigLength;
  2588. ASSERT( sigLength != 0 );
  2589. DNS_ASSERT( psecCtxt->Version != 0 );
  2590. if ( psecCtxt->Version >= TKEY_VERSION_XP_RC1 )
  2591. {
  2592. DNSDBG( SECURITY, (
  2593. "New signing including query sig length =%x\n",
  2594. sigLength ));
  2595. INLINE_WRITE_FLIPPED_WORD( pbuf, sigLength );
  2596. pbuf += sizeof(WORD);
  2597. }
  2598. RtlCopyMemory(
  2599. pbuf,
  2600. pSecPack->pQuerySig,
  2601. sigLength );
  2602. pbuf += sigLength;
  2603. }
  2604. // if server has just completed TKEY nego, it may sign response without query
  2605. // otherwise no query sig is invalid for response
  2606. else if ( !pSecPack->pTkeyRR )
  2607. {
  2608. DNS_PRINT((
  2609. "ERROR: no query sig available when signing response at %p!!!\n",
  2610. pMsgHead ));
  2611. ASSERT( FALSE );
  2612. status = DNS_ERROR_RCODE_SERVER_FAILURE;
  2613. goto Exit;
  2614. }
  2615. DNSDBG( SECURITY, (
  2616. "Signing TKEY response without query sig.\n" ));
  2617. }
  2618. //
  2619. // copy message
  2620. // - go right through, TSIG owner name
  2621. // - message header MUST be in network order
  2622. // - save XID in netorder, it is included in TSIG RR
  2623. //
  2624. DNS_BYTE_FLIP_HEADER_COUNTS( pMsgHead );
  2625. length = (DWORD)(ptsigRRHead - (PCHAR)pMsgHead);
  2626. netXid = pMsgHead->Xid;
  2627. RtlCopyMemory(
  2628. pbuf,
  2629. (PCHAR) pMsgHead,
  2630. length );
  2631. pbuf += length;
  2632. DNS_BYTE_FLIP_HEADER_COUNTS( pMsgHead );
  2633. // copy TSIG class (ANY) and TTL (0)
  2634. WRITE_UNALIGNED_WORD( pbuf, DNS_RCLASS_ANY );
  2635. pbuf += sizeof(WORD);
  2636. WRITE_UNALIGNED_DWORD( pbuf, 0 );
  2637. pbuf += sizeof(DWORD);
  2638. // copy TSIG RDATA through sig
  2639. length = (DWORD)(ptsigRdataEnd - ptsigRdataBegin);
  2640. RtlCopyMemory(
  2641. pbuf,
  2642. ptsigRdataBegin,
  2643. length );
  2644. pbuf += length;
  2645. // copy extended RCODE -- report back to caller
  2646. INLINE_WRITE_FLIPPED_WORD( pbuf, pSecPack->ExtendedRcode );
  2647. pbuf += sizeof(WORD);
  2648. // copy other data length and other data
  2649. // - currently just zero length field
  2650. *pbuf++ = 0;
  2651. *pbuf++ = 0;
  2652. length = (DWORD)(pbuf - pbufStart);
  2653. DNSDBG( SECURITY, (
  2654. "Copied %d bytes to TSIG signing buffer.\n",
  2655. length ));
  2656. //
  2657. // sign the packet
  2658. // buf[0] is data
  2659. // buf[1] is signature
  2660. //
  2661. // note: we write signature DIRECTLY into the real packet buffer
  2662. //
  2663. ASSERT( pch + g_SignatureMaxLength <= pMsgBufEnd );
  2664. outBufDesc.ulVersion = 0;
  2665. outBufDesc.cBuffers = 2;
  2666. outBufDesc.pBuffers = outBuffs;
  2667. outBuffs[0].pvBuffer = pbufStart;
  2668. outBuffs[0].cbBuffer = length;
  2669. outBuffs[0].BufferType = SECBUFFER_DATA; // | SECBUFFER_READONLY;
  2670. outBuffs[1].pvBuffer = pch + sizeof(WORD);
  2671. outBuffs[1].cbBuffer = g_SignatureMaxLength;
  2672. outBuffs[1].BufferType = SECBUFFER_TOKEN;
  2673. status = g_pSecurityFunctionTable->MakeSignature(
  2674. & psecCtxt->hSecHandle,
  2675. 0,
  2676. & outBufDesc,
  2677. 0 // sequence detection
  2678. );
  2679. if ( status != SEC_E_OK &&
  2680. status != SEC_E_CONTEXT_EXPIRED &&
  2681. status != SEC_E_QOP_NOT_SUPPORTED )
  2682. {
  2683. DNS_PRINT(( "MakeSignature() failed status = %08x (%d)\n", status, status ));
  2684. goto Exit;
  2685. }
  2686. IF_DNSDBG( SECURITY )
  2687. {
  2688. DnsPrint_Lock();
  2689. DnsDbg_MessageNoContext(
  2690. "Signed packet",
  2691. pMsgHead,
  2692. (WORD) (pch - (PCHAR)pMsgHead) );
  2693. DNS_PRINT((
  2694. "Signing info:\n"
  2695. "\tsign data buf %p\n"
  2696. "\t length %d\n"
  2697. "\tsignature buf %p (in packet)\n"
  2698. "\t length %d\n",
  2699. outBuffs[0].pvBuffer,
  2700. outBuffs[0].cbBuffer,
  2701. outBuffs[1].pvBuffer,
  2702. outBuffs[1].cbBuffer
  2703. ));
  2704. DnsDbg_RawOctets(
  2705. "Signing buffer:",
  2706. NULL,
  2707. outBuffs[0].pvBuffer,
  2708. outBuffs[0].cbBuffer
  2709. );
  2710. DnsDbg_RawOctets(
  2711. "Signature:",
  2712. NULL,
  2713. outBuffs[1].pvBuffer,
  2714. outBuffs[1].cbBuffer
  2715. );
  2716. DnsPrint_Unlock();
  2717. }
  2718. //
  2719. // continue building packet TSIG RDATA
  2720. // - siglength
  2721. // - signature
  2722. // - original id
  2723. // - error code
  2724. // - other length
  2725. // - other data
  2726. //
  2727. // get signature length
  2728. // set sig length in packet
  2729. //
  2730. // if this is query SAVE signature, to verify response
  2731. //
  2732. sigLength = (WORD) outBuffs[1].cbBuffer;
  2733. INLINE_WRITE_FLIPPED_WORD( pch, sigLength );
  2734. pch += sizeof(WORD);
  2735. //
  2736. // client saves off signature sent, to use in hash on response
  2737. // - server using client's sig in hash, blocks some attacks
  2738. //
  2739. if ( !pMsgHead->IsResponse )
  2740. {
  2741. ASSERT( !pSecPack->pQuerySig );
  2742. psig = ALLOCATE_HEAP( sigLength );
  2743. if ( !psig )
  2744. {
  2745. status = DNS_ERROR_NO_MEMORY;
  2746. goto Exit;
  2747. }
  2748. RtlCopyMemory(
  2749. psig,
  2750. pch,
  2751. sigLength );
  2752. pSecPack->pQuerySig = psig;
  2753. pSecPack->QuerySigLength = sigLength;
  2754. }
  2755. // jump over signature -- it was directly written to packet
  2756. pch += sigLength;
  2757. // original id follows signature
  2758. WRITE_UNALIGNED_WORD( pch, netXid );
  2759. //RtlCopyMemory( pch, (PCHAR)&netXid, sizeof(WORD) );
  2760. pch += sizeof(WORD);
  2761. // extended RCODE -- report back to caller
  2762. INLINE_WRITE_FLIPPED_WORD( pch, pSecPack->ExtendedRcode );
  2763. pch += sizeof(WORD);
  2764. // other length
  2765. WRITE_UNALIGNED_WORD( pch, 0 );
  2766. pch += sizeof(WORD);
  2767. // set TSIG record datalength
  2768. Dns_SetRecordDatalength(
  2769. (PDNS_WIRE_RECORD) ptsigRRHead,
  2770. (WORD) (pch - ptsigRdataBegin) );
  2771. // increment AdditionalCount
  2772. pMsgHead->AdditionalCount++;
  2773. DNSDBG( SECURITY, (
  2774. "Signed packet at %p with GSS TSIG.\n"
  2775. "\tsig length = %d\n"
  2776. "\tTSIG RR header = %p\n"
  2777. "\tTSIG RDATA = %p\n"
  2778. "\tTSIG RDATA End = %p\n"
  2779. "\tTSIG RDATA length = %d\n",
  2780. pMsgHead,
  2781. sigLength,
  2782. ptsigRRHead,
  2783. ptsigRdataBegin,
  2784. pch,
  2785. (WORD) (pch - ptsigRdataBegin)
  2786. ));
  2787. *ppCurrent = pch;
  2788. status = ERROR_SUCCESS;
  2789. Exit:
  2790. // free signing buffer
  2791. // note: no cleanup of allocated pQuerySig is needed; from point
  2792. // of allocation there is no failure scenario
  2793. if ( pbufStart )
  2794. {
  2795. FREE_HEAP( pbufStart );
  2796. }
  2797. return( status );
  2798. }
  2799. //
  2800. // Security record reading
  2801. //
  2802. DNS_STATUS
  2803. Dns_ExtractGssTsigFromMessage(
  2804. IN OUT PSECPACK pSecPack,
  2805. IN PDNS_HEADER pMsgHead,
  2806. IN PCHAR pMsgEnd
  2807. )
  2808. /*++
  2809. Routine Description:
  2810. Extracts a TSIG from packet and loads into security context.
  2811. Arguments:
  2812. pSecPack - security info for packet
  2813. pMsgHead - msg to extract security context from
  2814. pMsgEnd - end of message
  2815. Return Value:
  2816. ERROR_SUCCESS if successful.
  2817. DNS_ERROR_FORMERR if badly formed TSIG
  2818. DNS_STATUS_PACKET_UNSECURE if security context in response is same as query's
  2819. indicating non-security aware partner
  2820. RCODE or extended RCODE on failure.
  2821. --*/
  2822. {
  2823. DNS_STATUS status = ERROR_INVALID_DATA;
  2824. PCHAR pch;
  2825. PCHAR pnameOwner;
  2826. WORD nameLength;
  2827. WORD extRcode;
  2828. WORD sigLength;
  2829. DWORD currentTime;
  2830. PDNS_PARSED_RR pparsedRR;
  2831. PDNS_RECORD ptsigRR;
  2832. DNS_RECORD ptempRR;
  2833. PCHAR psig;
  2834. DNSDBG( SECURITY, (
  2835. "ExtractGssTsigFromMessage( %p )\n", pMsgHead ));
  2836. // clear any previous TSIG
  2837. if ( pSecPack->pTsigRR || pSecPack->pszContextName )
  2838. // if ( pSecPack->pTsigRR || pSecPack->pszContextName )
  2839. {
  2840. // Dns_RecordFree( pSecPack->pTsigRR );
  2841. FREE_HEAP( pSecPack->pTsigRR );
  2842. FREE_HEAP( pSecPack->pszContextName );
  2843. pSecPack->pTsigRR = NULL;
  2844. pSecPack->pszContextName = NULL;
  2845. }
  2846. // set message pointers
  2847. pSecPack->pMsgHead = pMsgHead;
  2848. pSecPack->pMsgEnd = pMsgEnd;
  2849. //
  2850. // if no additional record, don't bother, not a secure message
  2851. //
  2852. if ( pMsgHead->AdditionalCount == 0 )
  2853. {
  2854. status = DNS_STATUS_PACKET_UNSECURE;
  2855. goto Failed;
  2856. }
  2857. //
  2858. // skip to security record (last record in packet)
  2859. //
  2860. pch = Dns_SkipToRecord(
  2861. pMsgHead,
  2862. pMsgEnd,
  2863. (-1) // goto last record
  2864. );
  2865. if ( !pch )
  2866. {
  2867. status = DNS_ERROR_RCODE_FORMAT_ERROR;
  2868. goto Failed;
  2869. }
  2870. //
  2871. // read TSIG owner name
  2872. //
  2873. pparsedRR = &pSecPack->ParsedRR;
  2874. pparsedRR->pchName = pch;
  2875. pch = Dns_ReadPacketNameAllocate(
  2876. & pSecPack->pszContextName,
  2877. & nameLength,
  2878. 0,
  2879. 0,
  2880. pch,
  2881. (PCHAR)pMsgHead,
  2882. pMsgEnd );
  2883. if ( !pch )
  2884. {
  2885. DNSDBG( SECURITY, (
  2886. "WARNING: invalid TSIG RR owner name at %p.\n",
  2887. pch ));
  2888. status = DNS_ERROR_RCODE_FORMAT_ERROR;
  2889. goto Failed;
  2890. }
  2891. //
  2892. // parse record structure
  2893. //
  2894. pch = Dns_ReadRecordStructureFromPacket(
  2895. pch,
  2896. pMsgEnd,
  2897. pparsedRR );
  2898. if ( !pch )
  2899. {
  2900. DNSDBG( SECURITY, (
  2901. "ERROR: invalid security RR in packet at %p.\n"
  2902. "\tstructure or data not withing packet\n",
  2903. pMsgHead ));
  2904. status = DNS_ERROR_RCODE_FORMAT_ERROR;
  2905. goto Failed;
  2906. }
  2907. if ( pparsedRR->Type != DNS_TYPE_TSIG )
  2908. {
  2909. status = DNS_STATUS_PACKET_UNSECURE;
  2910. goto Failed;
  2911. }
  2912. if ( pch != pMsgEnd )
  2913. {
  2914. DNSDBG( SECURITY, (
  2915. "WARNING: security RR does NOT end at packet end.\n"
  2916. "\tRR end offset = %04x\n"
  2917. "\tmsg end offset = %04x\n",
  2918. pch - (PCHAR)pMsgHead,
  2919. pMsgEnd - (PCHAR)pMsgHead ));
  2920. }
  2921. //
  2922. // extract TSIG record
  2923. //
  2924. // TsigReadRecord() requires RR owner name for versioning
  2925. // - pass TSIG name in temp RR
  2926. //
  2927. ptsigRR = TsigRecordRead(
  2928. NULL,
  2929. DnsCharSetWire,
  2930. NULL,
  2931. pparsedRR->pchData,
  2932. pparsedRR->pchNextRR
  2933. );
  2934. if ( !ptsigRR )
  2935. {
  2936. DNSDBG( ANY, (
  2937. "ERROR: invalid TSIG RR in packet at %p.\n"
  2938. "\tstructure or data not withing packet\n",
  2939. pMsgHead ));
  2940. status = DNS_ERROR_RCODE_FORMAT_ERROR;
  2941. DNS_ASSERT( FALSE );
  2942. goto Failed;
  2943. }
  2944. pSecPack->pTsigRR = ptsigRR;
  2945. //
  2946. // currently callers expect error on Extract when ext RCODE is set
  2947. //
  2948. if ( ptsigRR->Data.TSIG.wError )
  2949. {
  2950. DNSDBG( SECURITY, (
  2951. "Leaving ExtractGssTsig(), TSIG had extended RCODE = %d\n",
  2952. ptsigRR->Data.TSIG.wError ));
  2953. status = DNS_ERROR_FROM_RCODE( ptsigRR->Data.TSIG.wError );
  2954. goto Failed;
  2955. }
  2956. //
  2957. // Server side:
  2958. // if query, save off signature for signing response
  2959. //
  2960. sigLength = ptsigRR->Data.TSIG.wSigLength;
  2961. if ( !pMsgHead->IsResponse )
  2962. {
  2963. ASSERT( !pSecPack->pQuerySig );
  2964. if ( pSecPack->pQuerySig )
  2965. {
  2966. FREE_HEAP( pSecPack->pQuerySig );
  2967. pSecPack->pQuerySig = NULL;
  2968. }
  2969. psig = ALLOCATE_HEAP( sigLength );
  2970. if ( !psig )
  2971. {
  2972. status = DNS_ERROR_NO_MEMORY;
  2973. goto Failed;
  2974. }
  2975. RtlCopyMemory(
  2976. psig,
  2977. ptsigRR->Data.TSIG.pSignature,
  2978. sigLength );
  2979. pSecPack->pQuerySig = psig;
  2980. pSecPack->QuerySigLength = sigLength;
  2981. }
  2982. //
  2983. // Client side:
  2984. // check for security record echo on response
  2985. //
  2986. // if we signed and got echo signature back, then may have security unaware
  2987. // server or lost\timed out key condition
  2988. //
  2989. else
  2990. {
  2991. if ( pSecPack->pQuerySig &&
  2992. pSecPack->QuerySigLength == sigLength &&
  2993. RtlEqualMemory(
  2994. ptsigRR->Data.TSIG.pSignature,
  2995. pSecPack->pQuerySig,
  2996. sigLength ) )
  2997. {
  2998. status = DNS_STATUS_PACKET_UNSECURE;
  2999. goto Failed;
  3000. }
  3001. }
  3002. status = ERROR_SUCCESS;
  3003. Failed:
  3004. if ( status != ERROR_SUCCESS )
  3005. {
  3006. DNS_ASSERT( status != DNS_ERROR_RCODE_FORMAT_ERROR );
  3007. ( status == DNS_STATUS_PACKET_UNSECURE )
  3008. ? (SecTsigEcho++)
  3009. : (SecTsigFormerr++);
  3010. }
  3011. DNSDBG( SECURITY, (
  3012. "Leave ExtractGssTsigFromMessage()\n"
  3013. "\tpMsgHead = %p\n"
  3014. "\tsig length = %d\n"
  3015. "\tpsig = %p\n"
  3016. "\tOriginalXid = 0x%x\n",
  3017. "\tpQuerySig = %p\n"
  3018. "\tQS length = %d\n",
  3019. pMsgHead,
  3020. sigLength,
  3021. ptsigRR->Data.TSIG.pSignature,
  3022. ptsigRR->Data.TSIG.wOriginalXid,
  3023. pSecPack->pQuerySig,
  3024. pSecPack->QuerySigLength ));
  3025. return( status );
  3026. }
  3027. DNS_STATUS
  3028. Dns_ExtractGssTkeyFromMessage(
  3029. IN OUT PSECPACK pSecPack,
  3030. IN PDNS_HEADER pMsgHead,
  3031. IN PCHAR pMsgEnd,
  3032. IN BOOL fIsServer
  3033. )
  3034. /*++
  3035. Routine Description:
  3036. Extracts a TKEY from packet and loads into security context.
  3037. Arguments:
  3038. pSecPack - security info for packet
  3039. pMsgHead - msg to extract security context from
  3040. pMsgEnd - end of message
  3041. fIsServer - performing this operation as DNS server?
  3042. Return Value:
  3043. ERROR_SUCCESS if successful.
  3044. DNS_ERROR_FORMERR if badly formed TKEY
  3045. DNS_STATUS_PACKET_UNSECURE if security context in response is same as query's
  3046. indicating non-security aware partner
  3047. RCODE or extended RCODE on failure.
  3048. --*/
  3049. {
  3050. DNS_STATUS status = ERROR_INVALID_DATA;
  3051. PCHAR pch;
  3052. PCHAR pnameOwner;
  3053. WORD nameLength;
  3054. DWORD currentTime;
  3055. PDNS_PARSED_RR pparsedRR;
  3056. PDNS_RECORD ptkeyRR;
  3057. WORD returnExtendedRcode = 0;
  3058. DWORD version;
  3059. DNSDBG( SECURITY, (
  3060. "ExtractGssTkeyFromMessage( %p )\n", pMsgHead ));
  3061. //
  3062. // free any previous TKEY
  3063. // - may have one from previous pass in two pass negotiation
  3064. //
  3065. // DCR: name should be attached to TKEY\TSIG record
  3066. // then lookup made with IP\name pair against context key
  3067. // no need for pszContextName field
  3068. //
  3069. if ( pSecPack->pTkeyRR || pSecPack->pszContextName )
  3070. {
  3071. // Dns_RecordFree( pSecPack->pTkeyRR );
  3072. FREE_HEAP( pSecPack->pTkeyRR );
  3073. FREE_HEAP( pSecPack->pszContextName );
  3074. pSecPack->pTkeyRR = NULL;
  3075. pSecPack->pszContextName = NULL;
  3076. }
  3077. // set message pointers
  3078. pSecPack->pMsgHead = pMsgHead;
  3079. pSecPack->pMsgEnd = pMsgEnd;
  3080. //
  3081. // skip to TKEY record (second record in packet)
  3082. //
  3083. pch = Dns_SkipToRecord(
  3084. pMsgHead,
  3085. pMsgEnd,
  3086. (1) // skip question only
  3087. );
  3088. if ( !pch )
  3089. {
  3090. status = DNS_ERROR_RCODE_FORMAT_ERROR;
  3091. goto Failed;
  3092. }
  3093. //
  3094. // read TKEY owner name
  3095. //
  3096. pparsedRR = &pSecPack->ParsedRR;
  3097. pparsedRR->pchName = pch;
  3098. pch = Dns_ReadPacketNameAllocate(
  3099. & pSecPack->pszContextName,
  3100. & nameLength,
  3101. 0,
  3102. 0,
  3103. pch,
  3104. (PCHAR)pMsgHead,
  3105. pMsgEnd );
  3106. if ( !pch )
  3107. {
  3108. DNSDBG( SECURITY, (
  3109. "WARNING: invalid TKEY RR owner name at %p.\n",
  3110. pch ));
  3111. status = DNS_ERROR_RCODE_FORMAT_ERROR;
  3112. goto Failed;
  3113. }
  3114. //
  3115. // parse record structure
  3116. //
  3117. pch = Dns_ReadRecordStructureFromPacket(
  3118. pch,
  3119. pMsgEnd,
  3120. pparsedRR );
  3121. if ( !pch )
  3122. {
  3123. DNSDBG( SECURITY, (
  3124. "ERROR: invalid security RR in packet at %p.\n"
  3125. "\tstructure or data not withing packet\n",
  3126. pMsgHead ));
  3127. status = DNS_ERROR_RCODE_FORMAT_ERROR;
  3128. goto Failed;
  3129. }
  3130. if ( pparsedRR->Type != DNS_TYPE_TKEY )
  3131. {
  3132. status = DNS_ERROR_RCODE_FORMAT_ERROR;
  3133. DNS_ASSERT( status != DNS_ERROR_RCODE_FORMAT_ERROR );
  3134. goto Failed;
  3135. }
  3136. if ( pch != pMsgEnd && pMsgHead->AdditionalCount == 0 )
  3137. {
  3138. DNSDBG( SECURITY, (
  3139. "WARNING: TKEY RR does NOT end at packet end and no TSIG is present.\n"
  3140. "\tRR end offset = %04x\n"
  3141. "\tmsg end offset = %04x\n",
  3142. pch - (PCHAR)pMsgHead,
  3143. pMsgEnd - (PCHAR)pMsgHead ));
  3144. }
  3145. //
  3146. // extract TKEY record
  3147. //
  3148. ptkeyRR = TkeyRecordRead(
  3149. NULL,
  3150. DnsCharSetWire,
  3151. NULL, // message buffer unknown
  3152. pparsedRR->pchData,
  3153. pparsedRR->pchNextRR
  3154. );
  3155. if ( !ptkeyRR )
  3156. {
  3157. DNSDBG( ANY, (
  3158. "ERROR: invalid TKEY RR data in packet at %p.\n",
  3159. pMsgHead ));
  3160. status = DNS_ERROR_RCODE_FORMAT_ERROR;
  3161. goto Failed;
  3162. }
  3163. pSecPack->pTkeyRR = ptkeyRR;
  3164. //
  3165. // verify GSS algorithm and mode name
  3166. //
  3167. // if server, save off version for later responses
  3168. //
  3169. if ( RtlEqualMemory(
  3170. ptkeyRR->Data.TKEY.pAlgorithmPacket,
  3171. g_pAlgorithmNameCurrent,
  3172. GSS_ALGORITHM_NAME_PACKET_LENGTH ) )
  3173. {
  3174. version = TKEY_VERSION_CURRENT;
  3175. }
  3176. else if ( RtlEqualMemory(
  3177. ptkeyRR->Data.TKEY.pAlgorithmPacket,
  3178. g_pAlgorithmNameW2K,
  3179. W2K_GSS_ALGORITHM_NAME_PACKET_LENGTH ) )
  3180. {
  3181. version = TKEY_VERSION_W2K;
  3182. }
  3183. else
  3184. {
  3185. DNSDBG( ANY, (
  3186. "ERROR: TKEY record is NOT GSS alogrithm.\n" ));
  3187. returnExtendedRcode = DNS_RCODE_BADKEY;
  3188. goto Failed;
  3189. }
  3190. // save client version
  3191. // need additional check on TKEY_VERSION_CURRENT as Whistler
  3192. // beta clients had fixed AlgorithmName but were still not
  3193. // generating unique keys, so need separate version to handle them
  3194. if ( fIsServer )
  3195. {
  3196. if ( version == TKEY_VERSION_CURRENT )
  3197. {
  3198. version = Dns_GetKeyVersion( pSecPack->pszContextName );
  3199. if ( version == 0 )
  3200. {
  3201. // note, this essentially means unknown non-MS client
  3202. DNSDBG( SECURITY, (
  3203. "Non-MS TKEY client.\n"
  3204. "\tkey name = %s\n",
  3205. pSecPack->pszContextName ));
  3206. version = TKEY_VERSION_CURRENT;
  3207. }
  3208. }
  3209. pSecPack->TkeyVersion = version;
  3210. }
  3211. // mode
  3212. if ( ptkeyRR->Data.TKEY.wMode != DNS_TKEY_MODE_GSS )
  3213. {
  3214. DNSDBG( SECURITY, (
  3215. "ERROR: non-GSS mode (%d) in TKEY\n",
  3216. ptkeyRR->Data.TKEY.wMode ));
  3217. returnExtendedRcode = DNS_RCODE_BADKEY;
  3218. goto Failed;
  3219. }
  3220. //
  3221. // allow small time slew, otherwise must have fresh key
  3222. //
  3223. currentTime = (DWORD) time(NULL);
  3224. if ( ptkeyRR->Data.TKEY.dwCreateTime > ptkeyRR->Data.TKEY.dwExpireTime ||
  3225. ptkeyRR->Data.TKEY.dwExpireTime + MAX_TIME_SKEW < currentTime )
  3226. {
  3227. DNSDBG( ANY, (
  3228. "ERROR: TKEY failed expire time check.\n"
  3229. "\tcreate time = %d\n"
  3230. "\texpire time = %d\n"
  3231. "\tcurrent time = %d\n",
  3232. ptkeyRR->Data.TKEY.dwCreateTime,
  3233. ptkeyRR->Data.TKEY.dwExpireTime,
  3234. currentTime ));
  3235. if ( !SecBigTimeSkew ||
  3236. ptkeyRR->Data.TKEY.dwExpireTime + SecBigTimeSkew < currentTime )
  3237. {
  3238. returnExtendedRcode = DNS_RCODE_BADTIME;
  3239. SecTkeyBadTime++;
  3240. goto Failed;
  3241. }
  3242. DNSDBG( ANY, (
  3243. "REPRIEVED: TKEY Time slew %d withing %d allowable slew!\n",
  3244. currentTime - ptkeyRR->Data.TKEY.dwCreateTime,
  3245. SecBigTimeSkew ));
  3246. SecBigTimeSkewBypass++;
  3247. }
  3248. //
  3249. // currently callers expect error on Extract when ext RCODE is set
  3250. //
  3251. if ( ptkeyRR->Data.TKEY.wError )
  3252. {
  3253. DNSDBG( SECURITY, (
  3254. "Leaving ExtractGssTkey(), TKEY had extended RCODE = %d\n",
  3255. ptkeyRR->Data.TKEY.wError ));
  3256. status = DNS_ERROR_FROM_RCODE( ptkeyRR->Data.TKEY.wError );
  3257. goto Failed;
  3258. }
  3259. #if 0
  3260. //
  3261. // check for security record echo on response
  3262. //
  3263. // if we get echo of TKEY back, then probably simple, no-secure server
  3264. //
  3265. #endif
  3266. //
  3267. // pack key token into GSS security token buffer
  3268. // do this here simply to avoid doing in both client and server routines
  3269. //
  3270. pSecPack->RemoteBuf.pvBuffer = ptkeyRR->Data.TKEY.pKey;
  3271. pSecPack->RemoteBuf.cbBuffer = ptkeyRR->Data.TKEY.wKeyLength;
  3272. pSecPack->RemoteBuf.BufferType = SECBUFFER_TOKEN;
  3273. status = ERROR_SUCCESS;
  3274. Failed:
  3275. if ( status != ERROR_SUCCESS )
  3276. {
  3277. SecTkeyInvalid++;
  3278. }
  3279. // if failed with extended RCODE, set for return
  3280. if ( returnExtendedRcode )
  3281. {
  3282. pSecPack->ExtendedRcode = returnExtendedRcode;
  3283. status = DNS_ERROR_FROM_RCODE( returnExtendedRcode );
  3284. }
  3285. DNSDBG( SECURITY, (
  3286. "Leave ExtractGssTkeyFromMessage()\n"
  3287. "\tstatus = %08x (%d)\n"
  3288. "\tpMsgHead = %p\n"
  3289. "\tpkey = %p\n"
  3290. "\tlength = %d\n",
  3291. status, status,
  3292. pMsgHead,
  3293. pSecPack->RemoteBuf.pvBuffer,
  3294. pSecPack->RemoteBuf.cbBuffer ));
  3295. return( status );
  3296. }
  3297. PCHAR
  3298. Dns_CopyAndCanonicalizeWireName(
  3299. IN PCHAR pszInput,
  3300. OUT PCHAR pszOutput,
  3301. OUT DWORD dwOutputSize
  3302. )
  3303. /*++
  3304. Routine Description:
  3305. Copy a UTF-8 uncompressed DNS wire packet name performing
  3306. canonicalization during the copy.
  3307. Arguments:
  3308. pszInput -- pointer to input buffer
  3309. pszOutput -- pointer to output buffer
  3310. dwOutputSize -- number of bytes available at output buffer
  3311. Return Value:
  3312. Returns a pointer to the byte after the last byte written into
  3313. the output buffer or NULL on error.
  3314. --*/
  3315. {
  3316. UCHAR labelLength;
  3317. WCHAR wszlabel[ DNS_MAX_LABEL_BUFFER_LENGTH + 1 ];
  3318. DWORD bufLength;
  3319. DWORD outputCharsRemaining = dwOutputSize;
  3320. DWORD dwtemp;
  3321. PCHAR pchlabelLength;
  3322. while ( ( labelLength = *pszInput++ ) != 0 )
  3323. {
  3324. //
  3325. // Error if this label is too long or if the output buffer can't
  3326. // hold at least as many chars as in the uncanonicalized buffer.
  3327. //
  3328. if ( labelLength > DNS_MAX_LABEL_LENGTH ||
  3329. outputCharsRemaining < labelLength )
  3330. {
  3331. goto Error;
  3332. }
  3333. //
  3334. // Copy this UTF-8 label to a Unicode buffer.
  3335. //
  3336. bufLength = DNS_MAX_NAME_BUFFER_LENGTH_UNICODE;
  3337. if ( !Dns_NameCopy(
  3338. ( PCHAR ) wszlabel,
  3339. &bufLength,
  3340. pszInput,
  3341. labelLength,
  3342. DnsCharSetUtf8,
  3343. DnsCharSetUnicode ) )
  3344. {
  3345. goto Error;
  3346. }
  3347. pszInput += labelLength;
  3348. //
  3349. // Canonicalize the buffer.
  3350. //
  3351. dwtemp = Dns_MakeCanonicalNameInPlaceW(
  3352. wszlabel,
  3353. ( DWORD ) labelLength );
  3354. if ( dwtemp == 0 || dwtemp > DNS_MAX_LABEL_LENGTH )
  3355. {
  3356. goto Error;
  3357. }
  3358. labelLength = ( UCHAR ) dwtemp;
  3359. //
  3360. // Copy the label to the output buffer.
  3361. //
  3362. pchlabelLength = pszOutput++; // Reserve byte for label length.
  3363. dwtemp = outputCharsRemaining;
  3364. if ( !Dns_NameCopy(
  3365. pszOutput,
  3366. &dwtemp,
  3367. ( PCHAR ) wszlabel,
  3368. labelLength,
  3369. DnsCharSetUnicode,
  3370. DnsCharSetUtf8 ) )
  3371. {
  3372. goto Error;
  3373. }
  3374. outputCharsRemaining -= dwtemp;
  3375. --dwtemp; // Don't include NULL in label length.
  3376. *pchlabelLength = ( UCHAR ) dwtemp;
  3377. pszOutput += dwtemp;
  3378. }
  3379. //
  3380. // Add name terminator.
  3381. //
  3382. *pszOutput++ = 0;
  3383. return pszOutput;
  3384. Error:
  3385. return NULL;
  3386. } // Dns_CopyAndCanonicalizeWireName
  3387. DNS_STATUS
  3388. Dns_VerifySignatureOnPacket(
  3389. IN PSECPACK pSecPack
  3390. )
  3391. /*++
  3392. Routine Description:
  3393. Verify signature on packet contained in security record.
  3394. Arguments:
  3395. pSecPack - security packet session info
  3396. Return Value:
  3397. ERROR_SUCCESS on success
  3398. DNS_ERROR_BADSIG if sig doesn't exist or doesn't verify
  3399. DNS_ERROR_BADTIME if sig expired
  3400. Extended RCODE from caller if set.
  3401. --*/
  3402. {
  3403. PSEC_CNTXT psecCtxt;
  3404. PDNS_HEADER pmsgHead = pSecPack->pMsgHead;
  3405. PCHAR pmsgEnd = pSecPack->pMsgEnd;
  3406. PDNS_RECORD ptsigRR;
  3407. PDNS_PARSED_RR pparsedRR;
  3408. DWORD currentTime;
  3409. PCHAR pbufStart = NULL;
  3410. PCHAR pbuf;
  3411. DNS_STATUS status;
  3412. DWORD length;
  3413. WORD returnExtendedRcode = 0;
  3414. SecBufferDesc bufferDesc;
  3415. SecBuffer buffer[2];
  3416. WORD msgXid;
  3417. DWORD version;
  3418. BOOL fcanonicalizeTsigOwnerName;
  3419. DNSDBG( SECURITY, (
  3420. "VerifySignatureOnPacket( %p )\n", pmsgHead ));
  3421. //
  3422. // get security context
  3423. //
  3424. psecCtxt = pSecPack->pSecContext;
  3425. if ( !psecCtxt )
  3426. {
  3427. DNS_PRINT(( "ERROR: attempted signing without security context!!!\n" ));
  3428. ASSERT( FALSE );
  3429. return( DNS_ERROR_RCODE_BADKEY );
  3430. }
  3431. //
  3432. // if no signature extracted from packet, we're dead
  3433. //
  3434. pparsedRR = &pSecPack->ParsedRR;
  3435. ptsigRR = pSecPack->pTsigRR;
  3436. if ( !ptsigRR )
  3437. {
  3438. returnExtendedRcode = DNS_RCODE_BADSIG;
  3439. goto Exit;
  3440. }
  3441. //
  3442. // validity check GSS-TSIG
  3443. // - GSS algorithm
  3444. // - valid time
  3445. // - extract extended RCODE
  3446. //
  3447. // DCR_ENHANCE: check tampering on bad TSIG?
  3448. // - for tampered algorithm all we can do is immediate return
  3449. // - but can check signature and detect tampering
  3450. // before excluding or basis or time or believing ext RCODE
  3451. //
  3452. // check algorithm name
  3453. if ( RtlEqualMemory(
  3454. ptsigRR->Data.TKEY.pAlgorithmPacket,
  3455. g_pAlgorithmNameCurrent,
  3456. GSS_ALGORITHM_NAME_PACKET_LENGTH ) )
  3457. {
  3458. version = TKEY_VERSION_CURRENT;
  3459. }
  3460. else if ( RtlEqualMemory(
  3461. ptsigRR->Data.TKEY.pAlgorithmPacket,
  3462. g_pAlgorithmNameW2K,
  3463. W2K_GSS_ALGORITHM_NAME_PACKET_LENGTH ) )
  3464. {
  3465. version = TKEY_VERSION_W2K;
  3466. }
  3467. else
  3468. {
  3469. DNSDBG( ANY, (
  3470. "ERROR: TSIG record is NOT GSS alogrithm.\n" ));
  3471. returnExtendedRcode = DNS_RCODE_BADSIG;
  3472. goto Exit;
  3473. }
  3474. //
  3475. // set version if server
  3476. // - if don't know our version, must be server
  3477. // note: alternative is fIsServer flag or IsServer to SecPack
  3478. //
  3479. if ( psecCtxt->Version == 0 )
  3480. {
  3481. psecCtxt->Version = version;
  3482. }
  3483. //
  3484. // time check
  3485. // - should be within specified fudge of signing time
  3486. //
  3487. currentTime = (DWORD) time(NULL);
  3488. if ( (LONGLONG)currentTime >
  3489. ptsigRR->Data.TSIG.i64CreateTime +
  3490. (LONGLONG)ptsigRR->Data.TSIG.wFudgeTime
  3491. ||
  3492. (LONGLONG)currentTime <
  3493. ptsigRR->Data.TSIG.i64CreateTime -
  3494. (LONGLONG)ptsigRR->Data.TSIG.wFudgeTime )
  3495. {
  3496. DNSDBG( ANY, (
  3497. "ERROR: TSIG failed fudge time check.\n"
  3498. "\tcreate time = %I64d\n"
  3499. "\tfudge time = %d\n"
  3500. "\tcurrent time = %d\n",
  3501. ptsigRR->Data.TSIG.i64CreateTime,
  3502. ptsigRR->Data.TSIG.wFudgeTime,
  3503. currentTime ));
  3504. //
  3505. // DCR_FIX: currently not enforcing time check
  3506. // in fact have ripped out the counter to track failures
  3507. // within some allowed skew
  3508. }
  3509. //
  3510. // extended RCODE -- follows signature
  3511. // - if set, report back to caller
  3512. //
  3513. if ( ptsigRR->Data.TSIG.wError )
  3514. {
  3515. DNSDBG( SECURITY, (
  3516. "Leaving ExtractGssTsig(), TSIG had extended RCODE = %d\n",
  3517. ptsigRR->Data.TSIG.wError ));
  3518. status = DNS_ERROR_FROM_RCODE( ptsigRR->Data.TSIG.wError );
  3519. goto Exit;
  3520. }
  3521. //
  3522. // create signing buffer
  3523. // - everything signed must fit into message
  3524. //
  3525. pbuf = ALLOCATE_HEAP( MAX_SIGNING_SIZE );
  3526. if ( !pbuf )
  3527. {
  3528. status = DNS_ERROR_NO_MEMORY;
  3529. goto Exit;
  3530. }
  3531. pbufStart = pbuf;
  3532. //
  3533. // verify signature over:
  3534. // - query signature (if exists)
  3535. // - message
  3536. // - without TSIG in Additional count
  3537. // - with original XID
  3538. // - TSIG owner name
  3539. // - TSIG header
  3540. // - class
  3541. // - TTL
  3542. // - TSIG RDATA
  3543. // - everything before SigLength
  3544. // - other data length and other data
  3545. //
  3546. if ( pmsgHead->IsResponse )
  3547. {
  3548. if ( pSecPack->pQuerySig )
  3549. {
  3550. WORD sigLength = pSecPack->QuerySigLength;
  3551. ASSERT( sigLength );
  3552. DNS_ASSERT( psecCtxt->Version != 0 );
  3553. if ( psecCtxt->Version >= TKEY_VERSION_XP_RC1 )
  3554. {
  3555. DNSDBG( SECURITY, (
  3556. "New verify sig including query sig length =%x\n",
  3557. sigLength ));
  3558. INLINE_WRITE_FLIPPED_WORD( pbuf, sigLength );
  3559. pbuf += sizeof(WORD);
  3560. }
  3561. RtlCopyMemory(
  3562. pbuf,
  3563. pSecPack->pQuerySig,
  3564. sigLength );
  3565. pbuf += sigLength;
  3566. }
  3567. // if server has just completed TKEY nego, it may sign response without query
  3568. // so client need not have query sig
  3569. // in all other cases client must have query sig to verify response
  3570. else if ( !pSecPack->pTkeyRR )
  3571. {
  3572. DNS_PRINT((
  3573. "ERROR: verify on response at %p without having QUERY signature!\n",
  3574. pmsgHead ));
  3575. ASSERT( FALSE );
  3576. returnExtendedRcode = DNS_RCODE_BADSIG;
  3577. goto Exit;
  3578. }
  3579. DNSDBG( SECURITY, (
  3580. "Verifying TSIG on TKEY response without query sig.\n" ));
  3581. }
  3582. //
  3583. // copy message
  3584. // - go right through, TSIG owner name
  3585. // - message header MUST be in network order
  3586. // - does NOT include TSIG record in additional count
  3587. // - must have orginal XID in place
  3588. // (save existing XID and replace with orginal, then
  3589. // restore after copy)
  3590. //
  3591. ASSERT( pmsgHead->AdditionalCount );
  3592. pmsgHead->AdditionalCount--;
  3593. msgXid = pmsgHead->Xid;
  3594. DNS_BYTE_FLIP_HEADER_COUNTS( pmsgHead );
  3595. //
  3596. // If need to canonicalize the TSIG owner name, copy to the start
  3597. // of the name; else copy to the end of the name.
  3598. //
  3599. fcanonicalizeTsigOwnerName = !psecCtxt->fClient &&
  3600. psecCtxt->Version >= TKEY_VERSION_CURRENT;
  3601. length = ( DWORD ) ( ( fcanonicalizeTsigOwnerName
  3602. ? pparsedRR->pchName
  3603. : pparsedRR->pchRR ) -
  3604. ( PCHAR ) pmsgHead );
  3605. // restore original XID
  3606. pmsgHead->Xid = ptsigRR->Data.TSIG.wOriginalXid;
  3607. RtlCopyMemory(
  3608. pbuf,
  3609. (PCHAR) pmsgHead,
  3610. length );
  3611. pbuf += length;
  3612. DNS_BYTE_FLIP_HEADER_COUNTS( pmsgHead );
  3613. pmsgHead->AdditionalCount++;
  3614. pmsgHead->Xid = msgXid;
  3615. //
  3616. // If the TSIG owner name needs to be canonicalized, write it out
  3617. // to the signing buffer in canonical form (lower case).
  3618. //
  3619. if ( fcanonicalizeTsigOwnerName )
  3620. {
  3621. pbuf = Dns_CopyAndCanonicalizeWireName(
  3622. pparsedRR->pchName,
  3623. pbuf,
  3624. MAXDWORD );
  3625. if ( pbuf == NULL )
  3626. {
  3627. DNSDBG( SECURITY, (
  3628. "Unable to canonicalize TSIG owner name at %p",
  3629. pparsedRR->pchName ));
  3630. returnExtendedRcode = DNS_RCODE_BADSIG;
  3631. goto Exit;
  3632. }
  3633. }
  3634. // copy TSIG class and TTL
  3635. // - currently always zero
  3636. INLINE_WRITE_FLIPPED_WORD( pbuf, pparsedRR->Class );
  3637. pbuf += sizeof(WORD);
  3638. INLINE_WRITE_FLIPPED_DWORD( pbuf, pparsedRR->Ttl );
  3639. pbuf += sizeof(DWORD);
  3640. // copy TSIG RDATA up to signature length
  3641. length = (DWORD)(ptsigRR->Data.TSIG.pSignature - sizeof(WORD) - pparsedRR->pchData);
  3642. ASSERT( (INT)length < (pparsedRR->DataLength - ptsigRR->Data.TSIG.wSigLength) );
  3643. RtlCopyMemory(
  3644. pbuf,
  3645. pparsedRR->pchData,
  3646. length );
  3647. pbuf += length;
  3648. // copy extended RCODE -- report back to caller
  3649. INLINE_WRITE_FLIPPED_WORD( pbuf, ptsigRR->Data.TSIG.wError );
  3650. pbuf += sizeof(WORD);
  3651. // copy other data length and other data
  3652. // - currently just zero length field
  3653. INLINE_WRITE_FLIPPED_WORD( pbuf, ptsigRR->Data.TSIG.wOtherLength );
  3654. pbuf += sizeof(WORD);
  3655. length = ptsigRR->Data.TSIG.wOtherLength;
  3656. if ( length )
  3657. {
  3658. RtlCopyMemory(
  3659. pbuf,
  3660. ptsigRR->Data.TSIG.pOtherData,
  3661. length );
  3662. pbuf += length;
  3663. }
  3664. // calculate total length signature is over
  3665. length = (DWORD)(pbuf - pbufStart);
  3666. //
  3667. // verify signature
  3668. // buf[0] is data
  3669. // buf[1] is signature
  3670. //
  3671. // signature is verified directly in packet buffer
  3672. //
  3673. bufferDesc.ulVersion = 0;
  3674. bufferDesc.cBuffers = 2;
  3675. bufferDesc.pBuffers = buffer;
  3676. // signature is over everything up to signature itself
  3677. buffer[0].pvBuffer = pbufStart;
  3678. buffer[0].cbBuffer = length;
  3679. buffer[0].BufferType = SECBUFFER_DATA;
  3680. // sig MUST be pointed to by remote buffer
  3681. //
  3682. // DCR: can pull copy when eliminate retry below
  3683. //
  3684. // copy packet signature as signing is destructive
  3685. // and want to allow for retry
  3686. //
  3687. buffer[1].pvBuffer = ptsigRR->Data.TSIG.pSignature;
  3688. buffer[1].cbBuffer = ptsigRR->Data.TSIG.wSigLength;
  3689. buffer[1].BufferType = SECBUFFER_TOKEN;
  3690. IF_DNSDBG( SECURITY )
  3691. {
  3692. DnsPrint_Lock();
  3693. DNS_PRINT((
  3694. "Doing VerifySignature() on packet %p.\n"
  3695. "\tpSecPack = %p\n"
  3696. "\tpSecCntxt = %p\n",
  3697. pmsgHead,
  3698. pSecPack,
  3699. psecCtxt
  3700. ));
  3701. DNS_PRINT((
  3702. "Verify sig info:\n"
  3703. "\tsign data buf %p\n"
  3704. "\t length %d\n"
  3705. "\tsignature buf %p (in packet)\n"
  3706. "\t length %d\n",
  3707. buffer[0].pvBuffer,
  3708. buffer[0].cbBuffer,
  3709. buffer[1].pvBuffer,
  3710. buffer[1].cbBuffer
  3711. ));
  3712. DnsDbg_RawOctets(
  3713. "Signing buffer:",
  3714. NULL,
  3715. buffer[0].pvBuffer,
  3716. buffer[0].cbBuffer
  3717. );
  3718. DnsDbg_RawOctets(
  3719. "Signature:",
  3720. NULL,
  3721. buffer[1].pvBuffer,
  3722. buffer[1].cbBuffer
  3723. );
  3724. DnsDbg_SecurityContext(
  3725. "Verify context",
  3726. psecCtxt );
  3727. DnsPrint_Unlock();
  3728. }
  3729. status = g_pSecurityFunctionTable->VerifySignature(
  3730. & psecCtxt->hSecHandle,
  3731. & bufferDesc,
  3732. 0,
  3733. NULL
  3734. );
  3735. if ( status != SEC_E_OK )
  3736. {
  3737. IF_DNSDBG( SECURITY )
  3738. {
  3739. DnsPrint_Lock();
  3740. DNS_PRINT((
  3741. "ERROR: TSIG does not match on packet %p.\n"
  3742. "\tVerifySignature() status = %d (%08x)\n"
  3743. "\tpSecPack = %p\n"
  3744. "\tpSecCntxt = %p\n"
  3745. "\thSecHandle = %p\n",
  3746. pmsgHead,
  3747. status, status,
  3748. pSecPack,
  3749. psecCtxt,
  3750. & psecCtxt->hSecHandle
  3751. ));
  3752. DNS_PRINT((
  3753. "Verify sig info:\n"
  3754. "\tsign data buf %p\n"
  3755. "\t length %d\n"
  3756. "\tsignature buf %p (in packet)\n"
  3757. "\t length %d\n",
  3758. buffer[0].pvBuffer,
  3759. buffer[0].cbBuffer,
  3760. buffer[1].pvBuffer,
  3761. buffer[1].cbBuffer
  3762. ));
  3763. DnsDbg_RawOctets(
  3764. "Signing buffer:",
  3765. NULL,
  3766. buffer[0].pvBuffer,
  3767. buffer[0].cbBuffer
  3768. );
  3769. DnsDbg_RawOctets(
  3770. "Signature:",
  3771. NULL,
  3772. buffer[1].pvBuffer,
  3773. buffer[1].cbBuffer
  3774. );
  3775. DnsDbg_SecurityContext(
  3776. "Verify failed context",
  3777. psecCtxt );
  3778. DnsDbg_MessageNoContext(
  3779. "Message TSIG verify failed on:",
  3780. pmsgHead,
  3781. 0 );
  3782. DnsPrint_Unlock();
  3783. }
  3784. SecTsigVerifyFailed++;
  3785. returnExtendedRcode = DNS_RCODE_BADSIG;
  3786. goto Exit;
  3787. }
  3788. SecTsigVerifySuccess++;
  3789. Exit:
  3790. // free signing data buffer
  3791. FREE_HEAP( pbufStart );
  3792. // if failed with extended RCODE, set for return
  3793. if ( returnExtendedRcode )
  3794. {
  3795. pSecPack->ExtendedRcode = returnExtendedRcode;
  3796. status = DNS_ERROR_FROM_RCODE( returnExtendedRcode );
  3797. }
  3798. DNSDBG( SECURITY, (
  3799. "Leave VerifySignatureOnPacket( %p )\n"
  3800. "\tstatus %d (%08x)\n"
  3801. "\text RCODE %d\n",
  3802. pmsgHead,
  3803. status, status,
  3804. pSecPack->ExtendedRcode ));
  3805. return( status );
  3806. }
  3807. //
  3808. // Client session routines
  3809. //
  3810. DNS_STATUS
  3811. Dns_NegotiateTkeyWithServer(
  3812. OUT PHANDLE phContext,
  3813. IN DWORD dwFlag,
  3814. IN LPSTR pszNameServer,
  3815. IN PIP_ARRAY aipServer,
  3816. IN PCHAR pCreds, OPTIONAL
  3817. IN PCHAR pszContext, OPTIONAL
  3818. IN DWORD Version
  3819. )
  3820. /*++
  3821. Routine Description:
  3822. Negotiate TKEY with a DNS server.
  3823. Arguments:
  3824. phContext -- addr to recv context (SEC_CNTXT) negotiated
  3825. dwFlags -- flags
  3826. pszNameServer -- server to update
  3827. apiServer -- server to update
  3828. pCreds -- credentials; if not given use default process creds
  3829. pszContext -- security context name; name for unique negotiated security
  3830. session between client and server; if not given create made up
  3831. server\pid name for context
  3832. Version -- verion
  3833. Return Value:
  3834. ERROR_SUCCESS if successful.
  3835. Error status on failure.
  3836. --*/
  3837. {
  3838. DNS_STATUS status;
  3839. PSEC_CNTXT psecCtxt = NULL;
  3840. SECPACK secPack;
  3841. PCHAR pch;
  3842. PWSTR pcredKey = NULL;
  3843. DNS_SECCTXT_KEY key;
  3844. DWORD i;
  3845. BOOL fdoneNegotiate = FALSE;
  3846. PDNS_MSG_BUF pmsgSend = NULL;
  3847. PDNS_MSG_BUF pmsgRecv = NULL;
  3848. WORD length;
  3849. IP_ADDRESS serverIp = aipServer->AddrArray[0];
  3850. CHAR defaultContextBuffer[64];
  3851. BOOL fserverW2K = FALSE;
  3852. DWORD recvCount;
  3853. PCHAR pcurrentAfterQuestion;
  3854. DNSDBG( SECURITY, (
  3855. "Enter Dns_NegotiateTkeyWithServer()\n"
  3856. "\tflags = %08x\n"
  3857. "\tserver IP = %s\n"
  3858. "\tserver name = %s\n"
  3859. "\tpCreds = %p\n"
  3860. "\tcontext = %s\n",
  3861. dwFlag,
  3862. IP_STRING( serverIp ),
  3863. pszNameServer,
  3864. pCreds,
  3865. pszContext
  3866. ));
  3867. DNS_ASSERT( pszNameServer ); // it better be there!
  3868. // init first so all error paths are safe
  3869. Dns_InitSecurityPacketInfo( &secPack, NULL );
  3870. // start security
  3871. status = Dns_StartSecurity( FALSE );
  3872. if ( status != ERROR_SUCCESS )
  3873. {
  3874. goto Cleanup;
  3875. }
  3876. //
  3877. // build key
  3878. //
  3879. RtlZeroMemory(
  3880. &key,
  3881. sizeof(key) );
  3882. //
  3883. // if have creds, create a "cred key" to uniquely identify
  3884. //
  3885. if ( pCreds )
  3886. {
  3887. pcredKey = MakeCredKey( pCreds );
  3888. if ( !pcredKey )
  3889. {
  3890. DNSDBG( ANY, (
  3891. "Failed cred key alloc -- failing nego!\n" ));
  3892. status = DNS_ERROR_NO_MEMORY;
  3893. goto Cleanup;
  3894. }
  3895. key.pwsCredKey = pcredKey;
  3896. }
  3897. //
  3898. // context name
  3899. // - if no context name, concatentate
  3900. // - process ID
  3901. // - current user's domain-relative ID
  3902. // this makes ID unique to process\security context
  3903. // (IP handles issue of different machines)
  3904. //
  3905. // versioning note:
  3906. // - it is NOT necessary to version using the KEY name
  3907. // - the point is to allow us to easily interoperate with previous
  3908. // client versions which may have bugs relative to the final spec
  3909. //
  3910. // versions so far
  3911. // - W2K beta2 (-02) included XID
  3912. // - W2K (-03) sent TKEY in answer and used "gss.microsoft.com"
  3913. // as algorithm name
  3914. // - SP1(or2) and whistler beta2 (-MS-04) used "gss-tsig"
  3915. // - XP post beta 2 (-MS-05) generates unique context name to
  3916. // avoid client collisions
  3917. // - XP RC1 (-MS-06) RFC compliant signing with query sig length included
  3918. // - XP RC2+ canonicalization of TSIG name in signing buffer
  3919. //
  3920. // server version use:
  3921. // - the Win2K server does detect version 02 and fixup the XID
  3922. // signing to match client
  3923. // - current (whistler) server does NOT use the version field
  3924. //
  3925. // however to enable server to detect whistler beta2 client --
  3926. // just in case there's another problem relative to the spec --
  3927. // i'm maintaining field;
  3928. // however note that the field will be 04, even if the client
  3929. // realizes it is talking to a W2K server and falls back to W2K
  3930. // client behavior; in other words NEW server will see 04, but
  3931. // W2K server only knows it is NOT talking to 02 server which is
  3932. // all it cares about;
  3933. //
  3934. // key idea: this can be used to detect a particular MS client
  3935. // when there's a behavior question ... but it is NOT a spec'd
  3936. // versioning mechanism and other clients will come in with
  3937. // no version tag and must be treated per spec
  3938. //
  3939. // Key string selection: it is important that the key string be
  3940. // in "canonical" form as per RFC 2535 section 8.1 - basically this
  3941. // means lower case. Since the key string is canonical it doesn't
  3942. // matter if the server does or doesn't canonicalize the string
  3943. // when building the signing buffer.
  3944. //
  3945. if ( Version == 0 )
  3946. {
  3947. Version = TKEY_VERSION_CURRENT;
  3948. }
  3949. if ( !pszContext )
  3950. {
  3951. sprintf(
  3952. defaultContextBuffer,
  3953. "%d-ms-%d",
  3954. //Dns_GetCurrentRid(),
  3955. GetCurrentProcessId(),
  3956. Version );
  3957. pszContext = defaultContextBuffer;
  3958. DNSDBG( SECURITY, (
  3959. "Generated secure update key context %s\n",
  3960. pszContext ));
  3961. }
  3962. key.pszClientContext = pszContext;
  3963. //
  3964. // check for negotiated security context
  3965. // - check for context to any of server IPs
  3966. // - dump, if partially negotiated or forcing renegotiated
  3967. //
  3968. for( i=0; i<aipServer->AddrCount; i++ )
  3969. {
  3970. key.IpRemote = aipServer->AddrArray[i];
  3971. psecCtxt = Dns_DequeueSecurityContextByKey( key, TRUE );
  3972. if ( psecCtxt )
  3973. {
  3974. if ( !psecCtxt->fNegoComplete ||
  3975. (dwFlag & DNS_UPDATE_FORCE_SECURITY_NEGO) )
  3976. {
  3977. DNSDBG( ANY, (
  3978. "Warning: Deleting context to negotiate a new one.\n"
  3979. "\tKey: [%s, %s]\n"
  3980. "\tReason: %s\n",
  3981. IP_STRING( key.IpRemote ),
  3982. key.pszTkeyName,
  3983. psecCtxt->fNegoComplete
  3984. ? "User specified FORCE_SECURITY_NEGO flag."
  3985. : "Incomplete negotiation key exists." ));
  3986. Dns_FreeSecurityContext( psecCtxt );
  3987. }
  3988. else // have valid context -- we're done!
  3989. {
  3990. ASSERT( psecCtxt->fNegoComplete );
  3991. DNSDBG( SECURITY, (
  3992. "Returning existing negotiated context at %p\n",
  3993. psecCtxt ));
  3994. goto Cleanup;
  3995. }
  3996. }
  3997. }
  3998. //
  3999. // create new context and security packet info
  4000. // - use first server IP in key
  4001. //
  4002. key.IpRemote = serverIp;
  4003. psecCtxt = Dns_FindOrCreateSecurityContext( key );
  4004. if ( !psecCtxt )
  4005. {
  4006. status = DNS_RCODE_SERVER_FAILURE;
  4007. goto Cleanup;
  4008. }
  4009. secPack.pSecContext = psecCtxt;
  4010. psecCtxt->Version = Version;
  4011. //
  4012. // have creds -- get cred handle
  4013. //
  4014. if ( pCreds )
  4015. {
  4016. status = Dns_AcquireCredHandle(
  4017. &psecCtxt->CredHandle,
  4018. FALSE, // client
  4019. pCreds );
  4020. if ( status != ERROR_SUCCESS )
  4021. {
  4022. DNSDBG( SECURITY, (
  4023. "Failed AcquireCredHandle -- failing nego!\n" ));
  4024. goto Cleanup;
  4025. }
  4026. psecCtxt->fHaveCredHandle = TRUE;
  4027. }
  4028. // allocate message buffers
  4029. length = DNS_TCP_DEFAULT_ALLOC_LENGTH;
  4030. pmsgSend= Dns_AllocateMsgBuf( length );
  4031. if ( !pmsgSend)
  4032. {
  4033. DNS_PRINT(( "ERROR: failed allocation.\n" ));
  4034. status = GetLastError();
  4035. goto Cleanup;
  4036. }
  4037. pmsgRecv = Dns_AllocateMsgBuf( length );
  4038. if ( !pmsgRecv )
  4039. {
  4040. DNS_PRINT(( "ERROR: failed allocation.\n"));
  4041. status = GetLastError();
  4042. goto Cleanup;
  4043. }
  4044. // init remote sockaddr and socket
  4045. // setup receive buffer for TCP
  4046. DnsInitializeMsgRemoteSockaddr(
  4047. pmsgSend,
  4048. serverIp );
  4049. pmsgSend->Socket = 0;
  4050. pmsgSend->fTcp = TRUE;
  4051. SET_MESSAGE_FOR_TCP_RECV( pmsgRecv );
  4052. pmsgRecv->Timeout = SECURE_UPDATE_TCP_TIMEOUT;
  4053. //
  4054. // build packet
  4055. // - query opcode
  4056. // - leave non-recursive (so downlevel server doesn't recurse query)
  4057. // - write TKEY question
  4058. // - write TKEY itself
  4059. //
  4060. pch = Dns_WriteQuestionToMessage(
  4061. pmsgSend,
  4062. psecCtxt->Key.pszTkeyName,
  4063. DNS_TYPE_TKEY,
  4064. FALSE // not unicode
  4065. );
  4066. if ( !pch )
  4067. {
  4068. status = ERROR_INVALID_PARAMETER;
  4069. goto Cleanup;
  4070. }
  4071. pcurrentAfterQuestion = pch;
  4072. pmsgSend->MessageHead.RecursionDesired = 0;
  4073. pmsgSend->MessageHead.Opcode = DNS_OPCODE_QUERY;
  4074. //
  4075. // init XID to something fairly random
  4076. //
  4077. pmsgSend->MessageHead.Xid = Dns_GetRandomXid( pmsgSend );
  4078. //
  4079. // for given server send in a loop
  4080. // - write TKEY context to packet
  4081. // - send \ recv
  4082. // - may have multiple sends until negotiate a TKEY
  4083. //
  4084. while ( 1 )
  4085. {
  4086. // setup session context
  4087. // on first pass this just builds our context,
  4088. // on second pass we munge in servers response
  4089. status = Dns_InitClientSecurityContext(
  4090. &secPack,
  4091. pszNameServer,
  4092. & fdoneNegotiate
  4093. );
  4094. // always recover context pointer, as bad context may be deleted
  4095. psecCtxt = secPack.pSecContext;
  4096. ASSERT( psecCtxt ||
  4097. (status != ERROR_SUCCESS && status != DNS_STATUS_CONTINUE_NEEDED) );
  4098. if ( status == ERROR_SUCCESS )
  4099. {
  4100. DNSDBG( SECURITY, ( "Successfully negotiated TKEY.\n" ));
  4101. ASSERT( psecCtxt->fNegoComplete );
  4102. //
  4103. // if completed and remote packet had SIG -- verify SIG
  4104. //
  4105. status = Dns_ExtractGssTsigFromMessage(
  4106. &secPack,
  4107. & pmsgRecv->MessageHead,
  4108. DNS_MESSAGE_END( pmsgRecv )
  4109. );
  4110. if ( status == ERROR_SUCCESS )
  4111. {
  4112. status = Dns_VerifySignatureOnPacket( &secPack );
  4113. if ( status != ERROR_SUCCESS )
  4114. {
  4115. DNSDBG( SECURITY, (
  4116. "Verify signature failed on TKEY nego packet %p.\n"
  4117. "\tserver = %s\n"
  4118. "\tstatus = %d (%08x)\n"
  4119. "\treturning BADSIG\n",
  4120. pmsgRecv,
  4121. IP_STRING( serverIp ),
  4122. status, status ));
  4123. status = DNS_ERROR_RCODE_BADSIG;
  4124. }
  4125. }
  4126. else if ( status == DNS_STATUS_PACKET_UNSECURE )
  4127. {
  4128. DNSDBG( SECURITY, (
  4129. "WARNING: Unsigned final TKEY nego response packet %p.\n"
  4130. "\tfrom server %s\n",
  4131. pmsgRecv,
  4132. IP_STRING( serverIp ) ));
  4133. status = ERROR_SUCCESS;
  4134. }
  4135. // nego is done, break out of nego loop
  4136. // any other error on TSIG, falls through as failure
  4137. break;
  4138. }
  4139. //
  4140. // if not complete, then anything other than continue is failure
  4141. //
  4142. else if ( status != DNS_STATUS_CONTINUE_NEEDED )
  4143. {
  4144. goto Cleanup;
  4145. }
  4146. //
  4147. // loop for sign and send
  4148. //
  4149. // note this is only in a loop to enable backward compatibility
  4150. // with "TKEY-in-answer" bug in Win2000 DNS server
  4151. //
  4152. recvCount = 0;
  4153. while ( 1 )
  4154. {
  4155. //
  4156. // backward compatibility with Win2000 TKEY
  4157. // - set version to write like W2K
  4158. // - reset packet to just-wrote-question state
  4159. //
  4160. if ( fserverW2K && recvCount == 0 )
  4161. {
  4162. psecCtxt->Version = TKEY_VERSION_W2K;
  4163. pmsgSend->pCurrent = pcurrentAfterQuestion;
  4164. pmsgSend->MessageHead.AdditionalCount = 0;
  4165. pmsgSend->MessageHead.AnswerCount = 0;
  4166. Dns_CloseConnection( pmsgSend->Socket );
  4167. pmsgSend->Socket = 0;
  4168. }
  4169. //
  4170. // write security record with context into packet
  4171. //
  4172. // note: fNeedTkeyInAnswer determines whether write
  4173. // to Answer or Additional section
  4174. status = Dns_WriteGssTkeyToMessage(
  4175. (HANDLE) &secPack,
  4176. & pmsgSend->MessageHead,
  4177. pmsgSend->pBufferEnd,
  4178. & pmsgSend->pCurrent,
  4179. FALSE // client
  4180. );
  4181. if ( status != ERROR_SUCCESS )
  4182. {
  4183. goto Cleanup;
  4184. }
  4185. //
  4186. // if finished negotiation -- sign
  4187. //
  4188. if ( fdoneNegotiate )
  4189. {
  4190. DNSDBG( SECURITY, (
  4191. "Signing TKEY packet at %p, after successful nego.\n",
  4192. pmsgSend ));
  4193. status = Dns_SignMessageWithGssTsig(
  4194. & secPack,
  4195. & pmsgSend->MessageHead,
  4196. pmsgSend->pBufferEnd,
  4197. & pmsgSend->pCurrent
  4198. );
  4199. if ( status != ERROR_SUCCESS )
  4200. {
  4201. DNSDBG( SECURITY, (
  4202. "ERROR: Failed signing TKEY packet at %p, after successful nego.\n"
  4203. "\tsending without TSIG ...\n",
  4204. pmsgSend ));
  4205. }
  4206. }
  4207. //
  4208. // if already connected, send
  4209. // if first pass, try server IPs, until find one to can connect to
  4210. //
  4211. if ( pmsgSend->Socket )
  4212. {
  4213. status = DnsSend( pmsgSend );
  4214. }
  4215. else
  4216. {
  4217. for( i=0; i<aipServer->AddrCount; i++ )
  4218. {
  4219. serverIp = aipServer->AddrArray[i];
  4220. status = Dns_OpenTcpConnectionAndSend(
  4221. pmsgSend,
  4222. serverIp,
  4223. TRUE );
  4224. if ( status != ERROR_SUCCESS )
  4225. {
  4226. if ( pmsgSend->Socket )
  4227. {
  4228. Dns_CloseSocket( pmsgSend->Socket );
  4229. pmsgSend->Socket = 0;
  4230. }
  4231. continue;
  4232. }
  4233. psecCtxt->Key.IpRemote = serverIp;
  4234. break;
  4235. }
  4236. }
  4237. if ( status != ERROR_SUCCESS )
  4238. {
  4239. goto Done;
  4240. }
  4241. //
  4242. // receive response
  4243. // - if successful receive, done
  4244. // - if timeout continue
  4245. // - other errors indicate some setup or system level
  4246. // problem
  4247. //
  4248. pmsgRecv->Socket = pmsgSend->Socket;
  4249. status = Dns_RecvTcp( pmsgRecv );
  4250. if ( status != ERROR_SUCCESS )
  4251. {
  4252. // W2K server may "eat" bad TKEY packet
  4253. // if just got a connection, and then timed out, good
  4254. // chance the problem is W2K server
  4255. if ( status == ERROR_TIMEOUT &&
  4256. recvCount == 0 &&
  4257. !fserverW2K )
  4258. {
  4259. DNS_PRINT(( "Timeout on TKEY nego -- retry with W2K protocol.\n" ));
  4260. fserverW2K = TRUE;
  4261. recvCount = 0;
  4262. continue;
  4263. }
  4264. // indicate error only with this server by setting RCODE
  4265. pmsgRecv->MessageHead.ResponseCode = DNS_RCODE_SERVER_FAILURE;
  4266. goto Done;
  4267. }
  4268. recvCount++;
  4269. //
  4270. // verify XID match
  4271. //
  4272. if ( pmsgRecv->MessageHead.Xid != pmsgSend->MessageHead.Xid )
  4273. {
  4274. DNS_PRINT(( "ERROR: Incorrect XID in response. Ignoring.\n" ));
  4275. goto Done;
  4276. }
  4277. //
  4278. // RCODE failure
  4279. //
  4280. // special case Win2K gold DNS server accepting only TKEY
  4281. // in Answer section
  4282. // - rcode FORMERR
  4283. // - haven't already switched to Additional (prevent looping)
  4284. //
  4285. if ( pmsgRecv->MessageHead.ResponseCode != DNS_RCODE_NO_ERROR )
  4286. {
  4287. if ( pmsgRecv->MessageHead.ResponseCode == DNS_RCODE_FORMERR &&
  4288. ! fserverW2K &&
  4289. recvCount == 1 )
  4290. {
  4291. DNS_PRINT(( "Formerr TKEY nego -- retry with W2K protocol.\n" ));
  4292. fserverW2K = TRUE;
  4293. recvCount = 0;
  4294. continue;
  4295. }
  4296. // done with this server, may be able to continue with others
  4297. // depending on RCODE
  4298. goto Done;
  4299. }
  4300. // successful send\recv
  4301. break;
  4302. }
  4303. //
  4304. // not yet finished negotiation
  4305. // use servers security context to reply to server
  4306. // if server replied with original context then it is unsecure
  4307. // => we're done
  4308. //
  4309. status = Dns_ExtractGssTkeyFromMessage(
  4310. (HANDLE) &secPack,
  4311. &pmsgRecv->MessageHead,
  4312. DNS_MESSAGE_END( pmsgRecv ),
  4313. FALSE // fIsServer
  4314. );
  4315. if ( status != ERROR_SUCCESS )
  4316. {
  4317. if ( status == DNS_STATUS_PACKET_UNSECURE )
  4318. {
  4319. DNSDBG( SECURITY, (
  4320. "Unsecure update response from server %s.\n"
  4321. "\tupdate considered successful, quiting.\n",
  4322. IP_STRING( aipServer->AddrArray[i] ) ));
  4323. status = ERROR_SUCCESS;
  4324. ASSERT( FALSE );
  4325. goto Cleanup;
  4326. }
  4327. break;
  4328. }
  4329. }
  4330. Done:
  4331. //
  4332. // check response code
  4333. // - consider some response codes
  4334. //
  4335. switch( status )
  4336. {
  4337. case ERROR_SUCCESS:
  4338. status = Dns_MapRcodeToStatus( pmsgRecv->MessageHead.ResponseCode );
  4339. break;
  4340. case ERROR_TIMEOUT:
  4341. DNS_PRINT((
  4342. "ERROR: connected to server at %s\n"
  4343. "\tbut no response to packet at %p\n",
  4344. MSG_REMOTE_IP_STRING( pmsgSend ),
  4345. pmsgSend
  4346. ));
  4347. break;
  4348. default:
  4349. DNS_PRINT((
  4350. "ERROR: connected to server at %s to send packet %p\n"
  4351. "\tbut error %d (%08x) encountered on receive.\n",
  4352. MSG_REMOTE_IP_STRING( pmsgSend ),
  4353. pmsgSend,
  4354. status, status
  4355. ));
  4356. break;
  4357. }
  4358. Cleanup:
  4359. DNSDBG( SECURITY, (
  4360. "Leaving Dns_NegotiateTkeyWithServer() status = %08x (%d)\n",
  4361. status, status ));
  4362. //
  4363. // if successful return context handle
  4364. // if not returned or cached, clean up
  4365. //
  4366. if ( status == ERROR_SUCCESS )
  4367. {
  4368. if ( phContext )
  4369. {
  4370. *phContext = (HANDLE) psecCtxt;
  4371. psecCtxt = NULL;
  4372. }
  4373. else if ( dwFlag & DNS_UPDATE_CACHE_SECURITY_CONTEXT )
  4374. {
  4375. Dns_EnlistSecurityContext( psecCtxt );
  4376. psecCtxt = NULL;
  4377. }
  4378. }
  4379. if ( psecCtxt )
  4380. {
  4381. Dns_FreeSecurityContext( psecCtxt );
  4382. }
  4383. // cleanup session info
  4384. Dns_CleanupSecurityPacketInfoEx( &secPack, FALSE );
  4385. // close connection
  4386. if ( pmsgSend && pmsgSend->Socket )
  4387. {
  4388. Dns_CloseConnection( pmsgSend->Socket );
  4389. }
  4390. //
  4391. // DCR_CLEANUP: what's the correct screening here for error codes?
  4392. // possibly should take all security errors to
  4393. // status = DNS_ERROR_RCODE_BADKEY;
  4394. // or some to some status that means unsecure server
  4395. // and leave BADKEY for actual negotiations that yield bad token
  4396. //
  4397. FREE_HEAP( pmsgRecv );
  4398. FREE_HEAP( pmsgSend );
  4399. FREE_HEAP( pcredKey );
  4400. return( status );
  4401. }
  4402. DNS_STATUS
  4403. Dns_DoSecureUpdate(
  4404. IN PDNS_MSG_BUF pMsgSend,
  4405. OUT PDNS_MSG_BUF pMsgRecv,
  4406. IN OUT PHANDLE phContext,
  4407. IN DWORD dwFlag,
  4408. IN PDNS_NETINFO pNetworkInfo,
  4409. IN PIP_ARRAY aipServer,
  4410. IN LPSTR pszNameServer,
  4411. IN PCHAR pCreds, OPTIONAL
  4412. IN PCHAR pszContext OPTIONAL
  4413. )
  4414. /*++
  4415. Routine Description:
  4416. Main client routine to do secure update.
  4417. Arguments:
  4418. pMsgSend - message to send
  4419. ppMsgRecv - and reuse
  4420. aipServer -- IP array DNS servers
  4421. pNetworkInfo -- network info blob for update
  4422. pszNameServer -- name server name
  4423. pCreds -- credentials; if not given use default process creds
  4424. pszContext -- name for security context; this is unique name for
  4425. session between this process and this server with these creds
  4426. Return Value:
  4427. ERROR_SUCCESS if successful.
  4428. Error status on failure.
  4429. --*/
  4430. {
  4431. #define FORCE_VERSION_OLD ("Force*Version*Old")
  4432. DNS_STATUS status = ERROR_SUCCESS;
  4433. PSEC_CNTXT psecCtxt = NULL;
  4434. DWORD i;
  4435. INT retry;
  4436. IP_ADDRESS serverIp = aipServer->AddrArray[0];
  4437. SECPACK secPack;
  4438. #if 0
  4439. DWORD version;
  4440. #endif
  4441. DNS_ASSERT( pMsgSend->MessageHead.Opcode == DNS_OPCODE_UPDATE );
  4442. DNS_ASSERT( serverIp && pszNameServer ); // it better be there!
  4443. DNSDBG( SEND, (
  4444. "Enter Dns_DoSecureUpdate()\n"
  4445. "\tsend msg at %p\n"
  4446. "\tsec context %p\n"
  4447. "\tserver name %s\n"
  4448. "\tserver IP %s\n"
  4449. "\tpCreds %p\n"
  4450. "\tcontext %s\n",
  4451. pMsgSend,
  4452. phContext ? *phContext : NULL,
  4453. pszNameServer,
  4454. IP_STRING( serverIp ),
  4455. pCreds,
  4456. pszContext
  4457. ));
  4458. //
  4459. // version setting
  4460. //
  4461. // note: to set different version we'd need some sort of tag
  4462. // like pszContext (see example)
  4463. // but a better way to do this would be tail recursion in just
  4464. // NegotiateTkey -- unless there's a reason to believe the nego
  4465. // would be successful with old version, but the update still fail
  4466. //
  4467. #if 0
  4468. iversion = TKEY_CURRENT_VERSION;
  4469. if ( pszContext && strcmp(pszContext, FORCE_VERSION_OLD) == 0 )
  4470. {
  4471. iversion = TKEY_VERSION_OLD;
  4472. pszContext = NULL;
  4473. }
  4474. #endif
  4475. // init security packet info
  4476. Dns_InitSecurityPacketInfo( &secPack, NULL );
  4477. //
  4478. // loop
  4479. // - get valid security context
  4480. // - connect to server
  4481. // - do update
  4482. //
  4483. // loop to allow retry with new security context if server
  4484. // rejects existing one
  4485. //
  4486. retry = 0;
  4487. while ( 1 )
  4488. {
  4489. // clean up any previous connection
  4490. // cache security context if negotiated one
  4491. if ( retry )
  4492. {
  4493. if ( pMsgSend->fTcp )
  4494. {
  4495. DnsCloseConnection( pMsgSend->Socket );
  4496. }
  4497. if ( psecCtxt )
  4498. {
  4499. Dns_EnlistSecurityContext( psecCtxt );
  4500. psecCtxt = NULL;
  4501. }
  4502. }
  4503. retry++;
  4504. //
  4505. // passed in security context?
  4506. //
  4507. if ( phContext )
  4508. {
  4509. psecCtxt = *phContext;
  4510. }
  4511. //
  4512. // no existing security context
  4513. // - see if one is cached
  4514. // - otherwise, negotiate one with server
  4515. //
  4516. if ( !psecCtxt )
  4517. {
  4518. status = Dns_NegotiateTkeyWithServer(
  4519. & psecCtxt,
  4520. dwFlag,
  4521. pszNameServer,
  4522. aipServer,
  4523. pCreds,
  4524. pszContext,
  4525. 0 // use current version
  4526. //iversion // if need versioning
  4527. );
  4528. if ( status != ERROR_SUCCESS )
  4529. {
  4530. // note: if failed we could do a version retry here
  4531. goto Cleanup;
  4532. }
  4533. ASSERT( psecCtxt );
  4534. }
  4535. //
  4536. // init XID to something fairly random
  4537. //
  4538. pMsgSend->MessageHead.Xid = Dns_GetRandomXid( psecCtxt );
  4539. //
  4540. // DCR_PERF: nego should try UDP, if doesn't fit (attaching TSIG) TCP
  4541. // especially useful down the road with OPT and large packets
  4542. //
  4543. //
  4544. // init remote sockaddr and socket
  4545. // setup receive buffer for TCP
  4546. // set timeout and receive
  4547. //
  4548. DnsInitializeMsgRemoteSockaddr(
  4549. pMsgSend,
  4550. serverIp );
  4551. pMsgSend->Socket = 0;
  4552. SET_MESSAGE_FOR_TCP_RECV( pMsgRecv );
  4553. if ( pMsgRecv->Timeout == 0 )
  4554. {
  4555. pMsgRecv->Timeout = SECURE_UPDATE_TCP_TIMEOUT;
  4556. }
  4557. //
  4558. // write security record with context into packet
  4559. //
  4560. Dns_ResetSecurityPacketInfo( &secPack );
  4561. secPack.pSecContext = psecCtxt;
  4562. status = Dns_SignMessageWithGssTsig(
  4563. & secPack,
  4564. & pMsgSend->MessageHead,
  4565. pMsgSend->pBufferEnd,
  4566. & pMsgSend->pCurrent
  4567. );
  4568. if ( status != ERROR_SUCCESS )
  4569. {
  4570. goto Cleanup;
  4571. }
  4572. //
  4573. // need TCP
  4574. //
  4575. if ( DNS_MESSAGE_CURRENT_OFFSET(pMsgSend) > DNS_RFC_MAX_UDP_PACKET_LENGTH )
  4576. {
  4577. //
  4578. // connect and send
  4579. // try server IPs, until find one to can connect to
  4580. //
  4581. pMsgSend->fTcp = TRUE;
  4582. for( i=0; i<aipServer->AddrCount; i++ )
  4583. {
  4584. serverIp = aipServer->AddrArray[i];
  4585. status = Dns_OpenTcpConnectionAndSend(
  4586. pMsgSend,
  4587. serverIp,
  4588. TRUE );
  4589. if ( status != ERROR_SUCCESS )
  4590. {
  4591. if ( pMsgSend->Socket )
  4592. {
  4593. Dns_CloseSocket( pMsgSend->Socket );
  4594. pMsgSend->Socket = 0;
  4595. continue;
  4596. }
  4597. }
  4598. psecCtxt->Key.IpRemote = serverIp;
  4599. break;
  4600. }
  4601. pMsgRecv->Socket = pMsgSend->Socket;
  4602. // receive response
  4603. // - if successful receive, done
  4604. // - if timeout continue
  4605. // - other errors indicate some setup or system level
  4606. // problem
  4607. status = Dns_RecvTcp( pMsgRecv );
  4608. if ( status != ERROR_SUCCESS )
  4609. {
  4610. // indicate error only with this server by setting RCODE
  4611. pMsgRecv->MessageHead.ResponseCode = DNS_RCODE_SERVER_FAILURE;
  4612. goto Cleanup;
  4613. }
  4614. }
  4615. //
  4616. // use UDP
  4617. //
  4618. else
  4619. {
  4620. pMsgSend->fTcp = FALSE;
  4621. SET_MESSAGE_FOR_UDP_RECV( pMsgRecv );
  4622. status = Dns_SendAndRecvUdp(
  4623. pMsgSend,
  4624. pMsgRecv,
  4625. 0,
  4626. NULL,
  4627. pNetworkInfo );
  4628. if ( status != ERROR_SUCCESS )
  4629. {
  4630. goto Cleanup;
  4631. }
  4632. }
  4633. //
  4634. // verify XID match
  4635. //
  4636. if ( pMsgRecv->MessageHead.Xid != pMsgSend->MessageHead.Xid )
  4637. {
  4638. DNS_PRINT(( "ERROR: Incorrect XID in response. Ignoring.\n" ));
  4639. goto Cleanup;
  4640. }
  4641. //
  4642. // check RCODE, if REFUSED and TSIG extended error, then may simply
  4643. // need to refresh the TKEY
  4644. //
  4645. if ( pMsgRecv->MessageHead.ResponseCode != DNS_RCODE_NO_ERROR )
  4646. {
  4647. if ( pMsgRecv->MessageHead.ResponseCode == DNS_RCODE_REFUSED )
  4648. {
  4649. status = Dns_ExtractGssTsigFromMessage(
  4650. & secPack,
  4651. & pMsgRecv->MessageHead,
  4652. DNS_MESSAGE_END(pMsgRecv)
  4653. );
  4654. if ( status != ERROR_SUCCESS )
  4655. {
  4656. if ( secPack.pTsigRR && secPack.pTsigRR->Data.TSIG.wError && retry==1 )
  4657. {
  4658. DNSDBG( SECURITY, (
  4659. "TSIG signed query (%p) rejected with %d and\n"
  4660. "\textended RCODE = %d\n"
  4661. "\tretrying rebuilding new TKEY\n",
  4662. pMsgSend,
  4663. pMsgRecv->MessageHead.ResponseCode,
  4664. secPack.pTsigRR->Data.TSIG.wError
  4665. ));
  4666. pMsgSend->MessageHead.AdditionalCount = 0;
  4667. IF_DNSDBG( SECURITY )
  4668. {
  4669. DnsDbg_Message(
  4670. "Update message after reset for retry:",
  4671. pMsgSend );
  4672. }
  4673. Dns_FreeSecurityContext( psecCtxt );
  4674. psecCtxt = NULL;
  4675. continue;
  4676. }
  4677. }
  4678. }
  4679. // if TSIG done, no point in checking signature
  4680. goto Cleanup;
  4681. }
  4682. // extract TSIG record
  4683. // shouldn't get any error
  4684. status = Dns_ExtractGssTsigFromMessage(
  4685. & secPack,
  4686. & pMsgRecv->MessageHead,
  4687. DNS_MESSAGE_END(pMsgRecv)
  4688. );
  4689. if ( status != ERROR_SUCCESS )
  4690. {
  4691. DNS_PRINT((
  4692. "ERROR: TSIG parse failed on NO_ERROR response!\n" ));
  4693. //ASSERT( FALSE );
  4694. break;
  4695. }
  4696. // verify server signature
  4697. status = Dns_VerifySignatureOnPacket( &secPack );
  4698. if ( status != ERROR_SUCCESS )
  4699. {
  4700. // DCR_LOG: log event -- been hacked or misbehaving server
  4701. // or bad bytes in transit
  4702. DNS_PRINT((
  4703. "ERROR: signature verification failed on update\n"
  4704. "\tto server %s\n",
  4705. IP_STRING( serverIp ) ));
  4706. }
  4707. break;
  4708. }
  4709. //
  4710. // check response code
  4711. // - consider some response codes
  4712. //
  4713. switch( status )
  4714. {
  4715. case ERROR_SUCCESS:
  4716. status = Dns_MapRcodeToStatus( pMsgRecv->MessageHead.ResponseCode );
  4717. break;
  4718. case ERROR_TIMEOUT:
  4719. DNS_PRINT((
  4720. "ERROR: connected to server at %s\n"
  4721. "\tbut no response to packet at %p\n",
  4722. MSG_REMOTE_IP_STRING( pMsgSend ),
  4723. pMsgSend
  4724. ));
  4725. break;
  4726. default:
  4727. DNS_PRINT((
  4728. "ERROR: connected to server at %s to send packet %p\n"
  4729. "\tbut error %d encountered on receive.\n",
  4730. MSG_REMOTE_IP_STRING( pMsgSend ),
  4731. pMsgSend,
  4732. status
  4733. ));
  4734. break;
  4735. }
  4736. Cleanup:
  4737. //
  4738. // save security context?
  4739. //
  4740. if ( psecCtxt )
  4741. {
  4742. if ( dwFlag & DNS_UPDATE_CACHE_SECURITY_CONTEXT )
  4743. {
  4744. Dns_EnlistSecurityContext( psecCtxt );
  4745. if ( phContext )
  4746. {
  4747. *phContext = NULL;
  4748. }
  4749. }
  4750. else if ( phContext )
  4751. {
  4752. *phContext = (HANDLE) psecCtxt;
  4753. }
  4754. else
  4755. {
  4756. Dns_FreeSecurityContext( psecCtxt );
  4757. }
  4758. }
  4759. if ( pMsgSend->fTcp )
  4760. {
  4761. DnsCloseConnection( pMsgSend->Socket );
  4762. }
  4763. //
  4764. // free security packet session sub-allocations
  4765. // - structure itself is on the stack
  4766. Dns_CleanupSecurityPacketInfoEx( &secPack, FALSE );
  4767. #if 0
  4768. //
  4769. // versioning failure retry?
  4770. // if failed, reenter function forcing old version
  4771. //
  4772. if ( status != ERROR_SUCCESS &&
  4773. status != DNS_ERROR_RCODE_NOT_IMPLEMENTED &&
  4774. iversion != TKEY_VERSION_OLD )
  4775. {
  4776. DNS_PRINT((
  4777. "SecureUpdate failed with status == %d\n"
  4778. "\tRetrying forcing version %d signing.\n",
  4779. status,
  4780. TKEY_VERSION_OLD ));
  4781. status = Dns_DoSecureUpdate(
  4782. pMsgSend,
  4783. pMsgRecv,
  4784. phContext,
  4785. dwFlag,
  4786. pNetworkInfo,
  4787. aipServer,
  4788. pszNameServer,
  4789. pCreds,
  4790. FORCE_VERSION_OLD
  4791. );
  4792. }
  4793. #endif
  4794. return( status );
  4795. }
  4796. //
  4797. // Server security session routines
  4798. //
  4799. DNS_STATUS
  4800. Dns_FindSecurityContextFromAndVerifySignature(
  4801. OUT PHANDLE phContext,
  4802. IN IP_ADDRESS IpRemote,
  4803. IN PDNS_HEADER pMsgHead,
  4804. IN PCHAR pMsgEnd
  4805. )
  4806. /*++
  4807. Routine Description:
  4808. Find security context associated with TSIG and verify
  4809. the signature.
  4810. Arguments:
  4811. phContext -- addr to receive context handle
  4812. IpRemote -- IP of remote machine
  4813. pMsgHead -- ptr to message head
  4814. pMsgEnd -- ptr to message end (byte past end)
  4815. Return Value:
  4816. ERROR_SUCCESS if successful.
  4817. ErrorCode on failure.
  4818. --*/
  4819. {
  4820. DNS_STATUS status;
  4821. DNS_SECCTXT_KEY key;
  4822. PSEC_CNTXT psecCtxt = NULL;
  4823. PSECPACK psecPack = NULL;
  4824. DNSDBG( SECURITY, (
  4825. "Dns_FindSecurityContextFromAndVerifySignature()\n"
  4826. ));
  4827. // security must already be running to have negotiated a TKEY
  4828. if ( !g_fSecurityPackageInitialized )
  4829. {
  4830. status = Dns_StartServerSecurity();
  4831. if ( status != ERROR_SUCCESS )
  4832. {
  4833. return( DNS_RCODE_SERVER_FAILURE );
  4834. }
  4835. }
  4836. //
  4837. // read TSIG from packet
  4838. //
  4839. psecPack = Dns_CreateSecurityPacketInfo();
  4840. if ( !psecPack )
  4841. {
  4842. return( DNS_RCODE_SERVER_FAILURE );
  4843. }
  4844. status = Dns_ExtractGssTsigFromMessage(
  4845. psecPack,
  4846. pMsgHead,
  4847. pMsgEnd
  4848. );
  4849. if ( status != ERROR_SUCCESS )
  4850. {
  4851. goto Cleanup;
  4852. }
  4853. //
  4854. // find existing security context
  4855. // - TSIG name node
  4856. // - client IP
  4857. // together specify context
  4858. //
  4859. RtlZeroMemory(
  4860. &key,
  4861. sizeof(key) );
  4862. key.pszTkeyName = psecPack->pszContextName;
  4863. key.IpRemote = IpRemote;
  4864. psecCtxt = Dns_DequeueSecurityContextByKey( key, TRUE );
  4865. if ( !psecCtxt )
  4866. {
  4867. DNSDBG( SECURITY, (
  4868. "Desired security context %s %s is NOT cached.\n"
  4869. "\treturning BADKEY\n",
  4870. key.pszTkeyName,
  4871. IP_STRING( key.IpRemote ) ));
  4872. status = DNS_ERROR_RCODE_BADKEY;
  4873. SecTsigBadKey++;
  4874. goto Cleanup;
  4875. }
  4876. // attach context to session info
  4877. psecPack->pSecContext = psecCtxt;
  4878. //
  4879. // verify signature
  4880. //
  4881. status = Dns_VerifySignatureOnPacket( psecPack );
  4882. if ( status != ERROR_SUCCESS )
  4883. {
  4884. DNSDBG( SECURITY, (
  4885. "Verify signature failed %08x %d.\n"
  4886. "\treturning BADSIG\n",
  4887. status, status ));
  4888. status = DNS_ERROR_RCODE_BADSIG;
  4889. goto Cleanup;
  4890. }
  4891. Cleanup:
  4892. // return security info blob
  4893. // if failed delete session info,
  4894. // - return security context to cache if failure is just TSIG
  4895. // being invalid
  4896. if ( status == ERROR_SUCCESS )
  4897. {
  4898. *phContext = psecPack;
  4899. }
  4900. else
  4901. {
  4902. Dns_FreeSecurityPacketInfo( psecPack );
  4903. if ( psecCtxt )
  4904. {
  4905. DNSDBG( SECURITY, (
  4906. "Re-enlisting security context at %p after TSIG verify failure.\n",
  4907. psecCtxt ));
  4908. Dns_EnlistSecurityContext( psecCtxt );
  4909. }
  4910. }
  4911. return( status );
  4912. }
  4913. DNS_STATUS
  4914. Dns_ServerNegotiateTkey(
  4915. IN IP_ADDRESS IpRemote,
  4916. IN PDNS_HEADER pMsgHead,
  4917. IN PCHAR pMsgEnd,
  4918. IN PCHAR pMsgBufEnd,
  4919. IN BOOL fBreakOnAscFailure,
  4920. OUT PCHAR * ppCurrent
  4921. )
  4922. /*++
  4923. Routine Description:
  4924. Negotiate TKEY with client.
  4925. Arguments:
  4926. Return Value:
  4927. ERROR_SUCCESS if successful.
  4928. ErrorCode on failure.
  4929. DCR_CLEANUP: note this is currently returning RCODEs not status.
  4930. --*/
  4931. {
  4932. DNS_STATUS status;
  4933. SECPACK secPack;
  4934. DNS_SECCTXT_KEY key;
  4935. PSEC_CNTXT psecCtxt = NULL;
  4936. PSEC_CNTXT ppreviousContext = NULL;
  4937. WORD extRcode = 0;
  4938. DNSDBG( SECURITY, (
  4939. "Dns_ServerNegotiateTkey()\n"
  4940. ));
  4941. // security must already be running to have negotiated a TKEY
  4942. if ( !g_fSecurityPackageInitialized )
  4943. {
  4944. return( DNS_RCODE_REFUSED );
  4945. }
  4946. //
  4947. // read TKEY from packet
  4948. //
  4949. Dns_InitSecurityPacketInfo( &secPack, NULL );
  4950. status = Dns_ExtractGssTkeyFromMessage(
  4951. & secPack,
  4952. pMsgHead,
  4953. pMsgEnd,
  4954. TRUE ); // fIsServer
  4955. if ( status != ERROR_SUCCESS )
  4956. {
  4957. DNSDBG( SECURITY, (
  4958. "TKEY Extract failed for msg at %p\n"
  4959. "\tstatus = %d (%08x)\n",
  4960. pMsgHead, status, status ));
  4961. goto Cleanup;
  4962. }
  4963. //
  4964. // find existing security context from this client
  4965. // - client IP
  4966. // - TKEY record
  4967. // together specify context
  4968. //
  4969. // if previously negotiated context, doesn't match key length from
  4970. // new TKEY, then renegotiate
  4971. //
  4972. RtlZeroMemory(
  4973. &key,
  4974. sizeof(key) );
  4975. key.IpRemote = IpRemote;
  4976. key.pszTkeyName = secPack.pszContextName;
  4977. psecCtxt = Dns_DequeueSecurityContextByKey( key, FALSE );
  4978. if ( psecCtxt )
  4979. {
  4980. ppreviousContext = psecCtxt;
  4981. DNSDBG( SECURITY, (
  4982. "Found security context matching TKEY %s %s.\n",
  4983. key.pszTkeyName,
  4984. IP_STRING( key.IpRemote ) ));
  4985. //
  4986. // previously negotiated key?
  4987. //
  4988. // DCR_QUESTION: no client comeback after server side nego complete?
  4989. // treating client coming back on server side negotiated context
  4990. // as NEW context -- not sure this is correct, client may complete
  4991. // and become negotiated and want to echo
  4992. //
  4993. // to fix we'd need to hold this issue open and see if got "echo"
  4994. // in accept
  4995. //
  4996. if ( psecCtxt->fNegoComplete )
  4997. {
  4998. DNSDBG( SECURITY, (
  4999. "WARNING: Client nego request on existing negotiated context:\n"
  5000. "\tTKEY %s\n"
  5001. "\tIP %s\n",
  5002. key.pszTkeyName,
  5003. IP_STRING( key.IpRemote ) ));
  5004. //
  5005. // for Win2K (through whistler betas) allow clobbering nego
  5006. //
  5007. // DCR: pull Whistler Beta support for Win2001 server ship?
  5008. // against would be JDP deployed whister client\servers
  5009. // with this? should be zero by server ship
  5010. //
  5011. if ( psecCtxt->Version == TKEY_VERSION_W2K ||
  5012. psecCtxt->Version == TKEY_VERSION_WHISTLER_BETA )
  5013. {
  5014. DNSDBG( SECURITY, (
  5015. "WIN2K context -- overwriting negotiated security context\n"
  5016. "\twith new negotiation.\n" ));
  5017. psecCtxt = NULL;
  5018. }
  5019. // post-Win2K clients should ALWAYS send with a new name
  5020. // nego attempts on negotiated context are attacks
  5021. //
  5022. // DCR: again client echo issue here
  5023. else
  5024. {
  5025. DNSDBG( SECURITY, (
  5026. "ERROR: post-Win2K client nego request on existing key.\n"
  5027. "\terroring with BADKEY!\n" ));
  5028. DNS_ASSERT( FALSE );
  5029. psecCtxt = NULL;
  5030. status = DNS_ERROR_RCODE_BADKEY;
  5031. extRcode = DNS_RCODE_BADKEY;
  5032. goto Cleanup;
  5033. }
  5034. }
  5035. }
  5036. //
  5037. // if context not found, create one
  5038. // - tag it with version of TKEY found
  5039. //
  5040. if ( !psecCtxt )
  5041. {
  5042. psecCtxt = Dns_FindOrCreateSecurityContext( key );
  5043. if ( !psecCtxt )
  5044. {
  5045. status = DNS_ERROR_NO_MEMORY;
  5046. goto Cleanup;
  5047. }
  5048. psecCtxt->Version = secPack.TkeyVersion;
  5049. }
  5050. //
  5051. // have context -- attach to security session
  5052. //
  5053. secPack.pSecContext = psecCtxt;
  5054. //
  5055. // accept this security context
  5056. // if continue needed, then write response TKEY using
  5057. //
  5058. // DCR_ENHANCE: in COMPLETE_AND_CONTINUE case should be adding TSIG signing
  5059. // need to break out this response from ServerAcceptSecurityContext
  5060. //
  5061. status = Dns_ServerAcceptSecurityContext(
  5062. &secPack,
  5063. fBreakOnAscFailure );
  5064. if ( status != ERROR_SUCCESS )
  5065. {
  5066. if ( status != DNS_STATUS_CONTINUE_NEEDED )
  5067. {
  5068. DNSDBG( ANY, (
  5069. "FAILURE: ServerAcceptSecurityContext failed status=%d\n",
  5070. status ));
  5071. status = DNS_ERROR_RCODE_BADKEY;
  5072. goto Cleanup;
  5073. }
  5074. status = Dns_WriteGssTkeyToMessage(
  5075. &secPack,
  5076. pMsgHead,
  5077. pMsgBufEnd,
  5078. ppCurrent,
  5079. TRUE ); // fIsServer
  5080. if ( status != ERROR_SUCCESS )
  5081. {
  5082. status = DNS_RCODE_SERVER_FAILURE;
  5083. goto Cleanup;
  5084. }
  5085. //
  5086. // sign packet if we are now completed
  5087. //
  5088. if ( psecCtxt->fNegoComplete )
  5089. {
  5090. goto Sign;
  5091. }
  5092. goto Cleanup;
  5093. }
  5094. //
  5095. // verify signature, if present
  5096. //
  5097. status = Dns_ExtractGssTsigFromMessage(
  5098. &secPack,
  5099. pMsgHead,
  5100. pMsgEnd
  5101. );
  5102. if ( status == ERROR_SUCCESS )
  5103. {
  5104. status = Dns_VerifySignatureOnPacket( &secPack );
  5105. if ( status != ERROR_SUCCESS )
  5106. {
  5107. DNSDBG( SECURITY, (
  5108. "Verify signature failed on TKEY nego packet %p.\n"
  5109. "\tstatus = %d (%08x)\n"
  5110. "\treturning BADSIG\n",
  5111. pMsgHead,
  5112. status, status ));
  5113. status = DNS_ERROR_RCODE_BADSIG;
  5114. extRcode = DNS_RCODE_BADSIG;
  5115. }
  5116. }
  5117. else if ( status == DNS_STATUS_PACKET_UNSECURE )
  5118. {
  5119. status = ERROR_SUCCESS;
  5120. }
  5121. else
  5122. {
  5123. extRcode = DNS_RCODE_BADSIG;
  5124. }
  5125. //
  5126. // sign server's response
  5127. //
  5128. Sign:
  5129. DNSDBG( SECURITY, (
  5130. "Signing TKEY nego packet at %p after nego complete\n"
  5131. "\tstatus = %d (%08x)\n"
  5132. "\textRcode = %d\n",
  5133. pMsgHead,
  5134. status, status,
  5135. extRcode ));
  5136. pMsgHead->IsResponse = TRUE;
  5137. status = Dns_SignMessageWithGssTsig(
  5138. &secPack,
  5139. pMsgHead,
  5140. pMsgBufEnd,
  5141. ppCurrent );
  5142. if ( status != ERROR_SUCCESS )
  5143. {
  5144. DNS_PRINT((
  5145. "ERROR: failed to sign successful TKEY nego packet at %p\n"
  5146. "\tstatus = %d (%08x)\n",
  5147. pMsgHead,
  5148. status, status ));
  5149. status = ERROR_SUCCESS;
  5150. }
  5151. Cleanup:
  5152. //
  5153. // if failed, respond in in TKEY extended error field
  5154. //
  5155. // if extended RCODE not set above
  5156. // - default to BADKEY
  5157. // - unless status is extended RCODE
  5158. //
  5159. if ( status != ERROR_SUCCESS )
  5160. {
  5161. if ( !secPack.pTkeyRR )
  5162. {
  5163. status = DNS_RCODE_FORMERR;
  5164. }
  5165. else
  5166. {
  5167. if ( secPack.ExtendedRcode == 0 )
  5168. {
  5169. if ( extRcode == 0 )
  5170. {
  5171. extRcode = DNS_RCODE_BADKEY;
  5172. if ( status > DNS_ERROR_RCODE_BADSIG &&
  5173. status < DNS_ERROR_RCODE_LAST )
  5174. {
  5175. extRcode = (WORD)(status - DNS_ERROR_MASK);
  5176. }
  5177. }
  5178. // write extended RCODE directly into TKEY extRCODE field
  5179. // - it is a DWORD (skipping KeyLength) before Key itself
  5180. INLINE_WRITE_FLIPPED_WORD(
  5181. ( secPack.pTkeyRR->Data.TKEY.pKey - sizeof(DWORD) ),
  5182. extRcode );
  5183. }
  5184. status = DNS_RCODE_REFUSED;
  5185. }
  5186. }
  5187. //
  5188. // if successful
  5189. // - whack any previous context with new context
  5190. // if failed
  5191. // - restore any previous context, if any
  5192. // - dump any new failed context
  5193. //
  5194. // this lets us clients retry in any state they like, yet preserves
  5195. // any existing negotiation, if this attempt was security attack or bad data
  5196. // but if client successful in this negotiation, then any old context is
  5197. // dumped
  5198. //
  5199. if ( status == ERROR_SUCCESS )
  5200. {
  5201. ASSERT( secPack.pSecContext == psecCtxt );
  5202. if ( ppreviousContext != psecCtxt )
  5203. {
  5204. Dns_FreeSecurityContext( ppreviousContext );
  5205. }
  5206. DNSDBG( SECURITY, (
  5207. "Re-enlisting security context at %p\n",
  5208. psecCtxt ));
  5209. Dns_EnlistSecurityContext( psecCtxt );
  5210. }
  5211. else
  5212. {
  5213. DNSDBG( SECURITY, (
  5214. "Failed nego context at %p\n"
  5215. "\tstatus = %d\n"
  5216. "\text RCODE = %d\n"
  5217. "\tclient IP = %s\n"
  5218. "\tTKEY name = %s\n"
  5219. "\tnego complete = %d\n",
  5220. psecCtxt,
  5221. status,
  5222. extRcode,
  5223. psecCtxt ? IP_STRING( psecCtxt->Key.IpRemote ) : "NULL",
  5224. psecCtxt ? psecCtxt->Key.pszTkeyName : "NULL",
  5225. psecCtxt ? psecCtxt->fNegoComplete : 0 ));
  5226. // free any new context that failed in nego -- if any
  5227. if ( psecCtxt )
  5228. {
  5229. Dns_FreeSecurityContext( psecCtxt );
  5230. }
  5231. //
  5232. // reenlist any previously negotiated context
  5233. //
  5234. // the reenlistment protects against denial of service attack
  5235. // that spoofs client and attempts to trash their context,
  5236. // either during nego or after completed
  5237. //
  5238. // however, must dump Win2K contexts as clients can reuse
  5239. // the TKEY name and may NOT have saved the context; this
  5240. // produces BADKEY from AcceptSecurityContext() and must
  5241. // cause server to dump to reopen TKEY name to client
  5242. //
  5243. // DCR_QUESTION: is it possible to "reuse" partially nego'd context
  5244. // that fails further negotiation
  5245. // in other words can we protect against DOS attack in the middle
  5246. // of nego that tries to message with nego, by requeuing the context
  5247. // so real nego can complete?
  5248. if ( ppreviousContext &&
  5249. ppreviousContext != psecCtxt )
  5250. {
  5251. DNS_ASSERT( ppreviousContext->fNegoComplete );
  5252. DNSDBG( ANY, (
  5253. "WARNING: reenlisting security context %p after failed nego\n"
  5254. "\tthis indicates client problem OR security attack!\n"
  5255. "\tclient IP = %s\n"
  5256. "\tTKEY name = %s\n"
  5257. "\tnego complete = %d\n",
  5258. ppreviousContext,
  5259. IP_STRING( ppreviousContext->Key.IpRemote ),
  5260. ppreviousContext->Key.pszTkeyName,
  5261. ppreviousContext->fNegoComplete ));
  5262. Dns_EnlistSecurityContext( ppreviousContext );
  5263. }
  5264. }
  5265. // cleanup security packet info
  5266. // - parsed records, buffers, etc
  5267. // - stack struct, no free
  5268. Dns_CleanupSecurityPacketInfoEx( &secPack, FALSE );
  5269. return( status );
  5270. }
  5271. VOID
  5272. Dns_CleanupSessionAndEnlistContext(
  5273. IN OUT HANDLE hSession
  5274. )
  5275. /*++
  5276. Routine Description:
  5277. Cleanup security session and return context to cache.
  5278. Arguments:
  5279. hSession -- session handle (security packet info)
  5280. Return Value:
  5281. None
  5282. --*/
  5283. {
  5284. PSECPACK psecPack = (PSECPACK) hSession;
  5285. DNSDBG( SECURITY, (
  5286. "Dns_CleanupSessionAndEnlistContext( %p )\n", psecPack ));
  5287. // reenlist security context
  5288. Dns_EnlistSecurityContext( psecPack->pSecContext );
  5289. // cleanup security packet info
  5290. // - parsed records, buffers, etc
  5291. // - since handle based, this is free structure
  5292. Dns_CleanupSecurityPacketInfoEx( psecPack, TRUE );
  5293. }
  5294. //
  5295. // API calling context
  5296. //
  5297. HANDLE
  5298. Dns_CreateAPIContext(
  5299. IN DWORD Flags,
  5300. IN PVOID Credentials OPTIONAL,
  5301. IN BOOL fUnicode
  5302. )
  5303. /*++
  5304. Routine Description:
  5305. Initializes a DNS API context possibly associated with a
  5306. particular set of credentials.
  5307. Flags - Type of credentials pointed to by Credentials
  5308. Credentials - a pointer to a SEC_WINNT_AUTH_IDENTITY structure
  5309. that contains the user, domain, and password
  5310. that is to be associated with update security contexts
  5311. fUnicode - ANSI is FALSE, UNICODE is TRUE to indicate version of
  5312. SEC_WINNT_AUTH_IDENTITY structure in Credentials
  5313. Return Value:
  5314. Returns context handle successful; otherwise NULL is returned.
  5315. Structure defined at top of file looks like:
  5316. typedef struct _DnsAPIContext
  5317. {
  5318. DWORD Flags;
  5319. PVOID Credentials;
  5320. struct _DnsSecurityContext * pSecurityContext;
  5321. }
  5322. DNS_API_CONTEXT, *PDNS_API_CONTEXT;
  5323. --*/
  5324. {
  5325. PDNS_API_CONTEXT pcontext;
  5326. pcontext = (PDNS_API_CONTEXT) ALLOCATE_HEAP_ZERO( sizeof(DNS_API_CONTEXT) );
  5327. if ( !pcontext )
  5328. {
  5329. return( NULL );
  5330. }
  5331. pcontext->Flags = Flags;
  5332. if ( fUnicode )
  5333. {
  5334. pcontext->Credentials = Dns_AllocateAndInitializeCredentialsW(
  5335. (PSEC_WINNT_AUTH_IDENTITY_W)Credentials
  5336. );
  5337. }
  5338. else
  5339. {
  5340. pcontext->Credentials = Dns_AllocateAndInitializeCredentialsA(
  5341. (PSEC_WINNT_AUTH_IDENTITY_A)Credentials
  5342. );
  5343. }
  5344. pcontext->pSecurityContext = NULL;
  5345. return( (HANDLE)pcontext );
  5346. }
  5347. VOID
  5348. Dns_FreeAPIContext(
  5349. IN OUT HANDLE hContextHandle
  5350. )
  5351. /*++
  5352. Routine Description:
  5353. Cleans up DNS API context data.
  5354. Arguments:
  5355. hContext -- handle to context to clean up
  5356. Return Value:
  5357. TRUE if successful
  5358. FALSE otherwise
  5359. Structure defined at top of file looks like:
  5360. typedef struct _DnsAPIContext
  5361. {
  5362. DWORD Flags;
  5363. PVOID Credentials;
  5364. struct _DnsSecurityContext * pSecurityContext;
  5365. }
  5366. DNS_API_CONTEXT, *PDNS_API_CONTEXT;
  5367. --*/
  5368. {
  5369. PDNS_API_CONTEXT pcontext = (PDNS_API_CONTEXT)hContextHandle;
  5370. if ( !pcontext )
  5371. {
  5372. return;
  5373. }
  5374. if ( pcontext->Credentials )
  5375. {
  5376. Dns_FreeAuthIdentityCredentials( pcontext->Credentials );
  5377. }
  5378. if ( pcontext->pSecurityContext )
  5379. {
  5380. Dns_FreeSecurityContext( pcontext->pSecurityContext );
  5381. pcontext->pSecurityContext = NULL;
  5382. }
  5383. FREE_HEAP( pcontext );
  5384. }
  5385. PVOID
  5386. Dns_GetApiContextCredentials(
  5387. IN HANDLE hContextHandle
  5388. )
  5389. /*++
  5390. Routine Description:
  5391. returns pointer to credentials in context handle
  5392. Arguments:
  5393. hContext -- handle to context to clean up
  5394. Return Value:
  5395. TRUE if successful
  5396. FALSE otherwise
  5397. Structure defined at top of file looks like:
  5398. typedef struct _DnsAPIContext
  5399. {
  5400. DWORD Flags;
  5401. PVOID Credentials;
  5402. struct _DnsSecurityContext * pSecurityContext;
  5403. }
  5404. DNS_API_CONTEXT, *PDNS_API_CONTEXT;
  5405. --*/
  5406. {
  5407. PDNS_API_CONTEXT pcontext = (PDNS_API_CONTEXT)hContextHandle;
  5408. return pcontext ? pcontext->Credentials : NULL;
  5409. }
  5410. DWORD
  5411. Dns_GetCurrentRid(
  5412. VOID
  5413. )
  5414. /*++
  5415. Routine Description:
  5416. Get RID. This is used as unique ID for tagging security context.
  5417. Arguments:
  5418. None
  5419. Return Value:
  5420. Current RID if successful.
  5421. (-1) on error.
  5422. --*/
  5423. {
  5424. BOOL bstatus;
  5425. DNS_STATUS status = ERROR_SUCCESS;
  5426. HANDLE hToken = NULL;
  5427. PTOKEN_USER puserToken = NULL;
  5428. DWORD size;
  5429. UCHAR SubAuthCount;
  5430. DWORD rid = (DWORD)-1;
  5431. //
  5432. // get thread/process token
  5433. //
  5434. bstatus = OpenThreadToken(
  5435. GetCurrentThread(), // thread pseudo handle
  5436. TOKEN_QUERY, // query info
  5437. TRUE, // open as self
  5438. & hToken ); // returned handle
  5439. if ( !bstatus )
  5440. {
  5441. DNSDBG( SECURITY, (
  5442. "Note <%lu>: failed to open thread token\n",
  5443. GetLastError()));
  5444. //
  5445. // attempt to open process token
  5446. // - if not impersonating, this is fine
  5447. //
  5448. bstatus = OpenProcessToken(
  5449. GetCurrentProcess(),
  5450. TOKEN_QUERY,
  5451. & hToken );
  5452. if ( !bstatus )
  5453. {
  5454. status = GetLastError();
  5455. DNSDBG( SECURITY, (
  5456. "Error <%lu>: failed to open process token\n",
  5457. status ));
  5458. ASSERT( FALSE );
  5459. goto Cleanup;
  5460. }
  5461. }
  5462. //
  5463. // get length required for TokenUser
  5464. // - specify buffer length of 0
  5465. //
  5466. bstatus = GetTokenInformation(
  5467. hToken,
  5468. TokenUser,
  5469. NULL,
  5470. 0,
  5471. & size );
  5472. status = GetLastError();
  5473. if ( bstatus || status != ERROR_INSUFFICIENT_BUFFER )
  5474. {
  5475. DNSDBG( SECURITY, (
  5476. "Error <%lu>: unexpected error for token info\n",
  5477. status ));
  5478. ASSERT( FALSE );
  5479. goto Cleanup;
  5480. }
  5481. //
  5482. // allocate user token
  5483. //
  5484. puserToken = (PTOKEN_USER) ALLOCATE_HEAP( size );
  5485. if ( !puserToken )
  5486. {
  5487. status = GetLastError();
  5488. DNSDBG( SECURITY, (
  5489. "Error <%lu>: failed to allocate memory\n",
  5490. status ));
  5491. ASSERT( FALSE );
  5492. goto Cleanup;
  5493. }
  5494. //
  5495. // get SID of process token.
  5496. //
  5497. bstatus = GetTokenInformation(
  5498. hToken,
  5499. TokenUser,
  5500. puserToken,
  5501. size,
  5502. & size );
  5503. if ( !bstatus )
  5504. {
  5505. status = GetLastError();
  5506. DNSDBG( SECURITY, (
  5507. "Error <%lu>: failed to get user info\n",
  5508. status));
  5509. ASSERT( FALSE );
  5510. goto Cleanup;
  5511. }
  5512. //
  5513. // calculate the size of the domain sid
  5514. //
  5515. SubAuthCount = *GetSidSubAuthorityCount( puserToken->User.Sid );
  5516. status = GetLastError();
  5517. if ( status != ERROR_SUCCESS || SubAuthCount < 1 )
  5518. {
  5519. DNSDBG( SECURITY, (
  5520. "Error <%lu>: Invalid sid.\n",
  5521. status));
  5522. ASSERT( FALSE );
  5523. goto Cleanup;
  5524. }
  5525. size = GetLengthSid( puserToken->User.Sid );
  5526. //
  5527. // get rid from the account sid
  5528. //
  5529. rid = *GetSidSubAuthority(
  5530. puserToken->User.Sid,
  5531. SubAuthCount-1 );
  5532. status = GetLastError();
  5533. if ( status != ERROR_SUCCESS )
  5534. {
  5535. DNSDBG( SECURITY, (
  5536. "Error <%lu>: Invalid sid.\n",
  5537. status ));
  5538. ASSERT( FALSE );
  5539. goto Cleanup;
  5540. }
  5541. Cleanup:
  5542. if ( hToken )
  5543. {
  5544. CloseHandle( hToken );
  5545. }
  5546. if ( puserToken )
  5547. {
  5548. FREE_HEAP( puserToken );
  5549. }
  5550. return rid;
  5551. }
  5552. DWORD
  5553. Dns_GetKeyVersion(
  5554. IN PSTR pszTkeyName
  5555. )
  5556. /*++
  5557. Routine Description:
  5558. Get TKEY\TSIG version corresponding to a context.
  5559. Arguments:
  5560. pszTkeyName -- context (TSIG\TKEY owner name)
  5561. Return Value:
  5562. Version if found.
  5563. Zero if unable to read version.
  5564. --*/
  5565. {
  5566. LONGLONG clientId = 0;
  5567. DWORD version = 0;
  5568. INT iscan;
  5569. if ( !pszTkeyName )
  5570. {
  5571. DNSDBG( ANY, ( "ERROR: no context to Dns_GetKeyVersion()!\n" ));
  5572. ASSERT( FALSE );
  5573. return( 0 );
  5574. }
  5575. //
  5576. // Versioned contexts have format <64bits>-ms-<version#>
  5577. //
  5578. iscan = sscanf(
  5579. pszTkeyName,
  5580. "%I64d-ms-%d",
  5581. & clientId,
  5582. & version );
  5583. if ( iscan != 2 )
  5584. {
  5585. //
  5586. // Clients before Whistler RC2 use "MS" instead of "ms".
  5587. //
  5588. iscan = sscanf(
  5589. pszTkeyName,
  5590. "%I64d-MS-%d",
  5591. & clientId,
  5592. & version );
  5593. }
  5594. if ( iscan == 2 )
  5595. {
  5596. DNSDBG( SECURITY, (
  5597. "Dns_GetKeyVersion() extracted version %d\n",
  5598. version ));
  5599. }
  5600. else
  5601. {
  5602. DNSDBG( SECURITY, (
  5603. "Dns_GetKeyVersion() unable to extract version from %s\n"
  5604. "\treturning 0 as version\n",
  5605. pszTkeyName ));
  5606. version = 0;
  5607. }
  5608. return version;
  5609. }
  5610. DNS_STATUS
  5611. BuildCredsForPackage(
  5612. OUT PSEC_WINNT_AUTH_IDENTITY_EXW pAuthOut,
  5613. IN PWSTR pPackageName,
  5614. IN PSEC_WINNT_AUTH_IDENTITY_W pAuthIn
  5615. )
  5616. /*++
  5617. Description:
  5618. Builds auth identity info blob with specific package.
  5619. The purpose of this is to let us ONLY negotiate kerberos and
  5620. avoid wasting bandwidth negotiating NTLM.
  5621. Parameters:
  5622. pAuthOut -- auth identity info
  5623. pPackageName -- name of package
  5624. pAuthIn -- existing package
  5625. Return:
  5626. ERROR_SUCCESS if successful.
  5627. ErrorCode on failure.
  5628. --*/
  5629. {
  5630. DNSDBG( SECURITY, (
  5631. "BuildCredsForPackage( %p, %S )\n",
  5632. pAuthOut,
  5633. pPackageName ));
  5634. //
  5635. // currently don't limit passed in creds to kerberos
  5636. //
  5637. if ( pAuthIn )
  5638. {
  5639. return ERROR_INVALID_PARAMETER;
  5640. }
  5641. //
  5642. // auth-id with default creds
  5643. // - user, domain, password all zero
  5644. // - set length and version
  5645. // - set package
  5646. // - set flag to indicate unicode
  5647. //
  5648. RtlZeroMemory(
  5649. pAuthOut,
  5650. sizeof(*pAuthOut) );
  5651. pAuthOut->Version = SEC_WINNT_AUTH_IDENTITY_VERSION;
  5652. pAuthOut->Length = sizeof(*pAuthOut);
  5653. pAuthOut->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
  5654. pAuthOut->PackageList = pPackageName;
  5655. pAuthOut->PackageListLength = wcslen( pPackageName );
  5656. return ERROR_SUCCESS;
  5657. }
  5658. DNS_STATUS
  5659. Dns_AcquireCredHandle(
  5660. OUT PCredHandle pCredHandle,
  5661. IN BOOL fDnsServer,
  5662. IN PCHAR pCreds
  5663. )
  5664. /*++
  5665. Routine Description:
  5666. Acquire credentials handle.
  5667. Cover to handle issues like kerberos restriction.
  5668. Arguments:
  5669. fDnsServer -- TRUE if DNS server process; FALSE otherwise
  5670. pCreds -- credentials
  5671. Return Value:
  5672. success: ERROR_SUCCESS
  5673. --*/
  5674. {
  5675. SEC_WINNT_AUTH_IDENTITY_EXW clientCreds;
  5676. SECURITY_STATUS status = ERROR_SUCCESS;
  5677. PVOID pauthData = pCreds;
  5678. DNSDBG( SECURITY, (
  5679. "Dns_AcquireCredHandle( %p, server=%d, pcred=%p )\n",
  5680. pCredHandle,
  5681. fDnsServer,
  5682. pCreds ));
  5683. //
  5684. // kerberos for client
  5685. //
  5686. // if passed in creds
  5687. // - just append package (if possible)
  5688. //
  5689. // no creds
  5690. // - build creds with kerb package and all else NULL
  5691. //
  5692. if ( !fDnsServer && g_NegoKerberosOnly )
  5693. {
  5694. if ( !pCreds )
  5695. {
  5696. status = BuildCredsForPackage(
  5697. & clientCreds,
  5698. L"kerberos",
  5699. NULL );
  5700. DNS_ASSERT( status == NO_ERROR );
  5701. if ( status == NO_ERROR )
  5702. {
  5703. pauthData = &clientCreds;
  5704. }
  5705. }
  5706. }
  5707. //
  5708. // acquire cred handle
  5709. //
  5710. status = g_pSecurityFunctionTable->AcquireCredentialsHandleW(
  5711. NULL, // principal
  5712. PACKAGE_NAME,
  5713. fDnsServer ?
  5714. SECPKG_CRED_INBOUND :
  5715. SECPKG_CRED_OUTBOUND,
  5716. NULL, // LOGON id
  5717. pauthData, // auth data
  5718. NULL, // get key fn
  5719. NULL, // get key arg
  5720. pCredHandle, // out credentials
  5721. NULL // valid forever
  5722. );
  5723. if ( !SEC_SUCCESS(status) )
  5724. {
  5725. DNSDBG( ANY, (
  5726. "ERROR: AcquireCredentialHandle failed!\n"
  5727. "\tstatus = %08x %d\n"
  5728. "\tpauthId = %p\n",
  5729. status, status,
  5730. pauthData ));
  5731. }
  5732. DNSDBG( SECURITY, (
  5733. "Leave Dns_AcquireCredHandle() => %08x\n",
  5734. status ));
  5735. return (DNS_STATUS) status;
  5736. }
  5737. DNS_STATUS
  5738. Dns_RefreshSSpiCredentialsHandle(
  5739. IN BOOL fDnsServer,
  5740. IN PCHAR pCreds
  5741. )
  5742. /*++
  5743. Routine Description:
  5744. Refreshes the global credentials handle if it is expired.
  5745. Calls into SSPI to acquire a new handle.
  5746. Arguments:
  5747. fDnsServer -- TRUE if DNS server process; FALSE otherwise
  5748. pCreds -- credentials
  5749. Return Value:
  5750. success: ERROR_SUCCESS
  5751. --*/
  5752. {
  5753. SECURITY_STATUS status = ERROR_SUCCESS;
  5754. DNSDBG( SECURITY, (
  5755. "RefreshSSpiCredentialsHandle( %d, pcreds=%p )\n",
  5756. fDnsServer,
  5757. pCreds ));
  5758. EnterCriticalSection( &SecurityContextListCS );
  5759. //
  5760. // DCR: need check -- if handle for same credentials and still valid
  5761. // no need to fix up
  5762. //
  5763. if ( !SSPI_INVALID_HANDLE( &g_hSspiCredentials ) )
  5764. {
  5765. //
  5766. // Free previously allocated handle
  5767. //
  5768. status = g_pSecurityFunctionTable->FreeCredentialsHandle(
  5769. &g_hSspiCredentials );
  5770. if ( !SEC_SUCCESS(status) )
  5771. {
  5772. DNSDBG( ANY, (
  5773. "ERROR <%08x>: Cannot free credentials handle\n",
  5774. status ));
  5775. }
  5776. // continue regardless.
  5777. SecInvalidateHandle( &g_hSspiCredentials );
  5778. }
  5779. ASSERT( SSPI_INVALID_HANDLE( &g_hSspiCredentials ) );
  5780. //
  5781. // Acquire credentials
  5782. //
  5783. status = Dns_AcquireCredHandle(
  5784. & g_hSspiCredentials,
  5785. fDnsServer,
  5786. pCreds );
  5787. if ( !SEC_SUCCESS(status) )
  5788. {
  5789. DNS_PRINT((
  5790. "ERROR (0x%x): AcquireCredentialHandle failed: %u %p\n",
  5791. status ));
  5792. SecInvalidateHandle( &g_hSspiCredentials );
  5793. }
  5794. LeaveCriticalSection( &SecurityContextListCS );
  5795. DNSDBG( SECURITY, (
  5796. "Exit <0x%x>: RefreshSSpiCredentialsHandle()\n",
  5797. status ));
  5798. return (DNS_STATUS) status;
  5799. }
  5800. //
  5801. // Cred utils
  5802. //
  5803. PWSTR
  5804. MakeCredKeyFromStrings(
  5805. IN PWSTR pwsUserName,
  5806. IN PWSTR pwsDomain,
  5807. IN PWSTR pwsPassword
  5808. )
  5809. /*++
  5810. Description:
  5811. Allocates auth identity info and initializes pAuthIn info
  5812. Parameters:
  5813. pwsUserName -- user name
  5814. pwsDomain -- domain name
  5815. pwsPassword -- password
  5816. Return:
  5817. Ptr to newly create credentials.
  5818. NULL on failure.
  5819. --*/
  5820. {
  5821. DWORD length;
  5822. PWSTR pstr;
  5823. DNSDBG( SECURITY, (
  5824. "Enter MakeCredKeyFromStrings()\n"
  5825. "\tuser = %S\n"
  5826. "\tdomain = %S\n"
  5827. "\tpassword = %S\n",
  5828. pwsUserName,
  5829. pwsDomain,
  5830. pwsPassword ));
  5831. //
  5832. // determine length and allocate
  5833. //
  5834. length = wcslen( pwsUserName );
  5835. length += wcslen( pwsDomain );
  5836. length += wcslen( pwsPassword );
  5837. length += 3; // two separators and NULL terminator
  5838. pstr = ALLOCATE_HEAP( length * sizeof(WCHAR) );
  5839. if ( ! pstr )
  5840. {
  5841. return NULL;
  5842. }
  5843. //
  5844. // build cred info
  5845. //
  5846. wcscat( pstr, pwsDomain );
  5847. wcscat( pstr, L"\\" );
  5848. wcscpy( pstr, pwsUserName );
  5849. wcscat( pstr, L"\\" );
  5850. wcscat( pstr, pwsPassword );
  5851. DNSDBG( SECURITY, (
  5852. "Created cred string %S\n",
  5853. pstr ));
  5854. return pstr;
  5855. }
  5856. PWSTR
  5857. MakeCredKey(
  5858. IN PCHAR pCreds
  5859. )
  5860. /*++
  5861. Description:
  5862. Allocates auth identity info and initializes pAuthIn info
  5863. Parameters:
  5864. pCreds -- credentials
  5865. Return:
  5866. Ptr to newly create credentials.
  5867. NULL on failure.
  5868. --*/
  5869. {
  5870. PSEC_WINNT_AUTH_IDENTITY_EXW pauth = NULL;
  5871. SEC_WINNT_AUTH_IDENTITY_EXW dummyAuth;
  5872. PWSTR pstr = NULL;
  5873. DWORD length;
  5874. DNSDBG( SECURITY, (
  5875. "MakeCredKey( %p )\n",
  5876. pCreds ));
  5877. //
  5878. // determine AUTH_EX or old style credentials
  5879. // - if old style dummy up new version
  5880. //
  5881. pauth = (PSEC_WINNT_AUTH_IDENTITY_EXW) pCreds;
  5882. if ( pauth->Length == sizeof(*pauth) &&
  5883. pauth->Version < 0x10000 )
  5884. {
  5885. DNSDBG( SECURITY, (
  5886. "Creds at %p are new AuthEx creds.\n",
  5887. pCreds ));
  5888. }
  5889. else
  5890. {
  5891. DNSDBG( SECURITY, (
  5892. "Creds at %p are old style.\n",
  5893. pCreds ));
  5894. RtlCopyMemory(
  5895. (PBYTE) &dummyAuth.User,
  5896. pCreds,
  5897. sizeof(SEC_WINNT_AUTH_IDENTITY_W) );
  5898. pauth = &dummyAuth;
  5899. }
  5900. //
  5901. // sum lengths and allocate string
  5902. //
  5903. length = pauth->UserLength;
  5904. length += pauth->DomainLength;
  5905. length += pauth->PasswordLength;
  5906. length += 3;
  5907. pstr = ALLOCATE_HEAP( length * sizeof(WCHAR) );
  5908. if ( ! pstr )
  5909. {
  5910. return NULL;
  5911. }
  5912. //
  5913. // determine unicode \ ANSI -- write string
  5914. //
  5915. // it appears that with wprint functions the meaning of
  5916. // %s and %S is reversed
  5917. //
  5918. if ( pauth->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE )
  5919. {
  5920. swprintf(
  5921. pstr,
  5922. //L"%S\\%S\\%S",
  5923. L"%s\\%s\\%s",
  5924. pauth->Domain,
  5925. pauth->User,
  5926. pauth->Password );
  5927. DNSDBG( SECURITY, (
  5928. "Created cred string %S from unicode\n",
  5929. pstr ));
  5930. }
  5931. else
  5932. {
  5933. swprintf(
  5934. pstr,
  5935. //L"%s\\%s\\%s",
  5936. L"%S\\%S\\%S",
  5937. pauth->Domain,
  5938. pauth->User,
  5939. pauth->Password );
  5940. DNSDBG( SECURITY, (
  5941. "Created cred string %S from ANSI\n",
  5942. pstr ));
  5943. }
  5944. return pstr;
  5945. }
  5946. BOOL
  5947. CompareCredKeys(
  5948. IN PWSTR pwsCredKey1,
  5949. IN PWSTR pwsCredKey2
  5950. )
  5951. /*++
  5952. Description:
  5953. Compare cred strings for matching security contexts.
  5954. Parameters:
  5955. pwsCredKey1 -- cred string
  5956. pwsCredKey2 -- cred string
  5957. Return:
  5958. TRUE if match.
  5959. FALSE if no match.
  5960. --*/
  5961. {
  5962. DNSDBG( SECURITY, (
  5963. "CompareCredKeys( %S, %S )\n",
  5964. pwsCredKey1,
  5965. pwsCredKey2 ));
  5966. //
  5967. // most common case -- no creds
  5968. //
  5969. if ( !pwsCredKey1 || !pwsCredKey2 )
  5970. {
  5971. return( pwsCredKey2==pwsCredKey1 );
  5972. }
  5973. //
  5974. // cred strings are wide character strings
  5975. // - just string compare
  5976. //
  5977. return( wcscmp( pwsCredKey1, pwsCredKey2 ) == 0 );
  5978. }
  5979. //
  5980. // End security.c
  5981. //