Leaked source code of windows server 2003
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.

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