3

I'm working on a utility that runs in the background for a (potentially) long period of time, and automatically resumes when the system is restarted. The utility requires elevated privileges to run (i.e. "run as admin"), so I can't rely on the "Run" section of the registry (HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run) since that will only launch processes with default privileges.

So, I'm using a scheduled task with a "logon" trigger using the TaskScheduler API, and a run level of "TASK_RUNLEVEL_HIGHEST" which launches the app with the elevated privileges (and no UAC, which is necessary). So far, so good.

The problem is that this application also stores credentials in the Credential Manager, and re-reads them after reboot (e.g. when the scheduled task described above is triggered). This is done using the CredRead()/CredWrite() functions in advapi32.dll. For some reason, the CredRead() function returns false when the task is launched with RUNLEVEL_HIGHEST, and GetLastError() returns 1168 ("element not found").

According to the documents, the credentials stored by CredWrite() are associated with the logon session of the current user token; I stored my credentials with a persist type of "LOCAL_MACHINE", i.e. it should persist beyond just the current session - and indeed, it shows up in the Credential Manager even when the task fails to read it.

So it appears that the task being launched with highest privileges is causing it to not be able to see the credential created when the utility is originally launched - even though the application must be launched with elevated privileges (or else it fails for unrelated reasons before even storing the credential).

FWIW, I believe the problem is that the elevation due to being run from a "run as admin" command prompt, and the elevation due to being launched as a task with "run with highest privileges" is somehow resulting in different "levels" of elevation. I feel like I'm just missing something with either how the scheduled task is being created, or with CredWrite/CredRead. Any help is much appreciated!

UPDATE: Per CodyGray's suggestion in comments, I tried setting the requiredExecutionLevel in the app manifest. This didn't change the behavior with respect to my original problem.

I've also checked, and the token, user SID, and various "IsAuthenticated", etc. properties of the current WindowsIdentity (WindowsIdentity.GetCurrent()) are all the same between the working and non-working cases.

I've also added a call to CredEnumerate(), which returns "false" and sets the error code to 1168 ("not found", same as CredRead() above). So this indicates that the process can't find ANY of the stored credentials for that user, not just my app's credential specifically.

Finally, if I run the task manually from the Task Scheduler, it works as expected and finds the stored credential. The problem only seems to occur when the task is triggered at login.

UPDATE #2

I was (briefly) able to reproduce the problem on a dev machine, which I hadn't been able to previously. It seems like the problem will only occur if the user is a member of two groups (e.g. "Users" and "Administrators"). My default dev box login is only a member of "Administrators." When I created a new user that was a member of both groups, I reproduced the problem. However, after some amount of switching the user back and forth between Users only/Administrators only/both, I can no longer reproduce the issue with that user (even though they're back to being in both groups).

I've found a workaround, which is to store the credentials elsewhere (acceptable for now, not ideal long-term, but ultimately these aren't ultra-sensitive credentials anyway). I'd still like to understand what's happening here, however. Will continue experimenting as I have time but appreciate any insight in the meantime!

atkretsch
  • 2,367
  • 18
  • 24
  • 2
    Have you tried simply [adding a manifest](http://stackoverflow.com/questions/4383288/how-can-i-embed-an-application-manifest-into-an-application-using-vs2008/4385018#4385018) to the executable that demands elevation? The line you want to add is this one: `` – Cody Gray - on strike Mar 25 '13 at 22:50
  • I haven't, I'll give that a try tomorrow. Thanks! – atkretsch Mar 26 '13 at 02:57
  • [Manifest Tool - MT.exe](http://msdn.microsoft.com/en-us/library/windows/desktop/aa375649(v=vs.85).aspx) – Jeremy Thompson Mar 26 '13 at 03:11
  • No luck after changing the manifest, but I'll probably keep that change anyway since it is intended to be an admin-only utility. – atkretsch Mar 26 '13 at 16:31
  • Did you find any solution to this ? I am also facing the same issue. – The King Sep 03 '15 at 12:48

1 Answers1

3

I finally figured it out: Right click on your task in Windows Task Scheduler-->Click on Properties A new window opens up. Navigate to the triggers tab. Click on edit. Under Advanced Settings tick the check box for "Delay task for " and specify a time say 10 minutes. Click on OK to save your settings. Now close the task scheduler. Your scheduled task should be able to read the credentials from credential manager. This setting is also configurable through code using the task scheduler api.

The King
  • 833
  • 1
  • 15
  • 41
  • Glad you were able to get it figured out. The project I originally asked the question about has been thoroughly refactored several times so I never wound up circling back to this. – atkretsch Sep 09 '15 at 17:18