Compare username of running program to username logged into Windows - c++

I've tried to implement a program which controls if the name of the user logged into the computer and the user who ran a program is the same, but I think I've done something wrong. The name of the usernamed logged works, but not the one to get the user who ran the program.
#include <Windows.h>
#include <iostream>
#include <stdlib.h>
#include <Lmcons.h>
#include <tchar.h>
#include <stdio.h>
#define INFO_BUFFER_SIZE 32767
using namespace std;
void Test() {
WCHAR UsernameSSR[INFO_BUFFER_SIZE];
TCHAR UsernameWindows[INFO_BUFFER_SIZE];
DWORD bufCharCountWindows = INFO_BUFFER_SIZE;
DWORD bufCharCountSSR = INFO_BUFFER_SIZE;
GetUserName(UsernameWindows, &bufCharCountWindows);
GetUserNameW(UsernameSSR, &bufCharCountSSR);
cout << UsernameWindows << " and " << UsernameSSR;
return;
}

GetUserName() is just a preprocessor macro that maps to either GetUserNameA() (ANSI) or GetUserNameW() (Unicode), depending on your project setup. Either way, GetUserName(A|W) returns the username currently associated with the calling thread, which in your example is also the username that is used to run the program. No mixing of GetUserNameA() and GetUserNameW() together will get you the results you want, because they are going to return the same username just in different character encodings.
To get the username that is logged into Windows itself, you need a different function, such as WTSQuerySessionInformation() (A or W variant), eg:
#include <iostream>
#include <Windows.h>
#include <wtsapi32.h>
#include <Lmcons.h>
void Test()
{
WCHAR UsernameSSR[UNLEN+1];
DWORD bufCharCountSSR = UNLEN+1;
if (GetUserNameW(UsernameSSR, &bufCharCountSSR))
std::wcout << L"UsernameSSR: " << UsernameSSR << std::endl;
else
std::wcout << L"Error getting UserNameSSR" << std::endl;
// alternatively, use GetUserNameExW() instead...
LPWSTR UsernameWindows;
DWORD bufByteCountWindows;
if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSUserName, &UsernameWindows, &bufByteCountWindows))
{
LPWSTR Domain;
DWORD bufByteCountDomain;
std::wcout << L"UsernameWindows: ";
if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSDomainName, &Domain, &bufByteCountDomain))
{
if (*Domain)
std::wcout << Domain << L"\\";
WTSFreeMemory(Domain);
}
std::wcout << UsernameWindows << std::endl;
WTSFreeMemory(UsernameWindows);
}
else
std::wcout << L"Error getting UserNameWindows" << std::endl;
// alternatively, query WTSQuerySessionInformation() for WTSSessionInfo, which
// returns both Domain and UserName in a single call, amongst other pieces of info...
}
That being said, comparing username strings is not the best way to determine whether your program is being run by the same user that is logged into Windows.
For instance, another way would be to obtain the Security Identifier (SID) of the user who is running the program and then compare it to the SID of the user that is logged into Windows using EqualSid().
Getting the SID of the calling process is easy: you can use GetCurrentProcessId(), OpenProcessToken(TOKEN_QUERY), and GetTokenInformation(TokenUser).
However, getting the SID of the logged in Windows session is a bit trickier. You can either:
get the Session ID of the logged in user, such as with ProcessIdToSessionId() or QueryTokenInformation(TokenSessionId) for the parent process that spawned your program (to find the parent process ID, use CreateToolhelp32Snapshot(), Process32First(), and Process32Next()), and then pass that Session ID to WTSQueryUserToken(), and then query the SID from that token. The gotcha is that WTSQueryUserToken() can only be called from a service that is running under the LocalSystem account, so you will have to write such a service and delegate to it via an IPC mechanism of you choosing.
retrieve the domain\username of the user session as described further above, and then use WMI to query the Win32_UserAccount table for that specific user and read its Sid property, then parse the SID into its binary form using ConvertStringSidToSid().

You missed , after first parameter (name):
GetUserNameA((LPSTR)name(LPDWORD) & size)
Also, why did you use GetUserName and not GetUserNameW? In some cases GetUserName will be same as GetUserNameA, so if you try to get the difference write GetUserNameW directly.

Related

Modify access token's user(Token User) or running a process elevated in context of a Standard User

