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.
 
 
 
 
 
 

520 lines
18 KiB

#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=StreamId<SEC_ROOT;
deleteStreamAt(primary, pos);
return KRM_OK;
};
//------------------------------------------------------------------------------
// The 'handle' allows streams to be collected into a group and deleted together.
// It is mostly for debugging
DRM_STATUS StreamMgr::destroyAllStreamsByHandle(HANDLE Handle){
KCritical s(critMgr);
POS p=primary.getHeadPosition();
while(p!=NULL){
POS oldP=p;
StreamInfo* stream=primary.getNext(p);
if(stream->Handle==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;j<NumStreams;j++){
if(StreamInArray[j]==0)continue;
bool ok=newStream->parents.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);
ASSERT(!primary);
if(primary)return KRM_BADSTREAM;
return destroyStream(CompositeStreamId);
};
//------------------------------------------------------------------------------
// get the data encryption key for a stream.
DRM_STATUS StreamMgr::getKey(IN DWORD StreamId, OUT STREAMKEY*& Key){
KCritical s(critMgr);
Key=NULL;
if(StreamId>=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(StreamId<SEC_ROOT){
POS p=primary.getHeadPosition();
while(p!=NULL){
POS oldPos=p;
if(primary.getNext(p)->StreamId==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 StreamId<SEC_ROOT;
};
//------------------------------------------------------------------------------
// called recursively from the getRights parent
DRM_STATUS StreamMgr::getRightsWorker(DWORD StreamId, DRMRIGHTS* Rights){
if(isPrimaryStream(StreamId)){
if(StreamId==0){
// stream is unprotected - no further restrictions
return DRM_OK;
};
// else a protected primary stream
POS p=getStreamPos(StreamId);
if(p==NULL){
// if the primary stream has gone, then it does not care about
// the stream rights. We do not flag an error
_DbgPrintF(DEBUGLVL_BLAB,("Bad primary stream (getRightsWorker) %x", StreamId));
return KRM_OK;
};
StreamInfo* s=primary.getAt(p);
// set rights to most restrictive of current stream and current settings
if(s->Rights.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(count<MaxDrivers){
ProveFuncList[count]=pf;
};
count++;
};
NumDrivers=count;
// if there was an error on the walk, return that too.
if((stat!=DRM_OK) && (DRM_BADDRMLEVEL!=stat)){
// bug - todo - return some useful information
_DbgPrintF(DEBUGLVL_VERBOSE,("VRoot::initiateValidation(streeamId=%d) returned (%d, %x)", StreamId, stat, stat));
NumDrivers=0;
return stat;
};
// if checking stack and new funcs were added
if ((0 == MaxDrivers) && (stream->newProveFuncs))
return DRM_AUTHREQUIRED;
// and finally, inform if there was insufficient buffer space.
if((0 == MaxDrivers) || (count<MaxDrivers))
return (DRM_OK == stat) ? KRM_OK : stat;
else
return KRM_BUFSIZE;
}
};
//------------------------------------------------------------------------------
DRM_STATUS StreamMgr::setRecipient(DWORD StreamId, PFILE_OBJECT OutPinFileObject, PDEVICE_OBJECT OutPinDeviceObject){
KCritical s(critMgr);
StreamInfo* stream=getPrimaryStream(StreamId);
if(stream==NULL)return KRM_BADSTREAM;
stream->OutPinFileObject=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;
};
//------------------------------------------------------------------------------