Get the logged in Windows user name associated with a desktop - c

I wish to enumerate all desktops in a system and get the logged in user name for that desktop. So far I have the following code snippit as an example of obtaining a HDESK handle and trying to determine the user name associated with it (if any), but the call to LookupAccountSid fails with ERROR_NONE_MAPPED ("No mapping between account names and security IDs was done").
HDESK desk = OpenDesktop( "Default", 0, FALSE, READ_CONTROL | DESKTOP_READOBJECTS );
DWORD size = 4096;
SID * sid = (SID *)malloc( size );
GetUserObjectInformation( desk , UOI_USER_SID, sid, size, &size );
char name[512], domain[512];
int namesz = 512, domainsz = 512;
LookupAccountSid( NULL, sid, &name, &namesz, &domain, &domainsz, &s);
It might be because I am pulling out a logon SID via GetUserObjectInformation rather then a user SID. If so can I convert that to the logged in users SID?
Can anybody point me in the right direction for getting the logged in user name for an arbitrary desktop (via either it's respective HDESK or HNWD handle or even the desktop's stations HWINSTA handle)? thanks in advance.

If what you want is the user information then this will work.
call WTSEnumerateSessions to obtain an array of WTS_SESSION_INFO structures. for each structure, pass the SessionId member to WTSQuerySessionInformation with the WTSInfoClass member set to WTSUserName. This will give you the name of the user (if there is one) associated with the session.
Alternatively you can set the WTSInfoClass to WTSSessionInfo and get a WTSINFO structure back. This contains a lot of information including the user name and domain. Look at the header file definition of WTSINFO though as the MSDN page is wrong.
You have to call WTSEnumerateSessions twice, once to get the required buffer size and then once to get your information.
Relationships: One or more Desktop objects are in a Windows Station. A Windows Station is associated with a Session.

The problem is that desktops aren't associated with users at all. Try using psexec to run Notepad under the SYSTEM account. It's running on your window station, on your desktop. Otherwise, you wouldn't be able to see it.
But if you want to get the session associated with the window station, then yes it's possible. You need to call NtQueryObject with ObjectNameInformation to get the name of the object. For example, here's what I get: \Sessions\1\Windows\WindowStations\WinSta0. There's your session ID.

This is not a solution but is a good description of station/desktop. From http://www.microsoft.com/technet/security/bulletin/fq00-020.mspx
What's a windows station?
A windows station is a secure container that contains a clipboard, some global information, and a set of one or more desktops. A Windows 2000 session will have several windows stations, one assigned to the logon session of the interactive user, and others assigned to the Winlogon process, the secure screen saver process, and any service that runs in a security context other than that of the interactive user.
The interactive window station assigned to the logon session of the interactive user also contains the keyboard, mouse, and display device. The interactive window station is visible to the user and can receive input from the user. All other window stations are noninteractive, which means that they cannot be made visible to the user, and cannot receive user input.
What's a desktop?
A desktop is a secure container object that is contained within a window station. There may be many desktops contained within a windows station.
A desktop has a logical display surface and contains windows, menus, and hooks. Only the desktops of the interactive window station can be visible and receive user input. On the interactive window station, only one desktop at a time is active. This active desktop, also known as the input desktop, is the one that is currently visible to the user and that receives user input.

You could extract it from the end of the %USERPROFILE% environment variable

nbtstat used to be able to do this from the command line, with either a machine name or IP address. It's been a long time since I looked at it, though.

Correct code that worked for me:
TCHAR username[UNLEN + 1];
DWORD size = UNLEN + 1;
GetUserName((TCHAR*)username, &size);
I'm using Visual Studio Express 2012 on Windows 7 x86

Related

can we validate value of guestInfo properties set in ovf file

