What's wrong with GetUserName Win32 API? - windows

I'm using GetUserName Win32 API to get the user name of my computer, but I found the user name is different (uppercase vs. lowercase only) when using my VPN connection into work when I was at home. I’m wondering if the VPN client or other software could be affecting the username?

The GetUserName API states:
Retrieves the name of the user
associated with the current thread.
Use the GetUserNameEx function to
retrieve the user name in a specified
format. Additional information is
provided by the IADsADSystemInfo
interface.
So it looks like that GetUserName uses IADsADSystemInfo to get it's information.
If you look at the IADsADSystemInfo interface you see it has the method:
get_UserName
Retrieves the Active
Directory distinguished name of the
current user, which is the logged-on
user or the user impersonated by the
calling thread.
So when your connected via VPN to a domain login you will most likely get Active Directory distinguished name of the current user and when you aren't, you most likely get the user name that you typed in (in whatever case you typed in) to log onto the computer with.

This API returns the name as typed by the user when logging on to the computer. So if my username is 'esac', but I type 'Esac', this API will return 'Esac'. Subsequently if I type 'ESAC' that is what it will return as well.

Not 100% sure, but I suspect that GetUserName will end up talking to the DC when you're connected to your domain network, whereas it will use the local answer otherwise

Related

Get AzureAD email address from username using Windows API?

I am using
WTSEnumerateSessionsEx(WTS_CURRENT_SERVER_HANDLE, &level, 0, &pSessionInfo, &count);
to get session information. It will tell me all logged in users. If I use it on Windows 11 with another user logged in,
WTS User: DESKTOP-VVVVVVV\joew, session id: 1, state: 0, session name: Console
WTS User: AzureAD\JoeWillcoxso_garbage, session id: 17, state: 4
From the state, joew is the active user and JoeWillcoxso_garbage. It's not exactly that, but I'm protecting it...you get the idea.
The session info will give me the domain and username. When I login to Windows for an AzureAD with a non-standard credential provider, I use my AzureAD email address and the CP is smart enough to use AzureAD as the domain name. But, when the machine was joined to AzureAD, it created a local account for the email address as JoeWillcoxso_garbage. ( I write "created a local account", but in PowerShell if I do get-localuser *, that user does not show up in the list.)
I can use LsaEnumerateLogonSessions() and LsaGetLogonSessionData() to get session data from the local security authority. I can match things between the two calls (LsaGetLogonSessionData and WTSEnumerateSessionsEx) using session id and username. However, there is no way to get an email address. I can get a PSID in the session data, not sure if there is a way to turn that into an email address. On my local machine, I'm not attached to an AD. Just running as WORKGROUP.
For an AzureAD user, calling NetUserGetInfo() always fails for the AAD user even when running elevated. I haven't yet tried as NT_AUTHORITY account...
I'm looking for an API or maybe some way to query WMI to turn the AzureAD\JoeWillcoxso_garbage user name into joe#someaad.onmicrosoft.com email address. Or, maybe need a web API to hit login.windows.net. There are places in the registry where I can find the email address, but I don't think they are guaranteed to always be there.
Since a user may be logged in already (and on Windows 10/11 with fast switching), sometimes an unlock is not CPUS_UNLOCK_WORKSTATION but CPUS_LOGON. With fast switching, if I lock, come back 10 minutes later, and do an unlock, it's not an unlock but a logon (CPUS_LOGON) although it acts just like an unlock used to do.
For instance, at HKEY_USERS\THE_SID_OF_THE USER\Software\Microsoft\Office\Teams, HomeUserUpn has the email address. I can get the SID of the logged on users...so I could possible get the email to match up... but, that's assuming that key is there. I want something more bullet proof.
• You can surely get the email address from the UPN for the signed in user through the below powershell commands very easily as you must have remote script execution rights over the systems in your network.
a) Whoami /upn --> Displays the full email address of the logged in user
b) "$env:USERNAME#$env:USERDNSDOMAIN" --> Displays the full email address along with the DNS zone in which its entry is mapped to
c) $msAccountName = ([adsisearcher]"(samaccountname=$env:USERNAME)").FindOne().Properties.mail
$msAccountName --> Displays the email address from the online O365/Azure AD account
Thus, accordingly as per the above powershell commands, you can get the email address of the logged in session user for Azure AD. Also, if you want to use the WTS query function for this purpose, kindly refer to the below link for more details as it aptly describes the usage of WTS query commands for the said purpose: -
https://www.idrix.fr/Root/Samples/upn.cpp
I wanted something to use from Win32, hence the tag. What I did find is that from a process running as NT_System account, I could iterate all the LSA sessions.
I could use LSAEnumerateLogonSessions() to get a list of all the sessions.
Once I had the sessions, for each session I could call LsaGetLogonSessionData() which would return a PSECURITY_LOGON_SESSION_DATA which importantly returns session ID, logon domain, logon name, and SID of the user. Looking at the logon domain, I could know if it was AzureAD because it would literally be AzureAD. Knowing it was AzureAD, I could then use the SID and do a registry lookup at key (not entire function):
CString s;
s.Format(L"SOFTWARE\\Microsoft\\IdentityStore\\Cache\\%s\\IdentityCache\\%s", lpwzSid, lpwzSid);
ATL::CRegKey regKey;
if (ERROR_SUCCESS == regKey.Open(HKEY_LOCAL_MACHINE, s, KEY_READ))
{
WCHAR szUserName[MAX_PATH] = { 0 };
ULONG ulChars = MAX_PATH;
if (ERROR_SUCCESS == regKey.QueryStringValue(L"UserName", szUserName, &ulChars) && ulChars > 0)
{
return CString(szUserName);
}
}
That almost works except for one little nit... sometimes the AzureAD user has logged out but still has an open LSA session. I have no idea how this happens, but I have observed it on Windows 11. IDK if this is for all AzureAD users, but I have noticed it can be the case for the account used to join the AzureAD domain. So, in that case, it is necessary to backcheck and use WTSEnumerateSessionsEx() and verify that there really is an open session.

