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.

451 lines
11 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) Microsoft Corp. All rights reserved.
  4. //
  5. // FILE
  6. //
  7. // ezlogon.c
  8. //
  9. // SYNOPSIS
  10. //
  11. // Defines the IAS wrapper around LsaLogonUser
  12. //
  13. // MODIFICATION HISTORY
  14. //
  15. // 08/15/1998 Original version.
  16. // 09/09/1998 Fix AV when logon domain doesn't match user domain.
  17. // 10/02/1998 NULL out handle when LsaLogonUser fails.
  18. // 10/11/1998 Use SubStatus for STATUS_ACCOUNT_RESTRICTION.
  19. // 10/22/1998 PIAS_LOGON_HOURS is now a mandatory parameter.
  20. // 01/28/1999 Remove LogonDomainName check.
  21. // 04/19/1999 Add IASPurgeTicketCache.
  22. //
  23. ///////////////////////////////////////////////////////////////////////////////
  24. #include <nt.h>
  25. #include <ntrtl.h>
  26. #include <nturtl.h>
  27. #include <ntlsa.h>
  28. #include <kerberos.h>
  29. #include <windows.h>
  30. #include <ezlogon.h>
  31. #include <iaslsa.h>
  32. #include <iastrace.h>
  33. CONST CHAR LOGON_PROCESS_NAME[] = "IAS";
  34. CONST CHAR TOKEN_SOURCE_NAME[TOKEN_SOURCE_LENGTH] = "IAS";
  35. //////////
  36. // Misc. global variables used for logons.
  37. //////////
  38. LSA_HANDLE theLogonProcess; // The handle for the logon process.
  39. ULONG theMSV1_0_Package; // The MSV1_0 authentication package.
  40. ULONG theKerberosPackage; // The Kerberos authentication package.
  41. STRING theOriginName; // The origin of the logon requests.
  42. TOKEN_SOURCE theSourceContext; // The source context of the logon requests.
  43. /////////////////////////////////////////////////////////////////////////////// //
  44. // FUNCTION
  45. //
  46. // IASLogonInitialize
  47. //
  48. // DESCRIPTION
  49. //
  50. // Registers the logon process.
  51. //
  52. ///////////////////////////////////////////////////////////////////////////////
  53. DWORD
  54. WINAPI
  55. IASLogonInitialize( VOID )
  56. {
  57. DWORD status;
  58. BOOLEAN wasEnabled;
  59. LSA_STRING processName, packageName;
  60. LSA_OPERATIONAL_MODE opMode;
  61. //////////
  62. // Enable SE_TCB_PRIVILEGE.
  63. //////////
  64. status = RtlAdjustPrivilege(
  65. SE_TCB_PRIVILEGE,
  66. TRUE,
  67. FALSE,
  68. &wasEnabled
  69. );
  70. if (!NT_SUCCESS(status)) { goto exit; }
  71. //////////
  72. // Register as a logon process.
  73. //////////
  74. RtlInitString(
  75. &processName,
  76. LOGON_PROCESS_NAME
  77. );
  78. status = LsaRegisterLogonProcess(
  79. &processName,
  80. &theLogonProcess,
  81. &opMode
  82. );
  83. if (!NT_SUCCESS(status)) { goto exit; }
  84. //////////
  85. // Lookup the MSV1_0 authentication package.
  86. //////////
  87. RtlInitString(
  88. &packageName,
  89. MSV1_0_PACKAGE_NAME
  90. );
  91. status = LsaLookupAuthenticationPackage(
  92. theLogonProcess,
  93. &packageName,
  94. &theMSV1_0_Package
  95. );
  96. if (!NT_SUCCESS(status)) { goto deregister; }
  97. //////////
  98. // Lookup the Kerberos authentication package.
  99. //////////
  100. RtlInitString(
  101. &packageName,
  102. MICROSOFT_KERBEROS_NAME_A
  103. );
  104. status = LsaLookupAuthenticationPackage(
  105. theLogonProcess,
  106. &packageName,
  107. &theKerberosPackage
  108. );
  109. if (!NT_SUCCESS(status)) { goto deregister; }
  110. //////////
  111. // Initialize the source context.
  112. //////////
  113. memcpy(theSourceContext.SourceName,
  114. TOKEN_SOURCE_NAME,
  115. TOKEN_SOURCE_LENGTH);
  116. status = NtAllocateLocallyUniqueId(
  117. &theSourceContext.SourceIdentifier
  118. );
  119. if (!NT_SUCCESS(status)) { goto deregister; }
  120. return NO_ERROR;
  121. deregister:
  122. LsaDeregisterLogonProcess(theLogonProcess);
  123. theLogonProcess = NULL;
  124. exit:
  125. return RtlNtStatusToDosError(status);
  126. }
  127. /////////////////////////////////////////////////////////////////////////////// //
  128. // FUNCTION
  129. //
  130. // IASLogonShutdown
  131. //
  132. // DESCRIPTION
  133. //
  134. // Deregisters the logon process.
  135. //
  136. ///////////////////////////////////////////////////////////////////////////////
  137. VOID
  138. WINAPI
  139. IASLogonShutdown( VOID )
  140. {
  141. LsaDeregisterLogonProcess(theLogonProcess);
  142. theLogonProcess = NULL;
  143. }
  144. /////////////////////////////////////////////////////////////////////////////// //
  145. // FUNCTION
  146. //
  147. // IASInitAuthInfo
  148. //
  149. // DESCRIPTION
  150. //
  151. // Initializes the fields common to all MSV1_0_LM20* structs.
  152. //
  153. ///////////////////////////////////////////////////////////////////////////////
  154. VOID
  155. WINAPI
  156. IASInitAuthInfo(
  157. IN PVOID AuthInfo,
  158. IN DWORD FixedLength,
  159. IN PCWSTR UserName,
  160. IN PCWSTR Domain,
  161. OUT PBYTE* Data
  162. )
  163. {
  164. PMSV1_0_LM20_LOGON logon;
  165. // Zero out the fixed data.
  166. memset(AuthInfo, 0, FixedLength);
  167. // Set Data to point just past the fixed struct.
  168. *Data = FixedLength + (PBYTE)AuthInfo;
  169. // This cast is safe since all LM20 structs have the same initial fields.
  170. logon = (PMSV1_0_LM20_LOGON)AuthInfo;
  171. // We always do Network logons.
  172. logon->MessageType = MsV1_0NetworkLogon;
  173. // Copy in the strings common to all logons.
  174. IASInitUnicodeString(logon->LogonDomainName, *Data, Domain);
  175. IASInitUnicodeString(logon->UserName, *Data, UserName);
  176. IASInitUnicodeString(logon->Workstation, *Data, L"");
  177. }
  178. /////////////////////////////////////////////////////////////////////////////// //
  179. // FUNCTION
  180. //
  181. // IASLogonUser
  182. //
  183. // DESCRIPTION
  184. //
  185. // Wrapper around LsaLogonUser.
  186. //
  187. ///////////////////////////////////////////////////////////////////////////////
  188. DWORD
  189. WINAPI
  190. IASLogonUser(
  191. IN PVOID AuthInfo,
  192. IN ULONG AuthInfoLength,
  193. OUT PMSV1_0_LM20_LOGON_PROFILE *Profile,
  194. OUT PHANDLE Token
  195. )
  196. {
  197. NTSTATUS status, SubStatus;
  198. PMSV1_0_LM20_LOGON_PROFILE ProfileBuffer;
  199. ULONG ProfileBufferLength;
  200. LUID LogonId;
  201. QUOTA_LIMITS Quotas;
  202. // Make sure the OUT arguments are NULL.
  203. *Token = NULL;
  204. ProfileBuffer = NULL;
  205. status = LsaLogonUser(
  206. theLogonProcess,
  207. &theOriginName,
  208. Network,
  209. theMSV1_0_Package,
  210. AuthInfo,
  211. AuthInfoLength,
  212. NULL,
  213. &theSourceContext,
  214. &ProfileBuffer,
  215. &ProfileBufferLength,
  216. &LogonId,
  217. Token,
  218. &Quotas,
  219. &SubStatus
  220. );
  221. if (!NT_SUCCESS(status))
  222. {
  223. // For account restrictions, we can get a more descriptive error
  224. // from the SubStatus.
  225. if (status == STATUS_ACCOUNT_RESTRICTION && !NT_SUCCESS(SubStatus))
  226. {
  227. status = SubStatus;
  228. }
  229. // Sometimes LsaLogonUser returns an invalid handle value on failure.
  230. *Token = NULL;
  231. }
  232. if (Profile)
  233. {
  234. // Return the profile if requested ...
  235. *Profile = ProfileBuffer;
  236. }
  237. else if (ProfileBuffer)
  238. {
  239. // ... otherwise free it.
  240. LsaFreeReturnBuffer(ProfileBuffer);
  241. }
  242. return RtlNtStatusToDosError(status);
  243. }
  244. /////////////////////////////////////////////////////////////////////////////// //
  245. // FUNCTION
  246. //
  247. // IASCheckAccountRestrictions
  248. //
  249. // DESCRIPTION
  250. //
  251. // Checks whether an account can be used for logon.
  252. //
  253. ///////////////////////////////////////////////////////////////////////////////
  254. DWORD
  255. WINAPI
  256. IASCheckAccountRestrictions(
  257. IN PLARGE_INTEGER AccountExpires,
  258. IN PIAS_LOGON_HOURS LogonHours,
  259. OUT PLARGE_INTEGER KickOffTime
  260. )
  261. {
  262. LONGLONG now, logonHoursExpiry;
  263. TIME_ZONE_INFORMATION tzi;
  264. SYSTEMTIME st;
  265. size_t msecOfWeek, msecPerUnit, idx, lastUnit, msecLeft;
  266. const size_t msecPerWeek = 1000 * 60 * 60 * 24 * 7;
  267. GetSystemTimeAsFileTime((LPFILETIME)&now);
  268. if (AccountExpires->QuadPart == 0)
  269. {
  270. // An expiration time of zero means 'never'.
  271. KickOffTime->QuadPart = MAXLONGLONG;
  272. }
  273. else if (AccountExpires->QuadPart > now)
  274. {
  275. KickOffTime->QuadPart = AccountExpires->QuadPart;
  276. }
  277. else
  278. {
  279. return ERROR_ACCOUNT_EXPIRED;
  280. }
  281. // If LogonHours is empty, then we're done.
  282. if (LogonHours->UnitsPerWeek == 0)
  283. {
  284. return NO_ERROR;
  285. }
  286. // The LogonHours array does not account for bias.
  287. switch (GetTimeZoneInformation(&tzi))
  288. {
  289. case TIME_ZONE_ID_UNKNOWN:
  290. case TIME_ZONE_ID_STANDARD:
  291. // Bias is in minutes.
  292. now -= 60 * 10000000 * (LONGLONG)tzi.StandardBias;
  293. break;
  294. case TIME_ZONE_ID_DAYLIGHT:
  295. // Bias is in minutes.
  296. now -= 60 * 10000000 * (LONGLONG)tzi.DaylightBias;
  297. break;
  298. default:
  299. return ERROR_INVALID_LOGON_HOURS;
  300. }
  301. FileTimeToSystemTime((LPFILETIME)&now, &st);
  302. // Number of milliseconds into the week.
  303. msecOfWeek = st.wMilliseconds +
  304. st.wSecond * 1000 +
  305. st.wMinute * 1000 * 60 +
  306. st.wHour * 1000 * 60 * 60 +
  307. st.wDayOfWeek * 1000 * 60 * 60 * 24;
  308. // Compute the index of the current time (our starting point).
  309. msecPerUnit = msecPerWeek / LogonHours->UnitsPerWeek;
  310. idx = msecOfWeek / msecPerUnit;
  311. // Number of units until we hit an unset bit.
  312. lastUnit = 0;
  313. while (lastUnit < LogonHours->UnitsPerWeek)
  314. {
  315. // Test the corresponding bit.
  316. if ((LogonHours->LogonHours[idx / 8] & (0x1 << (idx % 8))) == 0)
  317. {
  318. break;
  319. }
  320. ++lastUnit;
  321. ++idx;
  322. // Wrap around if necessary.
  323. if (idx == LogonHours->UnitsPerWeek)
  324. {
  325. idx = 0;
  326. }
  327. }
  328. if (lastUnit == LogonHours->UnitsPerWeek)
  329. {
  330. // All bits are set, so leave the KickOffTime alone.
  331. }
  332. else if (lastUnit > 0)
  333. {
  334. // How many milliseconds left?
  335. msecLeft = (lastUnit - 1) * msecPerUnit;
  336. msecLeft += msecPerUnit - (msecOfWeek % msecPerUnit);
  337. // Add this to the current time to find out when logon hours expires.
  338. logonHoursExpiry = now + (msecLeft * 10000i64);
  339. // Is this more restrictive than the the current KickOffTime?
  340. if (logonHoursExpiry < KickOffTime->QuadPart)
  341. {
  342. KickOffTime->QuadPart = logonHoursExpiry;
  343. }
  344. }
  345. else
  346. {
  347. // Current bit isn't set.
  348. return ERROR_INVALID_LOGON_HOURS;
  349. }
  350. return NO_ERROR;
  351. }
  352. /////////////////////////////////////////////////////////////////////////////// //
  353. // FUNCTION
  354. //
  355. // IASPurgeTicketCache
  356. //
  357. // DESCRIPTION
  358. //
  359. // Purges the Kerberos ticket cache.
  360. //
  361. ///////////////////////////////////////////////////////////////////////////////
  362. DWORD
  363. WINAPI
  364. IASPurgeTicketCache( VOID )
  365. {
  366. KERB_PURGE_TKT_CACHE_REQUEST request;
  367. NTSTATUS status, subStatus;
  368. PVOID response;
  369. ULONG responseLength;
  370. memset(&request, 0, sizeof(request));
  371. request.MessageType = KerbPurgeTicketCacheMessage;
  372. response = NULL;
  373. responseLength = 0;
  374. subStatus = 0;
  375. status = LsaCallAuthenticationPackage(
  376. theLogonProcess,
  377. theKerberosPackage,
  378. &request,
  379. sizeof(request),
  380. &response,
  381. &responseLength,
  382. &subStatus
  383. );
  384. if (NT_SUCCESS(status) && (response != NULL))
  385. {
  386. LsaFreeReturnBuffer(response);
  387. }
  388. return RtlNtStatusToDosError(status);
  389. }