I am trying to implement a small privilege management application for my college. I am trying to elevate a process in the context of the standard user itself. I have the admin user credentials. Logonuser() api to login as admin user and obtain it's token. And then use CreateProcessAsUser() or CreateProcessWithToken() with obtained token. But the problem here is that, though the create process has administrator privileges(elevated in another terms) it runs in the context of the admini user and hence does not have access to all network folders shared to the standard user. So I want to run the process in the context of the standard user but with privileges of admin user.
So I got to know about this SetTokenInformation() where I can change the TokenUser property of the token. So that the TokenUser alone will be changed to the standard user and all other privileges will remain same of that of the admin user.
struct STokenUser
{
TOKEN_USER* tu;
DWORD sizeOfToken;
};
STokenUser GetTokenUserFromToken(HANDLE tokenHandle)
{
STokenUser tu;
TOKEN_USER *tokenUser = NULL;
DWORD tokenSize;
DWORD sidLength;
GetTokenInformation(tokenHandle,
TokenUser,
tokenUser,
0,
&tokenSize);
if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
std::cout << "GetTokenInformation failed with " << GetLastError() << std::endl;
}
tokenUser = (TOKEN_USER *)malloc(tokenSize);
if (!GetTokenInformation(tokenHandle,
TokenUser,
tokenUser,
tokenSize,
&tokenSize))
{
free(tokenUser);
std::cout << "GetTokenInformation failed with " << GetLastError() << std::endl;
}
tu.tu = tokenUser;
tu.sizeOfToken = tokenSize;
return tu;
}
auto tu = GetTokenUserFromToken(hToken);
tu.tu->User.Sid = psidOfTheStandardUser;
if(!SetTokenInformation(hToken, TokenUser, &tu.tu, tu.sizeOfToken))
{
std::cout << "SetTokenInformation fails with error " << GetLastError() << std::endl;
}
I expected the process to run elevated in the context of the standard user. But SetTokenInformation failed with error code 87(meaning invalid parameters passed).
I don't know where I am failing. I have read somewhere that it is impossible to change TokenUser of a token.
How to fix this? or is there any other way to achieve what I am trying to do?

get_accChildCount returns 0 when it shouldn't

