What is the counterpart to the GetExplicitEntriesFromAcl() Win32 API function? - c

The GetExplicitEntriesFromAcl Win32 API function allows to retrieve the explicit entries of a file ACL. But when I change some entries, convert the result into a new ACL using SetEntriesInAcl and finally apply the ACL back to the file with SetSecurityInfo all inherited entries seem to be lost and only the (changed) explicit entries are left.
Is there a counterpart function "SetExplicitEntriesInAcl" that only replaces the explicit entries within an ACL structure and keeps the inherited entries intact?
Edit1: Code Sample
I'm using code similar to the following lines for ACL update:
int RemoveAclAccessRights( HANDLE hFile, PSID SidPtr,
DWORD AccessRights, ACCESS_MODE AccessMode )
{
PACL OldAcl = NULL, NewAcl = NULL;
PSECURITY_DESCRIPTOR SecDesc = NULL;
PEXPLICIT_ACCESS EntryList = NULL, EntryItem;
ULONG EntryCount, EntryIndex;
int r;
// Get a pointer to the existing DACL
r = GetSecurityInfo(hFile, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
NULL, NULL, &OldAcl, NULL, &SecDesc);
if ( r != ERROR_SUCCESS )
goto _CleanUp;
r = GetExplicitEntriesFromAcl(OldAcl, &EntryCount, &EntryItem);
if ( r != ERROR_SUCCESS )
goto _CleanUp;
EntryList = EntryItem;
EntryIndex = 0;
while ( EntryIndex < EntryCount ) {
// ... update access entry ...
EntryIndex++;
EntryItem++;
}
// Create a new ACL from the explicit entries of the existing DACL
r = SetEntriesInAcl(EntryCount, EntryList, NULL, &NewAcl);
if ( r != ERROR_SUCCESS )
goto _CleanUp;
// Attach the new ACL as the object's DACL
r = SetSecurityInfo(hFile, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
NULL, NULL, NewAcl, NULL);
_CleanUp:
LocalFree(NewAcl);
LocalFree(EntryList);
LocalFree(SecDesc);
return r;
}
Edit2: ACLs of the file and parent directory
Output of icacls on the file:
> icacls TestAcl01.txt
TestAcl01.txt VORDEFINIERT\Gäste:(R)
VORDEFINIERT\Administratoren:(I)(F)
NT-AUTORITÄT\SYSTEM:(I)(F)
NT-AUTORITÄT\Authentifizierte Benutzer:(I)(M)
VORDEFINIERT\Benutzer:(I)(RX)
Output of icacls on the parent directory:
> icacls .
. VORDEFINIERT\Administratoren:(I)(F)
VORDEFINIERT\Administratoren:(I)(OI)(CI)(IO)(F)
NT-AUTORITÄT\SYSTEM:(I)(F)
NT-AUTORITÄT\SYSTEM:(I)(OI)(CI)(IO)(F)
NT-AUTORITÄT\Authentifizierte Benutzer:(I)(M)
NT-AUTORITÄT\Authentifizierte Benutzer:(I)(OI)(CI)(IO)(M)
VORDEFINIERT\Benutzer:(I)(RX)
VORDEFINIERT\Benutzer:(I)(OI)(CI)(IO)(GR,GE)
The file has one explicit entry which is "VORDEFINIERT\Gäste:(R)" (SID "S-1-5-32-546"). The other entries are inherited from the parent directory.
In the while loop above I am trying to delete the explicit entry if it matches the SID using code like
if ( (EntryItem->Trustee.TrusteeForm == TRUSTEE_IS_SID) && EqualSid(EntryItem->Trustee.ptstrName, SidPtr) ) {
if ( EntryIndex < (EntryCount-1) )
MoveMemory(&EntryList[EntryIndex], &EntryList[EntryIndex+1], (EntryCount-EntryIndex-1)*sizeof(EntryList[0]));
EntryCount--;
continue;
}

