Creating a service for user (S4U) token - windows

The Windows Task Scheduler can create tasks that run with the account of a particular user, without storing the user password. They call it "S4U", service for user. This should work something like the scheduler creates such a token for the current user and can use it to run the scheduled process under that user account. They claim that it cannot access network or encrypted resources with this system. The scheduler itself runs with the SYSTEM account for it to work. Here's an article that describes it. The relevant quote from it:
TASK_LOGON_S4U is yet another option that provides a more secure
alternative. It takes advantage of a service for user (S4U) logon to
run the task on behalf of the specified user, but without having to
store the password. Since the Task Scheduler runs within the local
system account, it can create a S4U logon session and receive a token
that can not only be used for identification, but also for
impersonation on the local computer. Normally a S4U token is only good
for identification.
I need to use this authentication scheme in my application, but can't let the Task Scheduler do it but need to do it myself, because I need it for any number of accounts. Whenever a user registers a task with my application, any followup tasks must run under the same user. But since they cannot overlap, I need to do the serialisation myself.
I cannot find any information about this "S4U" thing. How could I implement it in my application? C# preferred, but WINAPI and C is okay.
Update: This is what I've tried, and it doesn't work.
// The WindowsIdentity(string) constructor uses the new
// Kerberos S4U extension to get a logon for the user
// without a password.
WindowsIdentity wi = new WindowsIdentity(identity);
WindowsImpersonationContext wic = null;
try
{
wic = wi.Impersonate();
// Code to access network resources goes here.
}
catch()
{
// Ensure that an exception is not propagated higher in the call stack.
}
finally
{
// Make sure to remove the impersonation token
if( wic != null)
wic.Undo();
}
But I've got the impression now, that you can't just say you want to be a certain user. Not even as System. You need to be logged in as that user and can generate some token that allows you to become that user later again, without the password. So this must be a two-step thing, first I need to get the token and store it on disk; later I can use that token to impersonate. None of the examples explains this.

"The computer may not be joined to the domain"
S4U requires access to a KDC. S4U is actually two protocols. S4U2Self and S4U2Proxy. What it is doing is using an addition to Kerberos to get service tickets for a user, but that account that goes and gets the ticket has to have a special delegation enabled on it. See here for this set up.
But unless you are actually letting the process die etc, why not just get the users service ticket or TGT? Is your application local or is it a service running remote to the user?
Task scheduler needs to go get a new one because a service ticket isn't valid forever. Or in some delegation schemes the user hasn't passed a service ticket to the Application Server and then the AS goes and requests and service ticket via S4U2Self, and then uses that service ticket to request a ticket to a second service via S4U2Proxy.

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.

Keycloak/OpenID: request user-information on behalf of the client application

I have a Spring Boot-application with a public REST-API. The user authentication is handled by Keycloak. Users can use the API to submit jobs, which are then executed periodically as a Spring Batch. Some of these jobs require the user to be notified via eMail after completion.
The application currently does not store any user-related information, except the ID of the user, who submitted the job. My problem is now, that I need the eMail-address belonging to that User-ID to send the job-completion notification. The JWT passed to the API, which contains this information, can't be used at this time, since the job is executed asynchronous in the batch-context.
I came up with 2 possible solutions so far, which both have their own drawbacks in my opinion:
solution 1: extracting the required information from the JWT and persist them in the application's database for later use
drawbacks:
the application should not be concerned with storing user-information; it also shouldn't duplicate data controlled by other applications
the user might change its eMail-address in the primary user-database, without getting noticed by my application
solution 2: requesting user-details as needed by using Keycloak-APIs.
This looks theoretically like a good approach to me. The suggested solutions on SO utilize the Keycloak Admin-API (endpoint /auth/admin/realms/{realm}/users/{user-id}) to fetch user-details. This requires the application to be configured as a "confidential client" with own client-credentials and an enabled service account. What bothers me here: It appears a bit strange to me, that a regular application without any special privileges uses an Admin-API. Also, the users-endpoint is not restricted to users, which have previously given consent to access their data.
Is there a good way to solve this problem?
If job requires an e-mail for its completion, why doesn't it request it as start parameter and just keep it in memory?
This is how I would proceed:
#Controller extracts e-mail from ID token on the end-point from which the user triggers job start
user e-mail is provided among other parameters to the job
job completes sending the e-mail
e-mail is lost, with other job resources

user without password can be impersonated only if they are in admin group

