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.

509 lines
14 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation
  6. //
  7. // File: ktkerb.cxx
  8. //
  9. // Contents: Kerberos Tunneller, routines that parse the kerb req/req
  10. //
  11. // History: 23-Jul-2001 t-ryanj Created
  12. //
  13. //------------------------------------------------------------------------
  14. #include <nt.h>
  15. #include <ntrtl.h>
  16. #include <nturtl.h>
  17. #include <windows.h>
  18. #ifndef SECURITY_WIN32
  19. #define SECURITY_WIN32
  20. #endif
  21. #include <wincrypt.h>
  22. #include <asn1util.h>
  23. #include <kerbcomm.h>
  24. #include "ktdebug.h"
  25. #include "ktkerb.h"
  26. #include "ktmem.h"
  27. //#define KT_REGKEY_REALMS TEXT("System\\CurrentControlSet\\Services\\kerbtunnel\\Realms\\")
  28. TCHAR KT_REGKEY_REALMS[] = TEXT("System\\CurrentControlSet\\Services\\kerbtunnel\\Realms\\");
  29. #define KT_INITIAL_KDCBUFFER_SIZE 64
  30. //+-------------------------------------------------------------------------
  31. //
  32. // Function: KtSetPduValue
  33. //
  34. // Synopsis: Parses the kerberos request and sets the PduValue memeber
  35. // of pContext to the appropriate value to be passed to
  36. // KerbUnpackData
  37. //
  38. // Effects:
  39. //
  40. // Arguments: pContext - context that has the coalesced request in
  41. // pContext->emptybuf->buffer
  42. //
  43. // Requires:
  44. //
  45. // Returns: Success value.
  46. //
  47. // Notes:
  48. //
  49. //--------------------------------------------------------------------------
  50. BOOL
  51. KtSetPduValue(
  52. PKTCONTEXT pContext
  53. )
  54. {
  55. BOOL fRet = TRUE;
  56. BYTE AsnTag;
  57. //
  58. // If we don't have the first byte of the request, we can't parse it.
  59. //
  60. if( pContext->emptybuf->buflen < sizeof(DWORD) + 1 )
  61. goto Error;
  62. //
  63. // The last five bits of the first byte of the kerb message tell us
  64. // what kind of message it is. We set the pdu value accordingly.
  65. //
  66. switch( (AsnTag = pContext->emptybuf->buffer[sizeof(DWORD)] & 0x1f) ) /* last 5 bits */
  67. {
  68. case 0x0a: /* AS-REQUEST */
  69. DebugLog( DEB_TRACE, "%s(%d): Handing AS-REQUEST.\n", __FILE__, __LINE__ );
  70. pContext->PduValue = KERB_AS_REQUEST_PDU;
  71. break;
  72. case 0x0b: /* AS-REPLY */
  73. DebugLog( DEB_TRACE, "%s(%d): Handling AS-REPLY.\n", __FILE__, __LINE__ );
  74. pContext->PduValue = KERB_AS_REPLY_PDU;
  75. break;
  76. case 0x0c: /* TGS-REQUEST */
  77. DebugLog( DEB_TRACE, "%s(%d): Handling TGS-REQUEST.\n", __FILE__, __LINE__ );
  78. pContext->PduValue = KERB_TGS_REQUEST_PDU;
  79. break;
  80. case 0x0d: /* TGS-REPLY */
  81. DebugLog( DEB_TRACE, "%s(%d): Handing TGS-REPLY.\n", __FILE__, __LINE__ );
  82. pContext->PduValue = KERB_TGS_REPLY_PDU;
  83. break;
  84. case 0x1e: /* KERB_ERROR */
  85. DebugLog( DEB_TRACE, "%s(%d): Handling KERB-ERROR.\n", __FILE__, __LINE__ );
  86. pContext->PduValue = KERB_ERROR_PDU;
  87. break;
  88. default:
  89. DebugLog( DEB_WARN, "%s(%d): Unhandled message type. ASN tag: 0x%x.\n", __FILE__, __LINE__, AsnTag );
  90. DsysAssert( (AsnTag >= 0x0a && AsnTag <= 0x0d) ||
  91. AsnTag == 0x1e );
  92. break;
  93. }
  94. Cleanup:
  95. return fRet;
  96. Error:
  97. fRet = FALSE;
  98. goto Cleanup;
  99. }
  100. //+-------------------------------------------------------------------------
  101. //
  102. // Function: KtParseExpectedLength
  103. //
  104. // Synopsis: Parses the expected length of the message.
  105. // When using TCP, the message is prepended by four bytes
  106. // indicating its length in network byte order.
  107. // When using UDP, the ASN.1 encoded length in the message
  108. // must be parsed out.
  109. //
  110. // Effects:
  111. //
  112. // Arguments: pContext - context that has the beginning of the request in
  113. // pContext->emptybuf->buffer
  114. //
  115. // Requires:
  116. //
  117. // Returns: Success value.
  118. //
  119. // Notes:
  120. //
  121. //--------------------------------------------------------------------------
  122. BOOL
  123. KtParseExpectedLength(
  124. PKTCONTEXT pContext
  125. )
  126. {
  127. BOOL fRet = TRUE;
  128. DWORD cbContent;
  129. #if 0 /* this is the udp way, not the tcp way */
  130. LONG cbLength;
  131. #endif
  132. //
  133. // If we don't have the four bytes, we can't parse the length.
  134. //
  135. if( pContext->emptybuf->bytesused < sizeof(DWORD) )
  136. {
  137. DebugLog( DEB_ERROR, "%s(%d): Not enough data to parse length.\n", __FILE__, __LINE__ );
  138. goto Error;
  139. }
  140. //
  141. // Otherwise, we just take the first four bytes and convert them
  142. // to host byte order.
  143. //
  144. cbContent = ntohl( *(u_long*)pContext->emptybuf->buffer );
  145. //
  146. // If the most significant bit is set, this indicates that more
  147. // bytes are needed for the length. We're just going to discard
  148. // any messages that long.
  149. //
  150. if( cbContent & 0x80000000 )
  151. {
  152. DebugLog( DEB_ERROR, "%s(%d): Length won't fit in DWORD.\n", __FILE__, __LINE__ );
  153. goto Error;
  154. }
  155. //
  156. // Then the total length is the length of the length plus the length
  157. // of the content.
  158. //
  159. pContext->ExpectedLength = sizeof(DWORD) + cbContent;
  160. if( !KtSetPduValue(pContext) ) /* TODO: this call should be elsewhere */
  161. goto Error;
  162. #if 0 /* this will need to be added back in for udp support */
  163. cbLength = Asn1UtilDecodeLength( &cbContent,
  164. &(pContext->emptybuf->buffer[1]),
  165. pContext->emptybuf->bytesused - 1 );
  166. if( cbLength < 0 )
  167. {
  168. goto Error;
  169. }
  170. pContext->ExpectedLength = 1 + cbLength + cbContent;
  171. #endif
  172. Cleanup:
  173. return fRet;
  174. Error:
  175. fRet = FALSE;
  176. goto Cleanup;
  177. }
  178. //+-------------------------------------------------------------------------
  179. //
  180. // Function: KtFindProxy
  181. //
  182. // Synopsis: Unpacks the AS or TGS request, examines the realm, and
  183. // attempts to find some way to contact that realm using http,
  184. // first by looking on the Realms registry key, then by doing a
  185. // DNS SRV lookup.
  186. //
  187. // Effects:
  188. //
  189. // Arguments:
  190. //
  191. // Requires:
  192. //
  193. // Returns: rets
  194. //
  195. // Notes:
  196. //
  197. //--------------------------------------------------------------------------
  198. BOOL
  199. KtFindProxy(
  200. PKTCONTEXT pContext
  201. )
  202. {
  203. BOOL fRet = TRUE;
  204. PKERB_KDC_REQUEST pKdcReq = NULL;
  205. KERBERR KerbErr;
  206. HKEY RealmKey = NULL;
  207. LONG RegError;
  208. LPBYTE pbKdcBuffer = NULL;
  209. DWORD cbKdcBuffer = KT_INITIAL_KDCBUFFER_SIZE;
  210. DWORD cbKdcBufferWritten;
  211. LPWSTR RealmKeyName = NULL;
  212. LPWSTR WideRealm = NULL;
  213. ULONG ccRealm;
  214. int WideCharSuccess;
  215. //
  216. // Lets attempt to unpack the data into a struct.
  217. // This call will allocate pKdcReq if successful.
  218. //
  219. KerbErr = KerbUnpackData( pContext->buffers->buffer + sizeof(DWORD),
  220. pContext->buffers->bytesused - sizeof(DWORD),
  221. pContext->PduValue,
  222. (PVOID*)&pKdcReq );
  223. if( !KERB_SUCCESS(KerbErr) )
  224. {
  225. DebugLog( DEB_ERROR, "%s(%d): Kerberos Error unpacking ticket: 0x%x\n", __FILE__, __LINE__, KerbErr );
  226. goto Error;
  227. }
  228. DebugLog( DEB_TRACE, "%s(%d): Realm found to be %s.\n", __FILE__, __LINE__, pKdcReq->request_body.realm );
  229. //
  230. // This call discovers the number of wchars needed to hold
  231. // the converted realmname.
  232. //
  233. ccRealm = strlen(pKdcReq->request_body.realm)*sizeof(WCHAR);
  234. //
  235. // Allocate memory to hold the wchar converted realm name, and
  236. // allocate memory to hold the full name of the registry key to
  237. // peek at.
  238. //
  239. WideRealm = (LPWSTR)KtAlloc((ccRealm + 1)*sizeof(WCHAR));
  240. RealmKeyName = (LPWSTR)KtAlloc( sizeof(KT_REGKEY_REALMS) + ccRealm*sizeof(WCHAR) );
  241. if( !WideRealm || !RealmKeyName )
  242. {
  243. DebugLog( DEB_ERROR, "%s(%d): Allocation error.", __FILE__, __LINE__ );
  244. goto Error;
  245. }
  246. //
  247. // Copy the beginning of the realmkey.
  248. //
  249. wcscpy( RealmKeyName, KT_REGKEY_REALMS );
  250. //
  251. // Convert the realm to unicode.
  252. //
  253. WideCharSuccess = MultiByteToWideChar( CP_ACP,
  254. 0,
  255. pKdcReq->request_body.realm,
  256. -1,
  257. WideRealm,
  258. ccRealm );
  259. if( !WideCharSuccess )
  260. {
  261. DebugLog( DEB_ERROR, "%s(%d): Error converting realm to unicode: 0x%x\n", __FILE__, __LINE__, GetLastError() );
  262. goto Error;
  263. }
  264. //
  265. // Concat the realm to the regkey to get the full key.
  266. //
  267. wcscat( RealmKeyName, WideRealm );
  268. //
  269. // Now we look in the registry to see if a kproxy
  270. // server is provided for this realm.
  271. //
  272. /* TODO: All registry access should be moved to another file,
  273. read on startup, and cached. */
  274. RegError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  275. RealmKeyName,
  276. NULL, /* reserved */
  277. KEY_QUERY_VALUE,
  278. &RealmKey );
  279. if( RegError != ERROR_SUCCESS &&
  280. RegError != ERROR_FILE_NOT_FOUND )
  281. {
  282. DebugLog( DEB_ERROR, "%s(%d): Error opening regkey HKLM\\%ws: 0x%x.\n", __FILE__, __LINE__, KT_REGKEY_REALMS, RegError );
  283. fRet = FALSE;
  284. goto Cleanup;
  285. }
  286. //
  287. // If the registry key was there, read it.
  288. //
  289. if( RegError == ERROR_SUCCESS )
  290. {
  291. //
  292. // There's no way to determine how much data is in the
  293. // key, so we'll just try with increasing buffer sizes
  294. // until we succeed.
  295. //
  296. do
  297. {
  298. //
  299. // If we've already allocated memory, it must not have been enough,
  300. // so we need to free it then allocate the new amount of memory.
  301. // This should be faster than realloc, because realloc would copy
  302. // the memory if relocation were necessary.
  303. //
  304. if( pbKdcBuffer )
  305. {
  306. KtFree( pbKdcBuffer );
  307. }
  308. pbKdcBuffer = (LPBYTE)KtAlloc( cbKdcBuffer );
  309. if( !pbKdcBuffer )
  310. {
  311. DebugLog( DEB_ERROR, "%s(%d): Error allocating memory for reg values.\n", __FILE__, __LINE__ );
  312. goto Error;
  313. }
  314. //
  315. // Since RegQueryValueEx clobbers the size of our buffer if it
  316. // fails, we need to make sure we keep a good copy.
  317. //
  318. cbKdcBufferWritten = cbKdcBuffer;
  319. RegError = RegQueryValueEx( RealmKey,
  320. TEXT("KdcNames"),
  321. NULL, /* reserved */
  322. NULL, /* type is known: REG_MULTI_SZ */
  323. pbKdcBuffer,
  324. &cbKdcBufferWritten );
  325. if( RegError == ERROR_FILE_NOT_FOUND )
  326. {
  327. DebugLog( DEB_TRACE, "%s(%d): KdcNames key not found.\n", __FILE__, __LINE__ );
  328. break;
  329. }
  330. if( RegError != ERROR_SUCCESS &&
  331. RegError != ERROR_MORE_DATA )
  332. {
  333. DebugLog( DEB_ERROR, "%s(%d): Error querying regkey HKLM\\%ws\\%ws: 0x%x.\n", __FILE__, __LINE__, KT_REGKEY_REALMS, WideRealm, RegError );
  334. goto Error;
  335. }
  336. } while( RegError == ERROR_MORE_DATA &&
  337. (cbKdcBuffer *= 2) );
  338. }
  339. //
  340. // If either the realm key wasn't found or for some reason there was
  341. // no KdcNames value, we'll use DNS SRV lookup.
  342. //
  343. if( RegError == ERROR_FILE_NOT_FOUND )
  344. {
  345. DebugLog( DEB_ERROR, "%s(%d): Registry key not found, and DNS SRV not yet implemented.\n", __FILE__, __LINE__ );
  346. goto Error;
  347. }
  348. DebugLog( DEB_TRACE, "%s(%d): First proxy found to be %ws.\n", __FILE__, __LINE__, pbKdcBuffer );
  349. pContext->pbProxies = pbKdcBuffer;
  350. pbKdcBuffer = NULL;
  351. Cleanup:
  352. if( RealmKeyName )
  353. KtFree( RealmKeyName );
  354. if( WideRealm )
  355. KtFree( WideRealm );
  356. if( pbKdcBuffer )
  357. KtFree( pbKdcBuffer );
  358. if( RealmKey )
  359. RegCloseKey( RealmKey );
  360. if( pKdcReq )
  361. KerbFreeData( pContext->PduValue, pKdcReq );
  362. return fRet;
  363. Error:
  364. fRet = FALSE;
  365. goto Cleanup;
  366. }
  367. //+-------------------------------------------------------------------------
  368. //
  369. // Function: KtParseKerbError
  370. //
  371. // Synopsis: Since it's difficult to understand sniffs of tunnelled
  372. // traffic, this routine allows for the on-the-fly parsing
  373. // of KERB_ERROR codes.
  374. //
  375. // Effects:
  376. //
  377. // Arguments: pContext - context that has the message
  378. //
  379. // Requires:
  380. //
  381. // Returns:
  382. //
  383. // Notes:
  384. //
  385. //--------------------------------------------------------------------------
  386. VOID
  387. KtParseKerbError(
  388. PKTCONTEXT pContext
  389. )
  390. {
  391. PKERB_ERROR pKerbError = NULL;
  392. KERBERR KerbErr;
  393. if( !(pContext->PduValue == KERB_ERROR_PDU) )
  394. goto Cleanup;
  395. KerbErr = KerbUnpackKerbError( pContext->emptybuf->buffer + sizeof(DWORD),
  396. pContext->ExpectedLength,
  397. &pKerbError );
  398. if( !KERB_SUCCESS(KerbErr) )
  399. {
  400. DebugLog( DEB_ERROR, "%s(%d): Found KERB_ERROR, but couldn't unpack: 0x%x\n", __FILE__, __LINE__, KerbErr );
  401. goto Cleanup;
  402. }
  403. DebugLog( DEB_ERROR, "%s(%d): KERB_ERROR. Error code = 0x%x\n", __FILE__, __LINE__, pKerbError->error_code );
  404. if( pKerbError->bit_mask && error_text_present )
  405. DebugLog( DEB_ERROR, "%s(%d): KERB_ERROR, Error text = %s.\n", __FILE__, __LINE__, pKerbError->error_text );
  406. if( pKerbError->bit_mask && error_data_present )
  407. DebugLog( DEB_ERROR, "%s(%d): KERB_ERROR, Error data is present.\n", __FILE__, __LINE__ );
  408. Cleanup:
  409. if( pKerbError )
  410. KerbFreeData( pContext->PduValue,
  411. pKerbError );
  412. }
  413. //+-------------------------------------------------------------------------
  414. //
  415. // Function: KtIsAsRequest
  416. //
  417. // Synopsis: Determines if the message is an AS-REQUEST.
  418. //
  419. // Effects:
  420. //
  421. // Arguments: pContext - context that has the message
  422. //
  423. // Requires:
  424. //
  425. // Returns: TRUE if AS-REQUEST, otherwise FALSE.
  426. //
  427. // Notes:
  428. //
  429. //--------------------------------------------------------------------------
  430. BOOL
  431. KtIsAsRequest(
  432. PKTCONTEXT pContext
  433. )
  434. {
  435. return (pContext->PduValue == KERB_AS_REQUEST_PDU);
  436. }