Given the information in the latest edit, I can now replicate your problem. It only occurs in the case when you are removing all of the explicit entries from the DACL.
It turns out there's a nasty (and undocumented, so far as I can see) catch in SetEntriesInAcl: if you pass it a zero-length array, it silently returns NULL as the new ACL rather than returning an empty ACL as you might reasonably expect.
The documentation for SetSecurityInfo explains what happens in this case:
If the value of the SecurityInfo parameter includes the DACL_SECURITY_INFORMATION flag and the value of this parameter is set to NULL, full access to the object is granted to everyone.
That implicitly removes the inherited permissions (which are redundant anyway).
One way to fix the problem:
ACL empty_acl;
if (!InitializeAcl(&empty_acl, sizeof(empty_acl), ACL_REVISION))
goto _CleanUp;
// Create a new ACL from the explicit entries of the existing DACL
r = SetEntriesInAcl(EntryCount, EntryList, &empty_acl, &NewAcl);
if ( r != ERROR_SUCCESS )
goto _CleanUp;

The following code seems to work but uses GetAclInformation and GetAce instead of GetExplicitEntriesFromAcl which requires pointer type checking and is thus somehow less convenient:
int UpdateAclAccessRights( HANDLE hFile, PSID SidPtr,
DWORD AccessRights, ACCESS_MODE AccessMode )
{
PACL DiscAcl = NULL;
PSECURITY_DESCRIPTOR SecDesc = NULL;
ACL_SIZE_INFORMATION AclSizeInfo;
PACCESS_ALLOWED_ACE AceItem;
int AceIndex;
int r;
// Get a pointer to the existing DACL
r = GetSecurityInfo(hFile, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
NULL, NULL, &DiscAcl, NULL, &SecDesc);
if ( r != ERROR_SUCCESS )
goto _CleanUp;
ZeroMemory(&AclSizeInfo, sizeof(AclSizeInfo));
if (!GetAclInformation(DiscAcl, &AclSizeInfo, sizeof(AclSizeInfo), AclSizeInformation))
goto _CleanUp;
for (AceIndex = AclSizeInfo.AceCount-1; AceIndex >= 0; AceIndex--) {
if (!GetAce(DiscAcl, AceIndex, &((void*)AceItem)))
continue;
if ( (AceItem->Header.AceType != ACCESS_ALLOWED_ACE_TYPE) &&
(AceItem->Header.AceType != ACCESS_DENIED_ACE_TYPE) )
continue; // entry pointer structure does not match AceItem
if ( (AceItem->Header.AceFlags && INHERITED_ACE) > 0 )
continue; // not an explicit entry
// ... update/delete access entry in case it matches SidPtr ...
}
// Attach updated ACL to the file object
r = SetSecurityInfo(hFile, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
NULL, NULL, DiscAcl, NULL);
_CleanUp:
LocalFree(SecDesc);
return r;
}

Related

How to properly create an RSA key from raw data in OpenSSL 3.0 in C language?

