Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

487 lines
20 KiB

#include "drmkPCH.h"
#include "KList.h"
#include "StreamMgr.h"
#include "iohelp.h"
//-------------------------------------------------------------------------------------------------
// Package implements the DRMK authentication stubs. Routines are called to notify DRMK of downstream
// components and to notify DRMK of the creation and destruction of composite streams. ContentId
// in this file is called StreamId elsewhere.
//-------------------------------------------------------------------------------------------------
static NTSTATUS GetDeviceObjectDispatchTable(IN DWORD ContentId, IN _DEVICE_OBJECT* pDevO, IN BOOL fCheckAttached);
static NTSTATUS GetFileObjectDispatchTable(IN DWORD ContentId, IN PFILE_OBJECT pF);
//-------------------------------------------------------------------------------------------------
/*
Routine called by a splitter component. Any stream with ContentId==0 is considered unprotected.
*/
NTSTATUS DrmCreateContentMixed(IN PULONG paContentId,
IN ULONG cContentId,
OUT PULONG pMixedContentId)
{
KCritical s(TheStreamMgr->getCritMgr());
if((NULL==paContentId && !(cContentId!=0)) || NULL==pMixedContentId){
_DbgPrintF(DEBUGLVL_VERBOSE,("Invalid NULL-parameter for DrmCreateContentMixed"));
TheStreamMgr->logErrorToStream(0, STATUS_INVALID_PARAMETER);
return STATUS_INVALID_PARAMETER;
};
_DbgPrintF(DEBUGLVL_VERBOSE,("DrmCreateMixed for N streams, N= %d", cContentId));
DRM_STATUS stat = TheStreamMgr->createCompositeStream(pMixedContentId, paContentId, cContentId);
if(stat==DRM_OK){
return STATUS_SUCCESS;
}
// only error is out-of-memory
TheStreamMgr->setFatalError(STATUS_INSUFFICIENT_RESOURCES);
return STATUS_INSUFFICIENT_RESOURCES;
}
//------------------------------IO-------------------------------------------------------------------
/*
Routine called by a component to notify KRM of a downstream COM object that will process audio.
DrmForwardContent will collect its authentication function, and set the DRMRIGHTS bits appropriately.
*/
NTSTATUS DrmForwardContentToInterface(ULONG ContentId, PUNKNOWN pUnknown, ULONG NumMethods)
{
NTSTATUS Status;
PDRMAUDIOSTREAM DrmAudioStream;
_DbgPrintF(DEBUGLVL_VERBOSE,("***IN ForwardToInterface"));
if(NULL == pUnknown){
_DbgPrintF(DEBUGLVL_VERBOSE,("Invalid NULL-parameter for DrmForwardContentToInterface"));
TheStreamMgr->logErrorToStream(ContentId, STATUS_INVALID_PARAMETER);
return STATUS_INVALID_PARAMETER;
};
Status = pUnknown->QueryInterface(IID_IDrmAudioStream, (PVOID*)&DrmAudioStream);
if (!NT_SUCCESS(Status)){
_DbgPrintF(DEBUGLVL_VERBOSE,("QI Failed for StreamId= %x (Status=%d, %x)", ContentId, Status, Status));
TheStreamMgr->logErrorToStream(ContentId, Status);
return Status;
};
// ReferenceAquirer calls Release() when it goes out of scope
ReferenceAquirer<PDRMAUDIOSTREAM> aq(DrmAudioStream);
// rights are most permissive. If ContentId!=0, we query the mixed stream that compose
// this stream to restrict the rights.
DRMRIGHTS DrmRights={FALSE, FALSE, FALSE};
if(ContentId!=0){
KCritical s(TheStreamMgr->getCritMgr());
_DbgPrintF(DEBUGLVL_VERBOSE,("Adding %d methods", NumMethods));
// get the pointer to the vtbl
PVOID* vtbl= *((PVOID**) pUnknown);
// and add NumMethods of from the vtbl
for(ULONG j=0;j<NumMethods;j++){
_DbgPrintF(DEBUGLVL_VERBOSE,("ADDING = %x", vtbl[j]));
if (vtbl[j]) {
Status = TheStreamMgr->addProvingFunction(ContentId, vtbl[j]);
} else {
Status = STATUS_INVALID_PARAMETER;
}
if(!NT_SUCCESS(Status)){
_DbgPrintF(DEBUGLVL_VERBOSE,("addProveFunc Failed for StreamId= %x", ContentId));
TheStreamMgr->logErrorToStream(ContentId, Status);
return Status;
};
};
Status=TheStreamMgr->getRights(ContentId, &DrmRights);
if(!NT_SUCCESS(Status)){
_DbgPrintF(DEBUGLVL_VERBOSE,("getRights failed for StreamId= %x", ContentId));
TheStreamMgr->logErrorToStream(ContentId, Status);
return Status;
};
};
_DbgPrintF(DEBUGLVL_VERBOSE,("About to SetContentId "));
Status = DrmAudioStream->SetContentId(ContentId, &DrmRights);
if(!NT_SUCCESS(Status)){
_DbgPrintF(DEBUGLVL_VERBOSE,("SetContentId failed for StreamId= %x (Status=%d, %x)", ContentId, Status, Status));
if (STATUS_NOT_IMPLEMENTED == Status) {
TheStreamMgr->logErrorToStream(ContentId, DRM_RIGHTSNOTSUPPORTED);
} else {
TheStreamMgr->logErrorToStream(ContentId, Status);
}
return Status;
};
return STATUS_SUCCESS;
}
//-------------------------------------------------------------------------------------------------
/*
Routine called by a component to notify KRM of a downstream FILE object that will process audio.
DrmForwardContent will collect its authentication function, and set the DRMRIGHTS bits appropriately.
*/
NTSTATUS DrmForwardContentToFileObject(IN ULONG ContentId,
IN PFILE_OBJECT FileObject)
{
KSP_DRMAUDIOSTREAM_CONTENTID Property;
KSDRMAUDIOSTREAM_CONTENTID PropertyValue;
ULONG cbReturned;
NTSTATUS Status;
_DbgPrintF(DEBUGLVL_VERBOSE,("***IN ForwardToFileObject"));
if (FileObject)
{
KCritical s(TheStreamMgr->getCritMgr());
if (0 != ContentId) {
NTSTATUS stat=GetFileObjectDispatchTable(ContentId, FileObject);
}
Property.Property.Set = KSPROPSETID_DrmAudioStream;
Property.Property.Id = KSPROPERTY_DRMAUDIOSTREAM_CONTENTID;
Property.Property.Flags = KSPROPERTY_TYPE_SET;
Property.Context = FileObject;
Property.DrmAddContentHandlers = DrmAddContentHandlers;
Property.DrmCreateContentMixed = DrmCreateContentMixed;
Property.DrmDestroyContent = DrmDestroyContent;
Property.DrmForwardContentToDeviceObject = DrmForwardContentToDeviceObject;
Property.DrmForwardContentToFileObject = DrmForwardContentToFileObject;
Property.DrmForwardContentToInterface = DrmForwardContentToInterface;
Property.DrmGetContentRights = DrmGetContentRights;
PropertyValue.ContentId = ContentId;
Status = TheStreamMgr->getRights(ContentId, &PropertyValue.DrmRights);
if(!NT_SUCCESS(Status)){
_DbgPrintF(DEBUGLVL_VERBOSE,("Bad getRights for StreamId= %x", ContentId));
return Status;
};
} else {
_DbgPrintF(DEBUGLVL_VERBOSE,("Invalid NULL-parameter for DrmForwardContentToFileObject"));
TheStreamMgr->logErrorToStream(ContentId, STATUS_INVALID_PARAMETER);
return STATUS_INVALID_PARAMETER;
}
Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY,
&Property, sizeof(Property),
&PropertyValue, sizeof(PropertyValue),
&cbReturned);
// TBD: translate STATUS_PROPSET_NOT_FOUND to something better
if(!NT_SUCCESS(Status)){
_DbgPrintF(DEBUGLVL_VERBOSE,("Bad IoControl(1b) for StreamId= %x on driver. Device with load address [%x] does not support DRM property,(Status=%d, %x)",
ContentId, IoGetRelatedDeviceObject(FileObject)->DriverObject->DriverStart,Status, Status));
if (STATUS_NOT_IMPLEMENTED == Status) {
TheStreamMgr->logErrorToStream(ContentId, DRM_RIGHTSNOTSUPPORTED);
} else {
TheStreamMgr->logErrorToStream(ContentId, Status);
}
return Status;
};
//This may be confusing. We're logging an error here to indicate
//that DrmForwardContentToFileObject was called. This error will
//later be propagated up to krmproxy and used to adjust the security
//level of the drivers, since DrmForwardContentToFileObject opens a
//security hole. We return success from the function after logging
//because we want driver walking to continue from this point, not
//fail, since this is not a fatal error.
//This error code can be overwritten later by another call to
//logErrorToStream, but it will be overwritten either with a fatal
//error or with DRM_BADDRMLEVEL again.
TheStreamMgr->logErrorToStream(ContentId, DRM_BADDRMLEVEL);
return STATUS_SUCCESS;
}
//-------------------------------------------------------------------------------------------------
/*
Routine called by a component to notify KRM of a downstream DEVICE object that will process audio.
DrmForwardContent will collect its authentication function, and set the DRMRIGHTS bits appropriately.
*/
NTSTATUS DrmForwardContentToDeviceObject(IN ULONG ContentId,
IN PVOID Reserved,
IN PCDRMFORWARD DrmForward)
{
_DbgPrintF(DEBUGLVL_VERBOSE,("***IN ForwardToDeviceObject"));
PDEVICE_OBJECT DeviceObject;
PFILE_OBJECT FileObject;
PVOID Context;
NTSTATUS Status;
KSP_DRMAUDIOSTREAM_CONTENTID Property;
KSDRMAUDIOSTREAM_CONTENTID PropertyValue;
Status = STATUS_SUCCESS;
if (NULL != Reserved) {
//
// This is an older driver which passes the DeviceObject as the
// second param and the Context as the third param.
//
DeviceObject = (PDEVICE_OBJECT)Reserved;
FileObject = NULL;
Context = (PVOID)DrmForward;
} else {
if (0 != DrmForward->Flags) {
Status = STATUS_INVALID_PARAMETER;
TheStreamMgr->logErrorToStream(ContentId, Status);
} else {
DeviceObject = DrmForward->DeviceObject;
FileObject = DrmForward->FileObject;
Context = DrmForward->Context;
}
}
if (!NT_SUCCESS(Status)) return Status;
if (DeviceObject)
{
KCritical s(TheStreamMgr->getCritMgr());
if (0 != ContentId) {
NTSTATUS stat=GetDeviceObjectDispatchTable(ContentId, DeviceObject, FALSE);
}
Property.Property.Set = KSPROPSETID_DrmAudioStream;
Property.Property.Id = KSPROPERTY_DRMAUDIOSTREAM_CONTENTID;
Property.Property.Flags = KSPROPERTY_TYPE_SET;
Property.Context = Context;
Property.DrmAddContentHandlers = DrmAddContentHandlers;
Property.DrmCreateContentMixed = DrmCreateContentMixed;
Property.DrmDestroyContent = DrmDestroyContent;
Property.DrmForwardContentToDeviceObject = DrmForwardContentToDeviceObject;
Property.DrmForwardContentToFileObject = DrmForwardContentToFileObject;
Property.DrmForwardContentToInterface = DrmForwardContentToInterface;
Property.DrmGetContentRights = DrmGetContentRights;
PropertyValue.ContentId = ContentId;
Status = TheStreamMgr->getRights(ContentId, &PropertyValue.DrmRights);
if(!NT_SUCCESS(Status)){
_DbgPrintF(DEBUGLVL_VERBOSE,("Bad getRights for StreamId= %x", ContentId));
return Status;
};
} else {
_DbgPrintF(DEBUGLVL_VERBOSE,("Invalid NULL-parameter for DrmForwardContentToFileObject"));
TheStreamMgr->logErrorToStream(ContentId, STATUS_INVALID_PARAMETER);
return STATUS_INVALID_PARAMETER;
}
KEVENT Event;
PIRP Irp;
IO_STATUS_BLOCK IoStatusBlock;
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(
IOCTL_KS_PROPERTY,
DeviceObject,
&Property,
sizeof(Property),
&PropertyValue,
sizeof(PropertyValue),
FALSE,
&Event,
&IoStatusBlock);
if (Irp) {
//
// Originating in kernel, no need to probe buffers, etc.
//
Irp->RequestorMode = KernelMode;
//
// Set the file object in the next stack location
//
IoGetNextIrpStackLocation(Irp)->FileObject = FileObject;
//
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING) {
//
// This waits using KernelMode, so that the stack, and therefore the
// event on that stack, is not paged out.
//
KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
// TBD: translate STATUS_PROPSET_NOT_FOUND to something better
if(!NT_SUCCESS(Status)){
_DbgPrintF(DEBUGLVL_VERBOSE,("Bad IoControl for StreamId(2)= %x (Status=%d, %x)", ContentId, Status, Status));
if (STATUS_NOT_IMPLEMENTED == Status) {
TheStreamMgr->logErrorToStream(ContentId, DRM_RIGHTSNOTSUPPORTED);
} else {
TheStreamMgr->logErrorToStream(ContentId, Status);
}
return Status;
};
return STATUS_SUCCESS;
}
//--------------------------------------------------------------------------
NTSTATUS DrmDestroyContent(IN ULONG ContentId)
{
KCritical s(TheStreamMgr->getCritMgr());
_DbgPrintF(DEBUGLVL_VERBOSE,("DestroyStream for StreamId= %x", ContentId));
NTSTATUS stat = TheStreamMgr->destroyStream(ContentId);
if (!NT_SUCCESS(stat)){
_DbgPrintF(DEBUGLVL_VERBOSE,("Bad destroyStream for StreamId= %d", ContentId));
// not sure if we should flag this as fatal (we sure can't log it to any stream)
// TheStreamMgr->logErrorToStream(0, Status);
return stat;
};
return STATUS_SUCCESS;
}
//---------------------------------------------------------------------------
NTSTATUS DrmGetContentRights(IN DWORD ContentId, OUT DRMRIGHTS* DrmRights){
KCritical s(TheStreamMgr->getCritMgr());
NTSTATUS Status=TheStreamMgr->getRights(ContentId, DrmRights);
if(!NT_SUCCESS(Status)){
_DbgPrintF(DEBUGLVL_VERBOSE,("getRights failed for StreamId= %x", ContentId));
return Status;
};
return Status;
};
//---------------------------------------------------------------------------
NTSTATUS DrmAddContentHandlers(IN ULONG ContentId, IN PVOID* paHandlers, IN ULONG NumHandlers)
{
KCritical s(TheStreamMgr->getCritMgr());
ULONG i;
NTSTATUS Status = STATUS_SUCCESS;
if (0 != ContentId) {
for (i = 0; i < NumHandlers && NT_SUCCESS(Status); i++) {
if (paHandlers[i]) {
Status = TheStreamMgr->addProvingFunction(ContentId, paHandlers[i]);
if(!NT_SUCCESS(Status)){
_DbgPrintF(DEBUGLVL_VERBOSE,("addProveFunc Failed for StreamId= %x", ContentId));
TheStreamMgr->logErrorToStream(ContentId, Status);
};
}
}
}
return Status;
}
//---------------------------------------------------------------------------
static NTSTATUS GetFileObjectDispatchTable(IN DWORD ContentId, IN PFILE_OBJECT pF){
if(pF==NULL){
_DbgPrintF(DEBUGLVL_VERBOSE,("Invalid FILE_OBJECT on stream %x", ContentId));
return STATUS_INVALID_PARAMETER;
};
PDEVICE_OBJECT pDevO=pF->DeviceObject;
if(pDevO==NULL){
_DbgPrintF(DEBUGLVL_VERBOSE,("Invalid DEVICE_OBJECT for stream %x on PFILE_OBJECT = %x", ContentId, pF));
return STATUS_INVALID_PARAMETER;
};
NTSTATUS stat=GetDeviceObjectDispatchTable(ContentId, pDevO, TRUE);
if(!NT_SUCCESS(stat)){
return stat;
};
return stat;
};
//---------------------------------------------------------------------------
static NTSTATUS GetDeviceObjectDispatchTable(IN DWORD ContentId, IN _DEVICE_OBJECT* pDevO, BOOL fCheckAttached){
_DRIVER_OBJECT* pDriverObject=pDevO->DriverObject;
if(pDriverObject==NULL){
_DbgPrintF(DEBUGLVL_VERBOSE,("Invalid PDRIVER_OBJECT for stream %x", ContentId));
return STATUS_INVALID_PARAMETER;
};
// collect the dispatch table.
for(DWORD j=0;j<IRP_MJ_MAXIMUM_FUNCTION;j++){
PDRIVER_DISPATCH pDisp=pDriverObject->MajorFunction[j];
if(pDisp==NULL)continue;
// _DbgPrintF(DEBUGLVL_VERBOSE,("DISPATCH (%3d) devO =%10x, func=%10x", j, pDevO, pDisp));
DRM_STATUS stat=TheStreamMgr->addProvingFunction(ContentId, pDisp);
if(stat!=DRM_OK){
_DbgPrintF(DEBUGLVL_VERBOSE,("bad AddProve on stream %x (error=%x)", ContentId));
return STATUS_INSUFFICIENT_RESOURCES;
};
};
// collect the other driver entry points
const DWORD numMiscEntries=4;
PVOID miscEntry[numMiscEntries];
miscEntry[0]=pDriverObject->DriverExtension->AddDevice;
miscEntry[1]=pDriverObject->DriverUnload;
miscEntry[2]=pDriverObject->DriverStartIo;
miscEntry[3]=pDriverObject->DriverInit;
for(j=0;j<numMiscEntries;j++){
if(NULL!=miscEntry[j]){
DRM_STATUS stat=TheStreamMgr->addProvingFunction(ContentId, miscEntry[j]);
if(stat!=DRM_OK){
_DbgPrintF(DEBUGLVL_VERBOSE,("bad AddProve on stream %x (error=%x)", ContentId));
return STATUS_INSUFFICIENT_RESOURCES;
};
};
};
// collect the fastIo dispatch points (if they are present)
FAST_IO_DISPATCH* pFastIo=pDriverObject->FastIoDispatch;
if(NULL!=pFastIo){
ULONG numFastIo=(pFastIo->SizeOfFastIoDispatch - sizeof(pFastIo->SizeOfFastIoDispatch)) / sizeof(PVOID);
if(numFastIo!=0){
_DbgPrintF(DEBUGLVL_VERBOSE,("FASTIO DISPATCH: Num=", numFastIo));
// Collect the FastIo entries. wdm.h makes has some strict requirements on
// editing this structure, which means that we can pick up the entries as if
// they were in a real array.
PVOID* fastIoTable= (PVOID*)&(pFastIo->FastIoCheckIfPossible);
for(ULONG j=0;j<numFastIo;j++){
PVOID fastIoEntry= *(fastIoTable+j);
if(NULL!=fastIoEntry){
DRM_STATUS stat=TheStreamMgr->addProvingFunction(ContentId, fastIoEntry);
if(stat!=DRM_OK){
_DbgPrintF(DEBUGLVL_VERBOSE,("bad AddProve on stream %x (error=%x)", ContentId));
return STATUS_INSUFFICIENT_RESOURCES;
};
};
};
};
};
// now traverse the driver stack (if there is one)
if (fCheckAttached) {
_DEVICE_OBJECT* pNextDevice=pDevO->AttachedDevice;
if(NULL == pNextDevice)return STATUS_SUCCESS;
NTSTATUS stat=GetDeviceObjectDispatchTable(ContentId, pNextDevice, fCheckAttached);
if(!NT_SUCCESS(stat)){
_DbgPrintF(DEBUGLVL_VERBOSE,("Failed to add dispatch entries from attached device on stream=%x ", ContentId));
return stat;
};
}
// Verifier and Acpi are special case filter drivers. Instead of modifying them
// to handle DRM, we assume that it blindly "forwards" everything to the next
// lower driver
if (NT_SUCCESS(IoDeviceIsVerifier(pDevO)) || NT_SUCCESS(IoDeviceIsAcpi(pDevO)))
{
PDEVICE_OBJECT LowerDeviceObject = IoGetLowerDeviceObject(pDevO);
_DbgPrintF(DEBUGLVL_TERSE,("Detected Verifier or Acpi on DO %p, checked lower DO", pDevO));
if (LowerDeviceObject)
{
NTSTATUS status = GetDeviceObjectDispatchTable(ContentId, LowerDeviceObject, FALSE);
ObDereferenceObject(LowerDeviceObject);
if (!NT_SUCCESS(status)) return status;
}
else
{
return STATUS_INVALID_DEVICE_REQUEST;
}
}
return STATUS_SUCCESS;
};
//---------------------------------------------------------------------------