The goal is to have a service create a process which has the security context of a user whose password is not known.
I understand and accept the limitation that the new process will only be able to access local resources
I think I am close to a solution. Everything works fine so long as the user to be impersonated is in the local administrators group.
But if not I get an exception 0xc06d007e.
It seems the user to be impersonated lacks some permission or privilege that it has when it is in the administrators group.
But how can I find out what this missing right/permission/privilege is without by trial and error going through the thousands of permutations
get a token for the user to be impersonated
token = new WindowsIdentity("username#domain").Token
create a primary token
token2 = duplicatetokenex(token)
The process to be created is a simple console application and indeed I don't even need a console.
process_creation_flags.DETACHED_PROCESS
| process_creation_flags.CREATE_NO_WINDOW
| process_creation_flags.CREATE_NEW_PROCESS_GROUP
try to start the new process as the impersonated user
createprocessasuser(token2, "myapplication.exe" etc etc)`
As I say this works fine if username#domain is in the local admin group
otherwise I get exception 0xc06d007e I think when the new process is trying to start
As one of the steps towards creating the service, I was running the code as a console application and it was during that stage I was getting the exception mentioned above.
But I found that running the code as a service works fine!!
So I never found out why the user-to-be-impersonated (impersonatee?) needs to be in the local admin group when the impersonation is done from a console application.
I suspect it is something to do with sessions and / or desktops.
I have not found many discussions where the scenario is impersonating a user whose password you do not know. So I just want to re-assure anyone having problems that it is indeed possible and the main points are:
1) run the code as a service (with local system or a user with all the required privileges)
2) get the s4u token from windowsidentity (this will be an impersonation token if the call has the required privileges)
3) create a primary token from the impersonation token using duplicatetokenex
4) createprocessasuser using the token from 3
note the process will only have access to local resources

Allow admin user to login as other users

Is there any way to login other users account for admin user ?
Currently authentication based on Meteor Accounts
I saw this post but didn't working at all now.
The feature is important for us because when user have problem in system then admin need to see it this by simulating user account.
Thanks in advance.
It seems you want to impersonate a user. This means that you want to have Meteor.userId (or this.userId depending on context) reflect the _id of a specific user both on the client and the server.
afaict the only way to do this is to login as the user. Presumably you don't want to ask the user for their password so you have a couple of choices:
Save their existing password, replace it (temporarily) with a password of your choosing, then after you're done impersonating their account, restore their existing password.
You probably don't want to ask the user for their password and you don't need to. All you need to do is set aside Meteor.user.findOne(userId).services.password.bcrypt, then reset the password to your temporary value, then restore the original bcrypt value later.
The downside is that the original user would not be able to login while you are logged-in. Plus it's really hacky.
Extend Meteor's Accounts package to provide impersonation capability in a more elegant manner.
You might also look at validateLoginAttempt. The docs are unclear as to whether a failed login attempt could be overridden with a successful one but if it could then that would provide another pathway to solve your problem.
Instead of logging in as the users, which requires their password and which is a total no-no, you may use rather alanning:roles and allow the admin to assign the role of any user in order to draw views based the user's role.
This requires a well designed role system.
As a plus you could then at least load the documents associated with the user who you want to support.
This requires a well designed document and data model.
But generally spoken you should rather focus on writing good tests (test driven development) for components as unit tests, integration tests and UI tests.
This will reduce the need to manually view the app as an end user a lot.
The most common end user problems can be reduced by creating a good knowledge base like a wiki or video tutorials.
Even if then an error occurs in the end user side, I would rather try to implement a well designed error log that allows users automatically create tickets on error which also include the error stack.
All the above methods are to be favored before logging in AS THE USER.
As #Jankpunkt has already mentioned alanning-roles I can add something you can use without installing any external package.
Just keep a type key in the profile object of the users collection. Then define some types like 1 for super-admin, 2 for admin, 3 for general etc. Then check the authorisation of particular action by checking the value of user.profile.type key.
Caveats: Make sure you are checking the type in server side. By default profile field is writable from the client end, so if you are putting type field in the profile object make sure that you are not allowing users to modify users collection in the client end.
Here is how to restrict client end update in users collection:
Meteor.users.deny({
update() { return true; }
});
Read more on roles and permissions here:
https://guide.meteor.com/accounts.html#roles-and-permissions

SSO equivalents to Windows AccessCheck call

We have an windows app that runs as an Administrative account but we need to determine the access rights to files and directories of a particular user that is logged in to the app.
One solution we have used is to use the user name only to generate an access mask:
BuildTrusteeWithName(&trustee, username);
GetEffectiveRightsFromAcl(pSecurityInfo->pAcl, &trustee, pAccessMask);
The problem with this is that it takes quite a long time on some customer sites with complex DFS setups. We believe the time is taken in looking up the user's groups etc...
So another solution we have used is to cache the user name and password to 'impersonate' the user, temporarily caching a handle to the 'impersonation' token:
// Here we get the handle to the 'impersonation' token
LogonUser(owner, NULL, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, pTempHandle);
DuplicateToken( *pTempHandle, DEFAULT_IMPERSONATION_LEVEL, pOwnerHandleCacheEntry->pHandle );
// This is then called for all files and directories traversed. pOwnerHandle is the handle of the 'impersonation' token obtained above.
// This means the overhead of getting the user's groups etc... is only done once.
AccessCheck(pSd, *pOwnerHandle, MAXIMUM_ALLOWED, &genericMapping, &privilegeSet, &dwPrivSetSize, &fileAccessMask, &accessStatus);
The problem with this is if we wish to introduce Single Sign On, we don't have access to the user's password. This will leave some sites that wish to use SSO with a system that doesn't perform very well as they will need to switch back to first solution (above).
So, my questions are:
1) Is there a way of caching the user information used to build the access rights using just the user name (in order to avoid doing lookups every time) ?
2) Failing question 1 (above), is there an equivalent of our preferred solution within an SSO environment (for example: impersonating a user using a kerberos ticket)?
Roughly speaking, the SSO equivalent of LogonUser is SSPI. SSPI allows a server to authenticate a client without needing the client's password. After successfully authenticating the client, the server will have access to a user token, but as with everything in SSPI it's complicated how to get it. From there, you can call AccessCheck which should be very fast.

Resources