i'm fairly new to OpenSSL and my current task at work is updating the code of a project from OpenSSL 1.1.1 to OpenSSL 3.0 and I'm stuck on a really weird problem.
I'm trying to create an RSA key from given raw data, precisely binary modulus and exponent that are converted to BIGNUM. I've tried to make it as it is described in the manual and the key creation works, but the problem is whatever I try to do with that key (or CTX based on it) fails, whether it's signature decryption or verification.
Here's the code where I create the key:
/* function set up above */
ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
if ( ctx == NULL) {
ERROR("Error: failed to initialize CTX from name.\n");
goto OPENSSL_ERROR;
}
modulus = BN_bin2bn(pubkey->data, pubkey->size, NULL);
exponent = BN_bin2bn(exp_arr, 3, NULL);
if ( modulus == NULL || exponent == NULL ) {
goto OPENSSL_ERROR;
}
OSSL_PARAM params[] = {
OSSL_PARAM_BN("n", &modulus, BN_num_bytes(modulus)*8),
OSSL_PARAM_BN("e", &exponent, BN_num_bytes(exponent)*8),
OSSL_PARAM_BN("d", NULL, 0),
OSSL_PARAM_END
};
status = EVP_PKEY_fromdata_init(ctx);
if ( status <= 0 ) {
ERROR("Error: failed to initialize key creation.\n");
goto OPENSSL_ERROR;
}
status = EVP_PKEY_fromdata(ctx, &evp_key, EVP_PKEY_PUBLIC_KEY, params);
if ( status <= 0 || evp_key == NULL ) {
ERROR("Error: failed to create key.\n");
goto OPENSSL_ERROR;
}
/* goes on to decrypt signature and later verify it against data - both fail */
It goes through swiftly, but trying to do whatever with that key or CTX based on that key fails miserably.
I tried creating CTX with EVP_PKEY_CTX_new_id(), but it changed nothing.
I tried omitting the parameter "d", but it changed nothing.
If anyone knows what I'm doing wrong and what should be changed - all help will be greatly appreciated.
P.S.
pubkey is passed to the function as argument, data is a binary array, size is obviously it's size (size_t)
the params given to the key are the same as in the previous version of the code which used the RSA_set0_key() function
unsigned char exp_arr[] = {0x01, 0x00, 0x01};
It is not obvious in the manual (nothing new with OpenSSL).
The problem is that the OSSL_PARAM params array cannot be initialized the way it is in the code above. It needs to be built using OSSL_PARAM_BLD, like this:
OSSL_PARAM_BLD *params_build = OSSL_PARAM_BLD_new();
if ( params_build == NULL ) {
goto OPENSSL_ERROR;
}
if ( !OSSL_PARAM_BLD_push_BN(params_build, "n", modulus) ) {
ERROR("Error: failed to push modulus into param build.\n");
goto OPENSSL_ERROR;
}
if ( !OSSL_PARAM_BLD_push_BN(params_build, "e", exponent) ) {
ERROR("Error: failed to push exponent into param build.\n");
goto OPENSSL_ERROR;
}
if ( !OSSL_PARAM_BLD_push_BN(params_build, "d", NULL) ) {
ERROR("Error: failed to push NULL into param build.\n");
goto OPENSSL_ERROR;
}
OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(params_build);
if ( params == NULL ) {
ERROR("Error: failed to construct params from build.\n");
goto OPENSSL_ERROR;
}
It'll probably work the same with all other types of keys created with EVP_PKEY_fromdata() function.
[IMPORTANT!]
Remember to check what are settable parameters for the key you're trying to create, otherwise you might get stuck on that part for a long time not knowing what's wrong with your code ;)

Windows API authenticode get root certificate

I want to walk the certificate chain of a authenticode signed PE binary using the Windows API.
To get the certificate store I followed the example from Microsoft:
https://support.microsoft.com/en-us/help/323809/how-to-get-information-from-authenticode-signed-executables
With that I get the leaf certificate and the intermediate certificate, but not the root certificate. Tested with different Windows binaries (eg. explorer.exe)
I tried the following loops to walk the store:
while (pCertContext = CertFindCertificateInStore(hStore, ENCODING, 0, CERT_FIND_ANY, NULL, pCertContext));
while (pCertContext = CertEnumCertificatesInStore(hStore, pCertContext));
Is the root certificate not included in the authenticode signature?
Do I miss some option?
Thanks #RbMm for your suggestion with CertGetCertificateChain, that does solve my question.
To get the whole chain, you need to start at the leaf certificate (store seams to start top-down).
Adapted from https://learn.microsoft.com/de-de/windows/desktop/SecCrypto/example-c-program-creating-a-certificate-chain:
CERT_INFO CertInfo;
CertInfo.Issuer = pSignerInfo->Issuer;
CertInfo.SerialNumber = pSignerInfo->SerialNumber;
pCertContext = CertFindCertificateInStore(hStore, ENCODING, 0, CERT_FIND_SUBJECT_CERT, (PVOID)&CertInfo, NULL);
if (!pCertContext) {
_tprintf(_T("CertFindCertificateInStore failed with %x\n"), GetLastError());
__leave;
}
CERT_ENHKEY_USAGE EnhkeyUsage;
CERT_USAGE_MATCH CertUsage;
CERT_CHAIN_PARA ChainPara;
EnhkeyUsage.cUsageIdentifier = 0;
EnhkeyUsage.rgpszUsageIdentifier = NULL;
CertUsage.dwType = USAGE_MATCH_TYPE_AND;
CertUsage.Usage = EnhkeyUsage;
ChainPara.cbSize = sizeof(CERT_CHAIN_PARA);
ChainPara.RequestedUsage = CertUsage;
if (!CertGetCertificateChain(
NULL, // use the default chain engine
pCertContext, // pointer to the end certificate
NULL, // use the default time
NULL, // search no additional stores
&ChainPara, // use AND logic and enhanced key usage
// as indicated in the ChainPara
// data structure
dwFlags,
NULL, // currently reserved
&pChainContext)) {
cerr << "Error on CertGetCertificateChain" << endl;
__leave;
}
PCERT_SIMPLE_CHAIN rgpChain = NULL;
PCERT_CHAIN_ELEMENT rgpElement = NULL;
rgpChain = pChainContext->rgpChain[0];
for (int j = 0; j < rgpChain->cElement; j++) {
rgpElement = rgpChain->rgpElement[j];
PrintCertificateInfo(rgpElement->pCertContext);
cout << endl;
}

