dahall / TaskScheduler

Provides a .NET wrapper for the Windows Task Scheduler. It aggregates the multiple versions, provides an editor and allows for localization.
MIT License
1.21k stars 191 forks source link

E_ACCESSDENIED on RegisterTaskDefinition with InteractiveToken from WindowsService #881

Closed rutemja closed 3 years ago

rutemja commented 3 years ago

Describe the bug I am running a Windows Service, which is starting a Windows Forms application ("BrowserOverlay") via TaskScheduler.

Private Function TryStartBrowserOverlay(domain As String, windowsUser As String, windowsUserPassword As String, adminUser As String, adminUserPassword As String) As Boolean
        Try
            _log.Debug($"Trying to create a scheduled task which will start the Browser overlay for user '{domain}\{windowsUser}'. AdminUser: {adminUser}")

            Dim args = $"/machineId {_machineId}"

            Using scheduler = New TaskService(Nothing, adminUser, domain, adminUserPassword)

                Dim trigger = New RegistrationTrigger()
                trigger.Delay = TimeSpan.FromSeconds(1)

                Dim definition = scheduler.NewTask()
                definition.Triggers.Add(trigger)
                definition.Actions.Add(_browserOverlayFilePath, args, _browserOverlayFolderPath)

                definition.RegistrationInfo.Author = "MyApp"
                definition.Settings.ExecutionTimeLimit = TimeSpan.Zero ' Otherwise the BrowserOverlay will be stopped after three days
                ' definition.Settings.DeleteExpiredTaskAfter = TimeSpan.FromHours(1) ' Scheduled task can not be registered when this property is set

                scheduler.RootFolder.RegisterTaskDefinition(Nothing, definition, TaskCreation.Create, $"{domain}\\{windowsUser}", windowsUserPassword, TaskLogonType.InteractiveToken)

            End Using

            _log.Info($"Successfully created a scheduled task which will start the BrowserOverlay for user '{domain}\{windowsUser}' in a second. Args: {args}")
            Return True

        Catch ex As Exception
            _log.Debug($"Could not start BrowserOverlay via TaskScheduler for user '{domain}\{windowsUser}' because an exception occurred. AdminUser: {adminUser}", ex)
            Return False
        End Try
    End Function

This is working fine on Windows 7, but on Windows 10 I get the following exception:

System.UnauthorizedAccessException: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
   at Microsoft.Win32.TaskScheduler.V2Interop.ITaskFolder.RegisterTaskDefinition(String Path, ITaskDefinition pDefinition, Int32 flags, Object UserId, Object password, TaskLogonType LogonType, Object sddl)
   at Microsoft.Win32.TaskScheduler.TaskFolder.RegisterTaskDefinition(String path, TaskDefinition definition, TaskCreation createType, String userId, String password, TaskLogonType logonType, String sddl)
   at MyApp.TryStartBrowserOverlay(String domain, String windowsUser, String windowsUserPassword, String adminUser, String adminUserPassword)

To Reproduce Run the code above in a demo application.

Expected behavior Register the task (like it does on Windows 7).

Environment (please complete the following information):

rutemja commented 3 years ago

Some more findings:

dahall commented 3 years ago

Please search all closed issues. This has come up before and the fix mimics running under ASP.NET. It is a permissions problem, not a library problem.

rutemja commented 3 years ago

@dahall, thanks for your quick answer. I searched for "access denied" and looked into all the issues. Unfortunately I have not found something that solves my problem. Could you provide a link to the issue you have in mind?

Anyway, I agree with you that this might be a permission problem, but I can't see why this works on Windows 7 and not on Windows 10.

To dig deeper into this, I extracted my code into a minimal test application and started it as a local admin user. Like this the adminUser is the same as the windowsUser. But I still get the E_ACCESSDENIED error at RegisterTaskDefinition.

My .NET Framework 4.5.2 program is running with Administrator privileges (either as Windows Service or for testing I started my Visual Studio with "Run As Administrator"). Otherwise, the exception would be thrown at new TaskService already.

Do you have any idea how to solve this? Should I provide the source of my test application?

dahall commented 3 years ago

The Windows 10 implementation of Task Scheduler added new security and group policies. Have you run the Troubleshooting Tool on the system having problems? Also, have you checked the Wiki page on security?

rutemja commented 3 years ago

@dahall thanks a lot for your input.

Actually in my new test set up the local users "Log on as a batch job" and "Log on as a service" privileges have not been set. I will try to ensure these permissions by using this C# implementation or your Vanara.Security package. (Maybe setting these permissions is something you can add to this library.)

Also, the "Remote Registry" service was started by the Troubleshooting Tool. Which should not be necessary in my local set up, correct? (Maybe you can add checking and/or starting of this service to this library.)

(Like I had it on my previous test set up) The Troubleshooting Tool has now only one a V1 issue with the firewall. I guess this should not be the problem.

But unfortunately all this did not help. I still get the same behavior.

Do you have any other ideas?

rutemja commented 3 years ago