How to get the SAM compatible domain name on a Hybrid domain computer?

EDIT:
Thank you #RbMm for your clarification questions!
I am implementing MFA in a credential provider (not a wrapper provider). One of the options I must support is to verify the same UPN is used for logging in to the computer and validating with the MFA provider. I use LsaLogonUser, ImpersonateLoggedOnUser and GetUserNameEx(UserNamePrincipal) to obtain this information. This works in most environments; however, on a computer connected to a hybrid Azure AD domain, LsaLogonUser fails. This lead to an investigation the results of which are elaborated below:
I have a computer belonging to an AzureAD hybrid domain, but I have no way of getting the SAM-compatible domain name from the AzureAD domain name.
For example,
I can log in with a user whose AzureAD upn is, for example's sake user#domain.com. The SAM-compatible name is localdomain\localuser.
GetUserNameEx(NameUserPrincipal) reutrns user#domain.com
GetUserNameEx(NameSamCompatible) returns localdomain\localuser
HOWEVER in the following code, TranslateName fails with GetLastError() returning ERROR_NO_SUCH_DOMAIN.
TCHAR validBuffer[MAX_PATH+1];
ULONG nValidSize = MAX_PATH;
TranslateName("user#domain.com", NameUserPrincipal, NameSamCompatible, validBuffer, &nValidSize);
I have also tried the following APIs without success, they all fail with different return values:
GetComputerObjectName fails with any value passed to NameFormat
GetCompterNameEx fails with ERROR_CANT_ACCESS_DOMAIN_INFO for any relevant value of NameType
NetWkstaGetInfo does not return any useful information in any field
Also, if you look at the computer's join information in This PC > Properties > Advanced System Properties, u see it as part of a workgroup, not a domain.
-if you run the dsregcmd /status command:
executed within the aforementioned user's logon session: the 'Diagnostic' part lists the localdomain in the output
executed from a command prompt running with the LocalSystem account, the output does not list the localdomain anywhere.
An important point (thanks #RbMm) - the code services a Windows Credential Provider, when using the 'Other User' tile. I am trying to pre-verify the entered credentials using LsaLogonUser, before serializing them successfully in my implementation of ICredentialProviderCredential2::GetSerialization. Using 'user#domain.com' fails, while using 'localdomain\user#domain.com' or 'localdomain\localuser' succeeds. When logging in with Windows' built in password provider, using 'user#domain.com' of course works. Maybe I should be using a different authentication package for LsaLogonUser?
I am STUMPED.
thanks for anyone's help..
Uriel

What patterns are possible in Windows for the login field

Our Web Application has a login screen: User can login with their Windows Accounts (the admin can use local accounts).
The Web Application uses the UserPrincipal in .NET-Framework to match the login name and password. The entered login will be split by the '\' into Domain part and SamAccountName part. E.g.:
Calamitas\Simon
But for local accounts in Windows it is possible to use the computer name instead the domain name:
ComputerName\LocalUser
And instead for the computer name it is possible to use a dot:
.\LocalUser
Where can I get a list of all these login patterns?
If you are calling FindByIdentity then the formats supported are listed in the IdentityType enum. This seems to be a subset of the types GetUserNameEx can return.
You have most of the important patterns covered already except for name#example.com, name#domain and example.com/name? It might also accept just a name but that lookup is going to be slow in a large domain forest.
There are also distinguished names (CN=Jeff Smith,OU=Sales,DC=Fabrikam,DC=COM) but they are hard to parse/validate.
Users are probably not going to use a GUID or a SID to log in.
If you are trying to validate form input then I would suggest allowing ?*\?* and ?*#?* (DOS syntax). Add S-?-?* to support SIDs.

LookupAccountName to get user account type VC++

I need to check whether a provided user name is a local or domain user in VC++. I thought LookupAccountName was going to give me what I needed because of the last argument. I used it to get the SID from an account name, it works perfectly fine and gives me the correct SID, but not he correct SID_NAME_USE. The website says:
peUse [out]: A pointer to a SID_NAME_USE enumerated type that indicates the type of the account when the function returns.
However peUse is always SidTypeUser no matter what type of user I supply (local user or domain user). I also tried with LookupAccountSid and I'm getting the same thing. Here are my questions then:
Can I use LookUpAccountName or LookupAccountSid to get the correct account type?
Why is the function always returning SidTypeUser as the user type?
Is there any other way to check whether a user is domain or local user from its SID or username?
Please someone help me, I have stuck on this for a while now.
Thanks!
Use LsaQueryInformationPolicy with PolicyDnsDomainInformation to retrieve the SID for the computer's primary domain. (If the Sid member is NULL, the computer is not joined to a domain.)
Otherwise, use LookupAccountName to retrieve the SID you are interested in, then use GetWindowsAccountDomainSid to extract the domain part of the user's SID.
Compare the primary domain's SID to the user's domain SID using EqualSid. If the SIDs are equal, the user is logged into a domain account; otherwise, the user is logged into a local account.

Getting logged on user's name with or without domain in Windows

Is there a direct API to get the currently logged in user's name with the domain? So, it would return something like "domain\user" when a machine is on the domain, but at the same time it would return "user" when the machine is not on the domain? If there's not, what's the best way to get this information?
I noticed there's a LookupAccountName function - would that be the right direction to take?
Try GetUserNameEx(). It supports various name formats.

Resources