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

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

Related

Access Shared Preferences externally / Store a value into a new file and access it externally

I have the two following methods and I am using them to store a special value locally and be able to access it on application restart:
(Store value locally:)
private void SaveSet(string key, string value)
{
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
ISharedPreferencesEditor prefEditor = prefs.Edit();
prefEditor.PutString(key, value);
// editor.Commit(); // applies changes synchronously on older APIs
prefEditor.Apply(); // applies changes asynchronously on newer APIs
}
(Read it again:)
private string RetrieveSet(string key)
{
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
return prefs.GetString(key, null);
}
This works perfectly. Now is it possible to access and edit this Shared Preferences externally? Unfortunately, I cannot find any file when searching in folder
Phone\Android\data\com.<company_name>.<application_name>\files
nor anywhere else. I want / try to edit this value from my computer, after connecting the phone to it. Is this possible?
Alternatively: Can anyone maybe show me how to create a new file in the given path above, write/read it programmatically and how it stays there, even if application is closed / started again? So I can then edit this file with my computer anyhow?
I tried it with the following code, but unfortunately it doesn't work / no file is created or at least i cannot see it in the given path above:
//"This code snippet is one example of writing an integer to a UTF-8 text file to the internal storage directory of an application:"
public void SaveValueIntoNewFile(int value)
{
var backingFile = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "newFile.txt");
using (var writer = System.IO.File.CreateText(backingFile))
{
writer.WriteLine(value.ToString());
}
}
Would be very happy about every answer, thanks in advance and best regards
What you're looking for is where Android stores the Shared Preference file for applications that make use of it's default PreferenceManager.
I'd refer to this SO post which answers your question pretty well
SharedPreferences are stored in an xml file in the app data folder,
i.e.
/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PREFS_NAME.xml
or the default preferences at:
/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PACKAGE_NAME_preferences.xml
SharedPreferences added during runtime are not stored in the Eclipse
project.
Note: Accessing /data/data/ requires superuser
privileges
A simple method is to use Android Device Monotor,you can open it by clicking Tools--> android-->Android Device Monotor...
For example:
The path in my device is as follows:
/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PACKAGE_NAME_preferences.xml
And we notice three buttons in the upper right corner of the picture.
The first one is used toPull a file from the device,the second one is used to Push a file onto the device,and the last one is used to delete the preferences.xml file.
So we can pull the preferences.xml file from current device to our computer and edit it as we want, and then push the updated preferences.xml to the folder again.Then we will get the value of preferences.xml file .

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! :-(

IFSFile when user PASSWORD *NONE

I am facing some troubles while trying to create an IFSFile using IFSFile object from JT400.jar. The problem that I am facing is when the process that calls the JAVA is called by a BATCH user(without login enabled into AS400 machine).
Exception given
Password is *NONE.:XXXXXX com.ibm.as400.access.AS400SecurityException: Password is *NONE.:XXXXXX
at com.ibm.as400.access.AS400ImplRemote.returnSecurityException(AS400ImplRemote.java:2219)
at com.ibm.as400.access.CurrentUser.getUserInfo(CurrentUser.java:79)
at com.ibm.as400.access.AS400ImplRemote.getPassword(AS400ImplRemote.java:1411)
at com.ibm.as400.access.AS400ImplRemote.signon(AS400ImplRemote.java:2507)
at com.ibm.as400.access.AS400.sendSignonRequest(AS400.java:3351)
at com.ibm.as400.access.AS400.promptSignon(AS400.java:2938)
at com.ibm.as400.access.AS400.signon(AS400.java:4246)
at com.ibm.as400.access.AS400.connectService(AS400.java:1336)
at com.ibm.as400.access.IFSFile.chooseImpl(IFSFile.java:630)
at com.ibm.as400.access.IFSFile.copyTo(IFSFile.java:729)
at com.ibm.as400.access.IFSFile.copyTo(IFSFile.java:699)
Code used:
AS400 as400 = new AS400("localhost");
//Obtain the template path
String templatePath ="/HOME/XXX/auth.txt";
IFSFile templateAuth = new IFSFile(as400,templatePath);
templateAuth.copyTo(fileXML + ".xml");
I have check some opened threads but no results obtained. (find below the threads commented)
JT400 Read File From IFS with user without password
Java IFSFile on Iseries testing over PC
There is any option to generate an IFSFile when the process is called by a BATCH user(note that when the process is called by a user with login enabled, the process is working as expected)
I need something similar to what is done when a JDBCAppender is created, JDBCAppender object allows setUser(null) and setPassword(null) to enable batch users to write into a table.
Thanks all!
I have recently had a similar problem when trying to use CommandCall whilst running under a profile with PASSWORD is *NONE. I (eventually) solved it by using the native Java toolbox at /QIBM/ProdData/Java400/jt400ntv.jar (which happens to be a symbolic link). I didn't have to make any code changes:
AS400 as400 = new AS400();
CommandCall cc = new CommandCall(as400);
String wrkenvvar_cmd = MessageFormat.format("ADDENVVAR ENVVAR('JAVA_HOME') VALUE(''{0}'') REPLACE(*YES)",path);
boolean ok = cc.run(wrkenvvar_cmd);
I hope that helps.

QSqlDatabase Connecting to Multiple Databases

I am having issues attempting to connect to two different databases in one Qt Application. I have my information database that stores all the information collected by the application and the new Log database which allows me to track all the changes that occur to the Application, button presses, screen loads etc, for easy debugging after its release. Separately, the databases work perfectly, but when I try to use both of them, only one will work. I read that this could be because I wasn't naming the connections and obviously only the most recently connected database could use the default connection. However when I give the databases names they wont work at all, isOpen() will return true on both, but as soon as they attempt to execute a query I get the errors
"QSqlQuery::prepare: database not open"
"QSqlError(-1, "Driver not loaded", "Driver not loaded")"
My two database declarations are:
database_location = filepath.append("/logger.sqlite");
logDB = QSqlDatabase::addDatabase("QSQLITE", "LoggerDatabaseConnection");
logDB.setHostName("localhost");
logDB.setDatabaseName(database_location);
for the Logger Database connection and :
database_location = filepath.append("/db.sqlite");
db = QSqlDatabase::addDatabase("QSQLITE", "NormalDB");
db.setHostName("localhost");
db.setDatabaseName(database_location);
Also when I am running the first query on the databases to see if their tables exist I am using
QSqlQuery query("LoggerDatabaseConnection");
and likewise for the normal database, but I am still getting connection issues even after declaring the database connection to run the query on.
The database used for the application is declared as a static QSqlDatabase in a namespace to create a global effect, so everyone can access it, that was a previous programmer, and I created the Log database as Singleton with a private database connection. Like I said both versions of the code work separately but when they are together they are fighting each other. I know there is a huge debate over the proper design of Singleton vs Dependecy Injection, but again the code works separately so I am happy with how it is designed for now. If there is any missing information or if you have any ideas, please let me know. Thank you.
QSqlQuery query("LoggerDatabaseConnection");
The first parameter of the constructor is the query, not the connection name. It will use the default connection since you specified no database object.
Try something like this:
QSqlQuery query1("YourFirstQuery", db);
QSqlQuery query2("YourSecondQuery", logDB);
Important: Also do not forget to open and close the database before / after using it by calls to QSqlDatabase::open() and QSqlDatabase::close().
The correct way to have multiple databases is to not use the pointer returned from the static addConnection method. You should use the connectionName argument:
https://doc.qt.io/qt-5/qsqldatabase.html#addDatabase-1 during initilization and query usage:
example:
void MyClass::initDb(QString dbPath, QString connName)
{
// initial db usage, etc
QSqlDatabase db = QSqlDatabase::addDatabase(YOUR_DRIVER, connName);
db.setDatabaseName(dbPath);
// open it, etc
}
void MyClass::updateThing(QString val, QString name, QString connName)
{
QString q = QString("UPDATE THINGS SET val=%1 WHERE name=%2").arg(val, name);
// add the reference to your database via the connection name
QSqlDatabase db = QSqlDatabase::database(connName);
QSqlQuery query(db);
query.exec(q);
// handle the query normally, etc
}

Get the logged in Windows user name associated with a desktop

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

Resources