//+----------------------------------------------------------------------- // // Microsoft Windows // // Copyright (c) Microsoft Corporation 1992 - 1997 // // File: userlist.cxx // // Contents: Routines for logging a client with a PAC onto an existing // NT account. // // // History: 21-Febuary-1997 Created MikeSw // //------------------------------------------------------------------------ #include #include #ifndef WIN32_CHICAGO extern "C" { #include #include #include } #include //+------------------------------------------------------------------------- // // Function: KerbReadRegistryString // // Synopsis: Reads & allocates a string from the registry // // Effects: // // Arguments: // // Requires: // // Returns: STATUS_SUCCESS - the value was found // STATUS_NO_SUCH_USER - the value was not found // STATUS_INSUFFICIENT_RESOURCES - memory allocation failure // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS KerbReadRegistryString( IN HKEY Key, IN LPWSTR Value, IN OUT PUNICODE_STRING String ) { ULONG Type; ULONG WinError = ERROR_SUCCESS; ULONG StringSize = 0; NTSTATUS Status = STATUS_NO_SUCH_USER; WinError = RegQueryValueEx( Key, Value, NULL, &Type, NULL, &StringSize ); if ((WinError == ERROR_MORE_DATA) || (WinError == ERROR_SUCCESS)) { // // The value exists - get the name from it // String->Buffer = (LPWSTR) KerbAllocate(StringSize); if (String->Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } WinError = RegQueryValueEx( Key, Value, NULL, &Type, (PUCHAR) String->Buffer, &StringSize ); if (WinError != ERROR_SUCCESS) { goto Cleanup; } RtlInitUnicodeString( String, String->Buffer ); Status = STATUS_SUCCESS; } Cleanup: if (!NT_SUCCESS(Status) && (String->Buffer != NULL)) { KerbFreeString(String); } return(Status); } //+------------------------------------------------------------------------- // // Function: KerbMapClientName // // Synopsis: Maps a Kerberos client name to an NT user name. First it // tries converting the name to a string and looking for a // value with that name. If that fails, it looks for a // value with the client realm name. // // Effects: // // Arguments: MappedName - receives the name of the local account the // client maps to // ClientName - Kerberos principal name of the client // ClientRealm - Kerberos realm of the client // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS KerbMapClientName( OUT PUNICODE_STRING MappedName, IN PKERB_INTERNAL_NAME ClientName, IN PUNICODE_STRING ClientRealm ) { LPWSTR UserName = NULL; DWORD WinError; NTSTATUS Status = STATUS_NO_SUCH_USER; HKEY Key = NULL; UNICODE_STRING ClientString = {0}; EMPTY_UNICODE_STRING( MappedName ); // // First convert the MIT client name to a registry value name. We do // this by adding a '/' between every component of the client name // and appending "@ClientRealm" // // // Make sure the client realm is null terminated // DsysAssert(ClientRealm->Length == 0 || (ClientRealm->MaximumLength >= ClientRealm->Length + sizeof(WCHAR))); DsysAssert(ClientRealm->Length == 0 || (ClientRealm->Buffer[ClientRealm->Length/sizeof(WCHAR)] == L'\0')); // // The value length is the length of all the components of the names, // all the '/' separtors, and the name of the domain name plus '@' // if (!KERB_SUCCESS(KerbConvertKdcNameToString( &ClientString, ClientName, ClientRealm))) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } D_DebugLog((DEB_TRACE,"Mapping client name %wZ\n",&ClientString )); // // Also build just the username, which is used when users are mapped // back to their own name // UserName = (LPWSTR) KerbAllocate(ClientName->Names[0].Length + sizeof(WCHAR)); if (UserName == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } RtlCopyMemory( UserName, ClientName->Names[0].Buffer, ClientName->Names[0].Length ); UserName[ClientName->Names[0].Length / sizeof(WCHAR)] = L'\0'; // // Now check the registry for a mapping for this name // WinError = RegOpenKey( HKEY_LOCAL_MACHINE, KERB_USERLIST_KEY, &Key ); if (WinError != ERROR_SUCCESS) { goto Cleanup; } // // Read out the value // Status = KerbReadRegistryString( Key, ClientString.Buffer, MappedName ); if (Status == STATUS_NO_SUCH_USER) { // // Try again with just the domain name - this allows all users in // a domain to be mapped // Status = KerbReadRegistryString( Key, ClientRealm->Buffer, MappedName ); } if (Status == STATUS_NO_SUCH_USER) { Status = KerbReadRegistryString( Key, KERB_ALL_USERS_VALUE, MappedName ); } if (!NT_SUCCESS(Status)) { goto Cleanup; } // // If the mapped name is '*', then use the client's name // if (_wcsicmp(MappedName->Buffer,KERB_MATCH_ALL_NAME) == 0) { KerbFree(MappedName->Buffer); RtlInitUnicodeString( MappedName, UserName ); UserName = NULL; } Status = STATUS_SUCCESS; Cleanup: if (!NT_SUCCESS(Status)) { KerbFreeString(MappedName); } KerbFreeString(&ClientString); if (UserName != NULL) { KerbFree(UserName); } if (Key != NULL) { RegCloseKey(Key); } return(Status); } //+------------------------------------------------------------------------- // // Function: KerbCreatePacForKerbClient // // Synopsis: Creates a PAC structure for a kerb client without a PAC // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS KerbCreatePacForKerbClient( OUT PPACTYPE * Pac, IN PKERB_INTERNAL_NAME ClientName, IN PUNICODE_STRING ClientRealm, IN OPTIONAL PUNICODE_STRING MappedClientName ) { NTSTATUS Status = STATUS_SUCCESS; PLSAPR_POLICY_INFORMATION PolicyInfo = NULL; SAMPR_HANDLE SamHandle = NULL; SAMPR_HANDLE DomainHandle = NULL; SAMPR_HANDLE UserHandle = NULL; PSAMPR_GET_GROUPS_BUFFER Groups = NULL; SID_AND_ATTRIBUTES_LIST TransitiveGroups = {0}; PSAMPR_USER_INFO_BUFFER UserInfo = NULL; PPACTYPE LocalPac = NULL; SAMPR_ULONG_ARRAY RidArray; SAMPR_ULONG_ARRAY UseArray; SECPKG_CLIENT_INFO ClientInfo; // // local variables containing copy of globals. // UNICODE_STRING LocalMachineName; UNICODE_STRING LocalDomainName; UNICODE_STRING LocalAccountName = NULL_UNICODE_STRING; KERBEROS_MACHINE_ROLE LocalRole = KerbRoleWorkstation; BOOLEAN GlobalsLocked = FALSE; RidArray.Element = NULL; UseArray.Element = NULL; LocalMachineName.Buffer = NULL; LocalDomainName.Buffer = NULL; // // Verify that the caller has TCB privilege. Otherwise anyone can forge // a ticket to themselves to logon with any name in the list. // Status = LsaFunctions->GetClientInfo(&ClientInfo); if (!NT_SUCCESS(Status)) { return(Status); } if (!ClientInfo.HasTcbPrivilege) { return(STATUS_PRIVILEGE_NOT_HELD); } // // If we are a domain controller, call SAM to do the mapping. // Otherwise, do it ourselves. // // // Common code for both wksta and DC - open SAM // However, if we're a realmless wksta, we know we have a client // mapping to a local account so skip lookup on DC // // // Call the LSA to get our domain sid // Status = LsaIQueryInformationPolicyTrusted( PolicyAccountDomainInformation, &PolicyInfo ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Open SAM to get the account information // Status = SamIConnect( NULL, // no server name &SamHandle, 0, // no desired access TRUE // trusted caller ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = SamrOpenDomain( SamHandle, 0, // no desired access (PRPC_SID) PolicyInfo->PolicyAccountDomainInfo.DomainSid, &DomainHandle ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // grab the globals while holding the lock. // ... then release the lock prior to making the call! // KerbGlobalReadLock(); GlobalsLocked = TRUE; LocalRole = KerbGlobalRole; Status = KerbDuplicateString( &LocalMachineName, &KerbGlobalMachineName ); if(!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Failed to duplicate KerbGlobalMachineName\n")); goto Cleanup; } if( LocalRole == KerbRoleDomainController ) { Status = KerbDuplicateString( &LocalDomainName, &KerbGlobalDomainName ); if(!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Failed to duplicate KerbGlobalDomainName\n")); goto Cleanup; } } KerbGlobalReleaseLock(); GlobalsLocked = FALSE; // // If the is a DC, try to look up the name in SAM as an AltSecId. // If that fails, we will try looking at the registry mapping. // if (LocalRole == KerbRoleDomainController) { UNICODE_STRING AltSecId = {0}; KERBERR KerbErr; KerbErr = KerbBuildAltSecId( &AltSecId, ClientName, NULL, // no unicode realm ClientRealm ); if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; } Status = SamIGetUserLogonInformationEx( SamHandle, SAM_OPEN_BY_ALTERNATE_ID, &AltSecId, USER_ALL_PAC_INIT, &UserInfo, &TransitiveGroups, NULL // no user handle ); KerbFreeString(&AltSecId); } if (!NT_SUCCESS(Status) || (UserInfo == NULL)) { if (!ARGUMENT_PRESENT(MappedClientName) || MappedClientName->Buffer == NULL) { Status = KerbMapClientName( &LocalAccountName, ClientName, ClientRealm ); if (!NT_SUCCESS(Status)) { goto Cleanup; } } else { KerbDuplicateString( &LocalAccountName, MappedClientName ); } Status = SamrLookupNamesInDomain( DomainHandle, 1, (PRPC_UNICODE_STRING) &LocalAccountName, &RidArray, &UseArray ); if (!NT_SUCCESS(Status)) { goto Cleanup; } if ((UseArray.Element[0] != SidTypeUser) && (UseArray.Element[0] != SidTypeComputer)) { Status = STATUS_NONE_MAPPED; goto Cleanup; } // // Finally open the user // Status = SamrOpenUser( DomainHandle, 0, // no desired access, RidArray.Element[0], &UserHandle ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = SamrQueryInformationUser( UserHandle, UserAllInformation, &UserInfo ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = SamrGetGroupsForUser( UserHandle, &Groups ); if (!NT_SUCCESS(Status)) { goto Cleanup; } } // // This is common code // // // Set the password must changes time to inifinite because we don't // want spurious password must change popups // UserInfo->All.PasswordMustChange = *(POLD_LARGE_INTEGER) &KerbGlobalWillNeverTime; // // Finally build the PAC // Status = PAC_Init( &UserInfo->All, Groups, &TransitiveGroups, // no extra groups PolicyInfo->PolicyAccountDomainInfo.DomainSid, ((LocalRole == KerbRoleDomainController) ? &LocalDomainName : &LocalMachineName), &LocalMachineName, 0, // no signature 0, // no additional data NULL, // no additional data &LocalPac ); if (!NT_SUCCESS(Status)) { goto Cleanup; } *Pac = LocalPac; LocalPac = NULL; Cleanup: if( GlobalsLocked ) { KerbGlobalReleaseLock(); } KerbFreeString( &LocalMachineName ); KerbFreeString( &LocalDomainName ); KerbFreeString( &LocalAccountName ); if (UserHandle != NULL) { SamrCloseHandle( &UserHandle ); } if (DomainHandle != NULL) { SamrCloseHandle( &DomainHandle ); } if (SamHandle != NULL) { SamrCloseHandle( &SamHandle ); } if (Groups != NULL) { SamIFree_SAMPR_GET_GROUPS_BUFFER( Groups ); } SamIFreeSidAndAttributesList(&TransitiveGroups); if (UserInfo != NULL) { SamIFree_SAMPR_USER_INFO_BUFFER( UserInfo, UserAllInformation ); } if (PolicyInfo != NULL) { LsaIFree_LSAPR_POLICY_INFORMATION( PolicyAccountDomainInformation, PolicyInfo ); } SamIFree_SAMPR_ULONG_ARRAY( &UseArray ); SamIFree_SAMPR_ULONG_ARRAY( &RidArray ); if (LocalPac != NULL) { MIDL_user_free(LocalPac); } return(Status); } #endif // WIN32_CHICAGO