Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1211 lines
34 KiB

  1. /*++
  2. Copyright (C) 1997-2001 Microsoft Corporation
  3. Module Name:
  4. CSSPI.H
  5. Abstract:
  6. SSPI wrapper implementation for NTLM/MSN network authentication.
  7. History:
  8. raymcc 15-Jul-97 Created
  9. --*/
  10. #include "precomp.h"
  11. #include <tchar.h>
  12. #include <stdio.h>
  13. #include <csspi.h>
  14. // #define trace(x) printf x
  15. #define trace(x)
  16. static BOOL IsNt(void);
  17. //***************************************************************************
  18. //
  19. // String helper macros
  20. //
  21. //***************************************************************************
  22. #define Macro_CloneLPWSTR(x) \
  23. (x ? _wcsdup(x) : 0)
  24. #define Macro_CloneLPSTR(x) \
  25. (x ? _strdup(x) : 0)
  26. #ifdef _UNICODE
  27. #define Macro_CloneLPTSTR(x) (x ? _wcsdup(x) : 0)
  28. #else
  29. #define Macro_CloneLPTSTR(x) (x ? _strdup(x) : 0)
  30. #endif
  31. //***************************************************************************
  32. //
  33. // BOOL IsNt
  34. //
  35. // DESCRIPTION:
  36. //
  37. // Returns true if running windows NT.
  38. //
  39. // RETURN VALUE:
  40. //
  41. // see description.
  42. //
  43. //***************************************************************************
  44. // ok
  45. static BOOL IsNt(void)
  46. {
  47. OSVERSIONINFO os;
  48. os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  49. if(!GetVersionEx(&os))
  50. return FALSE; // should never happen
  51. return os.dwPlatformId == VER_PLATFORM_WIN32_NT;
  52. }
  53. //***************************************************************************
  54. //
  55. // CSSPI Static Data Members
  56. //
  57. //***************************************************************************
  58. ULONG CSSPI::m_uNumPackages = 0;
  59. PSecPkgInfo CSSPI::m_pEnumPkgInfo = 0;
  60. PSecurityFunctionTable CSSPI::pVtbl = 0;
  61. //***************************************************************************
  62. //
  63. // CSSPI::Initialize
  64. //
  65. // This must be called prior to any other operations, but may be
  66. // called multiple times.
  67. //
  68. // Return value:
  69. // TRUE on success, FALSE on failure.
  70. //
  71. //***************************************************************************
  72. // ok
  73. BOOL CSSPI::Initialize()
  74. {
  75. static HMODULE hLib = 0;
  76. // If we have already called this function and everything
  77. // is already ok, short-circuit.
  78. // ======================================================
  79. if (hLib != 0 && pVtbl != 0)
  80. {
  81. return TRUE;
  82. }
  83. if (IsNt())
  84. {
  85. hLib = LoadLibrary(__TEXT("SECURITY.DLL"));
  86. }
  87. else
  88. {
  89. hLib = LoadLibrary(__TEXT("SECUR32.DLL"));
  90. }
  91. if (hLib == 0)
  92. {
  93. trace(("CSSPI::Startup() Library failed to load\n"));
  94. return FALSE;
  95. }
  96. #ifdef _UNICODE
  97. INIT_SECURITY_INTERFACE_W pInitFn =
  98. (INIT_SECURITY_INTERFACE_W)
  99. GetProcAddress(hLib, "InitSecurityInterfaceW");
  100. #else
  101. INIT_SECURITY_INTERFACE_A pInitFn =
  102. (INIT_SECURITY_INTERFACE_A)
  103. GetProcAddress(hLib, "InitSecurityInterfaceA");
  104. #endif
  105. if (pInitFn == 0)
  106. {
  107. trace(("CSSPI::Startup() ERROR : Unable to locate function InitSecurityInterface()\n"
  108. ));
  109. FreeLibrary(hLib);
  110. hLib = 0;
  111. return FALSE;
  112. }
  113. pVtbl = pInitFn();
  114. if (pVtbl == 0)
  115. {
  116. trace(("CSSPI::Startup() ERROR : Function table not found\n"));
  117. FreeLibrary(hLib);
  118. hLib = 0;
  119. return FALSE;
  120. }
  121. return TRUE;
  122. }
  123. //***************************************************************************
  124. //
  125. // CSSPI::TranslateError
  126. //
  127. // Translates an error code to a displayable message.
  128. // Treat the return value as read-only (you must, since it is 'const').
  129. //
  130. //***************************************************************************
  131. // ok
  132. const LPTSTR CSSPI::TranslateError(
  133. ULONG uCode
  134. )
  135. {
  136. LPTSTR p = 0;
  137. switch (uCode)
  138. {
  139. case SEC_E_INSUFFICIENT_MEMORY: p = _T("SEC_E_INSUFFICIENT_MEMORY"); break;
  140. case SEC_E_INVALID_HANDLE : p = _T("SEC_E_INVALID_HANDLE"); break;
  141. case SEC_E_UNSUPPORTED_FUNCTION : p = _T("SEC_E_UNSUPPORTED_FUNCTION"); break;
  142. case SEC_E_TARGET_UNKNOWN : p = _T("SEC_E_TARGET_UNKNOWN"); break;
  143. case SEC_E_INTERNAL_ERROR : p = _T("SEC_E_INTERNAL_ERROR"); break;
  144. case SEC_E_SECPKG_NOT_FOUND : p = _T("SEC_E_SECPKG_NOT_FOUND"); break;
  145. case SEC_E_NOT_OWNER : p = _T("SEC_E_NOT_OWNER"); break;
  146. case SEC_E_CANNOT_INSTALL : p = _T("SEC_E_CANNOT_INSTALL"); break;
  147. case SEC_E_INVALID_TOKEN : p = _T("SEC_E_INVALID_TOKEN"); break;
  148. case SEC_E_CANNOT_PACK : p = _T("SEC_E_CANNOT_PACK"); break;
  149. case SEC_E_QOP_NOT_SUPPORTED : p = _T("SEC_E_QOP_NOT_SUPPORTED"); break;
  150. case SEC_E_NO_IMPERSONATION : p = _T("SEC_E_NO_IMPERSONATION"); break;
  151. case SEC_E_LOGON_DENIED : p = _T("SEC_E_LOGON_DENIED"); break;
  152. case SEC_E_UNKNOWN_CREDENTIALS : p = _T("SEC_E_UNKNOWN_CREDENTIALS"); break;
  153. case SEC_E_NO_CREDENTIALS : p = _T("SEC_E_NO_CREDENTIALS"); break;
  154. case SEC_E_MESSAGE_ALTERED : p = _T("SEC_E_MESSAGE_ALTERED"); break;
  155. case SEC_E_OUT_OF_SEQUENCE : p = _T("SEC_E_OUT_OF_SEQUENCE"); break;
  156. case SEC_E_NO_AUTHENTICATING_AUTHORITY : p = _T("SEC_E_NO_AUTHENTICATING_AUTHORITY"); break;
  157. case SEC_I_CONTINUE_NEEDED : p = _T("SEC_I_CONTINUE_NEEDED"); break;
  158. case SEC_I_COMPLETE_NEEDED : p = _T("SEC_I_COMPLETE_NEEDED"); break;
  159. case SEC_I_COMPLETE_AND_CONTINUE : p = _T("SEC_I_COMPLETE_AND_CONTINUE"); break;
  160. case SEC_I_LOCAL_LOGON : p = _T("SEC_I_LOCAL_LOGON"); break;
  161. case SEC_E_BAD_PKGID : p = _T("SEC_E_BAD_PKGID"); break;
  162. case SEC_E_CONTEXT_EXPIRED : p = _T("SEC_E_CONTEXT_EXPIRED"); break;
  163. case SEC_E_INCOMPLETE_MESSAGE : p = _T("SEC_E_INCOMPLETE_MESSAGE"); break;
  164. case SEC_E_INCOMPLETE_CREDENTIALS : p = _T("SEC_E_INCOMPLETE_CREDENTIALS"); break;
  165. case SEC_E_BUFFER_TOO_SMALL : p = _T("SEC_E_BUFFER_TOO_SMALL"); break;
  166. case SEC_I_INCOMPLETE_CREDENTIALS : p = _T("SEC_I_INCOMPLETE_CREDENTIALS"); break;
  167. case SEC_I_RENEGOTIATE : p = _T("SEC_I_RENEGOTIATE"); break;
  168. default:
  169. p = _T("<UNDEFINED ERROR CODE>");
  170. }
  171. return (const LPTSTR) p;
  172. }
  173. //***************************************************************************
  174. //
  175. // CSSPI::DisplayContextAttributes
  176. //
  177. // Display authentication context attribute bits in readable form.
  178. //
  179. //***************************************************************************
  180. // ok
  181. void CSSPI::DisplayContextAttributes(
  182. ULONG uAttrib
  183. )
  184. {
  185. if (uAttrib & ISC_RET_DELEGATE)
  186. printf("ISC_RET_DELEGATE\n");
  187. if (uAttrib & ISC_RET_MUTUAL_AUTH)
  188. printf("ISC_RET_MUTUAL_AUTH\n");
  189. if (uAttrib & ISC_RET_REPLAY_DETECT)
  190. printf("ISC_RET_REPLAY_DETECT\n");
  191. if (uAttrib & ISC_RET_SEQUENCE_DETECT)
  192. printf("ISC_RET_SEQUENCE_DETECT\n");
  193. if (uAttrib & ISC_RET_CONFIDENTIALITY)
  194. printf("ISC_RET_CONFIDENTIALITY\n");
  195. if (uAttrib & ISC_RET_USE_SESSION_KEY)
  196. printf("ISC_RET_USE_SESSION_KEY\n");
  197. if (uAttrib & ISC_RET_USED_COLLECTED_CREDS)
  198. printf("ISC_RET_USED_COLLECTED_CREDS\n");
  199. if (uAttrib & ISC_RET_USED_SUPPLIED_CREDS)
  200. printf("ISC_RET_USED_SUPPLIED_CREDS\n");
  201. if (uAttrib & ISC_RET_ALLOCATED_MEMORY)
  202. printf("ISC_RET_ALLOCATED_MEMORY\n");
  203. if (uAttrib & ISC_RET_USED_DCE_STYLE)
  204. printf("ISC_RET_USED_DCE_STYLE\n");
  205. if (uAttrib & ISC_RET_DATAGRAM)
  206. printf("ISC_RET_DATAGRAM\n");
  207. if (uAttrib & ISC_RET_CONNECTION)
  208. printf("ISC_RET_CONNECTION\n");
  209. if (uAttrib & ISC_RET_INTERMEDIATE_RETURN)
  210. printf("ISC_RET_INTERMEDIATE_RETURN\n");
  211. if (uAttrib & ISC_RET_CALL_LEVEL)
  212. printf("ISC_RET_CALL_LEVEL\n");
  213. if (uAttrib & ISC_RET_EXTENDED_ERROR)
  214. printf("ISC_RET_EXTENDED_ERROR\n");
  215. if (uAttrib & ISC_RET_STREAM)
  216. printf("ISC_RET_STREAM\n");
  217. if (uAttrib & ISC_RET_INTEGRITY)
  218. printf("ISC_RET_INTEGRITY\n");
  219. }
  220. //***************************************************************************
  221. //
  222. // CSSPI::DisplayPkgInfo
  223. //
  224. // Dumps package information to console.
  225. //
  226. //***************************************************************************
  227. // ok
  228. void CSSPI::DisplayPkgInfo(PSecPkgInfo pPkg)
  229. {
  230. printf("---------------------------------------------\n");
  231. printf("Name = <%s>\n", pPkg->Name);
  232. printf("Comment = <%s>\n", pPkg->Comment);
  233. printf("Version = 0x%X\n", pPkg->wVersion);
  234. printf("Max token = %d bytes\n", pPkg->cbMaxToken);
  235. printf("DCE RPC Id = 0x%X\n", pPkg->wRPCID);
  236. printf("Capabilities = \n");
  237. if (pPkg->fCapabilities & SECPKG_FLAG_INTEGRITY)
  238. printf(" SECPKG_FLAG_INTEGRITY\n");
  239. if (pPkg->fCapabilities & SECPKG_FLAG_PRIVACY)
  240. printf(" SECPKG_FLAG_PRIVACY\n");
  241. if (pPkg->fCapabilities & SECPKG_FLAG_TOKEN_ONLY)
  242. printf(" SECPKG_FLAG_TOKEN_ONLY\n");
  243. if (pPkg->fCapabilities & SECPKG_FLAG_DATAGRAM)
  244. printf(" SECPKG_FLAG_DATAGRAM\n");
  245. if (pPkg->fCapabilities & SECPKG_FLAG_CONNECTION)
  246. printf(" SECPKG_FLAG_CONNECTION\n");
  247. if (pPkg->fCapabilities & SECPKG_FLAG_MULTI_REQUIRED)
  248. printf(" SECPKG_FLAG_MULTI_REQUIRED\n");
  249. if (pPkg->fCapabilities & SECPKG_FLAG_CLIENT_ONLY)
  250. printf(" SECPKG_FLAG_CLIENT_ONLY\n");
  251. if (pPkg->fCapabilities & SECPKG_FLAG_EXTENDED_ERROR)
  252. printf(" SECPKG_FLAG_EXTENDED_ERROR\n");
  253. if (pPkg->fCapabilities & SECPKG_FLAG_IMPERSONATION)
  254. printf(" SECPKG_FLAG_IMPERSONATION\n");
  255. if (pPkg->fCapabilities & SECPKG_FLAG_ACCEPT_WIN32_NAME)
  256. printf(" SECPKG_FLAG_ACCEPT_WIN32_NAME\n");
  257. if (pPkg->fCapabilities & SECPKG_FLAG_STREAM)
  258. printf(" SECPKG_FLAG_STREAM\n");
  259. printf("---------------------------------------------\n");
  260. }
  261. //***************************************************************************
  262. //
  263. // CSSPI::GetNumPkgs
  264. //
  265. // Gets the number of SSPI packages available on the current machine.
  266. //
  267. // Returns 0 if none available.
  268. //
  269. //***************************************************************************
  270. // ok
  271. ULONG CSSPI::GetNumPkgs()
  272. {
  273. // Short-circuit to see if this function has been called before.
  274. // It is not possible for new SSPI packages to appear between
  275. // reboots, so we cache all info.
  276. // =============================================================
  277. if (m_uNumPackages != 0 && m_pEnumPkgInfo)
  278. return m_uNumPackages;
  279. // Enumerate security packages.
  280. // ============================
  281. SECURITY_STATUS SecStatus =
  282. pVtbl->EnumerateSecurityPackages(&m_uNumPackages, &m_pEnumPkgInfo);
  283. if (SecStatus)
  284. {
  285. trace(("EnumerateSecurityPackages() failed. Error = %s\n", TranslateError(SecStatus)
  286. ));
  287. return 0;
  288. }
  289. return m_uNumPackages;
  290. }
  291. //***************************************************************************
  292. //
  293. // CSSPI::GetPkgInfo
  294. //
  295. // Retrieves a single read-only PSecPkgInfo pointer, describing the
  296. // requested package. This is to be used in conjunction with GetNumPkgs()
  297. // in order to iterate through the SSPI package list.
  298. //
  299. // Returns NULL on error or a read-only PSecPkgInfo pointer.
  300. //
  301. //***************************************************************************
  302. // ok
  303. const PSecPkgInfo CSSPI::GetPkgInfo(ULONG ulPkgNum)
  304. {
  305. if (ulPkgNum >= m_uNumPackages)
  306. return 0;
  307. if (m_pEnumPkgInfo == 0)
  308. return 0;
  309. return &m_pEnumPkgInfo[ulPkgNum];
  310. }
  311. //***************************************************************************
  312. //
  313. // CSSPI::DumpSecurityPackages
  314. //
  315. // Dumps all available security packages to the console.
  316. //
  317. //***************************************************************************
  318. // ok
  319. BOOL CSSPI::DumpSecurityPackages()
  320. {
  321. // Enumerate security packages.
  322. // ============================
  323. unsigned long lNum = 0;
  324. PSecPkgInfo pPkgInfo;
  325. lNum = GetNumPkgs();
  326. if (lNum == 0)
  327. return FALSE;
  328. trace(("SSPI Supports %d security packages\n", lNum));
  329. for (unsigned long i = 0; i < lNum; i++)
  330. {
  331. pPkgInfo = GetPkgInfo(i);
  332. if (pPkgInfo)
  333. DisplayPkgInfo(pPkgInfo);
  334. }
  335. return TRUE;
  336. }
  337. //***************************************************************************
  338. //
  339. // CSSPI::ServerSupport
  340. //
  341. // Determines whether the a server-side package can be expected to work.
  342. //
  343. // Parameters:
  344. // <pszPkgName> The authentication package. Usually "NTLM"
  345. //
  346. // Return value:
  347. // TRUE if the package will support a server-side authentication, FALSE
  348. // if not supported.
  349. //
  350. //***************************************************************************
  351. BOOL CSSPI::ServerSupport(LPTSTR pszPkgName)
  352. {
  353. if (pszPkgName == 0 || lstrlen(pszPkgName) == 0)
  354. return FALSE;
  355. if (Initialize() == FALSE)
  356. return FALSE;
  357. CSSPIServer Server(pszPkgName);
  358. if (Server.GetStatus() != CSSPIServer::Waiting)
  359. return FALSE;
  360. return TRUE;
  361. }
  362. //***************************************************************************
  363. //
  364. // CSSPI::ClientSupport
  365. //
  366. // Determines whether the a client-side package can be expected to work.
  367. //
  368. // Parameters:
  369. // <pszPkgName> The authentication package. Usually "NTLM"
  370. //
  371. // Return value:
  372. // TRUE if the package will support a client-side authentication, FALSE
  373. // if not supported.
  374. //
  375. //***************************************************************************
  376. BOOL CSSPI::ClientSupport(LPTSTR pszPkgName)
  377. {
  378. if (pszPkgName == 0 || lstrlen(pszPkgName) == 0)
  379. return FALSE;
  380. if (Initialize() == FALSE)
  381. return FALSE;
  382. CSSPIClient Client(pszPkgName);
  383. if (Client.GetStatus() != CSSPIClient::Waiting)
  384. return FALSE;
  385. return TRUE;
  386. }
  387. //***************************************************************************
  388. //
  389. // CSSPIClient constructor
  390. //
  391. // Parameters:
  392. // <pszPkgName> A valid SSPI package. Usually "NTLM".
  393. //
  394. // After construction has completed, GetStatus() should return
  395. // CSSPIClient::Waiting. CSSPIClient::InvalidPackage indicates that
  396. // the object will not function.
  397. //
  398. //***************************************************************************
  399. CSSPIClient::CSSPIClient(LPTSTR pszPkgName)
  400. {
  401. // Initialize variables.
  402. // =====================
  403. m_dwStatus = InvalidPackage;
  404. memset(&m_ClientCredential, 0, sizeof(CredHandle));
  405. m_pPkgInfo = 0;
  406. m_pszPkgName = 0;
  407. m_cbMaxToken = 0;
  408. m_bValidCredHandle = FALSE;
  409. memset(&m_ClientContext, 0, sizeof(CtxtHandle));
  410. m_bValidContextHandle = FALSE;
  411. // Copy package name.
  412. // ===================
  413. m_pszPkgName = Macro_CloneLPTSTR(pszPkgName);
  414. if (!m_pszPkgName)
  415. {
  416. trace(("CSSPIClient failed to construct (out of memory).\n"));
  417. m_dwStatus = InvalidPackage;
  418. return;
  419. }
  420. // Init the requested package.
  421. // ===========================
  422. SECURITY_STATUS SecStatus = 0;
  423. SecStatus = CSSPI::pVtbl->QuerySecurityPackageInfo(pszPkgName, &m_pPkgInfo);
  424. if (SecStatus != 0)
  425. {
  426. trace(("CSSPIClient fails to construct. Error = %s\n",
  427. CSSPI::TranslateError(SecStatus)
  428. ));
  429. m_dwStatus = InvalidPackage;
  430. return;
  431. }
  432. // If here, things are ok. We are waiting
  433. // for client to set login information.
  434. // ========================================
  435. m_cbMaxToken = m_pPkgInfo->cbMaxToken;
  436. m_dwStatus = Waiting;
  437. }
  438. //***************************************************************************
  439. //
  440. // CSSPIClient
  441. //
  442. // Destructor
  443. //
  444. //***************************************************************************
  445. CSSPIClient::~CSSPIClient()
  446. {
  447. if (m_pPkgInfo)
  448. CSSPI::pVtbl->FreeContextBuffer(m_pPkgInfo);
  449. if (m_pszPkgName)
  450. delete [] m_pszPkgName;
  451. if (m_bValidCredHandle)
  452. CSSPI::pVtbl->FreeCredentialHandle(&m_ClientCredential);
  453. if (m_bValidContextHandle)
  454. CSSPI::pVtbl->DeleteSecurityContext(&m_ClientContext);
  455. }
  456. //***************************************************************************
  457. //
  458. // CSSPIClient::SetLoginInfo
  459. //
  460. // Sets login info prior to beginning a login session.
  461. // pszUser, pszDomain, pszPassword must be either all NULL or all not
  462. // NULL. If NULLs are used, the current user's security context
  463. // is propagated.
  464. //
  465. // This must be the first call after constructing the CSSPIClient.
  466. // If this returns NoError, then ContinueLogin must be called next
  467. // before communicating with the server process.
  468. //
  469. // Parameters:
  470. // <pszUser> The user name
  471. // <pszDomain> The NTLM domain
  472. // <pszPassword> The cleartext password
  473. // <dwLoginFlags> Reserved
  474. //
  475. // Return value:
  476. // <NoError> if the caller can immediately proceed to ContinueLogin().
  477. // <InvalidParameter> if one or more parameters were not valid.
  478. // <InvalidPackage> if the SSPI package is not operational
  479. //
  480. // <InvalidUser> if the user/domain/password parameters are
  481. // of the wrong format.
  482. //
  483. // After successful completion, <NoError> is returned and
  484. // GetStatus() will return CSSPIClient::LoginContinue.
  485. //
  486. //***************************************************************************
  487. DWORD CSSPIClient::SetLoginInfo(
  488. IN LPTSTR pszUser,
  489. IN LPTSTR pszDomain,
  490. IN LPTSTR pszPassword,
  491. IN DWORD dwLoginFlags
  492. )
  493. {
  494. if (m_dwStatus != Waiting)
  495. return m_dwStatus = InvalidPackage;
  496. // If the user specifies any of one {user,password,domain}, they
  497. // all must be specified.
  498. // =============================================================
  499. if (pszUser || pszDomain || pszPassword)
  500. {
  501. if (!pszUser || !pszDomain || !pszPassword)
  502. return InvalidParameter;
  503. }
  504. // Acquire a credentials handle for subsequent calls.
  505. // ==================================================
  506. SEC_WINNT_AUTH_IDENTITY AdditionalCredentials;
  507. BOOL bSupplyCredentials = FALSE;
  508. TimeStamp Expiration;
  509. AdditionalCredentials.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
  510. // Due to the parameter validation, we know that if
  511. // a user was specified, the other parameters have been, too.
  512. // ==========================================================
  513. if (pszUser != 0)
  514. {
  515. bSupplyCredentials = TRUE;
  516. AdditionalCredentials.User = pszUser;
  517. AdditionalCredentials.UserLength = lstrlen(pszUser);
  518. AdditionalCredentials.Password = pszPassword;
  519. AdditionalCredentials.PasswordLength = lstrlen(pszPassword);
  520. AdditionalCredentials.Domain = pszDomain;
  521. AdditionalCredentials.DomainLength = lstrlen(pszDomain);
  522. }
  523. // Get client credentials handle.
  524. // ==============================
  525. SECURITY_STATUS SecStatus = CSSPI::pVtbl->AcquireCredentialsHandle(
  526. NULL,
  527. m_pszPkgName,
  528. SECPKG_CRED_OUTBOUND,
  529. NULL,
  530. bSupplyCredentials ? &AdditionalCredentials : 0,
  531. NULL,
  532. NULL,
  533. &m_ClientCredential,
  534. &Expiration
  535. );
  536. if (SecStatus)
  537. {
  538. trace(("AcquireCredentialsHandle() failed. Error=%s\n",
  539. CSSPI::TranslateError(SecStatus) ));
  540. return m_dwStatus = InvalidUser;
  541. }
  542. m_bValidCredHandle = TRUE;
  543. // Signal to caller that login must continue.
  544. // ==========================================
  545. m_dwStatus = LoginContinue;
  546. return NoError;
  547. }
  548. //***************************************************************************
  549. //
  550. // CSSPIClient::SetDefaultLogin
  551. //
  552. // Convenience method to login using 'current' credentials.
  553. //
  554. // Return values same as for SetLoginInfo().
  555. //
  556. //***************************************************************************
  557. DWORD CSSPIClient::SetDefaultLogin(DWORD dwFlags)
  558. {
  559. return SetLoginInfo(0, 0, 0, dwFlags);
  560. }
  561. //***************************************************************************
  562. //
  563. // CSSPIClient::ContinueLogin
  564. //
  565. // Called immediately after SetDefaultLogin() or SetLoginInfo() or
  566. // after prior calls to ContinueLogin after receiving binary tokens
  567. // from the server-side of the conversation.
  568. //
  569. // This is used for both to prepare the logon request and the
  570. // response to the challenge from the server.
  571. //
  572. // Parameters:
  573. // <pInToken> A token received from the server. If no
  574. // token has been received yet, then use NULL.
  575. // This is treated as read-only.
  576. //
  577. // <dwInTokenSize> The number of bytes in the token pointed
  578. // to by <dwInTokenSize>, or 0 if the above
  579. // token is NULL.
  580. //
  581. // <pToken> Receives a pointer to a memory buffer
  582. // containing the token to transfer to the
  583. // server. Deallocate with operator delete.
  584. //
  585. // <pdwTokenSize> Points to a DWORD to receive the size
  586. // of the above token.
  587. //
  588. // Return value:
  589. // <NoError> Returned when no more calls to ContinueLogin
  590. // are completed. <pToken> and <pdwTokenSize>
  591. // are still returned in this case.
  592. // This completes the 'response' portion
  593. // in the challenge-response sequence. The server
  594. // will return an 'access denied' status through
  595. // private means. Note that <NoError> does not
  596. // imply successful logon or that 'access denied' will
  597. // not occur. It merely completes the response
  598. // to the 'challenge'.
  599. //
  600. // After the login request phase (the first call to
  601. // this function) GetStatus() should return
  602. // CSSPIClient::LoginContinue, to indicate that
  603. // another call to this function will be required.
  604. // If GetStatus() CSSPIClient::LoginComplete is
  605. // returned, no more SSPI operations will occur.
  606. // Final success or denial by the server will
  607. // be privately indicated by the server.
  608. //
  609. // <Failed> Internal error. No out-parms are returned in this
  610. // case.
  611. //
  612. //***************************************************************************
  613. DWORD CSSPIClient::ContinueLogin(
  614. IN LPBYTE pInToken,
  615. IN DWORD dwInTokenSize,
  616. OUT LPBYTE *pToken,
  617. OUT DWORD *pdwTokenSize
  618. )
  619. {
  620. // If here, we are ready to build up a token.
  621. // ==========================================
  622. SecBufferDesc OutputBufferDescriptor;
  623. SecBuffer OutputSecurityToken;
  624. ULONG uContextRequirements = 0;
  625. ULONG uContextAttributes;
  626. TimeStamp Expiration;
  627. // Set up the output buffers and out params by
  628. // default. On errors, we simply null the out params.
  629. // ===================================================
  630. OutputBufferDescriptor.cBuffers = 1;
  631. OutputBufferDescriptor.pBuffers = &OutputSecurityToken;
  632. OutputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
  633. LPBYTE pTokenBuf = new BYTE[m_cbMaxToken];
  634. OutputSecurityToken.BufferType = SECBUFFER_TOKEN;
  635. OutputSecurityToken.cbBuffer = m_cbMaxToken;
  636. OutputSecurityToken.pvBuffer = pTokenBuf;
  637. // Build up the input buffer, if required.
  638. // =======================================
  639. SecBufferDesc InputBufferDescriptor;
  640. SecBuffer InputSecurityToken;
  641. if (pInToken)
  642. {
  643. InputBufferDescriptor.cBuffers = 1;
  644. InputBufferDescriptor.pBuffers = &InputSecurityToken;
  645. InputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
  646. InputSecurityToken.BufferType = SECBUFFER_TOKEN;
  647. InputSecurityToken.cbBuffer = dwInTokenSize;
  648. InputSecurityToken.pvBuffer = pInToken;
  649. }
  650. PSecBufferDesc pInBuf = pInToken ? &InputBufferDescriptor : 0;
  651. // Determine if this is first-time or continued call.
  652. // ==================================================
  653. PCtxtHandle pTmp = m_bValidContextHandle ? &m_ClientContext : 0;
  654. SECURITY_STATUS SecStatus =
  655. CSSPI::pVtbl->InitializeSecurityContext(
  656. &m_ClientCredential,
  657. pTmp, // Context handle pointer(
  658. NULL, // Target name (server)
  659. uContextRequirements, // Requirements
  660. 0, // Reserved
  661. SECURITY_NATIVE_DREP, // Target data representation
  662. pInBuf, // Input buffer for continued calls
  663. 0, // Reserved
  664. &m_ClientContext, // Receives client context handle
  665. &OutputBufferDescriptor,
  666. &uContextAttributes,
  667. &Expiration
  668. );
  669. // Determine the next step.
  670. // ========================
  671. if (SecStatus == SEC_E_OK)
  672. {
  673. *pToken = pTokenBuf;
  674. *pdwTokenSize = OutputSecurityToken.cbBuffer;
  675. m_dwStatus = LoginCompleted;
  676. return NoError;
  677. }
  678. if (SecStatus == SEC_I_CONTINUE_NEEDED)
  679. {
  680. // Set up the output parameters.
  681. // =============================
  682. *pToken = pTokenBuf;
  683. *pdwTokenSize = OutputSecurityToken.cbBuffer;
  684. m_bValidContextHandle = TRUE;
  685. m_dwStatus = LoginContinue;
  686. return NoError;
  687. }
  688. // If here, an error occurred.
  689. // ===========================
  690. trace(("CSSPIClient::ContinueLogin failed. Status code = %s",
  691. CSSPI::TranslateError(SecStatus)
  692. ));
  693. CSSPI::pVtbl->DeleteSecurityContext(pTmp);
  694. // Deallocate useless return buffer and NULL the out-parameters.
  695. // =============================================================
  696. delete [] pTokenBuf;
  697. *pToken = 0;
  698. *pdwTokenSize = 0;
  699. m_dwStatus = Failed;
  700. return Failed;
  701. }
  702. //***************************************************************************
  703. //
  704. // CSSPIServer constructor
  705. //
  706. // Parameters:
  707. // <pszPkgName> The SSPI package to use, usually "NTLM".
  708. //
  709. // After construction, CSSPIServer::GetStatus() should return
  710. // CSSPIServer::Waiting. Any other value indicates that the object
  711. // is invalid and cannot be used.
  712. //
  713. //***************************************************************************
  714. CSSPIServer::CSSPIServer(LPTSTR pszPkgName)
  715. {
  716. m_dwStatus = 0;
  717. m_cbMaxToken = 0;
  718. m_pPkgInfo = 0;
  719. m_bValidCredHandle = FALSE;
  720. m_bValidContextHandle = FALSE;
  721. memset(&m_ServerCredential, 0, sizeof(CredHandle));
  722. memset(&m_ServerContext, 0, sizeof(CtxtHandle));
  723. m_pszPkgName = Macro_CloneLPTSTR(pszPkgName);
  724. if (!m_pszPkgName)
  725. {
  726. trace(("CSSPIServer failed to construct (out of memory).\n"));
  727. m_dwStatus = InvalidPackage;
  728. return;
  729. }
  730. // Init the requested package.
  731. // ===========================
  732. SECURITY_STATUS SecStatus = 0;
  733. SecStatus = CSSPI::pVtbl->QuerySecurityPackageInfo(pszPkgName, &m_pPkgInfo);
  734. if (SecStatus != 0)
  735. {
  736. trace(("CSSPIServer failed to construct. Error = %s\n",
  737. CSSPI::TranslateError(SecStatus)
  738. ));
  739. m_dwStatus = InvalidPackage;
  740. return;
  741. }
  742. m_cbMaxToken = m_pPkgInfo->cbMaxToken;
  743. // Now acquire a default credentials handle for this machine.
  744. // ==========================================================
  745. TimeStamp Expiration;
  746. SecStatus = CSSPI::pVtbl->AcquireCredentialsHandle(
  747. NULL, // No principal
  748. m_pszPkgName, // Authentication package to use
  749. SECPKG_CRED_INBOUND, // We are a 'server'
  750. NULL, // No logon identifier
  751. NULL, // No pkg specific data
  752. NULL, // No GetKey function
  753. NULL, // No GetKey function arg
  754. &m_ServerCredential, // Server credential
  755. &Expiration // Expiration timestamp
  756. );
  757. if (SecStatus != 0)
  758. {
  759. trace(("CSSPIServer failed in AcquireCredentialsHandle(). Error = %s\n",
  760. CSSPI::TranslateError(SecStatus)
  761. ));
  762. m_dwStatus = Failed;
  763. return;
  764. }
  765. m_bValidCredHandle = TRUE;
  766. m_dwStatus = Waiting;
  767. }
  768. //***************************************************************************
  769. //
  770. // CSSPIServer destructor
  771. //
  772. //***************************************************************************
  773. CSSPIServer::~CSSPIServer()
  774. {
  775. if (m_pPkgInfo)
  776. CSSPI::pVtbl->FreeContextBuffer(m_pPkgInfo);
  777. delete [] m_pszPkgName;
  778. if (m_bValidCredHandle)
  779. CSSPI::pVtbl->FreeCredentialHandle(&m_ServerCredential);
  780. if (m_bValidContextHandle)
  781. CSSPI::pVtbl->DeleteSecurityContext(&m_ServerContext);
  782. }
  783. //***************************************************************************
  784. //
  785. // CSSPIServer::ContinueClientLogin
  786. //
  787. // Used to
  788. //
  789. // (1) Receive the client's logon request, and compute the challenge as
  790. // the out-parameter <pToken>
  791. //
  792. // (2) Verify the response to the challenge.
  793. //
  794. // Parameters:
  795. // <pInToken> A read-only pointer to the response (on the second call).
  796. // NULL on the initial call.
  797. //
  798. // <dwInTokenSize> The number of bytes pointed to by the above pointers.
  799. //
  800. // <pToken> On the first call, receives a pointer to the
  801. // challenge to send to the client.
  802. //
  803. // <pdwTokenSize> The size of the above challenge.
  804. //
  805. // Return values:
  806. //
  807. // <LoginContinue> Sent back on the first call to indicate that a
  808. // challenge has been computed and returned to the
  809. // client in <pToken>.
  810. //
  811. // <Failed> No out-params assigned. Indicates internal failure.
  812. //
  813. // <NoError> Only possible on the second call. Indicates the
  814. // client was authenticated.
  815. //
  816. // <AccessDenied> Returned on the second call if the user was denied
  817. // logon.
  818. //
  819. //***************************************************************************
  820. DWORD CSSPIServer::ContinueClientLogin(
  821. IN LPBYTE pInToken,
  822. IN DWORD dwInTokenSize,
  823. OUT LPBYTE *pToken,
  824. OUT DWORD *pdwTokenSize
  825. )
  826. {
  827. TimeStamp Expiration;
  828. SecBufferDesc InputBufferDescriptor;
  829. SecBuffer InputSecurityToken;
  830. SecBufferDesc OutputBufferDescriptor;
  831. SecBuffer OutputSecurityToken;
  832. ULONG uContextRequirements = 0;
  833. ULONG uContextAttributes;
  834. // Build up the input buffer.
  835. // ==========================
  836. InputBufferDescriptor.cBuffers = 1;
  837. InputBufferDescriptor.pBuffers = &InputSecurityToken;
  838. InputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
  839. InputSecurityToken.BufferType = SECBUFFER_TOKEN;
  840. InputSecurityToken.cbBuffer = dwInTokenSize;
  841. InputSecurityToken.pvBuffer = pInToken;
  842. // Build the output buffer descriptor.
  843. // ===================================
  844. OutputBufferDescriptor.cBuffers = 1;
  845. OutputBufferDescriptor.pBuffers = &OutputSecurityToken;
  846. OutputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
  847. LPBYTE pOutBuf = new BYTE[m_cbMaxToken];
  848. OutputSecurityToken.BufferType = SECBUFFER_TOKEN;
  849. OutputSecurityToken.cbBuffer = m_cbMaxToken;
  850. OutputSecurityToken.pvBuffer = pOutBuf;
  851. // Set up partial or final context handle.
  852. // =======================================
  853. PCtxtHandle pTmp = m_bValidContextHandle ? &m_ServerContext : 0;
  854. // Process the client's initial token and see what happens.
  855. // ========================================================
  856. SECURITY_STATUS SecStatus = 0;
  857. SecStatus = CSSPI::pVtbl->AcceptSecurityContext(
  858. &m_ServerCredential,
  859. pTmp, // No context handle yet
  860. &InputBufferDescriptor,
  861. uContextRequirements, // No context requirements
  862. SECURITY_NATIVE_DREP,
  863. &m_ServerContext,
  864. &OutputBufferDescriptor,
  865. &uContextAttributes,
  866. &Expiration
  867. );
  868. *pToken = pOutBuf;
  869. *pdwTokenSize = OutputSecurityToken.cbBuffer;
  870. if (SecStatus == SEC_I_CONTINUE_NEEDED)
  871. {
  872. m_dwStatus = LoginContinue;
  873. m_bValidContextHandle = TRUE;
  874. return LoginContinue;
  875. }
  876. if (SecStatus == SEC_E_OK)
  877. {
  878. m_bValidContextHandle = TRUE;
  879. delete [] pOutBuf;
  880. *pToken = 0;
  881. *pdwTokenSize = 0;
  882. m_dwStatus = LoginCompleted;
  883. return NoError;
  884. }
  885. // If here, an error occurred.
  886. // ===========================
  887. trace(("CSSPIClient::ContinueLogin failed. Status code = %s",
  888. CSSPI::TranslateError(SecStatus)
  889. ));
  890. *pToken = 0;
  891. *pdwTokenSize = 0;
  892. delete [] pOutBuf;
  893. if (SecStatus == SEC_E_LOGON_DENIED)
  894. {
  895. m_dwStatus = AccessDenied;
  896. return AccessDenied;
  897. }
  898. return Failed;
  899. }
  900. //***************************************************************************
  901. //
  902. // CSSPIServer::QueryUserInfo
  903. //
  904. // Gets information about a client after authentication.
  905. //
  906. // Parameters:
  907. // <pszUser> Receives a pointer to the user name if TRUE is
  908. // returned. Use operator delete to deallocate.
  909. //
  910. // Return value:
  911. // TRUE if the user name was returned, FALSE if not.
  912. //
  913. //***************************************************************************
  914. BOOL CSSPIServer::QueryUserInfo(
  915. OUT LPTSTR *pszUser // Use operator delete
  916. )
  917. {
  918. SecPkgContext_Names Names;
  919. memset(&Names, 0, sizeof(SecPkgContext_Names));
  920. *pszUser = 0;
  921. SECURITY_STATUS SecStatus = CSSPI::pVtbl->QueryContextAttributes(
  922. &m_ServerContext,
  923. SECPKG_ATTR_NAMES,
  924. &Names
  925. );
  926. if (SecStatus != 0)
  927. {
  928. trace(("QueryContextAttributes() for Name fails with %s ",
  929. CSSPI::TranslateError(SecStatus)
  930. ));
  931. return FALSE;
  932. }
  933. if (pszUser)
  934. *pszUser = Macro_CloneLPTSTR(Names.sUserName);
  935. CSSPI::pVtbl->FreeContextBuffer(LPVOID(Names.sUserName));
  936. return TRUE;
  937. }
  938. //***************************************************************************
  939. //
  940. // CSSPIServer::IssueLoginToken
  941. //
  942. // Issues a login token for the client to use in subsequent access.
  943. // This will only succeed if the client successfully computed the
  944. // reponse to the challenge and <NoError> was returned from
  945. // ContinueClientLogin.
  946. //
  947. // Parameters:
  948. // <ClsId> Receives the CLSID which becomes the WBEM access token.
  949. //
  950. // Return value:
  951. // NoError, Failed
  952. //
  953. //***************************************************************************
  954. DWORD CSSPIServer::IssueLoginToken(
  955. OUT CLSID &ClsId
  956. )
  957. {
  958. if (m_dwStatus == LoginCompleted)
  959. {
  960. if (SUCCEEDED(CoCreateGuid(&ClsId)))
  961. return NoError;
  962. else
  963. return Failed;
  964. }
  965. memset(&ClsId, 0, sizeof(CLSID));
  966. return Failed;
  967. }