1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: lib/advapi32/misc/logon.c 5 * PURPOSE: Logon functions 6 * PROGRAMMER: Eric Kohl 7 */ 8 9 #include <advapi32.h> 10 WINE_DEFAULT_DEBUG_CHANNEL(advapi); 11 12 /* GLOBALS *****************************************************************/ 13 14 HANDLE LsaHandle = NULL; 15 ULONG AuthenticationPackage = 0; 16 17 /* FUNCTIONS ***************************************************************/ 18 19 static 20 NTSTATUS 21 OpenLogonLsaHandle(VOID) 22 { 23 LSA_STRING LogonProcessName; 24 LSA_STRING PackageName; 25 LSA_OPERATIONAL_MODE SecurityMode = 0; 26 NTSTATUS Status; 27 28 RtlInitAnsiString((PANSI_STRING)&LogonProcessName, 29 "User32LogonProcess"); 30 31 Status = LsaRegisterLogonProcess(&LogonProcessName, 32 &LsaHandle, 33 &SecurityMode); 34 if (!NT_SUCCESS(Status)) 35 { 36 TRACE("LsaRegisterLogonProcess failed (Status 0x%08lx)\n", Status); 37 goto done; 38 } 39 40 RtlInitAnsiString((PANSI_STRING)&PackageName, 41 MSV1_0_PACKAGE_NAME); 42 43 Status = LsaLookupAuthenticationPackage(LsaHandle, 44 &PackageName, 45 &AuthenticationPackage); 46 if (!NT_SUCCESS(Status)) 47 { 48 TRACE("LsaLookupAuthenticationPackage failed (Status 0x%08lx)\n", Status); 49 goto done; 50 } 51 52 TRACE("AuthenticationPackage: 0x%08lx\n", AuthenticationPackage); 53 54 done: 55 if (!NT_SUCCESS(Status)) 56 { 57 if (LsaHandle != NULL) 58 { 59 Status = LsaDeregisterLogonProcess(LsaHandle); 60 if (!NT_SUCCESS(Status)) 61 { 62 TRACE("LsaDeregisterLogonProcess failed (Status 0x%08lx)\n", Status); 63 } 64 } 65 } 66 67 return Status; 68 } 69 70 71 NTSTATUS 72 CloseLogonLsaHandle(VOID) 73 { 74 NTSTATUS Status = STATUS_SUCCESS; 75 76 if (LsaHandle != NULL) 77 { 78 Status = LsaDeregisterLogonProcess(LsaHandle); 79 if (!NT_SUCCESS(Status)) 80 { 81 TRACE("LsaDeregisterLogonProcess failed (Status 0x%08lx)\n", Status); 82 } 83 } 84 85 return Status; 86 } 87 88 89 static 90 BOOL 91 CreateProcessAsUserCommon( 92 _In_opt_ HANDLE hToken, 93 _In_ DWORD dwCreationFlags, 94 _Out_ LPPROCESS_INFORMATION lpProcessInformation) 95 { 96 NTSTATUS Status; 97 PROCESS_ACCESS_TOKEN AccessToken; 98 99 if (hToken != NULL) 100 { 101 TOKEN_TYPE Type; 102 ULONG ReturnLength; 103 OBJECT_ATTRIBUTES ObjectAttributes; 104 HANDLE hTokenDup; 105 BOOLEAN PrivilegeSet = FALSE, HavePrivilege; 106 107 /* Check whether the user-provided token is a primary token */ 108 // GetTokenInformation(); 109 Status = NtQueryInformationToken(hToken, 110 TokenType, 111 &Type, 112 sizeof(Type), 113 &ReturnLength); 114 if (!NT_SUCCESS(Status)) 115 { 116 ERR("NtQueryInformationToken() failed, Status 0x%08x\n", Status); 117 goto Quit; 118 } 119 if (Type != TokenPrimary) 120 { 121 ERR("Wrong token type for token 0x%p, expected TokenPrimary, got %ld\n", hToken, Type); 122 Status = STATUS_BAD_TOKEN_TYPE; 123 goto Quit; 124 } 125 126 /* Duplicate the token for this new process */ 127 InitializeObjectAttributes(&ObjectAttributes, 128 NULL, 129 0, 130 NULL, 131 NULL); // FIXME: Use a valid SecurityDescriptor! 132 Status = NtDuplicateToken(hToken, 133 0, 134 &ObjectAttributes, 135 FALSE, 136 TokenPrimary, 137 &hTokenDup); 138 if (!NT_SUCCESS(Status)) 139 { 140 ERR("NtDuplicateToken() failed, Status 0x%08x\n", Status); 141 goto Quit; 142 } 143 144 // FIXME: Do we always need SecurityImpersonation? 145 Status = RtlImpersonateSelf(SecurityImpersonation); 146 if (!NT_SUCCESS(Status)) 147 { 148 ERR("RtlImpersonateSelf(SecurityImpersonation) failed, Status 0x%08x\n", Status); 149 NtClose(hTokenDup); 150 goto Quit; 151 } 152 153 /* 154 * Attempt to acquire the process primary token assignment privilege 155 * in case we actually need it. 156 * The call will either succeed or fail when the caller has (or has not) 157 * enough rights. 158 * The last situation may not be dramatic for us. Indeed it may happen 159 * that the user-provided token is a restricted version of the caller's 160 * primary token (aka. a "child" token), or both tokens inherit (i.e. are 161 * children, and are together "siblings") from a common parent token. 162 * In this case the NT kernel allows us to assign the token to the child 163 * process without the need for the assignment privilege, which is fine. 164 * On the contrary, if the user-provided token is completely arbitrary, 165 * then the NT kernel will enforce the presence of the assignment privilege: 166 * because we failed (by assumption) to assign the privilege, the process 167 * token assignment will fail as required. It is then the job of the 168 * caller to manually acquire the necessary privileges. 169 */ 170 Status = RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, 171 TRUE, TRUE, &PrivilegeSet); 172 HavePrivilege = NT_SUCCESS(Status); 173 if (!HavePrivilege) 174 { 175 ERR("RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE) failed, Status 0x%08lx, " 176 "attempting to continue without it...\n", Status); 177 } 178 179 AccessToken.Token = hTokenDup; 180 AccessToken.Thread = lpProcessInformation->hThread; 181 182 /* Set the new process token */ 183 Status = NtSetInformationProcess(lpProcessInformation->hProcess, 184 ProcessAccessToken, 185 (PVOID)&AccessToken, 186 sizeof(AccessToken)); 187 188 /* Restore the privilege */ 189 if (HavePrivilege) 190 { 191 RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, 192 PrivilegeSet, TRUE, &PrivilegeSet); 193 } 194 195 RevertToSelf(); 196 197 /* Close the duplicated token */ 198 NtClose(hTokenDup); 199 200 /* Check whether NtSetInformationProcess() failed */ 201 if (!NT_SUCCESS(Status)) 202 { 203 ERR("NtSetInformationProcess() failed, Status 0x%08x\n", Status); 204 goto Quit; 205 } 206 207 if (!NT_SUCCESS(Status)) 208 { 209 Quit: 210 TerminateProcess(lpProcessInformation->hProcess, Status); 211 SetLastError(RtlNtStatusToDosError(Status)); 212 return FALSE; 213 } 214 } 215 216 /* Resume the main thread */ 217 if (!(dwCreationFlags & CREATE_SUSPENDED)) 218 { 219 ResumeThread(lpProcessInformation->hThread); 220 } 221 222 return TRUE; 223 } 224 225 226 /* 227 * @implemented 228 */ 229 BOOL 230 WINAPI 231 DECLSPEC_HOTPATCH 232 CreateProcessAsUserA( 233 _In_opt_ HANDLE hToken, 234 _In_opt_ LPCSTR lpApplicationName, 235 _Inout_opt_ LPSTR lpCommandLine, 236 _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, 237 _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, 238 _In_ BOOL bInheritHandles, 239 _In_ DWORD dwCreationFlags, 240 _In_opt_ LPVOID lpEnvironment, 241 _In_opt_ LPCSTR lpCurrentDirectory, 242 _In_ LPSTARTUPINFOA lpStartupInfo, 243 _Out_ LPPROCESS_INFORMATION lpProcessInformation) 244 { 245 TRACE("%p %s %s %p %p %d 0x%08x %p %s %p %p\n", hToken, debugstr_a(lpApplicationName), 246 debugstr_a(lpCommandLine), lpProcessAttributes, lpThreadAttributes, bInheritHandles, 247 dwCreationFlags, lpEnvironment, debugstr_a(lpCurrentDirectory), lpStartupInfo, lpProcessInformation); 248 249 /* Create the process with a suspended main thread */ 250 if (!CreateProcessA(lpApplicationName, 251 lpCommandLine, 252 lpProcessAttributes, 253 lpThreadAttributes, 254 bInheritHandles, 255 dwCreationFlags | CREATE_SUSPENDED, 256 lpEnvironment, 257 lpCurrentDirectory, 258 lpStartupInfo, 259 lpProcessInformation)) 260 { 261 ERR("CreateProcessA failed, last error: %d\n", GetLastError()); 262 return FALSE; 263 } 264 265 /* Call the helper function */ 266 return CreateProcessAsUserCommon(hToken, 267 dwCreationFlags, 268 lpProcessInformation); 269 } 270 271 272 /* 273 * @implemented 274 */ 275 BOOL 276 WINAPI 277 DECLSPEC_HOTPATCH 278 CreateProcessAsUserW( 279 _In_opt_ HANDLE hToken, 280 _In_opt_ LPCWSTR lpApplicationName, 281 _Inout_opt_ LPWSTR lpCommandLine, 282 _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, 283 _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, 284 _In_ BOOL bInheritHandles, 285 _In_ DWORD dwCreationFlags, 286 _In_opt_ LPVOID lpEnvironment, 287 _In_opt_ LPCWSTR lpCurrentDirectory, 288 _In_ LPSTARTUPINFOW lpStartupInfo, 289 _Out_ LPPROCESS_INFORMATION lpProcessInformation) 290 { 291 TRACE("%p %s %s %p %p %d 0x%08x %p %s %p %p\n", hToken, debugstr_w(lpApplicationName), 292 debugstr_w(lpCommandLine), lpProcessAttributes, lpThreadAttributes, bInheritHandles, 293 dwCreationFlags, lpEnvironment, debugstr_w(lpCurrentDirectory), lpStartupInfo, lpProcessInformation); 294 295 /* Create the process with a suspended main thread */ 296 if (!CreateProcessW(lpApplicationName, 297 lpCommandLine, 298 lpProcessAttributes, 299 lpThreadAttributes, 300 bInheritHandles, 301 dwCreationFlags | CREATE_SUSPENDED, 302 lpEnvironment, 303 lpCurrentDirectory, 304 lpStartupInfo, 305 lpProcessInformation)) 306 { 307 ERR("CreateProcessW failed, last error: %d\n", GetLastError()); 308 return FALSE; 309 } 310 311 /* Call the helper function */ 312 return CreateProcessAsUserCommon(hToken, 313 dwCreationFlags, 314 lpProcessInformation); 315 } 316 317 318 /* 319 * @implemented 320 */ 321 BOOL 322 WINAPI 323 LogonUserA( 324 _In_ LPSTR lpszUsername, 325 _In_opt_ LPSTR lpszDomain, 326 _In_opt_ LPSTR lpszPassword, 327 _In_ DWORD dwLogonType, 328 _In_ DWORD dwLogonProvider, 329 _Out_opt_ PHANDLE phToken) 330 { 331 return LogonUserExA(lpszUsername, 332 lpszDomain, 333 lpszPassword, 334 dwLogonType, 335 dwLogonProvider, 336 phToken, 337 NULL, 338 NULL, 339 NULL, 340 NULL); 341 } 342 343 344 /* 345 * @implemented 346 */ 347 BOOL 348 WINAPI 349 LogonUserExA( 350 _In_ LPSTR lpszUsername, 351 _In_opt_ LPSTR lpszDomain, 352 _In_opt_ LPSTR lpszPassword, 353 _In_ DWORD dwLogonType, 354 _In_ DWORD dwLogonProvider, 355 _Out_opt_ PHANDLE phToken, 356 _Out_opt_ PSID *ppLogonSid, 357 _Out_opt_ PVOID *ppProfileBuffer, 358 _Out_opt_ LPDWORD pdwProfileLength, 359 _Out_opt_ PQUOTA_LIMITS pQuotaLimits) 360 { 361 UNICODE_STRING UserName; 362 UNICODE_STRING Domain; 363 UNICODE_STRING Password; 364 BOOL ret = FALSE; 365 366 UserName.Buffer = NULL; 367 Domain.Buffer = NULL; 368 Password.Buffer = NULL; 369 370 if (!RtlCreateUnicodeStringFromAsciiz(&UserName, lpszUsername)) 371 { 372 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 373 goto UsernameDone; 374 } 375 376 if (!RtlCreateUnicodeStringFromAsciiz(&Domain, lpszDomain)) 377 { 378 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 379 goto DomainDone; 380 } 381 382 if (!RtlCreateUnicodeStringFromAsciiz(&Password, lpszPassword)) 383 { 384 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 385 goto PasswordDone; 386 } 387 388 ret = LogonUserExW(UserName.Buffer, 389 Domain.Buffer, 390 Password.Buffer, 391 dwLogonType, 392 dwLogonProvider, 393 phToken, 394 ppLogonSid, 395 ppProfileBuffer, 396 pdwProfileLength, 397 pQuotaLimits); 398 399 if (Password.Buffer != NULL) 400 RtlFreeUnicodeString(&Password); 401 402 PasswordDone: 403 if (Domain.Buffer != NULL) 404 RtlFreeUnicodeString(&Domain); 405 406 DomainDone: 407 if (UserName.Buffer != NULL) 408 RtlFreeUnicodeString(&UserName); 409 410 UsernameDone: 411 return ret; 412 } 413 414 415 /* 416 * @implemented 417 */ 418 BOOL 419 WINAPI 420 LogonUserW( 421 _In_ LPWSTR lpszUsername, 422 _In_opt_ LPWSTR lpszDomain, 423 _In_opt_ LPWSTR lpszPassword, 424 _In_ DWORD dwLogonType, 425 _In_ DWORD dwLogonProvider, 426 _Out_opt_ PHANDLE phToken) 427 { 428 return LogonUserExW(lpszUsername, 429 lpszDomain, 430 lpszPassword, 431 dwLogonType, 432 dwLogonProvider, 433 phToken, 434 NULL, 435 NULL, 436 NULL, 437 NULL); 438 } 439 440 441 /* 442 * @implemented 443 */ 444 BOOL 445 WINAPI 446 LogonUserExW( 447 _In_ LPWSTR lpszUsername, 448 _In_opt_ LPWSTR lpszDomain, 449 _In_opt_ LPWSTR lpszPassword, 450 _In_ DWORD dwLogonType, 451 _In_ DWORD dwLogonProvider, 452 _Out_opt_ PHANDLE phToken, 453 _Out_opt_ PSID *ppLogonSid, 454 _Out_opt_ PVOID *ppProfileBuffer, 455 _Out_opt_ LPDWORD pdwProfileLength, 456 _Out_opt_ PQUOTA_LIMITS pQuotaLimits) 457 { 458 SID_IDENTIFIER_AUTHORITY LocalAuthority = {SECURITY_LOCAL_SID_AUTHORITY}; 459 SID_IDENTIFIER_AUTHORITY SystemAuthority = {SECURITY_NT_AUTHORITY}; 460 PSID LogonSid = NULL; 461 PSID LocalSid = NULL; 462 LSA_STRING OriginName; 463 UNICODE_STRING DomainName; 464 UNICODE_STRING UserName; 465 UNICODE_STRING Password; 466 PMSV1_0_INTERACTIVE_LOGON AuthInfo = NULL; 467 ULONG AuthInfoLength; 468 ULONG_PTR Ptr; 469 TOKEN_SOURCE TokenSource; 470 PTOKEN_GROUPS TokenGroups = NULL; 471 PMSV1_0_INTERACTIVE_PROFILE ProfileBuffer = NULL; 472 ULONG ProfileBufferLength = 0; 473 LUID Luid = {0, 0}; 474 LUID LogonId = {0, 0}; 475 HANDLE TokenHandle = NULL; 476 QUOTA_LIMITS QuotaLimits; 477 SECURITY_LOGON_TYPE LogonType; 478 NTSTATUS SubStatus = STATUS_SUCCESS; 479 NTSTATUS Status; 480 481 if ((ppProfileBuffer != NULL && pdwProfileLength == NULL) || 482 (ppProfileBuffer == NULL && pdwProfileLength != NULL)) 483 { 484 SetLastError(ERROR_INVALID_PARAMETER); 485 return FALSE; 486 } 487 488 if (ppProfileBuffer != NULL && pdwProfileLength != NULL) 489 { 490 *ppProfileBuffer = NULL; 491 *pdwProfileLength = 0; 492 } 493 494 if (phToken != NULL) 495 *phToken = NULL; 496 497 switch (dwLogonType) 498 { 499 case LOGON32_LOGON_INTERACTIVE: 500 LogonType = Interactive; 501 break; 502 503 case LOGON32_LOGON_NETWORK: 504 LogonType = Network; 505 break; 506 507 case LOGON32_LOGON_BATCH: 508 LogonType = Batch; 509 break; 510 511 case LOGON32_LOGON_SERVICE: 512 LogonType = Service; 513 break; 514 515 default: 516 ERR("Invalid logon type: %ul\n", dwLogonType); 517 Status = STATUS_INVALID_PARAMETER; 518 goto done; 519 } 520 521 if (LsaHandle == NULL) 522 { 523 Status = OpenLogonLsaHandle(); 524 if (!NT_SUCCESS(Status)) 525 goto done; 526 } 527 528 RtlInitAnsiString((PANSI_STRING)&OriginName, 529 "Advapi32 Logon"); 530 531 RtlInitUnicodeString(&DomainName, 532 lpszDomain); 533 534 RtlInitUnicodeString(&UserName, 535 lpszUsername); 536 537 RtlInitUnicodeString(&Password, 538 lpszPassword); 539 540 AuthInfoLength = sizeof(MSV1_0_INTERACTIVE_LOGON)+ 541 DomainName.MaximumLength + 542 UserName.MaximumLength + 543 Password.MaximumLength; 544 545 AuthInfo = RtlAllocateHeap(RtlGetProcessHeap(), 546 HEAP_ZERO_MEMORY, 547 AuthInfoLength); 548 if (AuthInfo == NULL) 549 { 550 Status = STATUS_INSUFFICIENT_RESOURCES; 551 goto done; 552 } 553 554 AuthInfo->MessageType = MsV1_0InteractiveLogon; 555 556 Ptr = (ULONG_PTR)AuthInfo + sizeof(MSV1_0_INTERACTIVE_LOGON); 557 558 AuthInfo->LogonDomainName.Length = DomainName.Length; 559 AuthInfo->LogonDomainName.MaximumLength = DomainName.MaximumLength; 560 AuthInfo->LogonDomainName.Buffer = (DomainName.Buffer == NULL) ? NULL : (PWCHAR)Ptr; 561 if (DomainName.MaximumLength > 0) 562 { 563 RtlCopyMemory(AuthInfo->LogonDomainName.Buffer, 564 DomainName.Buffer, 565 DomainName.MaximumLength); 566 567 Ptr += DomainName.MaximumLength; 568 } 569 570 AuthInfo->UserName.Length = UserName.Length; 571 AuthInfo->UserName.MaximumLength = UserName.MaximumLength; 572 AuthInfo->UserName.Buffer = (PWCHAR)Ptr; 573 if (UserName.MaximumLength > 0) 574 RtlCopyMemory(AuthInfo->UserName.Buffer, 575 UserName.Buffer, 576 UserName.MaximumLength); 577 578 Ptr += UserName.MaximumLength; 579 580 AuthInfo->Password.Length = Password.Length; 581 AuthInfo->Password.MaximumLength = Password.MaximumLength; 582 AuthInfo->Password.Buffer = (PWCHAR)Ptr; 583 if (Password.MaximumLength > 0) 584 RtlCopyMemory(AuthInfo->Password.Buffer, 585 Password.Buffer, 586 Password.MaximumLength); 587 588 /* Create the Logon SID */ 589 AllocateLocallyUniqueId(&LogonId); 590 Status = RtlAllocateAndInitializeSid(&SystemAuthority, 591 SECURITY_LOGON_IDS_RID_COUNT, 592 SECURITY_LOGON_IDS_RID, 593 LogonId.HighPart, 594 LogonId.LowPart, 595 SECURITY_NULL_RID, 596 SECURITY_NULL_RID, 597 SECURITY_NULL_RID, 598 SECURITY_NULL_RID, 599 SECURITY_NULL_RID, 600 &LogonSid); 601 if (!NT_SUCCESS(Status)) 602 goto done; 603 604 /* Create the Local SID */ 605 Status = RtlAllocateAndInitializeSid(&LocalAuthority, 606 1, 607 SECURITY_LOCAL_RID, 608 SECURITY_NULL_RID, 609 SECURITY_NULL_RID, 610 SECURITY_NULL_RID, 611 SECURITY_NULL_RID, 612 SECURITY_NULL_RID, 613 SECURITY_NULL_RID, 614 SECURITY_NULL_RID, 615 &LocalSid); 616 if (!NT_SUCCESS(Status)) 617 goto done; 618 619 /* Allocate and set the token groups */ 620 TokenGroups = RtlAllocateHeap(RtlGetProcessHeap(), 621 HEAP_ZERO_MEMORY, 622 sizeof(TOKEN_GROUPS) + ((2 - ANYSIZE_ARRAY) * sizeof(SID_AND_ATTRIBUTES))); 623 if (TokenGroups == NULL) 624 { 625 Status = STATUS_INSUFFICIENT_RESOURCES; 626 goto done; 627 } 628 629 TokenGroups->GroupCount = 2; 630 TokenGroups->Groups[0].Sid = LogonSid; 631 TokenGroups->Groups[0].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED | 632 SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_LOGON_ID; 633 TokenGroups->Groups[1].Sid = LocalSid; 634 TokenGroups->Groups[1].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED | 635 SE_GROUP_ENABLED_BY_DEFAULT; 636 637 /* Set the token source */ 638 strncpy(TokenSource.SourceName, "Advapi ", sizeof(TokenSource.SourceName)); 639 AllocateLocallyUniqueId(&TokenSource.SourceIdentifier); 640 641 Status = LsaLogonUser(LsaHandle, 642 &OriginName, 643 LogonType, 644 AuthenticationPackage, 645 (PVOID)AuthInfo, 646 AuthInfoLength, 647 TokenGroups, 648 &TokenSource, 649 (PVOID*)&ProfileBuffer, 650 &ProfileBufferLength, 651 &Luid, 652 &TokenHandle, 653 &QuotaLimits, 654 &SubStatus); 655 if (!NT_SUCCESS(Status)) 656 { 657 ERR("LsaLogonUser failed (Status 0x%08lx)\n", Status); 658 goto done; 659 } 660 661 if (ProfileBuffer != NULL) 662 { 663 TRACE("ProfileBuffer: %p\n", ProfileBuffer); 664 TRACE("MessageType: %u\n", ProfileBuffer->MessageType); 665 666 TRACE("FullName: %p\n", ProfileBuffer->FullName.Buffer); 667 TRACE("FullName: %S\n", ProfileBuffer->FullName.Buffer); 668 669 TRACE("LogonServer: %p\n", ProfileBuffer->LogonServer.Buffer); 670 TRACE("LogonServer: %S\n", ProfileBuffer->LogonServer.Buffer); 671 } 672 673 TRACE("Luid: 0x%08lx%08lx\n", Luid.HighPart, Luid.LowPart); 674 675 if (TokenHandle != NULL) 676 { 677 TRACE("TokenHandle: %p\n", TokenHandle); 678 } 679 680 if (phToken != NULL) 681 *phToken = TokenHandle; 682 683 /* FIXME: return ppLogonSid and pQuotaLimits */ 684 685 done: 686 if (ProfileBuffer != NULL) 687 LsaFreeReturnBuffer(ProfileBuffer); 688 689 if (!NT_SUCCESS(Status)) 690 { 691 if (TokenHandle != NULL) 692 CloseHandle(TokenHandle); 693 } 694 695 if (TokenGroups != NULL) 696 RtlFreeHeap(RtlGetProcessHeap(), 0, TokenGroups); 697 698 if (LocalSid != NULL) 699 RtlFreeSid(LocalSid); 700 701 if (LogonSid != NULL) 702 RtlFreeSid(LogonSid); 703 704 if (AuthInfo != NULL) 705 RtlFreeHeap(RtlGetProcessHeap(), 0, AuthInfo); 706 707 if (!NT_SUCCESS(Status)) 708 { 709 SetLastError(RtlNtStatusToDosError(Status)); 710 return FALSE; 711 } 712 713 return TRUE; 714 } 715 716 /* EOF */ 717