ChangeServiceConfig problem setting logon credentials - winapi

I've got this weird problem - I'm calling ChangeServiceConfig on a newly installed service (I CreateService it myself) to supply the logon credentials, but while the function succeeds (returns TRUE), if I try to start the service, it fails with a 1069 (logon failed). If I go into the service manager and modify credentials by hand (I can see the user name is correct, but of course can't see the password), then it's all ok and it starts ok.
The call itself is trivial:
ChangeServiceConfig(hService, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, strUser, strPassword, NULL);
Any ideas on where I should be looking and what could be wrong?
Thanks in advance.

The user account must explicitly have rights to log on as a service (SeServiceLogonRight). Many users, including computer admins, may not have this flag set, and you may need to set it manually. The windows services control panel actually does this silently behind the scenes when you use it to configure services.
I also have some vague foggy memories about needing to fully qualify the username. It needs to be in DOMAIN\Username format - If it's a local account you need to specify .\Username or find out the machine name and use MACHINENAME\Username

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 permissions are required for the Win32 LogonUser function?

Suppose that I want to run the LogonUser function in my code for a user with uid Bob, what permissions must Bob have for me to be able to call this function to log in as Bob successfully?
I don't know if there is a list anywhere since parts of the security system can use custom "plugins" (Authentication Packages, Security Support Provider Interface/Security packages and GINA/Credential Providers) and they might have other requirements.
On a default system it probably goes something like this:
The named user account passed to LogonUser needs the SE_*_LOGON_NAME account right that matches the logon type (LOGON32_LOGON_*) and the logon needs to pass the LSA and/or domain controller requirements (Logon hours, password not expired etc)
The process calling LogonUser needs SE_CHANGE_NOTIFY_NAME (Everyone has this by default), maybe SE_TCB_NAME (Required on Win2000). If you are going to call CreateProcessAsUser on the returned token you also need SE_INCREASE_QUOTA_NAME and maybe SE_ASSIGNPRIMARYTOKEN_NAME...
If you just want to start a process as another user you might want to use CreateProcessWithLogonW, if you just want to validate the credentials this KB article has a code example that does not use LogonUser (It seems like it might have some guest account issues though)

Automatically running a program as administrator in VB.NET

I am developing a VB.NET based application which requires administrative privileges in order to run properly. However I went through several tutorials online and it all mentioned how to do this using a mainifest file. The problem with this technique is that whenever the program is opened I need to type in the Administrator password, however I want this to happen automatically. (The user should not type the Admin password, the password must be entered from the code level)
The following code is the closest I could find, but it too does not satisfy my problem,
Dim securePass As New Security.SecureString()
Dim pass As String = "password"
For Each c As Char In pass
securePass.AppendChar(c)
Next
' If there isn't a domain, set the argument to 'Nothing', as demonstrated below.
Process.Start("testsoftware\WinKBSel\WinKBSel1003.exe", "username", pass, Nothing)
Could anyone please let me know how I could do this without requiring the user to enter the administrator password?
You would probably be best splitting the application up, if you had a back-end service that runs under an administrator account your application could use that to do stuff it requires at an elevated privilege. UAC is designed to protect the user from themselves, you can get around it by adding a backend service... but be very careful about opening up holes in this way as they could be exploited by other applications/viruses. Storing an administror password inside an application is always a bad idea as the application could be decompiled and the password extracted.
You cannot create a program that will automatically log on as a user unless the username/password is already known.
That means that you're either restricted to running on systems where your app already knows the credentials, your user has to enter the credentials into your app, or your user has to use the "Run As" functionality in Windows to use the correct user.
If what you're proposing were actually possible, it would represent a huge security hole: you'd be able to run any code as any user without requiring authentication first. That is not a good thing!
Think about it like this: if you were sold a program that would automatically log on as administrator even without your permission, how would you feel?
UAC is designed so that you cannot do what you want. UAC ensures that in order to elevate the user must pass through a UAC dialog (either the admin consent dialog or the over-the-shoulder dialog). You need to adapt your expectations to the reality of UAC.
You don't need a code for this action, UAC is made so you can automatically log on without the user's permission. What you can do is use the manifest and change the startup so when the application starts it runs as administrator and asks for permission! Change the manifest. look it up!

Vista: create a process from service with out popping up any dialog boxes

I am new to vista and not a advanced programmer. From past few days i am trying to digest many technical details about vista. But still i have few basic questions on it. Hope you all will be help me in getting the answers for my questions.
Can we create a interactive process(which is having a embedded manifest file with "invokeAsAdministrator") from a service which is running under Local system account?
Here i know about the session 0 isolation and all.Still i am asking this question because, when i create a interactive process through CreateProcessAsUser(which requires admin privileges) it is failing with error 740. While using this i have given proper session id. The session id is that of my active desktop, where my user login as administrative privilages.
If the process can be created does that show up the UAC dialog?
If the UAC dialog shows up, can we avoid this?
It is well understandable problem. Local account has no privelegies of admin. The only thing you can do is impersonation. Temporary you emulates another account (in your case it is admin). Example of this technique you can find in MSDN for topic WindowsIdentity.Impersonate ( http://msdn.microsoft.com/en-us/library/chf6fbt4.aspx )
EDIT
Sorry, only after post noticed that you use vc++,
On Win API use ImpersonateLoggedOnUser
The createprocessasuser is failing in my case because by default when we query for the user token for the users desktop session I was getting a restricted token that was created for the user (for administrators two tokens are created 1)restricted token 2) full token; any how my application can be run only by administrators).
By browsing the net i have found that i have to find for linked token and use that token for creation of process.
The code for creation of process from service can be found in the following question:
Desktop problem with using CreateProcessAsUser from a service on Vista

Resources