I'm trying to enumerate tabs of IE from an extension and from a standalone application. For one of the MSAA nodes get_accChildCount returns 0 when called from extension, while it should return 1 according to inspect and a call from standalone application.
The problem was described previously at StackOverflow, yet it was solved via a hack that doesn't work for me. /clr and /MT are incompatible.
Also there was a topic on MSDN with the same issue. There's no single answer there.
If you run IE with administrator privileges, it works properly.
API Monitor shows several thousand calls in a minimal example, and it's unclear which of these are related. The minimal example is attached below.
What are the undocumented cases when get_accChildCount returns wrong child count?
What other method could I use to activate the tab by URL in most versions of IE?
#include <atlbase.h>
#include <atlcom.h>
#include <atlctl.h>
#include <atltypes.h>
#include <atlsafe.h>
#include <io.h>
#include <fcntl.h>
#include <windows.h>
#include <iostream>
#include <string>
#include <vector>
#include <boost/format.hpp>
#include <fstream>
using namespace std;
CComPtr<IAccessible> get_acc_by_hwnd(HWND hwnd) {
CComPtr<IAccessible> ret;
HRESULT hr = ::AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible, (void**) &ret);
if (FAILED(hr) || !ret) {
wcout << L"Accessible::Accessible invalid hwnd" << endl;
}
return ret;
}
std::vector<CComPtr<IAccessible>> get_acc_children(CComPtr<IAccessible> acc) {
std::vector<CComPtr<IAccessible>> ret;
long count;
if (FAILED(acc->get_accChildCount(&count))) return ret;
long count_obtained = 0;
if (!count) return ret;
std::vector<CComVariant> accessors(count);
if (FAILED(::AccessibleChildren(acc, 0, count, &*accessors.begin(), &count_obtained))) return ret;
accessors.resize(count_obtained);
for (auto vtChild : accessors) {
if (vtChild.vt != VT_DISPATCH) continue;
CComQIPtr<IAccessible> pChild = vtChild.pdispVal;
if (pChild) ret.push_back(pChild);
}
return ret;
}
bool is_client(CComPtr<IAccessible> acc) {
CComVariant var;
HRESULT hr = acc->get_accRole(CComVariant(CHILDID_SELF), &var);
return SUCCEEDED(hr) && var.vt == VT_I4 && var.lVal == 0xA;
}
std::wstring get_descr(CComPtr<IAccessible> acc) {
CComBSTR str;
HRESULT hr = acc->get_accDescription(CComVariant(CHILDID_SELF), &str);
return SUCCEEDED(hr) && str ? std::wstring(str) : L"";
}
int main() {
::CoInitialize(nullptr);
_setmode(_fileno(stdout), _O_U16TEXT);
// put HWND of the window that contains tab labels
// it's hardcoded to minimize quantity of API calls
HWND hwnd = reinterpret_cast<HWND>(0x002D0696);
CComPtr<IAccessible> iaccessible;
HRESULT hr = ::AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible, (void**) &iaccessible);
if (FAILED(hr) || !iaccessible) {
wcout << L"AccessibleBrowser::activate_tab " L"failed to get IAccessible for IE" << endl;
return EXIT_FAILURE;
}
wstring const sentinel = L"\r\n";
for (auto child : get_acc_children(iaccessible)) if (is_client(child)) {
for (auto child1 : get_acc_children(child)) { // fails here in extension
for (auto child2 : get_acc_children(child1)) {
std::wstring descr = get_descr(child2);
auto pos = descr.find(sentinel);
if (pos == string::npos) continue;
auto tab_url = descr.substr(pos + sentinel.size());
wcout << tab_url << endl;
}
}
}
}
I poked at your program for a while without having much to show for it. Perhaps I realized too late that it was not supposed to reproduce the problem :( I can only provide some possibly helpful hints to get you to look under the right kind of rock.
yet it was solved via a hack that doesn't work for me
These programmers made a simple mistake, they forgot to call CoInitialize/Ex(). A very common oversight. Using the /clr build option works around that mistake because it is now the CLR that calls it. You can easily repro this mishap, just comment out the CoInitialize() call. Unfortunately it works for a while without any loud errors being produced, but you do get 0 for certain accobjects. You'll notice your program no longer finds the tabs.
Not so sure I can cleanly explain this, certain COM-style object models don't actually use the COM infrastructure. DirectX is the best example, we can add UIAutomation to that list. I assume it will silently fail like this when the client app's component is COM-based. Unclear if it is, DirectUIHWnd is quite undocumented.
So stop looking for that as a workaround, you did not forget to call CoInitialize() and it will be taken care of by IE before it activates your extension.
If you run IE with administrator privileges, it works properly.
That's a better rock. Many programmers run VS elevated all the time, the roles might be reversed in the case of UIA. Do keep in mind that your extension runs in a sandbox inside IE. No real idea how elevating IE affects this. One thing you cannot do from code running in the low-integrity sandbox is poke at UI components owned by a code that runs in a higher integrity mode. Google "disable IE enhanced protected mode" and follow the guideline to see what effect that has on your addin.

Authentication against Active Directory with C++ on Linux

I'm surprised there's so few examples on this out there. I basically need to do a user/pass authentication against Active Directory. I'm able to initialize a connection to active directory, but it always gives me an "Invalid Credentials" error. I'm wondering if I'm passing it something wrong. This is my first attempt with LDAP. For that matter, I'm open to another (well documented perhaps) solution.
#include <iostream>
#include <stdio.h>
#define LDAP_SERVER "ldap://hq.mydomain.local/"
#include <ldap.h>
int main( int argc, char** argv )
{
LDAP *ld;
int version( LDAP_VERSION3 );
int rc;
char bind_dn[100];
berval creds;
berval* serverCreds;
if( ldap_initialize( &ld, LDAP_SERVER ) ) {
std::cerr << "Error initializing ldap..." << std::endl;
return 1;
}
ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
creds.bv_val = "password";
creds.bv_len = strlen("password");
rc = ldap_sasl_bind_s( ld, "sAMAccountName=MYDOMAIN\\UserName,dc=mydomain,dc=local", "GSSAPI", &creds, NULL, NULL, &serverCreds );
if ( rc != LDAP_SUCCESS ) {
std::cerr << "ERROR: " << ldap_err2string( rc ) << std::endl;
return 1;
} else {
std::cout << "Success." << std::endl;
}
return 0;
}
EDIT:
I wanted to make sure everything is okay on the server side, so did some tests with ldapsearch. It did not work at first, but I finally got it (with ldapsearch, anyway).
ldapsearch -D first.last#mydomain.local -H "ldap://hq.mydomain.local:389" -b "ou=Development,ou=Domain Users,dc=mydomain,dc=local" -W "sAMAccountName=first.last"
Maybe not the best way. For starters, the key is the -D argument, and passing sAMAccountName at the end. I'm not going to have the common name - only the windows login name, and their password. The above command will show a user his info, if the password passes.
The caveat (I think) is that ldap_sasl_bind_s() has no equivalent of setting the -D (binddn) flag. Looking at this question/answer it looks like ldap_interactive_bind_s() might, but it's a bit more involved, as I have to pass a call back.
With the example above where I set a password, but no binddn/username of any kind, who does it assume I'm trying to authenticate as?
sAMAccountName=MYDOMAIN\UserName,dc=mydomain,dc=local
This format of username is incorrect. You do not need to specify sAMAccountName in your username and don't need to specify dc unless you're using Distinguished Name. You have few options for username.
Distinguished Name
CN=Jeff Smith,OU=Sales,DC=Fabrikam,DC=Com
sAMaccountName
jsmith
The user path from a previous version of Windows
"Fabrikam\jeffsmith".
User Principal Name (UPN)
jeffsmith#Fabrikam.com
Having said that, I'm not certain if the username is the only issue you're experiencing. I have not run your code locally.
Although this answer may not directly answer your question, since I have not tested this code in Linux machine, it could give you an idea or put you in a right direction. I will not be surprised if this method is Windows specific only.
According to MSDN there're few methods you can use to authenticate a user.
The ADsOpenObject function binds to an ADSI object using explicit user name and password credentials.
This method is accepting the following parameters:
HRESULT ADsOpenObject(
_In_ LPCWSTR lpszPathName,
_In_ LPCWSTR lpszUserName,
_In_ LPCWSTR lpszPassword,
_In_ DWORD dwReserved,
_In_ REFIID riid,
_Out_ VOID **ppObject
);
Using this method you can bind to object in Active Directory by specifying username and password.
If the bind is successful, the return code is S_OK, otherwise you'll get different error messages.
I don't write programs in C++ on a daily basis. I typically work with Active Directory and Active Directory Lightweight Services in a C# world. But this sample code I wrote, shows you how to call ADsOpenObject method to bind to an ADSI object using specified credentials. In your case, just authenticate.
#include <iostream>
#include "activeds.h"
using namespace std;
int main(int argc, char* argv[])
{
HRESULT hr;
IADsContainer *pCont;
IDispatch *pDisp = NULL;
IADs *pUser;
CoInitialize(NULL);
hr = ADsOpenObject( L"LDAP://yourserver",
L"username",
L"password",
ADS_FAST_BIND, //authentication option
IID_IADs,
(void**) &pUser);
if (SUCCEEDED(hr))
{
cout << "Successfully authenticated";
}
else
cout << "Incorrect username or password";
return hr;
}
Depending on your setup, you might have to tweak ADS_AUTHENTICATION_ENUM. I suggest you install SSL Certificate and use ADS_USE_SSL binding. Dealing with passwords without SSL in AD, can be nightmare.

MS CryptoAPI doesn't work on Windows XP with CryptAcquireContext()

I wrote some code using the Microsoft CryptoAPI to calculate a SHA-1 and got the compiled exe working on Windows 7, Win Server 2008, Win Server 2003. However, when I run it under Windows XP SP3, it does not work.
I narrowed down the failure to the CryptAcquireContext() call.
I did notice that a previous post talked about the XP faulty naming of "… (Prototype)" and it must be accounted for by using a WinXP specific macro MS_ENH_RSA_AES_PROV_XP.
I did the XP specific code modifications and it still doesn't work. (The bResult returns 0 false on Win XP, all other platforms bResult returns 1 true.)
I checked the MS_ENH_RSA_AES_PROV_XP with the actual key+string values I see in regedit.exe so everything looks like it's set up to work but no success.
Have I overlooked something to make it work on Windows XP?
I've pasted shortest possible example to illustrate the issue. I used VS2010 C++.
// based on examples from http://msdn.microsoft.com/en-us/library/ms867086.aspx
#include "windows.h"
#include "wincrypt.h"
#include <iostream>
#include <iomanip> // for setw()
void main()
{
BOOL bResult;
HCRYPTPROV hProv;
// Attempt to acquire a handle to the default key container.
bResult = CryptAcquireContext(
&hProv, // Variable to hold returned handle.
NULL, // Use default key container.
MS_DEF_PROV, // Use default CSP.
PROV_RSA_FULL, // Type of provider to acquire.
0); // No special action.
std::cout << "line: " << std::setw(4) << __LINE__ << "; " << "bResult = " << bResult << std::endl;
if (! bResult) { // try Windows XP provider name
bResult = CryptAcquireContext(
&hProv, // Variable to hold returned handle.
NULL, // Use default key container.
MS_ENH_RSA_AES_PROV_XP, // Windows XP specific instead of using default CSP.
PROV_RSA_AES, // Type of provider to acquire.
0); // No special action.
std::cout << "line: " << std::setw(4) << __LINE__ << "; " << "bResult = " << bResult << std::endl;
}
if (bResult)
CryptReleaseContext(hProv, 0);
}
Windows 7 success:
Windows XP failure:
In your CryptAcquireContext code, it appears you are missing the parameter to get a context without a specific container set. You need to pass the CRYPT_VERIFYCONTEXT option in CryptAcquireContext.
Windows 7 might be working around this.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa379886(v=vs.85).aspx
For further diagnosis, the results of GetLastError() would be requisite.

How can I launch programmatically (C++) an application from a Windows shortcut (.lnk file)?

how can I launch programmatically an application from a Windows shortcut (.lnk file)?
I tried to use the API ShellExecute and it seems to work. Any caveat?
Thank you.
Here it is a snippet of my current code:
#include <windows.h>
#include <map>
#include <string>
#include <iostream>
int main( int, char** )
{
std::map< int, std::wstring > errors;
errors[0] = L"The operating system is out of memory or resources.";
errors[ERROR_FILE_NOT_FOUND] = L"The specified file was not found.";
errors[ERROR_PATH_NOT_FOUND] = L"The specified path was not found.";
errors[ERROR_BAD_FORMAT] = L"The .exe file is invalid (non-Microsoft Win32 .exe or error in .exe image).";
errors[SE_ERR_ACCESSDENIED] = L"The operating system denied access to the specified file.";
errors[SE_ERR_ASSOCINCOMPLETE] = L"The file name association is incomplete or invalid.";
errors[SE_ERR_DDEBUSY] = L"The Dynamic Data Exchange (DDE) transaction could not be completed because other DDE transactions were being processed.";
errors[SE_ERR_DDEFAIL] = L"The DDE transaction failed.";
errors[SE_ERR_DDETIMEOUT] = L"The DDE transaction could not be completed because the request timed out.";
errors[SE_ERR_DLLNOTFOUND] = L"The specified DLL was not found.";
errors[SE_ERR_FNF] = L"The specified file was not found.";
errors[SE_ERR_NOASSOC] = L"There is no application associated with the given file name extension. This error will also be returned if you attempt to print a file that is not printable.";
errors[SE_ERR_OOM] = L"There was not enough memory to complete the operation.";
errors[SE_ERR_PNF] = L"The specified path was not found.";
errors[SE_ERR_SHARE] = L"A sharing violation occurred.";
int ret = reinterpret_cast< int >( ::ShellExecute(0,L"open",L"\"C:\\Documents and Settings\\All Users\\Start Menu\\Programs\\Accessories\\Calculator.lnk\"",0,0,SW_SHOW) );
const int minimumRetOK = 33;
if ( ret < minimumRetOK ) {
if ( errors.count( ret ) ) {
std::wcout << L"Error " << ret << L" " << errors[ ret ];
} else {
std::wcout << L"Error " << ret << L" undocumented error";
}
}
return 0;
}
I am not sure what you are uncertain about, the behaviour you observe is documented.
The ShellExecute "open" operation, will do whatever the shell does when you "open" the file referenced by the file argument (you can right-click a shortcut and select "Open" explicitly, but that is also the default operation for .lnk, so is the same as a double-click).
"Opening" a short-cut file it will "open" the target, if the target is an executable it will run, if it is a document or data file, it will open in the associated program, or prompt for a program if none associated.
ShellExecute or CreateProcess should be able to open link file. If they can't find the associated file and/or the program, you can always use those API and delegate the hard work to "cmd start" or "explorer". E.g. ShellExecute(0, "open", "explorer", linkfile, 0, SW_SHOW);
ShellExecute should work.
But, ...
int main( int, wchar_t* )
... no compiler I know of supports this signature. Just write:
int main()
Also, for the dignostic messages, just use the FormatMessage Windows API function, or, if the code is exclusively for Visual C++, use the appropriate support class that's bundled with that compiler.
Cheers & hth.,