I tried to use the schtasks via cmd (Run As Administrator) directly and got a more specific error message. Maybe this helps?

C:\Windows\system32>schtasks /create /tn Test /tr c:\test.exe /sc once /st 18:00 /sd 08/04/2021 /s MyDomain /u MyDomain/Jan /p *** /ru Jan /rp ***

ERROR: User credentials are not allowed on the local machine.

Actually the same command worked, when I executed it on a remote Windows 7 machine.

C:\Windows\system32>schtasks /create /tn Test /tr c:\test.exe /sc once /st 18:00 /sd 08/04/2021 /s MyDomain /u MyDomain/Jan /p *** /ru Jan /rp ***

SUCCESS: The scheduled task "Test" has successfully been created.
rutemja commented 3 years ago

On the local machine it works without setting the user (only remote user). The RemoteRegistry service was stopped.

C:\Windows\system32>schtasks /create /tn Test /tr c:\test.exe /sc once /st 18:00 /sd 08/04/2021 /s MyDomain /ru Jan /rp ***

SUCCESS: The scheduled task "Test" has successfully been created.

So it seems to me like it is no permission issue.

I'm now tempted to build my own wrapper around schtasks. But I'm not sure, if the more complex use cases (non-interactive windows service creates a scheduled task by using the admin user + the application starts interactively by using a not-admin user) can be solved like this.

Can you think about a situation in your code, which could lead to ERROR: User credentials are not allowed on the local machine. that might be thrown as UnauthorizedAccessException (maybe because too many user parameters are set)?

dahall commented 3 years ago

There are two sets of credentials used by Task Scheduler: 1) Those to access the Task Scheduler engine on the machine

  • These are set with the TaskService constructor in this library by impersonating the credentials for connection
  • These are set with the /S and /U options in schtasks 2) Those used to run the scheduled task when triggered
  • These are set with the TaskFolder.RegisterTaskDefinition method in this library
  • These are set with the /RU option in schtasks

Based on that and the information on the schtasks commands that are working, I think you can mimic it with the following on your Win10 box:

// Shorthand
TaskService.Instance.AddTask("Test", new TimeTrigger(new DateTime(2021, 08, 04, 18, 0, 0)), new ExecAction("C:\\test.exe"), "Jan", "***");
// Full code
var taskDef = TaskService.Instance.NewTask();
taskDef.Triggers.Add(new TimeTrigger(new DateTime(2021, 08, 04, 18, 0, 0)));
taskDef.Actions.Add("C:\\test.exe");
TaskService.Instance.RootFolder.RegisterTaskDefinition("Test", taskDef, TaskCreation.Create, "Jan", "***");
dahall commented 3 years ago

If the above code still is having problems, supply full credentials to the TaskService constructor rather than using TaskService.Instance.

var ts = new TaskService("MACHINENAME", "UserName", "UserDomain", "**pwd**");
ts.AddTask...
rutemja commented 3 years ago

@dahall Thanks for your advice. I really appreciate your help.

But as far as I can see, your suggested code is exactly what I have programmed. I tried a lot these days, but I can't figure out why it is not working.

Have I overseen something?

dahall commented 3 years ago

@rutemja I don't know what else to recommend. I can tell you that my unit tests run on all versions of Windows since XP, and all versions of ASP.NET since .NET 3.5 using impersonation. This is part of a product line that undergoes full testing across over 100 environments. I am 99.9% confident it is something unique to your environment.

Have a look at #794 and #481 also. Check the UAC status of the application running on Win10.

rutemja commented 3 years ago

@dahall hmm...

Just to verify it is no UAC problem I disabled it completely and restarted my PC. The Troubleshooting Tool has no issues and my application is definitely running with enhanced privileges (I double-checked that with the Task Manager). I also tried your code.

But the problem is still occurring. Do you maybe have a possibility to throw a more detailed exception?

dahall commented 3 years ago

Just to be clear, schtasks IS working on the Win10 environment and the code using this library is giving the E_ACCESSDENIED error when calling RegisterTaskDefinition or AddTask on the same machine running elevated?

rutemja commented 3 years ago

Yes. I can confirm that now:

I'm very confused because it is working on some Windows 10 environments and on others not. Actually I have another PC with the exact same base image, and it is working on that one. I can't figure out which other setting causes this issue. Do you have a clue?

dahall commented 3 years ago

I only have one thought, is it possible that the task already exists in some environments and that the task was created using credentials that the current user would not have the rights to overwrite?

rutemja commented 3 years ago

Actually I got it running now. But I don't know how.

I'm now granting the user "Log on as a batch job" rights programmatically (like you do in your Troubleshooting Tool). But it did not work at first. Then I came back some days later with a version that only does some more logging, and it worked.

Seems like it takes some time (or a login/reboot) until the "Log on as a batch job" right is taken into account. But I did not have the opportunity to test this behavior with another environment, yet.

dahall commented 3 years ago

Thank you for closing the loop on how you got this resolved! It should help others.