I have set guestInfo properties in ovf file in ProductSection and i am able to read those properties inside VM, but i want to validate those properties when user tries to set values from UI during deployment of ova/ovf
eg.
<Property ovf:key="guestinfo.ipv4.ipaddr" ovf:type="string" ovf:userConfigurable="true"ovf:value="10.243.16.53">
<Label>IPv4 IPAddress</Label>
<Description>IP address for IPv4.(required only when IPv4 protocol is static, else can keep it blank)</Description>
</Property>
i want to validate user input(ip address) to see if its valid ipv4 or not and indicate user about it
As far as I am aware, the only way you can detect if someone has added extraConfig is through the vCenter events. How you go about indicating that to the user is up to you.
A potential solution might be to use GOVC to detect an extraConfig event, or another vSphere CLI/SDK:
govc events -f | grep 'extraConfig'
The above command will return something like this when someone alters the extraConfig:
config.extraConfig("guestinfo.test"): (key = "guestinfo.test", value = "I am some extraConfig");
You could then parse this and if it is invalid ipv4, trigger an alarm on the object, or whatever you deem to be appropriate.
A more rigorous solution would be to produce a vSphere local plugin to allow your users to add extraConfig in a manner that you can control and validate.
Hope this helps!

Revert to normal user in Windows program with requireAdministrator in manifest?

I'm trying to encapsulate a number of repetitive installation tasks into a private setup program. The program is for in-house use setting up custom, single purpose systems for industrial users. I need Administrator privileges to tweak a number of Windows settings for our environment and then I need to set some current user settings for the application software packages to use.
Is it possible for a Windows program (in plain C, created with Visual Studio 2017) that uses requireAdministrator in its manifest to revert to the user that started the program when the admin privilege is no longer needed? I've seen this done in linux, but have been unable to find any examples (or even mentions) of this being done in Windows. Help? Please?
You don't.
A way given awhile back is to track down the running instance of explorer, and spawn a remote thread in it (with CreateRemoteThread) that does what you want. However CreateRemoteTherad is touchy and what if explorer isn't running?
If you know the username you can use CreateService() to create a service that runs as that user and ServiceStart() to start it but that's its own pain and now the code has to deal with no access to the desktop. Getting code running as the user on the desktop involves digging into the undocumented.
The correct design is to use two executables, the first with asInvoker that starts the second with requireAdministrator using the API call ShellExecuteEx, waits for it to finish, checks the exit code, and on success does the individual user steps.
There is no way to de-elevate a running process. You might be able to lower your rights a little bit but not all the way. Even if it was possible, you would have to hardcode the list of groups and privileges to remove/disable in your token because I don't believe there is a API to restrict a token just like UAC does.
There are many half-assed solutions out there to start a un-elevated child process:
Using the Task Scheduler
IShellDispatch2::ShellExecute in the "main" Explorer.exe instance
CreateProcessAsUser with the token from the "main" Explorer.exe instance
Bootstrapper instance elevates another instance and communicates back to parent when it needs to perform un-elevated actions
Take advantage of Explorer bug and simply execute "%windir\Explorer.exe" "c:\path\to\myapp.exe"
All of those solutions have issues related to:
Explorer might not be running (custom shell or Explorer crash)
Explorer is running elevated
Non-admin users elevate with a different administrator account with a different SID
RunAs.exe has been used and your parent process is not the same as the "main" logon session nor Explorer.exe
Final Answer
When I was finally able to install a clean Windows 10 on a spare system for testing and create a pure non-admin account to test with, the new, improved answer did not work. I added a GetUserName() call after the impersonate and wrote it to the debug log to learn that the user name was the same before and after the impersonate, even though all of the functions returned success. I can only assume that the GetShellWindow() [I also tried GetDesktopWindow(), just in case] returned a handle to an explorer/shell under the Admin context. So, now I'm using a cleaned up (to make it plain C) version of the GetConsoleUserToken() function I found here: https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/17db92be-0ebd-4b54-9e88-a122c7bc351d/strange-problem-with-wtsqueryusertoken-and-impersonateloggedonuser?forum=windowsgeneraldevelopmentissues This is working on both my development system AND on the clean Windows 10 non-admin user. The function actually searches all running processes for the explorer.exe that belongs (is attached?) to the console session and while debugging it, I did see that it find more than one explorer.exe process, but only ONE is the right one.
Thanks for all of the suggestions and comments! They gave me good ideas and put me on a path that allowed me to use better search terms to find what I needed.
To Microsoft: This seems quite unnecessarily complex to do something that should not be difficult for an administrator-level process to do.
New, Improved Answer:
Following the suggestion made by eryksun, I created the following example function that shows all of the steps my program needed to get the current user key opened. My program has requireAdministrator in the manifest, if yours does not, you might need to make changes or additions to my sample. Here is what is working perfectly for me (but apparently not on a clean machine under a non-admin account):
BOOL ChangeHkcuSettings( void )
{
BOOL bResult = FALSE; // HKCU was not accessed
HWND hwndShell;
DWORD dwThreadId;
DWORD dwProcessId;
HKEY hKeyUserHive;
HANDLE hToken;
HANDLE hProcess;
hwndShell = GetShellWindow();
dwThreadId = GetWindowThreadProcessId( hwndShell, &dwProcessId );
hProcess = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, dwProcessId );
if( NULL != hProcess )
{
if( OpenProcessToken( hProcess,
TOKEN_QUERY
| TOKEN_DUPLICATE
| TOKEN_IMPERSONATE,
&hToken ) )
{
if( ImpersonateLoggedOnUser( hToken ) )
{
if( ERROR_SUCCESS == RegOpenCurrentUser( KEY_ALL_ACCESS, &hKeyUserHive ) )
{
// ... use the user hive key to access necessary HKCU items ...
RegCloseKey( hKeyUserHive );
bResult = TRUE; // HKCU was accessed
}
RevertToSelf();
}
CloseHandle( hToken );
}
CloseHandle( hProcess );
}
return bResult;
}
Thanks again to eryksun for the suggestion!
ORIGINAL ANSWER: I think I found another way that will work for me... Instead of using HKEY_CURRENT_USER (which will be the Administrator) the admin account can open the specific user's registry key(s) under HKEY_USERS instead. I will need to find the appropriate user's SID, but my setup knows the user's name (and password, for setting auto logon), so I think this is do-able. For me, this is much easier, since all of the code already exists in a single program that formerly write EVERYTHING to HKEY_LOCAL_MACHINE, which was easy and worked great. Trying to be "correct" is much more work, perhaps more than it should be! :-(

