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.
 
 
 
 
 
 

572 lines
19 KiB

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name :
scardss.cpp
Abstract:
Smart card subsystem Device object handles one redirected smart card subsystem
Revision History:
JoyC 9/11/2000 Created
--*/
#include "precomp.hxx"
#define TRC_FILE "scardss"
#include "trc.h"
#include "scioctl.h"
DrSmartCard::DrSmartCard(SmartPtr<DrSession> &Session, ULONG DeviceType, ULONG DeviceId,
PUCHAR PreferredDosName) : DrDevice(Session, DeviceType, DeviceId, PreferredDosName)
{
BEGIN_FN("DrSmartCard::DrSmartCard");
SetClassName("DrSmartCard");
_SmartCardState = dsCreated;
TRC_NRM((TB, "Create SmartCard object = %p", this));
}
BOOL DrSmartCard::IsDeviceNameValid()
{
BEGIN_FN("DrSmartCard::IsDeviceNameValid");
BOOL fRet = FALSE;
//
// device name is valid only if it contains the string
// "SCARD"
//
if (!strcmp((char*)_PreferredDosName, DR_SMARTCARD_SUBSYSTEM)) {
fRet = TRUE;
}
ASSERT(fRet);
return fRet;
}
NTSTATUS DrSmartCard::Initialize(PRDPDR_DEVICE_ANNOUNCE DeviceAnnounce, ULONG Length)
{
NTSTATUS Status;
DrSmartCardState smartcardState;
BEGIN_FN("DrSmartCard::Initialize");
if (!IsDeviceNameValid()) {
return STATUS_INVALID_PARAMETER;
}
Status = DrDevice::Initialize(DeviceAnnounce, Length);
// Initialize the device ref count if not already initialized
smartcardState = (DrSmartCardState)InterlockedExchange((long *)&_SmartCardState, dsInitialized);
if (smartcardState == dsCreated) {
_CreateRefCount = 0;
}
return Status;
}
void DrSmartCard::ClientConnect(PRDPDR_DEVICE_ANNOUNCE devAnnouceMsg, ULONG Length)
{
SmartPtr<DrExchange> Exchange;
ListEntry *ListEnum;
USHORT Mid;
BEGIN_FN("DrSmartCard::ClientConnect");
// Set the smartcard device to be connected by the client
// And set the real device id
_DeviceStatus = dsConnected;
_DeviceId = devAnnouceMsg->DeviceId;
LONG l;
l = InterlockedIncrement(&_CreateRefCount);
// walk through the mid list that's waiting on the client
// smartcard subsystem comes online and signal them
_MidList.LockShared();
ListEnum = _MidList.First();
while (ListEnum != NULL) {
Mid = (USHORT)ListEnum->Node();
if (_Session->GetExchangeManager().Find(Mid, Exchange)) {
if (MarkBusy(Exchange)) {
DrIoContext *Context = NULL;
PRX_CONTEXT RxContext;
Context = (DrIoContext *)Exchange->_Context;
ASSERT(Context != NULL);
//
// If the IRP was timed out, then we just discard this exchange
//
if (Context->_TimedOut) {
TRC_NRM((TB, "Irp was timed out"));
DiscardBusyExchange(Exchange);
}
else {
RxContext = Context->_RxContext;
if (RxContext != NULL) {
CompleteBusyExchange(Exchange, STATUS_SUCCESS, 0);
} else {
TRC_NRM((TB, "Irp was cancelled"));
DiscardBusyExchange(Exchange);
}
}
}
}
ListEnum = _MidList.Next(ListEnum);
}
_MidList.Unlock();
}
NTSTATUS DrSmartCard::Create(IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS Status = STATUS_SUCCESS;
RxCaptureFcb;
PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PMRX_SRV_CALL SrvCall = RxContext->Create.pSrvCall;
PMRX_NET_ROOT NetRoot = RxContext->Create.pNetRoot;
SmartPtr<DrSession> Session = _Session;
SmartPtr<DrFile> FileObj;
SmartPtr<DrDevice> Device(this);
BEGIN_FN("DrSmartCard::Create");
ASSERT(RxContext != NULL);
ASSERT(RxContext->MajorFunction == IRP_MJ_CREATE);
//
// Security check the irp.
//
Status = VerifyCreateSecurity(RxContext, Session->GetSessionId());
if (NT_ERROR(Status)) {
return Status;
}
//
// We already have an exclusive lock on the fcb. Finish the create.
//
if (NT_SUCCESS(Status)) {
//
// JC: Worry about this when do buffering
//
SrvOpen->Flags |= SRVOPEN_FLAG_DONTUSE_WRITE_CACHING;
SrvOpen->Flags |= SRVOPEN_FLAG_DONTUSE_READ_CACHING;
RxContext->pFobx = RxCreateNetFobx(RxContext, RxContext->pRelevantSrvOpen);
if (RxContext->pFobx != NULL) {
// Fobx keeps a reference to the device so it won't go away
AddRef();
RxContext->pFobx->Context = (DrDevice *)this;
Status = STATUS_SUCCESS;
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
//
// We are using a file object to keep track of file open instance
// and any information stored in the mini-redir for this instance
//
if (NT_SUCCESS(Status)) {
// NOTE: the special FileId agreed upon by both the client
// and server code is used here as the FileId
FileObj = new(NonPagedPool) DrFile(Device, DR_SMARTCARD_FILEID);
if (FileObj) {
//
// Explicit reference the file object here
//
FileObj->AddRef();
RxContext->pFobx->Context2 = (VOID *)(FileObj);
}
else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
//
// We don't send the create request to the client, always return TRUE
//
if (NT_SUCCESS(Status)) {
LONG l;
l = InterlockedIncrement(&_CreateRefCount);
FinishCreate(RxContext);
}
else {
// Release the Device Reference
if (RxContext->pFobx != NULL) {
((DrDevice *)RxContext->pFobx->Context)->Release();
RxContext->pFobx->Context = NULL;
}
}
return Status;
}
NTSTATUS DrSmartCard::Close(IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS Status = STATUS_SUCCESS;
RxCaptureFcb;
RxCaptureFobx;
PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
PMRX_SRV_CALL SrvCall = NetRoot->pSrvCall;
SmartPtr<DrSession> Session = _Session;
SmartPtr<DrDevice> Device = static_cast<DrDevice*>(this);
BEGIN_FN("DrSmartCard::Close");
//
// Make sure it's okay to access the Client at this time
// This is an optimization, we don't need to acquire the spin lock,
// because it is okay if we're not, we'll just catch it later
//
ASSERT(Session != NULL);
ASSERT(RxContext != NULL);
ASSERT(RxContext->MajorFunction == IRP_MJ_CLOSE);
// Remove the smartcard subsystem if we close the last handle
LONG l;
if ((l = InterlockedDecrement(&_CreateRefCount)) == 0) {
_DeviceStatus = dsDisabled;
Session->GetDevMgr().RemoveDevice(Device);
}
return Status;
}
BOOL DrSmartCard::SupportDiscon()
{
BOOL rc = TRUE;
DrSmartCardState smartcardState;
smartcardState = (DrSmartCardState)InterlockedExchange((long *)&_SmartCardState, dsDisconnected);
if (smartcardState == dsInitialized) {
// Remove the smartcard subsystem if we close the last handle
LONG l;
if ((l = InterlockedDecrement(&_CreateRefCount)) == 0) {
_DeviceStatus = dsDisabled;
rc = FALSE;
}
}
return rc;
}
void DrSmartCard::Disconnect ()
{
BEGIN_FN("DrSmartCard::Disconnect");
_DeviceStatus = dsAvailable;
}
NTSTATUS DrSmartCard::IoControl(IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS Status = STATUS_SUCCESS;
RxCaptureFcb;
RxCaptureFobx;
PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
PMRX_SRV_CALL SrvCall = NetRoot->pSrvCall;
SmartPtr<DrSession> Session = _Session;
DrFile *pFile = (DrFile *)RxContext->pFobx->Context2;
SmartPtr<DrFile> FileObj = pFile;
PRDPDR_IOREQUEST_PACKET pIoPacket;
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
ULONG cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) +
LowIoContext->ParamsFor.IoCtl.InputBufferLength;
ULONG IoControlCode = LowIoContext->ParamsFor.IoCtl.IoControlCode;
ULONG InputBufferLength = LowIoContext->ParamsFor.IoCtl.InputBufferLength;
ULONG OutputBufferLength = LowIoContext->ParamsFor.IoCtl.OutputBufferLength;
PVOID InputBuffer = LowIoContext->ParamsFor.IoCtl.pInputBuffer;
PVOID OutputBuffer = LowIoContext->ParamsFor.IoCtl.pOutputBuffer;
BEGIN_FN("DrDevice::IoControl");
//
// Make sure it's okay to access the Client at this time
// This is an optimization, we don't need to acquire the spin lock,
// because it is okay if we're not, we'll just catch it later
//
ASSERT(Session != NULL);
ASSERT(RxContext != NULL);
ASSERT(RxContext->MajorFunction == IRP_MJ_DEVICE_CONTROL ||
RxContext->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL ||
RxContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL);
//if (COMPARE_VERSION(Session->GetClientVersion().Minor,
// Session->GetClientVersion().Major, RDPDR_MINOR_VERSION_PORTS,
// RDPDR_MAJOR_VERSION_PORTS) < 0) {
// TRC_ALT((TB, "Failing IoCtl for client that doesn't support it"));
// return STATUS_NOT_IMPLEMENTED;
//}
//
// Make sure the device is still enabled
//
if (_DeviceStatus != dsConnected && IoControlCode != SCARD_IOCTL_SMARTCARD_ONLINE) {
TRC_ALT((TB, "Tried to send IoControl to client device which is not "
"available. State: %ld", _DeviceStatus));
return STATUS_DEVICE_NOT_CONNECTED;
}
//
// Validate the buffer
//
if (RxContext->CurrentIrp->RequestorMode != KernelMode) {
__try {
// If the buffering method is METHOD_NEITHER or METHOD_IN_DIRECT
// then we need to probe the input buffer
if ((IoControlCode & 0x1) &&
InputBuffer != NULL && InputBufferLength != 0) {
ProbeForRead(InputBuffer, InputBufferLength, sizeof(UCHAR));
}
// If the buffering method is METHOD_NEITHER or METHOD_OUT_DIRECT
// then we need to probe the output buffer
if ((IoControlCode & 0x2) &&
OutputBuffer != NULL && OutputBufferLength != 0) {
ProbeForWrite(OutputBuffer, OutputBufferLength, sizeof(UCHAR));
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
TRC_ERR((TB, "Invalid buffer parameter(s)"));
return STATUS_INVALID_PARAMETER;
}
}
//
// Send the request to the client
//
if (IoControlCode != SCARD_IOCTL_SMARTCARD_ONLINE) {
pIoPacket = (PRDPDR_IOREQUEST_PACKET)new(PagedPool) BYTE[cbPacketSize];
if (pIoPacket != NULL) {
memset(pIoPacket, 0, cbPacketSize);
//
// FS Control uses the same path as IO Control.
//
pIoPacket->Header.Component = RDPDR_CTYP_CORE;
pIoPacket->Header.PacketId = DR_CORE_DEVICE_IOREQUEST;
pIoPacket->IoRequest.DeviceId = _DeviceId;
pIoPacket->IoRequest.FileId = FileObj->GetFileId();
pIoPacket->IoRequest.MajorFunction = IRP_MJ_DEVICE_CONTROL;
pIoPacket->IoRequest.MinorFunction =
LowIoContext->ParamsFor.IoCtl.MinorFunction;
pIoPacket->IoRequest.Parameters.DeviceIoControl.OutputBufferLength =
LowIoContext->ParamsFor.IoCtl.OutputBufferLength;
pIoPacket->IoRequest.Parameters.DeviceIoControl.InputBufferLength =
LowIoContext->ParamsFor.IoCtl.InputBufferLength;
pIoPacket->IoRequest.Parameters.DeviceIoControl.IoControlCode =
LowIoContext->ParamsFor.IoCtl.IoControlCode;
if (LowIoContext->ParamsFor.IoCtl.InputBufferLength != 0) {
TRC_NRM((TB, "DrIoControl inputbufferlength: %lx",
LowIoContext->ParamsFor.IoCtl.InputBufferLength));
RtlCopyMemory(pIoPacket + 1, LowIoContext->ParamsFor.IoCtl.pInputBuffer,
LowIoContext->ParamsFor.IoCtl.InputBufferLength);
} else {
TRC_NRM((TB, "DrIoControl with no inputbuffer"));
}
Status = SendIoRequest(RxContext, pIoPacket, cbPacketSize,
(BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
TRC_NRM((TB, "IoRequestWrite returned to DrIoControl: %lx", Status));
delete pIoPacket;
} else {
TRC_ERR((TB, "DrIoControl unable to allocate packet: %lx", Status));
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
//
// This is the special IOCTL waiting for client smartcard subsystem come online
// We are already online, so just return
//
else if (_DeviceStatus == dsConnected){
Status = STATUS_SUCCESS;
}
//
// We'll have to wait for client to come online
//
else {
USHORT Mid = INVALID_MID;
BOOL ExchangeCreated = FALSE;
DrIoContext *Context = NULL;
SmartPtr<DrExchange> Exchange;
SmartPtr<DrDevice> Device(this);
Status = STATUS_PENDING;
// Need to keep a list of this.
// on create comes back, signal them
Context = new DrIoContext(RxContext, Device);
if (Context != NULL) {
Status = STATUS_SUCCESS;
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
if (NT_SUCCESS(Status)) {
//
// Set up a mapping so the completion response handler can
// find this context
//
TRC_DBG((TB, "Create the context for this I/O"));
KeClearEvent(&RxContext->SyncEvent);
ExchangeCreated =
_Session->GetExchangeManager().CreateExchange(this, Context, Exchange);
if (ExchangeCreated) {
//
// No need to explicit Refcount for the RxContext
// The place it's been used is the cancel routine.
// Since CreateExchange holds the ref count. we are okay
//
//Exchange->AddRef();
RxContext->MRxContext[MRX_DR_CONTEXT] = (DrExchange *)Exchange;
if (_MidList.CreateEntry((PVOID)Exchange->_Mid)) {
//
// successfully added this entry
//
Status = STATUS_SUCCESS;
}
else {
//
// Unable to add it to the list, clean up
//
Status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
delete Context;
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
if (NT_SUCCESS(Status)) {
TRC_DBG((TB, "Setting cancel routine for Io"));
//
// Set this after sending the IO to the client
// if cancel was requested already, we can just call the
// cancel routine ourselves
//
Status = RxSetMinirdrCancelRoutine(RxContext,
MinirdrCancelRoutine);
if (Status == STATUS_CANCELLED) {
TRC_NRM((TB, "Io was already cancelled"));
MinirdrCancelRoutine(RxContext);
Status = STATUS_SUCCESS;
}
}
if ((BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
//
// Some failure is going to prevent our completions routine from
// being called. Do that work now.
//
if (!ExchangeCreated) {
//
// If we couldn't even create the exchange, we need to just
// complete the IO as failed
//
CompleteRxContext(RxContext, Status, 0);
}
else {
LARGE_INTEGER TimeOut;
//
// If we created the exchange and then got a transport failure
// we'll be disconnected, and the the I/O will be completed
// the same way all outstanding I/O is completed when we are
// disconnected.
//
TRC_DBG((TB, "Waiting for IoResult for synchronous request"));
TimeOut = RtlEnlargedIntegerMultiply( 6000000, -1000 );
Status = KeWaitForSingleObject(&RxContext->SyncEvent, UserRequest,
KernelMode, FALSE, &TimeOut);
if (Status == STATUS_TIMEOUT) {
RxContext->IoStatusBlock.Status = Status;
TRC_DBG((TB, "Wait timed out"));
MarkTimedOut(Exchange);
}
else {
Status = RxContext->IoStatusBlock.Status;
}
}
}
else {
TRC_DBG((TB, "Not waiting for IoResult for asynchronous request"));
//
// Some failure is going to prevent our completions routine from
// being called. Do that work now.
//
if (!ExchangeCreated) {
//
// If we couldn't even create the exchange, we need to just
// complete the IO as failed
//
CompleteRxContext(RxContext, Status, 0);
}
else {
//
// If we created the exchange and then got a transport failure
// we'll be disconnected, and the the I/O will be completed
// the same way all outstanding I/O is completed when we are
// disconnected.
//
}
Status = STATUS_PENDING;
}
}
return Status;
}