c++ - net - ¿Cómo suplantar a un usuario de un servicio correctamente?
servicios was (1)
No necesita llamar a ImpersonateLoggedOnUser()
ya que está pasando el token del usuario a LoadUserProfile()
. Llame a ImpersonateLoggedOnUser()
solo si necesita llamar a API que no le permiten pasar un token de usuario.
Si lee el resto de la documentación de LoadUserProfile()
, dice:
El proceso de llamada debe tener los privilegios SE_RESTORE_NAME y SE_BACKUP_NAME.
Al suplantar al usuario para el que está intentando cargar un perfil, es probable que pierda esos privilegios. Así que no se haga pasar por el usuario.
Actualización: prueba algo como esto:
// 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 );
En cuanto al Registro, el manejador hProfile
es el HKEY
abierto para el árbol HKEY_CURRENT_USER
del usuario. Simpy lo HKEY
de HANDLE
a HKEY
al pasarlo a las funciones de la API de registro. Ya está abierto, por lo que no necesita llamar a RegOpenKeyEx()
para RegOpenKeyEx()
a abrir esa misma clave, pero puede usarla como clave raíz al crear / abrir subclaves, o leer / escribir valores en la clave raíz.
Estoy trabajando en un servicio, que debe suplantar al usuario que inició sesión.
Mi código hasta el momento, con manejo básico de errores:
// 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;
}
De acuerdo con MSDN:
Cuando un usuario inicia sesión de manera interactiva, el sistema carga automáticamente el perfil del usuario. Si un servicio o una aplicación suplanta a un usuario, el sistema no carga el perfil del usuario. Por lo tanto, el servicio o la aplicación debe cargar el perfil del usuario con LoadUserProfile.
Los servicios y aplicaciones que llaman a LoadUserProfile deben verificar si el usuario tiene un perfil itinerante. Si el usuario tiene un perfil itinerante, especifique su ruta como el miembro lpProfilePath de PROFILEINFO. Para recuperar la ruta del perfil móvil del usuario, puede llamar a la función NetUserGetInfo, especificando el nivel de información 3 o 4.
Tras el regreso exitoso, el miembro hProfile de PROFILEINFO es un identificador de clave de registro abierto en la raíz del subárbol del usuario. Se ha abierto con acceso completo (KEY_ALL_ACCESS). Si un servicio que se hace pasar por un usuario necesita leer o escribir en el archivo de registro del usuario, use este controlador en lugar de HKEY_CURRENT_USER. No cierre el mango hProfile. En cambio, páselo a la función UnloadUserProfile.
Si uso mi código como está ahora, entonces funciona. Sin embargo, es un poco extraño, porque primero tengo que suplantar al usuario que ha iniciado sesión y luego termino la suplantación, para cargar el perfil de los usuarios. Si no termino la suplantación, LoadUserProfile fallará con el error 5 (acceso denegado). ¿Y después de que LoadUserProfile tuvo éxito, debería suplantar al usuario nuevamente?
Entonces mi pregunta es, ¿esto se debe hacer de esta manera, o estoy haciendo algo mal? Otra pregunta es, que si LoadUserProfile tuvo éxito, podría usar hProfile como Handle para el registro de usuarios conectados. La pregunta es ¿cómo? Porque para usar RegOpenKeyEy y RegSetValueEx necesito pasar un HKEY, no un HANDLE. Entonces, ¿cómo puedo usar este mango?
¡Gracias!