How to get the user name of the current windows desktop? [duplicate]

I have a simple C++ program that prompt the user name
#include <windows.h>
#include <Lmcons.h>
#include <winbase.h>
int _tmain(int argc, _TCHAR* argv[])
{
wchar_t username[UNLEN + 1];
DWORD username_len = UNLEN + 1;
::GetUserName(username, &username_len);
MessageBox(NULL, username, NULL, 1);
return 1;
}
GetUserName() performs as expected in administrator accounts, meaning print the real user name.
However, when run as administrator in a non-administrator account, I get the administrator name, and not the the real logged user.
I believe this behavior is expected since it is documented in GetUserName():
If the current thread is impersonating another client, the GetUserName function returns the user name of the client that the thread is impersonating.
Question
Is there a way to get the real logged in user (the non-admin one), even if the process run as administrator?
I believe the question you want to ask Windows is "which user is logged into the current session".
To do this, call ProcessIdToSessionId() with your own process's ID to determine the current session ID.
Then call WTSQuerySessionInformation() with the WTSUserName option to fetch the user name.
The problem is not a thread which is impersonating. You're running the entire application under the administrator login. That's why Windows asked you to login with an administrator account, when you started it from a non-admin account.
Thus, the result you get from GetUserName() is correct. That name is the real logged-in user of your app.
If you wanted the other name, the standard solution is to start as a normal user, and have an "elevate" button which restarts your application with elevated privileges. Look as Task Manager, it does this if you want to see all running processes. At this point you can of course pass anything you want to the new process, including that user name.

Issue enumerating monitors in Windows

My task is to count the number of current monitors (screens) attached to the computer where my user-mode code below is running:
int nCnt = 0;
if(!EnumDisplayMonitors(NULL, NULL, countMonitorsProc, (LPARAM)&nCnt))
{
//Error
}
BOOL countMonitorsProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
int* pCnt = (int*)dwData;
(*pCnt)++;
return TRUE;
}
It works in most cases, but on some systems the EnumDisplayMonitors returns FALSE. Example of such system: my laptop where I attached an external monitor and shut the lid on it.
I'm curious, do I use the right approach for counting attached monitors?
If all you're wanting to know is how many monitors are connected, there should be no need to call EnumDisplayMonitors. The proper way to do this for physical (non-virtual) monitors is via GetSystemMetrics, with the index SM_CMONITORS.
SM_CMONITORS The number of display monitors on a desktop. For more
80 information, see the Remarks section in this topic.
From the Remarks section
GetSystemMetrics(SM_CMONITORS) counts only visible display monitors. This is different from EnumDisplayMonitors, which enumerates both visible display monitors and invisible pseudo-monitors that are associated with mirroring drivers. An invisible pseudo-monitor is associated with a pseudo-device used to mirror application drawing for remoting or other purposes.
The call then simply becomes
nCnt = GetSystemMetrics(SM_CMONITORS);