do while(0) or if else which one is good for single exit point? [duplicate]

This question already has answers here:
Best practice for compute the function return value
(5 answers)
Closed 5 years ago.
I want to achieve single exit point for a function. One way is to use a variable, and check condition and if condition fails, set the variable to error and return that variable at the end of function. But nested if else hampers code readability.
Other way is to enclose the code in do {} while(0); and inside this loop check for condition and if condition fails, set the error in a variable and break from loop and then return the error, this improves the code readability.
So which one is better nested if else or do while(0)?
What about using the goto statement with a single label placed just before (or somewhere before) your return statement?
//...
// if (...)
goto exit_func;
//...
// if (...)
goto exit_func;
//...
// if (...)
goto exit_func;
//...
exit_func:
// do some common stuff before leaving the function (e.g.: releasing resources)
// ...
return;
While you might not agree with the MISRA rules, you are not supposed to find creative attempt such as wrapping your code in do {} while(0); to achieve a single point of exit, or as a way to write less code.
MISRA forbids goto as well, so although it's a common idiom to use goto to jump to a common exit point in a function, it will not pass the MISRA rules.
Even if you find it verbose, you are supposed write the code as e.g.
int32 CFE_ES_GetAppInfo(CFE_ES_AppInfo_t *AppInfo, uint32 AppId)
{
int32 ReturnCode = CFE_SUCCESS;
if ( AppInfo != 0 )
{
if ( AppId < CFE_ES_MAX_APPLICATIONS )
{
if ( CFE_ES_Global.AppTable[AppId].RecordUsed == TRUE )
{
CFE_ES_GetAppInfoInternal(AppId, AppInfo);
ReturnCode = CFE_SUCCESS;
}
else
{
CFE_ES_WriteToSysLog("CFE_ES_GetAppInfo: App ID Not Active: %d\n",(int)AppId);
ReturnCode = CFE_ES_ERR_APPID;
}
}
else
{
CFE_ES_WriteToSysLog("CFE_ES_GetAppInfo: App ID Exceeds CFE_ES_APPLICATION_MAX: %d\n",(int)AppId);
ReturnCode = CFE_ES_ERR_APPID;
}
}
else
{
CFE_ES_WriteToSysLog("CFE_ES_GetAppInfo: Invalid Parameter ( Null Pointer )\n");
ReturnCode = CFE_ES_ERR_BUFFER;
}
return(ReturnCode);
} /* End of CFE_ES_GetAppInfo() */
And you are not supposed to find workarounds to avoid nested if statements to handle a single exit point.
If your code gets to many nested statements, you should rather break your function up into smaller pieces that each can handle its own part to reduce the nesting.
While the above code is verbose, it's still small enough to not need breaking up, but as a demonstration, it could be
static int32 CFE_ES_GetAppInfoImpl(CFE_ES_AppInfo_t *AppInfo, uint32 AppId)
{
int32 ReturnCode;
if ( CFE_ES_Global.AppTable[AppId].RecordUsed == TRUE )
{
CFE_ES_GetAppInfoInternal(AppId, AppInfo);
ReturnCode = CFE_SUCCESS;
}
else
{
CFE_ES_WriteToSysLog("CFE_ES_GetAppInfo: App ID Not Active: %d\n",(int)AppId);
ReturnCode = CFE_ES_ERR_APPID;
}
return ReturnCode;
}
int32 CFE_ES_GetAppInfo(CFE_ES_AppInfo_t *AppInfo, uint32 AppId)
{
int32 ReturnCode = CFE_SUCCESS;
if ( AppInfo != 0 )
{
if ( AppId < CFE_ES_MAX_APPLICATIONS )
{
ReturnCode = CFE_ES_GetAppInfoImpl(AppInfo, AppID);
}
else
{
CFE_ES_WriteToSysLog("CFE_ES_GetAppInfo: App ID Exceeds CFE_ES_APPLICATION_MAX: %d\n",(int)AppId);
ReturnCode = CFE_ES_ERR_APPID;
}
}
else
{
CFE_ES_WriteToSysLog("CFE_ES_GetAppInfo: Invalid Parameter ( Null Pointer )\n");
ReturnCode = CFE_ES_ERR_BUFFER;
}
return(ReturnCode);
} /* End of CFE_ES_GetAppInfo() */

