OK. I just stumbled up on this article. It has sample code that does exactly what we want. Unfortunately, it uses undocumented structures and values which I can't find anywhere.
The following forum might be worth watching:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=288831&SiteID=1
OK, the above can be found in the DDK. And to clarify, what I really want to do is launch a process which runs in virtualization mode. The following should do the trick, except that the calling process (whose TokenVirtualization flags are set to false) doesn't have permission to modify said flags on my new token.
Which is stupid.
I can understand being unable to set the flags to false, as that would allow developers to give tokens access rights they shouldn't have. However, I should always be able to lower the access rights of a token (such as by setting virtualization to true).
*SIGH*
Here's what I'm trying:
DWORD True = 1;
if (!::SetTokenInformation(hNewToken, TokenVirtualizationAllowed, &True, sizeof(True))){throw SystemException("LaunchMediumIntegrityProcess2.SetTokenInformation(3)");}
if (!::SetTokenInformation(hNewToken, TokenVirtualizationEnabled, &True, sizeof(True)))
{throw SystemException("LaunchMediumIntegrityProcess2.SetTokenInformation(4)");}
Folks, this is getting very complicated. All I want to do is, in a process running with elevated (i.e. administrator) rights, to start a process that has the ordinary set of rights that it would get if it were started by the user from explorer. I do not want to turn virtualization on or off. I do not want to start a low integrity process (as is described in http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ietechcol/dnwebgen/protectedmode.asp). I just want to get a plain, ordinary process. I do not want to turn off specific rights, one-by-one. That is too complicated and bug-prone. Who says that tomorrow an elevated process won't get yet another super-duper right that I also have to turn off, if I am turning off specific rights one-by-one? This is really a very simple thing, and there has to be a simple way to do it. Is anyone from Microsoft listening out there?
I have experimented a little with using SaferCreateLevel() and SaferComputeTokenFromLevel() to create a "normal user" process from an administrator process. I found that the normal user process created this way on a Vista system still had the "Mandatory Label\High Mandatory Level" SID in its token, whereas a default process created by Vista for an administrator user has instead the "Mandatory Label\Medium Mandatory Level" SID in its token. I was able to correct this by adding code such as the following after SaferCreateTokenFromLevel() and before CreateProcessAsUser():
SID_IDENTIFIER_AUTHORITY siaMLA = SECURITY_MANDATORY_LABEL_AUTHORITY;
PSID pSidMedium = NULL;
TOKEN_MANDATORY_LABEL TIL = {0};
if (AllocateAndInitializeSid(&siaMLA,
1,
SECURITY_MANDATORY_MEDIUM_RID,
0,
0,
0,
0,
0,
0,
0,
&pSidMedium)) {
TIL.Label.Attributes = SE_GROUP_INTEGRITY;
TIL.Label.Sid = pSidMedium;
if (SetTokenInformation(hToken,
TokenIntegrityLevel,
&TIL,
sizeof(TOKEN_MANDATORY_LABEL))) {
Sigh... Indeed it is true that SAFER does not (at least in Vista Beta 2) set the TokenIntegrityLevel to the medium SID, nor TokenVirtualizationAllowed to TRUE, nor even TokenElevationType (whatever its effect is...) to TokenElevationTypeLimited. It does set TokenElevation to FALSE. Please note that there are several more new token information types (e.g. TokenLinkedToken, TokenHasRestrictions, TokenAccessInformation, and TokenMandatoryPolicy) whose meaning I do not understand and which I did not even try checking, so I do not know if SAFER dealt with them. Also note that, as christophilus discovered above, setting TokenVirtualizationAllowed to TRUE does not even work via SetTokenInformation (at least in Beta 2). And also note that when setting TokenIntegrityLevel, TIL.Label.Attributes should probably be set to SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED, not just SE_GROUP_INTEGRITY. And still further note that under Beta 2, it is insufficient to set TokenIntegrityLevel - one must also set TokenIntegrityLevelDesktop (with SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED_DESKTOP). But in the July CTP version of the SDK, TokenIntegrityLevelDesktop has disappeared...
Furthermore, it turns out that processes with a token manipulated by SAFER and SetTokenInformation behave strangely in a number of ways. First, they are not considered to be of the same user as explorer for the purposes of COM, so you can't connect to a COM server running in such a process. Second, at least in the July CTP and pre-RC1, when such processes themselves ShellExecute an exe marked with a requiresAdministrator manifest that would normally result in an elevation dialog, strangely enough no elevation dialog results, and the new process runs without admin privileges (and fails).
At the risk of sounding repetetive, what all this boils down to is a need for Microsoft to provide a simple, single API that will do the same thing that Vista does to the token at logon, with as few options as possible (to minimize the chances of doing it wriong). It should not take ten bug-prone, not-quite-documented API calls to do this. This is not an esoteric task: any installer that wants to support a checkbox at the end to run the product needs it; any administrative program that wants to be able to run notepad to display some text file needs it, and so on. As I said, a single SAFER-type call to do the job would be the minimum acceptable, but a new CreateProcess flag is what's really needed.
I am trying to do the same thing and running into the same problems, it seems that the safer api does not do what we want it to do, the resulting token will have more priveleges entirely removed than the restricted token used by the explorer.
This really is a huge oversite on the part of microsoft, especially considered the goal is to limit the number of process's that have priveleged access. You think the ability to launch a restricted process that has the same user token as the one in explorer (from an admin process) would be the one of the first things put out there for developers.. and it should be something thats recommended for use... Geez?! If you guys are going to go this whole restricted route.. dont go halfway..
PS. let me know what you guys figure out for this.
I assume your service is running as Local System. If so, there is probably something wrong with the token you are passing to it. Did you get it from DuplicateTokenEx? We use:
HANDLE hTokenDup = NULL;
if (!DuplicateTokenEx(
hToken, MAXIMUM_ALLOWED, NULL,
SecurityIdentification, TokenPrimary,
&hTokenDup)
) {
CloseHandle(hToken);
return false;
}
LPVOID lpEnvironment = NULL;
if (!CreateEnvironmentBlock(
&lpEnvironment, hTokenDup, FALSE /* do not inherit */)
) {
CloseHandle(hToken);
CloseHandle(hTokenDup);
return false;
}
Also, see the Calling CreateProcessAsUser() from service thread.