MS CryptoAPI - Machine Keystore with Error 0x80090016 (NTE_BAD_KEYSET) with certreq created keys

Summary
I create a PKCS#10 CSR with certreq and have set the option Exportable=TRUE. This successfully creates a key under the location REQUEST. I also have a valid certificate with key in MY. If I try to access any one of them the CryptoAPI reports error code 0x80090016.
Running under different access rights could not solve this problem so far.
Goal
My goal is to get both the keys in MY and REQUEST. If I call CryptAcquireContextA() on any of those, it fails.
System
Windows 7 x64
Sample Source Code
My complete code looks like this:
hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, "REQUEST");
pCert = CertFindCertificateInStore(hStore, X509_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR_A, "CERTIFICATE_SUBJECT", NULL);
CertGetCertificateContextProperty(pCert, CERT_KEY_PROV_INFO_PROP_ID, NULL, &len);
pinfo = (CRYPT_KEY_PROV_INFO *) malloc(len);
CertGetCertificateContextProperty(pCert, CERT_KEY_PROV_INFO_PROP_ID, pinfo, &len);
provname = wide_to_asc(pinfo->pwszProvName);
contname = wide_to_asc(pinfo->pwszContainerName);
if(!CryptAcquireContextA(&hCryptProv, contname, provname, pinfo->dwProvType, 0)) {
err = GetLastError();
fprintf(stderr, "Error: 0x%x\n", err);
}
CryptGetUserKey(hCryptProv, pinfo->dwKeySpec, &hUserkey);
This code is mostly copied from the OpenSSL capi engine. Since the engine failed, I created the smallest possible code to search the error.
The error
If I run this, it fails with the output Error: 0x80090016. This means one of three things according to Microsoft:
Key container does not exist.
You do not have access to the key container.
The Protected Storage Service is not running.
What have I done so far?
Started service "Protected Storage"
Verified container exists with MMC & Certificate Snap-In for Local Computer
Ran the same code on the User store in user context - it worked
File System Permissions
After some googling, I tried to change permissions on the file system. I found the files by looking at the contname variable of my code and searching for the file. I changed permissions on them (more accurate, I changed permissions on the parent folder). While this fixed the issue for MY, it seems I cannot change it for REQUEST.
One note here is that my container for MY seems to be here:
%APPDATA%\Microsoft\Crypto\RSA\S-1-5-21-1650336054-1974872081-316617838-545102
For REQUEST I found it under a different address:
%ALLUSERSPROFILE%\Microsoft\Crypto\RSA\MachineKeys
I am not sure on the workings here so I cannot explain why it would put them in different locations (one being user centric, the other one a system folder). The MY store was created with a regular administrator prompt and the command certreq -new inf_file.inf cert-csr.csr and after I received my certificate, I issued certreq -accept cert.pem. Then I created a new csr with the same command.
Different privilege levels
I tried to execute my program with the following privileges:
my local user account
admin prompt (cmd->start as administrator)
nt authority\system (whoami output)
To recieve a service prompt, I executed psexec.exe –ids cmd.exe according to a tip from MaaSters Center
Final words
Any help or guidance on how to further narrow this problem down will be greatly appreciated.
I was finally able to solve this problem and it is a lot simpler than I thought. I was sure that I would receive an unambiguous container name and don't need to be more specific but CryptAcquireContext actually requires me to pass a flag CRYPT_MACHINE_KEYSET.
So my function call has to look like this:
CryptAcquireContextA(&hCryptProv, contname, provname, pinfo->dwProvType, CRYPT_MACHINE_KEYSET)
Unfortunately this is not supported by the OpenSSL engine, so you would have to alter it yourself in the engine.
See MSDN: "A key container created without this flag by a user that is not an administrator can be accessed only by the user creating the key container and the local system account."
Complete details here: https://msdn.microsoft.com/en-us/library/windows/desktop/aa379886(v=vs.85).aspx

Resources