[英]How to impersonate a user from a service correctly?
I'm working a service, which should impersonate the logged on user. 我正在使用一项服务,该服务应模拟已登录的用户。
My code so far, with basic error handling: 我的代码到目前为止,基本的错误处理:
// get the active console session ID of the logged on user
if ( !WTSQueryUserToken( WTSGetActiveConsoleSessionId(), &hToken ) )
{
ShowErrorText( "WTSQueryUserToken failed.", GetLastError( ), true );
return;
}
HANDLE hDuplicated;
// duplicate the token
if ( !DuplicateToken( hToken, SecurityImpersonation, &hDuplicated ) )
{
ShowErrorText( "DuplicateToken failed.", GetLastError( ), true );
}
else
{
ShowErrorText( "DuplicateToken succeeded.", 0, true );
}
// impersonate the logged on user
if ( !ImpersonateLoggedOnUser( hToken ) )
{
ShowErrorText( "ImpersonateLoggedOnUser failed.", GetLastError(), true );
return;
}
// retrieve the DC name
if ( !GetPrimaryDC( DC ) )
{
ShowErrorText( "GetPrimaryDC failed.", 0, true );
}
PROFILEINFO lpProfileInfo;
ZeroMemory( &lpProfileInfo, sizeof( PROFILEINFO ) );
lpProfileInfo.dwSize = sizeof( PROFILEINFO );
lpProfileInfo.lpUserName = CurrentUser;
// get type of profile. roaming, mandatory or temporary
int ret = GetTypeOfProfile();
if ( ret == 2 )
{
// if roaming profile get the path of it
if ( !GetRoamingProfilePath( DC, CurrentUser, RoamingProfilePath ) )
{
ShowErrorText( "Failed to retrieve roaming profile path.", GetLastError(), true );
}
}
if ( RevertToSelf( ) )
{
ShowErrorText( "Impersonation ended successfully.", 0, true );
}
if ( !LoadUserProfile( hDuplicated, &lpProfileInfo ) )
{
ShowErrorText( "LoadUserProfile failed.", GetLastError(), true );
}
else
{
ShowErrorText( "LoadUserProfile succeeded.", 0, true );
}
//do some stuff
if ( !UnloadUserProfile( hDuplicated, lpProfileInfo.hProfile ) )
{
ShowErrorText( "UnloadUserProfile failed.", GetLastError( ), true );
}
else
{
ShowErrorText( "UnloadUserProfile succeeded.", 0, true );
}
if ( !ImpersonateLoggedOnUser( hToken ) )
{
ShowErrorText( "ImpersonateLoggedOnUser failed.", GetLastError( ), true );
return;
}
According to MSDN: 根据MSDN:
When a user logs on interactively, the system automatically loads the user's profile.
当用户以交互方式登录时,系统会自动加载用户的配置文件。 If a service or an application impersonates a user, the system does not load the user's profile.
如果服务或应用程序模拟用户,则系统不会加载用户的配置文件。 Therefore, the service or application should load the user's profile with LoadUserProfile.
因此,服务或应用程序应使用LoadUserProfile加载用户的配置文件。
Services and applications that call LoadUserProfile should check to see if the user has a roaming profile.
调用LoadUserProfile的服务和应用程序应检查用户是否具有漫游配置文件。 If the user has a roaming profile, specify its path as the lpProfilePath member of PROFILEINFO.
如果用户具有漫游配置文件,请将其路径指定为PROFILEINFO的lpProfilePath成员。 To retrieve the user's roaming profile path, you can call the NetUserGetInfo function, specifying information level 3 or 4.
要检索用户的漫游配置文件路径,可以调用NetUserGetInfo函数,指定信息级别3或4。
Upon successful return, the hProfile member of PROFILEINFO is a registry key handle opened to the root of the user's hive.
成功返回后,PROFILEINFO的hProfile成员是一个向用户配置单元的根目录打开的注册表键句柄。 It has been opened with full access (KEY_ALL_ACCESS).
它已以完全访问权限(KEY_ALL_ACCESS)打开。 If a service that is impersonating a user needs to read or write to the user's registry file, use this handle instead of HKEY_CURRENT_USER.
如果模拟用户的服务需要读取或写入用户的注册表文件,请使用此句柄而不是HKEY_CURRENT_USER。 Do not close the hProfile handle.
不要关闭hProfile句柄。 Instead, pass it to the UnloadUserProfile function.
而是将其传递给UnloadUserProfile函数。
If i use my code as it is now, then it works. 如果我现在使用我的代码,那么它的工作原理。 However is it a little strange, because first i have to impersonate the logged on user, and then end the impersonation, to Load the users profile.
但是它有点奇怪,因为首先我必须冒充登录用户,然后结束模拟,加载用户配置文件。 If i don't end the impersonation then LoadUserProfile will fail with error 5 ( Access denied ).
如果我没有结束模拟,那么LoadUserProfile将失败,错误5(访问被拒绝)。 And after LoadUserProfile succeeded i should impersonate the user again?
在LoadUserProfile成功后,我应该再次冒充用户?
So my question is, this meant to be done this way, or i am doing something wrong? 所以我的问题是,这意味着这样做,或者我做错了什么? Another question is, that if LoadUserProfile succeeded i could use hProfile as a Handle to the logged on users registry.
另一个问题是,如果LoadUserProfile成功,我可以使用hProfile作为登录用户注册表的句柄。 Question is how?
问题是怎么样的? Because to use RegOpenKeyEy and RegSetValueEx i need to pass a HKEY, not a HANDLE.
因为要使用RegOpenKeyEy和RegSetValueEx,我需要传递HKEY,而不是HANDLE。 So how can i use this Handle?
那么我该如何使用这个Handle?
Thank! 谢谢!
You don't need to call ImpersonateLoggedOnUser()
since you are passing the user's token to LoadUserProfile()
. 您不需要调用
ImpersonateLoggedOnUser()
因为您将用户的令牌传递给LoadUserProfile()
。 Call ImpersonateLoggedOnUser()
only if you need to call APIs that do not allow you to pass a user token to them. 仅当您需要调用不允许您向其传递用户令牌的API时,才调用
ImpersonateLoggedOnUser()
。
If you read the rest of the LoadUserProfile()
documentation, it says: 如果您阅读了
LoadUserProfile()
文档的其余部分,它会说:
The calling process must have the SE_RESTORE_NAME and SE_BACKUP_NAME privileges.
调用进程必须具有SE_RESTORE_NAME和SE_BACKUP_NAME权限。
By impersonating the user you are trying to load a profile for, you are likely losing those privileges. 通过冒充您尝试加载配置文件的用户,您可能会失去这些权限。 So don't impersonate the user.
所以不要冒充用户。
Update: Try something like this: 更新:尝试这样的事情:
// get the active console session ID of the logged on user
DWORD dwSessionID = WTSGetActiveConsoleSessionId();
if ( dwSessionID == 0xFFFFFFFF )
{
ShowErrorText( "WTSGetActiveConsoleSessionId failed.", GetLastError( ), true );
return;
}
if ( !WTSQueryUserToken( dwSessionID, &hToken ) )
{
ShowErrorText( "WTSQueryUserToken failed.", GetLastError( ), true );
return;
}
// duplicate the token
HANDLE hDuplicated = NULL;
if ( !DuplicateToken( hToken, SecurityImpersonation, &hDuplicated ) )
{
ShowErrorText( "DuplicateToken failed.", GetLastError( ), true );
CloseHandle( hToken );
return;
}
// retrieve the DC name
if ( !GetPrimaryDC( DC ) )
{
ShowErrorText( "GetPrimaryDC failed.", 0, true );
CloseHandle( hDuplicated );
CloseHandle( hToken );
return;
}
PROFILEINFO lpProfileInfo;
ZeroMemory( &lpProfileInfo, sizeof( PROFILEINFO ) );
lpProfileInfo.dwSize = sizeof( PROFILEINFO );
lpProfileInfo.lpUserName = CurrentUser;
// get type of profile. roaming, mandatory or temporary
USER_INFO_4 *UserInfo = NULL;
int ret = GetTypeOfProfile();
if ( ret == 2 )
{
// if roaming profile get the path of it
if ( NetUserGetInfo( DC, CurrentUser, 4, (LPBYTE*)&UserInfo) != NERR_Success )
{
ShowErrorText( "NetUserGetInfo failed.", 0, true );
CloseHandle( hDuplicated );
CloseHandle( hToken );
return;
}
lpProfileInfo.lpProfilePath = UserInfo->usri3_profile;
}
if ( !LoadUserProfile( hDuplicated, &lpProfileInfo ) )
{
ShowErrorText( "LoadUserProfile failed.", GetLastError(), true );
if ( UserInfo )
NetApiBufferFree(UserInfo);
CloseHandle( hDuplicated );
CloseHandle( hToken );
return;
}
if ( UserInfo )
NetApiBufferFree(UserInfo);
ShowErrorText( "LoadUserProfile succeeded.", 0, true );
//do some stuff
if ( !UnloadUserProfile( hDuplicated, lpProfileInfo.hProfile ) )
{
ShowErrorText( "UnloadUserProfile failed.", GetLastError( ), true );
}
else
{
ShowErrorText( "UnloadUserProfile succeeded.", 0, true );
}
CloseHandle( hDuplicated );
CloseHandle( hToken );
As for the Registry, the hProfile
handle is the opened HKEY
for the user's HKEY_CURRENT_USER
tree. 对于Registry,
hProfile
句柄是用户的HKEY_CURRENT_USER
树的已打开HKEY
。 Simpy type-cast it from HANDLE
to HKEY
when passing it to Registry API functions. 当将它传递给Registry API函数时,Simpy将它从
HANDLE
类型转换为HKEY
。 It is already opened, so you do not need to call RegOpenKeyEx()
to open that same key again, but you can use it as the root key when creating/opening subkeys, or reading/writing values in the root key. 它已经打开,因此您无需再次调用
RegOpenKeyEx()
来打开相同的密钥,但在创建/打开子密钥或在根密钥中读取/写入值时,可以将其用作根密钥。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.