FltQuerySecurityObject and Windows Update

I have some code that does the following (pseudo code to try and be brief)

PreOp(PFLT_CALLBACK_DATA Data, ...)
    case IRP_MJ_SET_SECURITY:
	    //defer because we will call FltQuerySecurityObject which needs to be at IRQL PASSIVE
	   FltQueueDeferredIoWorkItem(Data, LookupFunc, CriticalWorkQueue)   
	   return FLT_PREOP_PENDING;

LookupFunc(PFLT_CALLBACK_DATA Data, ...)
	    FltQuerySecurityObject(Data)  //at IRQL PASSIVE now
	    FltCompletePendedPreOperation(FLT_PREOP_SUCCESS_WITH_CALLBACK)

With this code in place I find I can successfully change the security settings, file ownership settings, etc using Windows Explorer with no problem.

However, when Windows Update runs (and apparently Veeam’s restore app) they fail when trying to set file security with 0x8007051B - This security ID may not be assigned as the owner of this object

As far as I understand, the code above would pass all requests through to the lower level without any changes, albeit with a temporary pause to do the lookup.

Apparently there is hole in my understanding. Can anyone help me see what is wrong?

Thanks
Doug

I’m confused…Are you doing this as a result of IRP_MJ_SET_SECURITY or IRP_MJ_QUERY_SECURITY? Query Security is a METHOD_NEITHER request so you can’t post it unless you double buffer the output.

Also, are you checking the return values for failure?

Lastly…What IRQL are you worried about? The file systems can’t handle being called for security info at DISPATCH_LEVEL anyway.

Thanks for your time Scott.

It is for IRP_MJ_SET_SECURITY. I’m trying to get the before and after of a security change, so trying to grab the existing security descriptor before the new one goes down the stack and overwrites it.

The code is checking every function’s return value.

What IRQL are you worried about?
The docs for FltQuerySecurityObject say required IRQL level is PASSIVE_LEVEL if I’m reading it right, so posting a callback seems like the only way to be guaranteed to be at that level for the call.

The file systems can’t handle being called for security info at DISPATCH_LEVEL anyway.
I didn’t know that. Being called at PASSIVE_LEVEL should be safe then right? Or are you implying that IRP_MJ_SET_SECURITY will always come in at PASSIVE_LEVEL anyway? Could it come in at APC_LEVEL?

I saw this today on my system - Windows Update failed with the IRP_MJ_SET_SECURITY PreOp processing, but turning it off allowed updates to install. But I can set security properties, change owner, change inheritance, etc, etc from Windows Explorer while IRP_MJ_SET_SECURITY PreOp processing is running without issue.

It is for IRP_MJ_SET_SECURITY. I’m trying to get the before and after of a security change, so trying to grab the existing security descriptor before the new one goes down the stack and overwrites it.

OK, that makes sense, I get it now.

The code is checking every function’s return value.

OK, the overall set security operation must be failing then. 0x8007051B is the HRESULT version of ERROR_INVALID_OWNER, which is NTSTATUS STATUS_INVALID_OWNER (0xc000005a). You could do the NTFS status debugging trick to trap the error where it happens, but that probably won’t tell you more than the fact that it’s failing…

My theory would be that NTFS/Se are making assumptions about the calling thread in the IRP_MJ_SET_SECURITY request. By posting to a worker thread you’re disassociating the caller and the requestor. Impersonating the requestor might fix it OR just don’t post the call (more below).

The docs for FltQuerySecurityObject say required IRQL level is PASSIVE_LEVEL if I’m reading it right, so posting a callback seems like the only way to be guaranteed to be at that level for the call.

IRQL and PreOperation callbacks is murky so it’s easy to over engineer…IRP_MJ_SET_SECURITY requests are generated by {Zw|Nt|Flt}SetSecurityObject, all of which require PASSIVE_LEVEL operation. Sure, a driver could build the IRP and send it at DISPATCH_LEVEL, but that would be a really bad idea. All the SeXxx APIs require PASSIVE_LEVEL and NTFS even puts its IRP_MJ_SECURITY code in a pageable section.

The file systems can’t handle being called for security info at DISPATCH_LEVEL anyway.
I didn’t know that. Being called at PASSIVE_LEVEL should be safe then right? Or are you implying that IRP_MJ_SET_SECURITY will always come in at PASSIVE_LEVEL anyway? Could it come in at APC_LEVEL?

I wouldn’t expect to see it at APC_LEVEL or a Guarded Region (which has the same behavior as APC_LEVEL). I could maybe see another filter sending it in a Critical Region (e.g. if they had an ERESOURCE held). Though in any case none of these should cause a problem from a functional standpoint for this API. Worst case you might run afoul of FltMgr Verifier.

I saw this today on my system - Windows Update failed with the IRP_MJ_SET_SECURITY PreOp processing, but turning it off allowed updates to install. But I can set security properties, change owner, change inheritance, etc, etc from Windows Explorer while IRP_MJ_SET_SECURITY PreOp processing is running without issue.

It’s possible the files being laid out have just the right Security Descriptors associated with them to trigger the behavior. I’d do the NTFS status debug trick and then dump the Security Descriptor in the IRP with !sd so see if there’s anything special about it.

are generated by {Zw|Nt|Flt}SetSecurityObject, all of which require PASSIVE_LEVEL operation

Thanks Scott. That is great information to have. I’ll change the code do double-check the IRQL level, and if it is PASSIVE_LEVEL as expected, proceed. If it isn’t (because of odd situation you bring up of another driver creating the request), I guess I can fall back on the current posting of the request.

Thank you for your help.