#include "drmkPCH.h" #include "KList.h" #include "VRoot.h" #include "StreamMgr.h" //------------------------------------------------------------------------------ StreamMgr* TheStreamMgr=NULL; // 'secondary root - lowest secondary stream ID #define SEC_ROOT 0x80000000 //------------------------------------------------------------------------------ StreamMgr::StreamMgr(){ TheStreamMgr=this; nextStreamId=1; nextCompositeId=SEC_ROOT+1; criticalErrorCode=STATUS_SUCCESS; if(!critMgr.isOK()){ _DbgPrintF(DEBUGLVL_VERBOSE,("Out of memory")); criticalErrorCode=STATUS_INSUFFICIENT_RESOURCES; }; return; }; //------------------------------------------------------------------------------ StreamMgr::~StreamMgr(){ { KCritical s(critMgr); POS p=primary.getHeadPosition(); while(p!=NULL){ StreamInfo* info=primary.getNext(p); delete info; }; p=composite.getHeadPosition(); while(p!=NULL){ CompositeStreamInfo* info=composite.getNext(p); delete info; }; }; return; }; //------------------------------------------------------------------------------ DRM_STATUS StreamMgr::createStream(HANDLE Handle, DWORD* StreamId, const DRMRIGHTS* RightsStruct, IN STREAMKEY* Key){ *StreamId = 0xFFFFffff; StreamInfo* newInfo=new StreamInfo; if(newInfo==NULL){ _DbgPrintF(DEBUGLVL_BLAB,("Out of memory")); return DRM_OUTOFMEMORY; }; newInfo->StreamId=nextStreamId++; newInfo->Handle=Handle; newInfo->Key= *Key; newInfo->Rights= *RightsStruct; newInfo->drmFormat=NULL; newInfo->streamStatus=DRM_OK; newInfo->streamWalked=false; newInfo->newProveFuncs=false; newInfo->OutType=IsUndefined; newInfo->OutInt=NULL; newInfo->OutPinFileObject=NULL; newInfo->OutPinDeviceObject=NULL; bool ok=addStream(*newInfo); if(!ok){ _DbgPrintF(DEBUGLVL_BLAB,("Out of memory")); delete newInfo; return DRM_OUTOFMEMORY; }; *StreamId= newInfo->StreamId; return KRM_OK; }; //------------------------------------------------------------------------------ DRM_STATUS StreamMgr::destroyStream(DWORD StreamId){ KCritical s(critMgr); if(StreamId==0){ return KRM_OK; }; POS pos=getStreamPos(StreamId); if(pos==NULL)return KRM_BADSTREAM; bool primary=StreamIdHandle==Handle){ delete stream; primary.removeAt(oldP); }; }; return KRM_OK; }; //------------------------------------------------------------------------------ // Called by a filter to instuct StreamMgr that a mixed stream is being created. DRM_STATUS StreamMgr::createCompositeStream(OUT DWORD* StreamId, IN DWORD* StreamInArray, DWORD NumStreams){ KCritical s(critMgr); CompositeStreamInfo* newStream=new CompositeStreamInfo;; if(newStream==NULL){ _DbgPrintF(DEBUGLVL_BLAB,("Out of memory")); return DRM_OUTOFMEMORY; }; for(DWORD j=0;jparents.addTail(StreamInArray[j]); if(!ok){ delete newStream; _DbgPrintF(DEBUGLVL_BLAB,("Out of memory")); return DRM_OUTOFMEMORY; }; }; newStream->StreamId=nextCompositeId++; bool ok=composite.addTail(newStream); if(!ok){ delete newStream; _DbgPrintF(DEBUGLVL_BLAB,("Out of memory")); return DRM_OUTOFMEMORY; }; *StreamId=newStream->StreamId; return DRM_OK; }; //------------------------------------------------------------------------------ DRM_STATUS StreamMgr::destroyCompositeStream(IN DWORD CompositeStreamId){ bool primary=(CompositeStreamId=SEC_ROOT)return KRM_NOTPRIMARY; POS pos=getStreamPos(StreamId); if(pos==NULL)return KRM_BADSTREAM; Key=&(primary.getAt(pos)->Key); return KRM_OK; }; //------------------------------------------------------------------------------ bool StreamMgr::addStream(StreamInfo& NewInfo){ KCritical s(critMgr); return primary.addTail(&NewInfo); }; //------------------------------------------------------------------------------ POS StreamMgr::getStreamPos(DWORD StreamId){ KCritical s(critMgr); if(StreamIdStreamId==StreamId)return oldPos; }; return NULL; } else { POS p=composite.getHeadPosition(); while(p!=NULL){ POS oldPos=p; if(composite.getNext(p)->StreamId==StreamId)return oldPos; }; return NULL; }; }; //------------------------------------------------------------------------------ void StreamMgr::deleteStreamAt(bool Primary,POS pos){ KCritical s(critMgr); if(Primary){ StreamInfo* it=primary.getAt(pos); primary.removeAt(pos); delete it; } else { CompositeStreamInfo* it=composite.getAt(pos); composite.removeAt(pos); delete it; }; }; //------------------------------------------------------------------------------ DRM_STATUS StreamMgr::getRights(DWORD StreamId,DRMRIGHTS* Rights){ KCritical s(critMgr); DEFINE_DRMRIGHTS_DEFAULT(DrmRightsDefault); *Rights = DrmRightsDefault; return getRightsWorker(StreamId, Rights); }; //------------------------------------------------------------------------------ bool StreamMgr::isPrimaryStream(DWORD StreamId){ return StreamIdRights.CopyProtect)Rights->CopyProtect=TRUE; if(s->Rights.DigitalOutputDisable)Rights->DigitalOutputDisable=TRUE; return DRM_OK; } else { // For composite streams, any of the parent streams can reduce the // current settings. We descend to the primary parents thru recursion. // Note, for this to work, we must have 'monotonic rights' - there should // be no case where two components disagree on 'more restrictive' POS pos=getStreamPos(StreamId); if(pos==NULL){ _DbgPrintF(DEBUGLVL_BLAB,("Bad secondary stream")); Rights->CopyProtect=TRUE; Rights->DigitalOutputDisable=TRUE; return KRM_BADSTREAM; }; CompositeStreamInfo* thisComp=composite.getAt(pos); POS p=thisComp->parents.getHeadPosition(); while(p!=NULL){ DWORD streamId=thisComp->parents.getNext(p); if(streamId==0)continue; // unprotected - no change to rights // else allow the parent stream (and its parents) to // further restrict rights DRM_STATUS stat=getRightsWorker(streamId, Rights); if(stat!=DRM_OK)return stat; }; }; return DRM_OK; }; //------------------------------------------------------------------------------ // Proving function is only of interest to parent stream. We recurse up the // stream parentage to all parents and add the proving fucntion to their lists. DRM_STATUS StreamMgr::addProvingFunction(DWORD StreamId,PVOID Func){ KCritical s(critMgr); if(isPrimaryStream(StreamId)){ StreamInfo* si=getPrimaryStream(StreamId); if(si==NULL){ _DbgPrintF(DEBUGLVL_VERBOSE,("Bad primary stream (addProveFunc) %x", StreamId)); return DRM_BADPARAM; }; // check to see if we already have this provinFunc POS p=si->proveFuncs.getHeadPosition(); while(p!=NULL){ PVOID addr=si->proveFuncs.getNext(p); if(addr==Func)return DRM_OK; }; // if not, add it... bool ok=si->proveFuncs.addTail(Func); if(!ok){ _DbgPrintF(DEBUGLVL_VERBOSE,("Out of memory")); return DRM_OUTOFMEMORY; }; si->newProveFuncs = TRUE; return DRM_OK; }; // else is secondary...recurse to root. CompositeStreamInfo* comp=getCompositeStream(StreamId); if(comp==NULL){ _DbgPrintF(DEBUGLVL_VERBOSE,("Bad streamId %x", StreamId)); return DRM_BADPARAM; }; POS p=comp->parents.getHeadPosition(); while(p!=NULL){ DWORD parentId=comp->parents.getNext(p); addProvingFunction(parentId, Func); }; return DRM_OK; }; //------------------------------------------------------------------------------ StreamMgr::StreamInfo* StreamMgr::getPrimaryStream(DWORD StreamId){ KCritical s(critMgr); POS pos=getStreamPos(StreamId); if(pos==NULL)return NULL; return primary.getAt(pos); }; //------------------------------------------------------------------------------ StreamMgr::CompositeStreamInfo* StreamMgr::getCompositeStream(DWORD StreamId){ KCritical s(critMgr); POS pos=getStreamPos(StreamId); if(pos==NULL)return NULL; return composite.getAt(pos); }; //------------------------------------------------------------------------------ DRM_STATUS StreamMgr::walkDrivers(DWORD StreamId, PVOID* ProveFuncList, DWORD& NumDrivers, DWORD MaxDrivers) { DRM_STATUS stat; VRoot root; PFILE_OBJECT OutPinFileObject; PDEVICE_OBJECT OutPinDeviceObject; PUNKNOWN OutInt; OutPinFileObject = NULL; OutPinDeviceObject = NULL; OutInt = NULL; { KCritical s(critMgr); StreamInfo* stream; stream=getPrimaryStream(StreamId); if(stream==NULL)return KRM_BADSTREAM; if (0 != MaxDrivers) stream->proveFuncs.empty(); stream->newProveFuncs=false; stream->streamStatus=DRM_OK; if(stream->OutType==IsUndefined){ NumDrivers=0; // no output stream. _DbgPrintF(DEBUGLVL_VERBOSE,("No registered output module for stream %x", StreamId)); return KRM_BADSTREAM; }; // // We must reference the downstream object or interface before // releasing the StreamMgr mutex (i.e., before KCritical s goes out of // scope). Otherwise the downstream object/interface might be // destroyed after we release the StreamMgr mutex but before we // initiate validation on the downstream object/interface. // if ((stream->OutType == IsHandle) && stream->OutPinFileObject && stream->OutPinDeviceObject) { OutPinFileObject = stream->OutPinFileObject; OutPinDeviceObject = stream->OutPinDeviceObject; } else if ((stream->OutType == IsInterface) && stream->OutInt) { OutInt = stream->OutInt; } if (OutPinFileObject) ObReferenceObject(OutPinFileObject); if (OutInt) OutInt->AddRef(); } if (OutPinFileObject) stat = root.initiateValidation(OutPinFileObject, OutPinDeviceObject, StreamId); if (OutInt) stat = root.initiateValidation(OutInt, StreamId); if (OutPinFileObject) ObDereferenceObject(OutPinFileObject); if (OutInt) OutInt->Release(); { KCritical s(critMgr); StreamInfo* stream; // If STATUS_NOT_IMPLEMENTED, see if stream had DRM_RIGHTSNOTSUPPORTED logged as an error if (STATUS_NOT_IMPLEMENTED == stat) { DWORD errorStream; if (DRM_OK == TheStreamMgr->getStreamErrorCode(StreamId, errorStream)) { if (DRM_RIGHTSNOTSUPPORTED == errorStream) { stat = errorStream; } } } //Check to see if the stream had DRM_BADDRMLEVEL set. This return //code indicates that one or more drivers called //DrmForwardContentToFileObject, but otherwise no fatal errors //occurred. This should be treated as a success with the return //code propagated to the caller. { DWORD errorStream; if (DRM_OK == TheStreamMgr->getStreamErrorCode(StreamId, errorStream)) { if (DRM_BADDRMLEVEL == errorStream) { stat = errorStream; } } } // Although it would probably be due to a user-mode bug, we should not // assume that stream is still valid. Let's get the stream once again // from the StreamId stream=getPrimaryStream(StreamId); if(stream==NULL)return KRM_BADSTREAM; // pass out the array of ProveFuncs (there might've been an error, but we pass out what we can) POS p=stream->proveFuncs.getHeadPosition(); DWORD count=0; while(p!=NULL){ PVOID pf=stream->proveFuncs.getNext(p); if(countnewProveFuncs)) return DRM_AUTHREQUIRED; // and finally, inform if there was insufficient buffer space. if((0 == MaxDrivers) || (countOutPinFileObject=OutPinFileObject; stream->OutPinDeviceObject=OutPinDeviceObject; stream->OutType=IsHandle; return DRM_OK; }; //------------------------------------------------------------------------------ DRM_STATUS StreamMgr::setRecipient(DWORD StreamId, PUNKNOWN OutInt){ KCritical s(critMgr); StreamInfo* stream=getPrimaryStream(StreamId); if(stream==NULL)return KRM_BADSTREAM; stream->OutInt=OutInt; stream->OutType=IsInterface; return DRM_OK; }; //------------------------------------------------------------------------------ DRM_STATUS StreamMgr::clearRecipient(IN DWORD StreamId){ KCritical s(critMgr); StreamInfo* stream=getPrimaryStream(StreamId); if(stream==NULL)return KRM_BADSTREAM; stream->OutType=IsUndefined; stream->OutPinFileObject = NULL; stream->OutPinDeviceObject = NULL; stream->OutInt = NULL; return DRM_OK; }; //------------------------------------------------------------------------------ void StreamMgr::logErrorToStream(IN DWORD StreamId, DWORD ErrorCode){ KCritical s(critMgr); logErrorToStreamWorker(StreamId, ErrorCode); return; }; //------------------------------------------------------------------------------ void StreamMgr::logErrorToStreamWorker(IN DWORD StreamId, DWORD ErrorCode){ // don't allow an error to be cancelled too easily if(ErrorCode==0)return; if(isPrimaryStream(StreamId)){ StreamInfo* info=getPrimaryStream(StreamId); if(info==NULL){ _DbgPrintF(DEBUGLVL_BLAB,("Bad primary stream (logErrorToStreamWorker) %x", StreamId)); // if a primary stream does not exist, this is not considered // to be sufficient to set the panic flag. return; }; info->streamStatus=ErrorCode; return; }; CompositeStreamInfo* comp=getCompositeStream(StreamId); ASSERT(comp!=NULL); if(comp==NULL){ _DbgPrintF(DEBUGLVL_VERBOSE,("Bad streamId %x", StreamId)); // if the secondary stream does not exist, we do not know what streams // are affected by the error, so the only safe thing is to panic. setFatalError(KRM_BADSTREAM); return; }; // log the error with all of the streams parents, recursing back to the // primary streams. POS p=comp->parents.getHeadPosition(); while(p!=NULL){ DWORD parentId=comp->parents.getNext(p); logErrorToStreamWorker(parentId, ErrorCode); }; return; }; //------------------------------------------------------------------------------ DRM_STATUS StreamMgr::getStreamErrorCode(IN DWORD StreamId, OUT DWORD& ErrorCode){ KCritical s(critMgr); StreamInfo* info=getPrimaryStream(StreamId); ErrorCode=DRM_AUTHFAILURE; if(info==NULL){ _DbgPrintF(DEBUGLVL_BLAB,("Bad primary stream(getStreamErrorCode) %x", StreamId)); return KRM_BADSTREAM; }; ErrorCode=info->streamStatus; return DRM_OK; }; //------------------------------------------------------------------------------ DRM_STATUS StreamMgr::clearStreamError(IN DWORD StreamId){ KCritical s(critMgr); StreamInfo* info=getPrimaryStream(StreamId); if(info==NULL){ _DbgPrintF(DEBUGLVL_BLAB,("Bad primary stream (clearStreamError) %x", StreamId)); return KRM_BADSTREAM; }; info->streamStatus=DRM_OK; return DRM_OK; }; //------------------------------------------------------------------------------ void StreamMgr::setFatalError(DWORD ErrorCode){ if(criticalErrorCode!=STATUS_SUCCESS) return; criticalErrorCode=ErrorCode; }; //------------------------------------------------------------------------------ NTSTATUS StreamMgr::getFatalError(){ return criticalErrorCode; }; //------------------------------------------------------------------------------