How to access python class from C extension?

I have my extension for Python written in C. Currently I need to process in a function of my extension objects of a type declared in some external pure python module (Say, its name is ext_module). First of all I need to ensure that input object type corresponds some class from ext_module. How can I implement this?
Some additional code:
static PyObject * extensionFunction( PyObject* self, PyObject *args ) {
const char *inputString = NULL;
PyObject *inputList = NULL;
if ( 0 == PyArg_ParseTuple( args, "sO", &inputString, &inputList ) ) {
return NULL;
}
if ( 0 != PyList_Check( inputList ) ) {
// here I need to check whether the list items are instances of ext_module.ExtClass
} else {
PyErr_SetString( PyExc_TypeError, "extensionFunction: expected list" );
return NULL;
}
// process arguments
Py_RETURN_NONE;
}
Actually I can just try to call the expected methods and get an exception NotImplementedError, but I'm not sure if this is correct approach.

Checking if Kerberos tickets exist in cache

I have written some C code to connect to a Kerberized LDAP server. This all works fine, but at present, it currently generates a new TGT every time it connects, rather than using the one (assuming it already exists) in the default credentials cache.
I have looked into using the likes of krb5_cc_resolve and krb5_initialize to get a reference to the cache, but this seems to destroy the cache if it already exists, along with any tickets it holds.
Basically, what I want to know is: is there any way of checking the default credentials cache for existing TGTs without destroying it?
krb5_cc_initialize clears the cache, as the documentation says. Just don't do that if you want to access an existing cache
From the docs:
Any existing credentials are discarded and the principal name for the cache is set to the value specified
Look in the code for kstart where it implements the -H option.
http://git.eyrie.org/?p=kerberos/kstart.git;a=blob;f=framework.c;h=66e851413a9b4d71fa4d61ded2f3c0d71cd03b0c;hb=HEAD
Basically, you need to check the expire time for the principal in the ticket.
/* Obtain the ticket. */
memset(&increds, 0, sizeof(increds));
code = krb5_cc_resolve(ctx, config->cache, &ccache);
if (code != 0)
goto done;
increds.client = config->client;
else {
code = krb5_cc_get_principal(ctx, ccache, &increds.client);
if (code != 0)
goto done;
}
code = get_krbtgt_princ(ctx, increds.client, &increds.server);
if (code != 0)
goto done;
code = krb5_get_credentials(ctx, 0, ccache, &increds, &outcreds);
if (code != 0)
goto done;
increds_valid = true;
/* Check the expiration time and renewal limit. */
if (code == 0) {
now = time(NULL);
then = outcreds->times.endtime;
if (config->happy_ticket > 0)
offset = 60 * config->happy_ticket;
else
offset = 60 * config->keep_ticket + EXPIRE_FUDGE;
if (then < now + offset)
code = KRB5KRB_AP_ERR_TKT_EXPIRED;

Resources