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.

1264 lines
40 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. sspi.c
  5. Abstract:
  6. This file contains the implementation for SSPI Authentication
  7. The following functions are exported by this module:
  8. UnloadAuthenticateUser
  9. AuthenticateUser
  10. PreAuthenticateUser
  11. AuthenticateUserUI
  12. Author:
  13. Sudheer Koneru (SudK) Created 2/17/96
  14. Revision History:
  15. --*/
  16. #include "msnspmh.h"
  17. #ifdef DEBUG_WINSSPI
  18. #include <stdio.h>
  19. #endif
  20. #include "auth.h"
  21. LPSTR StrChrA(LPCSTR lpStart, WORD wMatch); // from shlwapi.h
  22. DWORD g_cSspiContexts;
  23. #ifdef UNIX_SHMEM_CREDS
  24. /* On Unix: once a user puts in his credentials, we want to save
  25. * it in Shared memory so that other processes can use this data.
  26. * The login/password/domain are saved encrypted and you need the
  27. * routines in libntlmssp.so to unencrypt them.
  28. * When the process exits, the shared memory is cleaned up. If the
  29. * credentials are outdated during a session, we ask the user for
  30. * new credentials and try again.
  31. */
  32. static BOOL g_fNeedNewCreds = FALSE;
  33. static FARPROC g_UXPCEFn = NULL;
  34. #endif
  35. #define NAME_SEPERATOR 0x5c // this is a backslash character which
  36. // seperates the domain name from user name
  37. VOID
  38. WINAPI
  39. UnloadAuthenticateUser(LPVOID *lppvContext,
  40. LPSTR lpszScheme,
  41. LPSTR lpszHost)
  42. {
  43. PWINCONTEXT pWinContext = (PWINCONTEXT) (*lppvContext);
  44. if (!SSPI_InitGlobals())
  45. return;
  46. if (*lppvContext == NULL) {
  47. return;
  48. }
  49. if (pWinContext->pInBuffer != NULL &&
  50. pWinContext->pInBuffer != pWinContext->szInBuffer)
  51. {
  52. LocalFree (pWinContext->pInBuffer);
  53. }
  54. pWinContext->pInBuffer = NULL;
  55. pWinContext->dwInBufferLength = 0;
  56. // Free SSPI security context
  57. //
  58. if (pWinContext->pSspContextHandle != NULL)
  59. (*(g_pSspData->pFuncTbl->DeleteSecurityContext))(pWinContext->pSspContextHandle);
  60. // Free SSPI credential handle
  61. //
  62. if (pWinContext->pCredential)
  63. (*(g_pSspData->pFuncTbl->FreeCredentialHandle))(pWinContext->pCredential);
  64. pWinContext->pCredential = NULL;
  65. pWinContext->pSspContextHandle = NULL;
  66. if ( (pWinContext->lpszServerName != NULL) &&
  67. (pWinContext->lpszServerName != pWinContext->szServerName) )
  68. {
  69. LocalFree(pWinContext->lpszServerName);
  70. }
  71. LocalFree(pWinContext);
  72. *lppvContext = NULL;
  73. g_cSspiContexts--;
  74. return;
  75. }
  76. //+---------------------------------------------------------------------------
  77. //
  78. // Function: SaveServerName
  79. //
  80. // Synopsis: This function saves the destination server name in this
  81. // connection context for AuthenticateUserUI
  82. //
  83. // Arguments: [lpszServerName] - points to the target server name
  84. // [pWinContext] - points to the connection context
  85. //
  86. // Returns: TRUE if server name is successfully saved in connection context.
  87. // Otherwise, FALSE is returned.
  88. //
  89. //----------------------------------------------------------------------------
  90. BOOL
  91. SaveServerName (
  92. LPSTR lpszServerName,
  93. PWINCONTEXT pWinContext
  94. )
  95. {
  96. DWORD dwLen = lstrlen(lpszServerName);
  97. if (dwLen < DEFAULT_SERVER_NAME_LEN)
  98. {
  99. lstrcpy(pWinContext->szServerName, lpszServerName);
  100. pWinContext->lpszServerName = pWinContext->szServerName;
  101. }
  102. else
  103. { //
  104. // Server name is longer, need to allocate memory for the name
  105. //
  106. // Free already allocated memory if any
  107. if (pWinContext->lpszServerName &&
  108. pWinContext->lpszServerName != pWinContext->szServerName)
  109. {
  110. LocalFree (pWinContext->lpszServerName);
  111. }
  112. pWinContext->lpszServerName = (char *) LocalAlloc(0, dwLen+1);
  113. if (pWinContext->lpszServerName == NULL)
  114. return FALSE;
  115. lstrcpy(pWinContext->lpszServerName, lpszServerName);
  116. }
  117. return TRUE;
  118. }
  119. // Function bHasExtendedChars
  120. // Check if an ANSI string contains extended characters
  121. BOOL bHasExtendedChars(char const *str)
  122. {
  123. signed char const *p;
  124. for (p = (signed char const *)str; *p; p++)
  125. if ( *p < 0)
  126. return TRUE;
  127. return FALSE;
  128. }
  129. //+---------------------------------------------------------------------------
  130. //
  131. // Function: BuildNTLMauthData
  132. //
  133. // Synopsis: This function builds SEC_WINNT_AUTH_IDENTITY structure
  134. // from the user name and password specified. If domain name
  135. // is not specified in the user name, the Domain field in
  136. // the structure is set to NULL. NOTE: This structure is
  137. // specific to the NTLM SSPI package.
  138. // This function allocates a chunck of memory big enough for
  139. // storing user name, domain, and password. Then setup
  140. // pointers in pAuthData to use sections of this memory.
  141. //
  142. // Arguments: [pAuthData] - points to the SEC_WINNT_AUTH_IDENTITY structure
  143. // [lpszUserName] - points to the user name, which may also
  144. // include user's domain name.
  145. // [lpszPassword] - points to user's password
  146. //
  147. // Returns: TRUE if SEC_WINNT_AUTH_IDENTITY structure is successfully
  148. // initialized and built. Otherwise, FALSE is returned.
  149. //
  150. //----------------------------------------------------------------------------
  151. BOOL
  152. BuildNTLMauthData (
  153. PSEC_WINNT_AUTH_IDENTITY pAuthData,
  154. LPTSTR lpszUserName,
  155. LPTSTR lpszPassword
  156. )
  157. {
  158. DWORD dwUserLen, dwDomainLen, dwPwdLen;
  159. LPTSTR pName;
  160. LPTSTR pDomain = NULL;
  161. BOOL bUnicodeAuth = FALSE;
  162. // SEC_WINNT_AUTH_IDENTITY_UNICODE is supported on Windows NT/2000
  163. if ( GetVersion() < 0x80000000 && (bHasExtendedChars(lpszUserName) || bHasExtendedChars(lpszPassword)))
  164. bUnicodeAuth = TRUE;
  165. pAuthData->Flags = bUnicodeAuth ? SEC_WINNT_AUTH_IDENTITY_UNICODE : SEC_WINNT_AUTH_IDENTITY_ANSI;
  166. //
  167. // Check to see if domain name is specified in lpszUserName
  168. //
  169. pName = StrChrA (lpszUserName, NAME_SEPERATOR);
  170. if (pName) // Domain name specified
  171. {
  172. // Make sure that we don't change the original string in lpszUserName
  173. // because that it would be reused for other connections
  174. // Calculate no. of bytes in domain name
  175. dwDomainLen = (int)(pName - lpszUserName);
  176. // Convert to no. of characters
  177. pAuthData->DomainLength = dwDomainLen / sizeof(TCHAR);
  178. pDomain = lpszUserName;
  179. pName++;
  180. }
  181. else // No domain specified
  182. {
  183. pName = lpszUserName;
  184. pAuthData->Domain = NULL;
  185. pDomain = NULL;
  186. dwDomainLen = pAuthData->DomainLength = 0;
  187. }
  188. dwUserLen = pAuthData->UserLength = lstrlen (pName);
  189. dwPwdLen = pAuthData->PasswordLength = lstrlen (lpszPassword);
  190. //
  191. // Allocate memory for all: name, domain, and password
  192. // The memory block is big enough for Unicode. Some bytes will be wasted in the ANSI case
  193. //
  194. pAuthData->User = (LPTSTR) LocalAlloc(LMEM_ZEROINIT, (dwUserLen + dwDomainLen + dwPwdLen + 3)*sizeof(wchar_t));
  195. if (pAuthData->User == NULL)
  196. return (FALSE);
  197. if (bUnicodeAuth)
  198. {
  199. // Convert the user name into Unicode and store in pAuthData->User
  200. if (0 == MultiByteToWideChar(CP_ACP, 0, pName, -1, (LPWSTR)(pAuthData->User), dwUserLen+1))
  201. return FALSE;
  202. }
  203. else
  204. CopyMemory (pAuthData->User, pName, dwUserLen);
  205. // Setup memory pointer for password
  206. //
  207. pAuthData->Password = pAuthData->User + (dwUserLen + 1) * sizeof(wchar_t);
  208. if (bUnicodeAuth)
  209. {
  210. if (0 == MultiByteToWideChar(CP_ACP, 0, lpszPassword, -1, (LPWSTR)(pAuthData->Password), dwPwdLen+1))
  211. return FALSE;
  212. }
  213. else
  214. CopyMemory (pAuthData->Password, lpszPassword, dwPwdLen);
  215. if (pAuthData->DomainLength > 0)
  216. {
  217. // Setup memory pointer for domain
  218. //
  219. pAuthData->Domain = pAuthData->Password + (dwPwdLen + 1) * sizeof(wchar_t);
  220. if (bUnicodeAuth)
  221. {
  222. // pDomain is not null terminated, so provide the length
  223. if (0 == MultiByteToWideChar(CP_ACP, 0, pDomain, dwDomainLen, (LPWSTR)(pAuthData->Domain), dwDomainLen))
  224. return FALSE;
  225. }
  226. else
  227. CopyMemory (pAuthData->Domain, pDomain, dwDomainLen);
  228. // Need not to zero terminate pAuthData->Domain, since the memory contents were initialized to zero.
  229. }
  230. else
  231. {
  232. pAuthData->Domain = NULL;
  233. }
  234. return (TRUE);
  235. }
  236. //+---------------------------------------------------------------------------
  237. //
  238. // Function: FreeNTLMauthData
  239. //
  240. // Synopsis: This function frees memory allocated for the
  241. // SEC_WINNT_AUTH_IDENTITY structure
  242. //
  243. // Arguments: [pAuthData] - points to the SEC_WINNT_AUTH_IDENTITY structure
  244. //
  245. // Returns: void.
  246. //
  247. //----------------------------------------------------------------------------
  248. VOID
  249. FreeNTLMauthData (
  250. PSEC_WINNT_AUTH_IDENTITY pAuthData
  251. )
  252. {
  253. //
  254. // Free User which points to memory for all domain, name, and password
  255. //
  256. if (pAuthData->User)
  257. LocalFree (pAuthData->User);
  258. }
  259. //+---------------------------------------------------------------------------
  260. //
  261. // Function: NewWinContext
  262. //
  263. // Synopsis: This function creates a new context and a new credential
  264. // handle for this connection. If a user name/password is
  265. // specified, the credential handle is created for the
  266. // specified user. Otherwise, the credential handle is created
  267. // for the local logon user.
  268. //
  269. // Arguments: [pkgId] - the package ID (index into SSPI package list)
  270. // [lpszScheme] - the name of the current authentication scheme,
  271. // which is also the SSPI package name
  272. // [ppCtxt] - this returns the pointer of the created context
  273. // to the caller.
  274. // [lpszUserName] - the name of a specific user to be used
  275. // for authentication. If this is NULL, the
  276. // credential of the currently logon user is
  277. // used for authentication.
  278. // [lpszPassword] - the password of the specified user, if any.
  279. //
  280. // Returns: ERROR_SUCCESS - if the new context is created successfully
  281. // ERROR_NOT_ENOUGH_MEMORY - if memory allocation failed
  282. // ERROR_INVALID_PARAMETER - the SSPI call for creating the
  283. // security credential handle failed
  284. //
  285. //----------------------------------------------------------------------------
  286. DWORD
  287. NewWinContext (
  288. INT pkgId,
  289. LPSTR lpszScheme,
  290. PWINCONTEXT *ppCtxt,
  291. BOOL fCanUseLogon,
  292. LPSTR lpszUserName,
  293. LPSTR lpszPassword
  294. )
  295. {
  296. SECURITY_STATUS ss;
  297. TimeStamp Lifetime;
  298. PWINCONTEXT pWinContext;
  299. SEC_WINNT_AUTH_IDENTITY AuthData;
  300. PSEC_WINNT_AUTH_IDENTITY pAuthData;
  301. DWORD Capabilities ;
  302. DWORD SecurityBlobSize;
  303. //
  304. // need space for maxtoken size for in+out, + base64 encoding overhead for each.
  305. // really 1.34 overhead, but just round up to 1.5
  306. //
  307. SecurityBlobSize = GetPkgMaxToken(pkgId);
  308. SecurityBlobSize += (SecurityBlobSize/2);
  309. //
  310. // note: for compatibility sake, make the buffer size the MAX_BLOB_SIZE at the minimum
  311. // consider removing this once we're convinced all packages return good cbMaxToken values.
  312. //
  313. if( SecurityBlobSize < MAX_BLOB_SIZE )
  314. {
  315. SecurityBlobSize = MAX_BLOB_SIZE;
  316. }
  317. pWinContext = (PWINCONTEXT) LocalAlloc(
  318. 0,
  319. sizeof(WINCONTEXT) +
  320. (SecurityBlobSize*2)
  321. );
  322. if (pWinContext == NULL)
  323. return (ERROR_NOT_ENOUGH_MEMORY);
  324. // Initialize context
  325. //
  326. ZeroMemory( pWinContext, sizeof(WINCONTEXT) );
  327. pWinContext->pkgId = (DWORD)pkgId;
  328. pWinContext->szOutBuffer = (char*)(pWinContext+1);
  329. pWinContext->cbOutBuffer = SecurityBlobSize;
  330. pWinContext->szInBuffer = pWinContext->szOutBuffer + pWinContext->cbOutBuffer;
  331. pWinContext->cbInBuffer = SecurityBlobSize;
  332. //
  333. // Get bitmask representing the package capabilities
  334. //
  335. Capabilities = GetPkgCapabilities( pkgId );
  336. if ( ( Capabilities & SSPAUTHPKG_SUPPORT_NTLM_CREDS ) == 0 )
  337. {
  338. // Always used cached credential for msn, dpa, etc.
  339. pAuthData = NULL;
  340. }
  341. else if (lpszUserName && lpszPassword)
  342. {
  343. // App Compat fix -- always use the app specified creds when available
  344. if ((lpszUserName[0] == '\0') && (lpszPassword[0] == '\0'))
  345. {
  346. if(fCanUseLogon)
  347. {
  348. pAuthData = NULL;
  349. }
  350. else
  351. {
  352. return ERROR_INTERNET_INCORRECT_PASSWORD;
  353. }
  354. }
  355. else
  356. {
  357. // Build AuthData from the specified user name/password
  358. if (!BuildNTLMauthData (&AuthData, lpszUserName, lpszPassword))
  359. return (ERROR_NOT_ENOUGH_MEMORY);
  360. pAuthData = &AuthData;
  361. }
  362. }
  363. #ifdef UNIX_SHMEM_CREDS
  364. else if (fCanUseLogon && UnixCachedCredentialExists())
  365. {
  366. pAuthData = NULL;
  367. }
  368. #else
  369. else if (fCanUseLogon)
  370. {
  371. // The zone policy allows silent use of the logon credential.
  372. pAuthData = NULL;
  373. }
  374. #endif /* UNIX_SHMEM_CREDS */
  375. else
  376. {
  377. // We must prompt the user for credentials.
  378. return ERROR_INTERNET_INCORRECT_PASSWORD;
  379. }
  380. //
  381. // Call SSPI function acquire security credential for this package
  382. //
  383. ss = (*(g_pSspData->pFuncTbl->AcquireCredentialsHandle))(
  384. NULL, // New principal
  385. lpszScheme, // SSPI Package Name
  386. SECPKG_CRED_OUTBOUND,// Credential Use
  387. NULL, // Logon ID
  388. pAuthData, // Auth Data
  389. NULL, // Get key func
  390. NULL, // Get key arg
  391. &pWinContext->Credential, // Credential Handle
  392. &Lifetime );
  393. if (pAuthData)
  394. FreeNTLMauthData (pAuthData);
  395. if (ss != STATUS_SUCCESS)
  396. {
  397. LocalFree (pWinContext);
  398. #ifdef UNIX_SHMEM_CREDS //If NTLM failed due to bad shared mem creds, prompt the user again...
  399. if (lstrcmpi(lpszScheme, "NTLM") == 0)
  400. {
  401. g_fNeedNewCreds = TRUE;
  402. return ERROR_INTERNET_INCORRECT_PASSWORD;
  403. }
  404. else
  405. #endif
  406. return (ERROR_INVALID_PARAMETER);
  407. }
  408. #ifdef UNIX_SHMEM_CREDS // if NTLM, the credentials were valid...
  409. if (lstrcmpi(lpszScheme, "NTLM") == 0)
  410. g_fNeedNewCreds = FALSE;
  411. #endif
  412. pWinContext->pCredential = &pWinContext->Credential;
  413. *ppCtxt = pWinContext;
  414. g_cSspiContexts++;
  415. return (ERROR_SUCCESS);
  416. }
  417. #ifdef UNIX_SHMEM_CREDS
  418. BOOL
  419. UnixCachedCredentialExists(
  420. )
  421. {
  422. BOOL fCached;
  423. if (g_fNeedNewCreds)
  424. return FALSE;
  425. if (!g_UXPCEFn)
  426. g_UXPCEFn = GetProcAddress(GetModuleHandle("ntlmssp"), "UnixXProcCredExists");
  427. fCached = g_UXPCEFn();
  428. return fCached;
  429. }
  430. #endif /* UNIX_SHMEM_CREDS */
  431. //+---------------------------------------------------------------------------
  432. //
  433. // Function: RedoNTLMAuth4User
  434. //
  435. // Synopsis: This function recreates a NTLM credential handle for the
  436. // specified user and generate a NEGOTIATE message in
  437. // the provided buffer with the new credential handle.
  438. //
  439. // Arguments: [pWinContext] - points to the connection context
  440. // [pkgId] - specifies the SSPI pkg to be used for authentication
  441. // [lpszUserName] - the name of the specific user to be used
  442. // for authentication.
  443. // [lpszPassword] - the password of the specified user,
  444. // [lpszServerName] - the target server name
  445. // [lpszScheme] - the name of the current authentication scheme,
  446. // which is also the SSPI package name
  447. // [lpOutBuffer] - points to the buffer for the new authorization
  448. // header including the UUENCODED NEGOTIATE msg
  449. // [lpdwOutBufferLength] - returns the length of the generated
  450. // authorization header.
  451. //
  452. // Returns: ERROR_SUCCESS - if the new authorization header is successfully
  453. // created for the new user name/password
  454. // ERROR_NOT_ENOUGH_MEMORY - if memory allocation failed
  455. // ERROR_INVALID_HANDLE - the SSPI call for generating the
  456. // new NEGOTIATE msg failed
  457. //
  458. //----------------------------------------------------------------------------
  459. DWORD
  460. RedoNTLMAuth4User (
  461. PWINCONTEXT pWinContext,
  462. INT pkgId,
  463. LPSTR lpszUserName,
  464. LPSTR lpszPassword,
  465. LPSTR lpszServerName,
  466. LPSTR lpszScheme,
  467. LPSTR lpOutBuffer,
  468. LPDWORD lpdwOutBufferLength,
  469. PCSTR lpszUrl,
  470. SECURITY_STATUS *pssResult
  471. )
  472. {
  473. SECURITY_STATUS ss;
  474. DWORD dwStatus;
  475. TimeStamp Lifetime;
  476. SEC_WINNT_AUTH_IDENTITY AuthData;
  477. PSEC_WINNT_AUTH_IDENTITY pAuthData = NULL;
  478. ULONG fContextReq = ISC_REQ_DELEGATE;
  479. DWORD dwMaxLen;
  480. //BOOL fCanUseCredMgr = FALSE;
  481. if (pWinContext->pSspContextHandle)
  482. {
  483. (*(g_pSspData->pFuncTbl->DeleteSecurityContext))(pWinContext->pSspContextHandle);
  484. pWinContext->pSspContextHandle = NULL;
  485. }
  486. // Free existing credential handle
  487. //
  488. if (pWinContext->pCredential)
  489. {
  490. (*(g_pSspData->pFuncTbl->FreeCredentialHandle))(pWinContext->pCredential);
  491. pWinContext->pCredential = NULL;
  492. }
  493. // App Compat fix -- always use the app specified creds when available
  494. if ((lpszUserName[0] == '\0') && (lpszPassword[0] == '\0'))
  495. {
  496. pAuthData = NULL;
  497. }
  498. else
  499. {
  500. //
  501. // Build the NTLM SSPI AuthData from the specified user name/password
  502. //
  503. if (!BuildNTLMauthData (&AuthData, lpszUserName, lpszPassword))
  504. return (ERROR_NOT_ENOUGH_MEMORY);
  505. pAuthData = &AuthData;
  506. }
  507. //
  508. // Call SSPI function acquire security credential for this user
  509. //
  510. ss = (*(g_pSspData->pFuncTbl->AcquireCredentialsHandle))(
  511. NULL, // New principal
  512. lpszScheme, // SSPI Package Name
  513. SECPKG_CRED_OUTBOUND,// Credential Use
  514. NULL, // Logon ID
  515. pAuthData, // Auth Data
  516. NULL, // Get key func
  517. NULL, // Get key arg
  518. &pWinContext->Credential, // Credential Handle
  519. &Lifetime );
  520. // if (!fCanUseCredMgr)
  521. if (pAuthData)
  522. {
  523. FreeNTLMauthData (&AuthData); // don't need it any more
  524. }
  525. if (ss != STATUS_SUCCESS)
  526. {
  527. return (ERROR_INVALID_HANDLE);
  528. }
  529. pWinContext->pCredential = &pWinContext->Credential;
  530. dwMaxLen = *lpdwOutBufferLength;
  531. //
  532. // Generate NEGOTIATE message in the provided buffer for this user
  533. //
  534. dwStatus = GetSecAuthMsg( g_pSspData,
  535. pWinContext->pCredential,
  536. pkgId,
  537. NULL,
  538. &(pWinContext->SspContextHandle),
  539. fContextReq,
  540. NULL,
  541. 0,
  542. lpOutBuffer,
  543. lpdwOutBufferLength,
  544. lpszServerName,
  545. TRUE,
  546. lpszScheme,
  547. lpszUrl,
  548. pssResult);
  549. if (dwStatus != SPM_STATUS_OK)
  550. {
  551. *lpdwOutBufferLength = 0; // no exchange blob generated
  552. return(ERROR_INVALID_HANDLE);
  553. }
  554. pWinContext->pSspContextHandle = &(pWinContext->SspContextHandle);
  555. //
  556. // If we are not in the initial state, continue to a RESPONSE message
  557. //
  558. if (pWinContext->pInBuffer != NULL && pWinContext->dwInBufferLength > 0)
  559. {
  560. *lpdwOutBufferLength = dwMaxLen;
  561. ZeroMemory( lpOutBuffer, dwMaxLen );
  562. dwStatus = GetSecAuthMsg( g_pSspData,
  563. pWinContext->pCredential,
  564. pWinContext->pkgId,
  565. pWinContext->pSspContextHandle,
  566. (PCtxtHandle) &(pWinContext->SspContextHandle),
  567. fContextReq,
  568. pWinContext->pInBuffer,
  569. pWinContext->dwInBufferLength,
  570. lpOutBuffer,
  571. lpdwOutBufferLength,
  572. pWinContext->lpszServerName,
  573. TRUE,
  574. lpszScheme,
  575. lpszUrl,
  576. pssResult);
  577. // Clear out the input exchange blob
  578. //
  579. if (pWinContext->pInBuffer != NULL)
  580. {
  581. if (pWinContext->pInBuffer != pWinContext->szInBuffer)
  582. LocalFree (pWinContext->pInBuffer);
  583. pWinContext->pInBuffer = NULL;
  584. pWinContext->dwInBufferLength = 0;
  585. }
  586. if (dwStatus != SPM_STATUS_OK)
  587. {
  588. *lpdwOutBufferLength = 0; // no exchange blob generated
  589. return(ERROR_INVALID_HANDLE);
  590. }
  591. }
  592. return (ERROR_SUCCESS);
  593. }
  594. //
  595. // functions
  596. //
  597. /*++
  598. Routine Description:
  599. Generates a Basic User Authentication string for WinINet or
  600. other callers can use
  601. Arguments:
  602. lpContext - if the package accepts the request & authentication
  603. requires multiple transactions, the package will supply
  604. a context value which will be used in subsequent calls,
  605. Currently this contains a pointer to a pointer of a
  606. User defined Void Pointer. Can be Assume to be NULL
  607. if this is the first instance of a Realm - Host Combo
  608. lpszServerName - the name of the server we are performing
  609. authentication for. We may want to supply the full URL
  610. lpszScheme - the name of the authentication scheme we are seeking, e.g. "MSN", in case the package supports multiple schemes
  611. dwFlags - on input, flags modifying how the package should behave,
  612. e.g. "only authenticate if you don't have to get user
  613. information" On output contains flags relevant to
  614. future HTTP requests, e.g. "don't cache any data from
  615. this connection". Note, this information should not be
  616. specific to HTTP - we may want to use the same flags
  617. for FTP, etc.
  618. lpszInBuffer - pointer to the string containing the response from
  619. the server (if any)
  620. dwInBufferLength - number of bytes in lpszInBuffer. No CR-LF sequence, no terminating NUL
  621. lpOutBuffer - pointer to a buffer where the challenge response will be written by the
  622. package if it can handle the request
  623. lpdwOutBufferLength - on input, contains the size of lpOutBuffer. On output, contains the
  624. number of bytes to return to the server in the next GET request
  625. (or whatever). If lpOutBuffer is too small, the package should
  626. return ERROR_INSUFFICIENT_BUFFER and set *lpdwOutBufferLength to be
  627. the required length
  628. We will keep a list of the authentication packages and the schemes they support,
  629. along with the entry point name (should be the same for all packages) in the registry.
  630. Wininet should keep enough information such that it can make a reasonable guess as to
  631. whether we need to authenticate a connection attempt, or whether we can use previously
  632. authenticated information
  633. Return Value:
  634. DWORD
  635. Success - non-zero
  636. Failure - 0. Error status is available by calling GetLastError()
  637. --*/
  638. DWORD
  639. WINAPI
  640. AuthenticateUser(
  641. IN OUT LPVOID *lppvContext,
  642. IN LPSTR lpszServerName,
  643. IN LPSTR lpszScheme,
  644. IN BOOL fCanUseLogon,
  645. IN LPSTR lpszInBuffer,
  646. IN DWORD dwInBufferLength,
  647. IN LPSTR lpszUserName,
  648. IN LPSTR lpszPassword,
  649. IN PCSTR lpszUrl,
  650. OUT SECURITY_STATUS *pssResult
  651. )
  652. {
  653. PWINCONTEXT pWinContext;
  654. LPSTR pServerBlob = NULL;
  655. int pkgId;
  656. DWORD SPMStatus;
  657. ULONG fContextReq = ISC_REQ_DELEGATE;
  658. BOOL bNonBlock = TRUE;
  659. if (!SSPI_InitGlobals())
  660. return ERROR_INVALID_PARAMETER;
  661. pkgId = GetPkgId(lpszScheme);
  662. if (pkgId == -1)
  663. return (ERROR_INVALID_PARAMETER);
  664. if (*lppvContext == NULL) // a new connection
  665. {
  666. char msg[1024];
  667. DWORD dwStatus;
  668. //
  669. // First time we are getting called here, there should be no input blob
  670. //
  671. if (dwInBufferLength != 0)
  672. return (ERROR_INVALID_PARAMETER);
  673. dwStatus = NewWinContext (pkgId, lpszScheme, &pWinContext,
  674. fCanUseLogon, lpszUserName, lpszPassword);
  675. if (dwStatus != ERROR_SUCCESS)
  676. return (dwStatus);
  677. (*lppvContext) = (LPVOID) pWinContext;
  678. #ifdef DEBUG_WINSSPI
  679. (void)wsprintf (msg, "AuthenticateUser> Scheme= %s Server= '%s'\n",
  680. lpszScheme, lpszServerName);
  681. OutputDebugString(msg);
  682. #endif
  683. }
  684. else
  685. {
  686. pWinContext = (PWINCONTEXT) (*lppvContext);
  687. //
  688. // The package Id better be the same. Cant just switch packageId
  689. // arbitrarily
  690. //
  691. if (pWinContext->pkgId != (DWORD)pkgId)
  692. return (ERROR_INVALID_PARAMETER);
  693. pServerBlob = lpszInBuffer;
  694. //++(pWinContext->dwCallId); // Increment Call Id
  695. //
  696. // BUGBUG: Hack for now to know when auth failed
  697. // The only time we get lpszInBuffer to be empty is when
  698. // Web server failed the authentication request
  699. //
  700. if (dwInBufferLength == 0)
  701. {
  702. //
  703. // This means auth has failed as far as NTLM/MSN are concerned.
  704. // Will result in UI being done again for new passwd
  705. //
  706. // Make sure we should have the same server name as before
  707. //
  708. if ( pWinContext->lpszServerName != NULL &&
  709. lstrcmp (pWinContext->lpszServerName, lpszServerName) != 0 )
  710. {
  711. return(ERROR_INVALID_PARAMETER);
  712. }
  713. if (!SaveServerName (lpszServerName, pWinContext))
  714. return (ERROR_NOT_ENOUGH_MEMORY);
  715. //
  716. // Delete the original SSPI context handle and
  717. // let UI recreate one.
  718. //
  719. if (pWinContext->pSspContextHandle)
  720. {
  721. (*(g_pSspData->pFuncTbl->DeleteSecurityContext))(pWinContext->pSspContextHandle);
  722. pWinContext->pSspContextHandle = NULL;
  723. }
  724. if (pWinContext->pInBuffer != NULL &&
  725. pWinContext->pInBuffer != pWinContext->szInBuffer)
  726. {
  727. LocalFree (pWinContext->pInBuffer);
  728. }
  729. pWinContext->pInBuffer = NULL;
  730. pWinContext->dwInBufferLength = 0;
  731. //
  732. // clear buffer length for the exchange blob
  733. //
  734. pWinContext->dwOutBufferLength = 0;
  735. //
  736. // The following is a temporary workaround for bugs in IE3
  737. // Should remove this special case for DPA package once IE3
  738. // bug is fixed (or once we move to new Wininet interface.
  739. //
  740. //***** BUGBUG *** Begin Special Case for DPA
  741. if (lstrcmpi (lpszScheme, "DPA") == 0)
  742. {
  743. fContextReq |= ISC_REQ_PROMPT_FOR_CREDS;
  744. }
  745. //***** BUGBUG *** End Special Case for DPA
  746. else
  747. return (ERROR_INTERNET_INCORRECT_PASSWORD);
  748. }
  749. }
  750. //
  751. // Setup dwOutBufferLength to represent max. memory in szOutBuffer
  752. //
  753. pWinContext->dwOutBufferLength = pWinContext->cbOutBuffer;
  754. ZeroMemory (pWinContext->szOutBuffer, pWinContext->cbOutBuffer);
  755. //
  756. // This will generate an authorization header with UUEncoded blob from SSPI.
  757. // BUGBUG: Better make sure outbuf buffer is big enough for this.
  758. //
  759. SPMStatus = GetSecAuthMsg( g_pSspData,
  760. pWinContext->pCredential,
  761. pkgId,
  762. pWinContext->pSspContextHandle,
  763. &(pWinContext->SspContextHandle),
  764. fContextReq,
  765. pServerBlob,
  766. dwInBufferLength,
  767. pWinContext->szOutBuffer,
  768. &pWinContext->dwOutBufferLength,
  769. lpszServerName,
  770. bNonBlock,
  771. lpszScheme,
  772. lpszUrl,
  773. pssResult);
  774. if (SPMStatus != SPM_STATUS_OK) // Fail to generate blob
  775. {
  776. pWinContext->dwOutBufferLength = 0; // no exchange blob generated
  777. //
  778. // if SSPI is requesting an opportunity to prompt for user credential
  779. //
  780. if (SPMStatus == SPM_STATUS_WOULD_BLOCK)
  781. {
  782. if (!SaveServerName (lpszServerName, pWinContext))
  783. return (ERROR_NOT_ENOUGH_MEMORY);
  784. // If there is a exchange blob, this is not the first call
  785. //
  786. if (pServerBlob && dwInBufferLength > 0)
  787. {
  788. // Save the exchange blob in the connection context
  789. // so we can call SSPI again with the exchange blob
  790. if (dwInBufferLength > MAX_BLOB_SIZE)
  791. {
  792. pWinContext->pInBuffer = (PCHAR) LocalAlloc(0,
  793. dwInBufferLength);
  794. if (pWinContext->pInBuffer == NULL)
  795. return (ERROR_NOT_ENOUGH_MEMORY);
  796. }
  797. else
  798. pWinContext->pInBuffer = pWinContext->szInBuffer;
  799. CopyMemory( pWinContext->szInBuffer, pServerBlob,
  800. dwInBufferLength );
  801. pWinContext->dwInBufferLength = dwInBufferLength;
  802. }
  803. else
  804. {
  805. //
  806. // Delete the original SSPI context handle and
  807. // let UI recreate one.
  808. //
  809. if (pWinContext->pSspContextHandle)
  810. {
  811. (*(g_pSspData->pFuncTbl->DeleteSecurityContext))(pWinContext->pSspContextHandle);
  812. pWinContext->pSspContextHandle = NULL;
  813. }
  814. //
  815. // clear buffer length for the exchange blob
  816. //
  817. if (pWinContext->pInBuffer != NULL &&
  818. pWinContext->pInBuffer != pWinContext->szInBuffer)
  819. {
  820. LocalFree (pWinContext->pInBuffer);
  821. }
  822. pWinContext->pInBuffer = NULL;
  823. pWinContext->dwInBufferLength = 0;
  824. }
  825. pWinContext->dwOutBufferLength = 0;
  826. return(ERROR_INTERNET_INCORRECT_PASSWORD);
  827. }
  828. return (ERROR_INTERNET_LOGIN_FAILURE);
  829. }
  830. else if (pWinContext->pSspContextHandle == NULL)
  831. {
  832. // This means that we've just created a security context
  833. //
  834. pWinContext->pSspContextHandle = &(pWinContext->SspContextHandle);
  835. }
  836. return (ERROR_INTERNET_FORCE_RETRY);
  837. }
  838. DWORD
  839. WINAPI
  840. PreAuthenticateUser(
  841. IN OUT LPVOID *lppvContext,
  842. IN LPSTR lpszServerName,
  843. IN LPSTR lpszScheme,
  844. IN DWORD dwFlags,
  845. OUT LPSTR lpOutBuffer,
  846. IN OUT LPDWORD lpdwOutBufferLength,
  847. IN LPSTR lpszUserName,
  848. IN LPSTR lpszPassword,
  849. IN PCSTR lpszUrl,
  850. SECURITY_STATUS *pssResult
  851. )
  852. {
  853. INT pkgId;
  854. DWORD dwStatus;
  855. PWINCONTEXT pWinContext;
  856. BOOL bNonBlock = TRUE;
  857. ULONG fContextReq = ISC_REQ_DELEGATE;
  858. DWORD Capabilities ;
  859. if (!SSPI_InitGlobals())
  860. return ERROR_INVALID_PARAMETER;
  861. if (lpszServerName == NULL || *lpszServerName == '\0')
  862. return(ERROR_INVALID_PARAMETER);
  863. pkgId = GetPkgId(lpszScheme);
  864. if (pkgId == -1) {
  865. return(ERROR_INVALID_PARAMETER);
  866. }
  867. Capabilities = GetPkgCapabilities( pkgId );
  868. //
  869. // If this is for an existing connection
  870. //
  871. if (*lppvContext != NULL)
  872. {
  873. pWinContext = (PWINCONTEXT) (*lppvContext);
  874. if ((DWORD)pkgId != pWinContext->pkgId)
  875. return(ERROR_INVALID_PARAMETER);
  876. //
  877. // For package that does not handle its own UI, if there is no
  878. // generated blob, it means that we have just collected
  879. // user name/password.
  880. //
  881. if ( ( pWinContext->dwOutBufferLength == 0 ) &&
  882. ( Capabilities & SSPAUTHPKG_SUPPORT_NTLM_CREDS ) )
  883. {
  884. if (lpszUserName == NULL || lpszPassword == NULL)
  885. {
  886. return(ERROR_INVALID_PARAMETER);
  887. }
  888. //
  889. // Need to recreate a credential handle and
  890. // generate a new NEGOTIATE message in lpOutBuffer
  891. //
  892. dwStatus = RedoNTLMAuth4User (pWinContext,
  893. pkgId,
  894. lpszUserName,
  895. lpszPassword,
  896. lpszServerName ,
  897. lpszScheme,
  898. lpOutBuffer,
  899. lpdwOutBufferLength,
  900. lpszUrl,
  901. pssResult);
  902. if (dwStatus != ERROR_SUCCESS)
  903. return (dwStatus);
  904. return(ERROR_SUCCESS);
  905. }
  906. else if (pWinContext->dwOutBufferLength == 0)
  907. //
  908. // For other packages, If there is no generated blob,
  909. // something is wrong
  910. //
  911. return(ERROR_INVALID_PARAMETER);
  912. }
  913. // If not NTLM, don't pre-auth.
  914. else if ( (Capabilities & SSPAUTHPKG_SUPPORT_NTLM_CREDS ) == 0 )
  915. {
  916. return (ERROR_INVALID_HANDLE);
  917. }
  918. else
  919. {
  920. // probably sending 1st request on a new connection for the same URL
  921. // Create a new context and SSPI credential handle for this connection
  922. //
  923. // Set fCanUseLogon to TRUE : we would not be pre-authing
  924. // unless we have a valid pwc which means we already checked
  925. // zone policy for silent logon.
  926. dwStatus = NewWinContext (pkgId, lpszScheme, &pWinContext,
  927. TRUE, lpszUserName, lpszPassword);
  928. if (dwStatus != ERROR_SUCCESS)
  929. return (dwStatus);
  930. #ifdef DEBUG_WINSSPI
  931. (void)wsprintf (msg,
  932. "PreAuthenticateUser> New Context for Scheme= %s Server= '%s'\n",
  933. lpszScheme, lpszServerName);
  934. OutputDebugString(msg);
  935. #endif
  936. pWinContext->dwOutBufferLength = pWinContext->cbOutBuffer;
  937. ZeroMemory (pWinContext->szOutBuffer, pWinContext->cbOutBuffer);
  938. //
  939. // This will generate an authorization header with the
  940. // UUEncoded blob from SSPI.
  941. // BUGBUG: Better make sure outbuf buffer is big enough for this.
  942. //
  943. dwStatus = GetSecAuthMsg( g_pSspData,
  944. pWinContext->pCredential,
  945. pkgId,
  946. NULL,
  947. &(pWinContext->SspContextHandle),
  948. fContextReq,
  949. NULL,
  950. 0,
  951. pWinContext->szOutBuffer,
  952. &pWinContext->dwOutBufferLength,
  953. lpszServerName,
  954. bNonBlock,
  955. lpszScheme,
  956. lpszUrl,
  957. pssResult);
  958. if (dwStatus != SPM_STATUS_OK)
  959. {
  960. // This is a rare case
  961. //
  962. pWinContext->dwOutBufferLength = 0; // no exchange blob generated
  963. return(ERROR_INVALID_HANDLE);
  964. }
  965. (*lppvContext) = (LPVOID) pWinContext;
  966. // Save the pointer of the created security ctxt
  967. //
  968. pWinContext->pSspContextHandle = &(pWinContext->SspContextHandle);
  969. }
  970. //
  971. // Copy exchange blob to the output buffer
  972. // Make sure output buffer provided is big enough
  973. //
  974. if (*lpdwOutBufferLength < pWinContext->dwOutBufferLength)
  975. {
  976. *lpdwOutBufferLength = pWinContext->dwOutBufferLength + 1;
  977. return(ERROR_INSUFFICIENT_BUFFER);
  978. }
  979. CopyMemory (lpOutBuffer, pWinContext->szOutBuffer,
  980. pWinContext->dwOutBufferLength);
  981. if (*lpdwOutBufferLength > pWinContext->dwOutBufferLength)
  982. lpOutBuffer[pWinContext->dwOutBufferLength] = '\0';
  983. *lpdwOutBufferLength = pWinContext->dwOutBufferLength;
  984. //
  985. // The exchange blob has being copied to request header, so clear its len
  986. //
  987. pWinContext->dwOutBufferLength = 0;
  988. return(ERROR_SUCCESS);
  989. }
  990. DWORD
  991. WINAPI
  992. AuthenticateUserUI(
  993. IN OUT LPVOID* lppvContext,
  994. IN HWND hWnd,
  995. IN DWORD dwError,
  996. IN DWORD dwFlags,
  997. IN OUT InvalidPassType* pAuthInfo,
  998. IN LPSTR lpszScheme,
  999. IN PCSTR lpszUrl,
  1000. OUT SECURITY_STATUS* pssResult
  1001. )
  1002. {
  1003. DWORD SPMStatus;
  1004. PWINCONTEXT pWinContext = NULL;
  1005. BOOL bNonBlock = FALSE;
  1006. ULONG fContextReq = ISC_REQ_PROMPT_FOR_CREDS | ISC_REQ_DELEGATE;
  1007. if (!SSPI_InitGlobals())
  1008. return ERROR_INVALID_PARAMETER;
  1009. if (*lppvContext == NULL) {
  1010. return(ERROR_INVALID_PARAMETER);
  1011. }
  1012. pWinContext = (PWINCONTEXT) (*lppvContext);
  1013. if (pWinContext->lpszServerName == NULL)
  1014. return(ERROR_INVALID_PARAMETER);
  1015. pWinContext->dwOutBufferLength = pWinContext->cbOutBuffer;
  1016. ZeroMemory (pWinContext->szOutBuffer, pWinContext->cbOutBuffer);
  1017. //
  1018. // Now make the password logon happen by calling GetSecAuthMsg (without
  1019. // context handle) with the ISC_REQ_PROMPT_FOR_CREDS flag set
  1020. //
  1021. // After this will it call PreAuthenticateUser
  1022. //
  1023. SPMStatus = GetSecAuthMsg( g_pSspData,
  1024. pWinContext->pCredential,
  1025. pWinContext->pkgId,
  1026. pWinContext->pSspContextHandle,
  1027. (PCtxtHandle) &(pWinContext->SspContextHandle),
  1028. fContextReq,
  1029. pWinContext->pInBuffer,
  1030. pWinContext->dwInBufferLength,
  1031. pWinContext->szOutBuffer,
  1032. &pWinContext->dwOutBufferLength,
  1033. pWinContext->lpszServerName,
  1034. bNonBlock,
  1035. lpszScheme,
  1036. lpszUrl,
  1037. pssResult);
  1038. // Clear out the input exchange blob
  1039. //
  1040. if (pWinContext->pInBuffer != NULL)
  1041. {
  1042. if (pWinContext->pInBuffer != pWinContext->szInBuffer)
  1043. LocalFree (pWinContext->pInBuffer);
  1044. pWinContext->pInBuffer = NULL;
  1045. pWinContext->dwInBufferLength = 0;
  1046. }
  1047. if (SPMStatus != SPM_STATUS_OK) {
  1048. pWinContext->dwOutBufferLength = 0;
  1049. return (ERROR_CANCELLED);
  1050. }
  1051. if (pWinContext->pSspContextHandle == NULL)
  1052. pWinContext->pSspContextHandle = &(pWinContext->SspContextHandle);
  1053. //
  1054. // BUGBUG
  1055. // Setup the UserName and Password to be " " to work around bugs in Wininet
  1056. //
  1057. lstrcpy (pAuthInfo->lpszUsername, " ");
  1058. lstrcpy (pAuthInfo->lpszPassword, " ");
  1059. return (ERROR_INTERNET_FORCE_RETRY);
  1060. }