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

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name :
  4. scardss.cpp
  5. Abstract:
  6. Smart card subsystem Device object handles one redirected smart card subsystem
  7. Revision History:
  8. JoyC 9/11/2000 Created
  9. --*/
  10. #include "precomp.hxx"
  11. #define TRC_FILE "scardss"
  12. #include "trc.h"
  13. #include "scioctl.h"
  14. DrSmartCard::DrSmartCard(SmartPtr<DrSession> &Session, ULONG DeviceType, ULONG DeviceId,
  15. PUCHAR PreferredDosName) : DrDevice(Session, DeviceType, DeviceId, PreferredDosName)
  16. {
  17. BEGIN_FN("DrSmartCard::DrSmartCard");
  18. SetClassName("DrSmartCard");
  19. _SmartCardState = dsCreated;
  20. TRC_NRM((TB, "Create SmartCard object = %p", this));
  21. }
  22. BOOL DrSmartCard::IsDeviceNameValid()
  23. {
  24. BEGIN_FN("DrSmartCard::IsDeviceNameValid");
  25. BOOL fRet = FALSE;
  26. //
  27. // device name is valid only if it contains the string
  28. // "SCARD"
  29. //
  30. if (!strcmp((char*)_PreferredDosName, DR_SMARTCARD_SUBSYSTEM)) {
  31. fRet = TRUE;
  32. }
  33. ASSERT(fRet);
  34. return fRet;
  35. }
  36. NTSTATUS DrSmartCard::Initialize(PRDPDR_DEVICE_ANNOUNCE DeviceAnnounce, ULONG Length)
  37. {
  38. NTSTATUS Status;
  39. DrSmartCardState smartcardState;
  40. BEGIN_FN("DrSmartCard::Initialize");
  41. if (!IsDeviceNameValid()) {
  42. return STATUS_INVALID_PARAMETER;
  43. }
  44. Status = DrDevice::Initialize(DeviceAnnounce, Length);
  45. // Initialize the device ref count if not already initialized
  46. smartcardState = (DrSmartCardState)InterlockedExchange((long *)&_SmartCardState, dsInitialized);
  47. if (smartcardState == dsCreated) {
  48. _CreateRefCount = 0;
  49. }
  50. return Status;
  51. }
  52. void DrSmartCard::ClientConnect(PRDPDR_DEVICE_ANNOUNCE devAnnouceMsg, ULONG Length)
  53. {
  54. SmartPtr<DrExchange> Exchange;
  55. ListEntry *ListEnum;
  56. USHORT Mid;
  57. BEGIN_FN("DrSmartCard::ClientConnect");
  58. // Set the smartcard device to be connected by the client
  59. // And set the real device id
  60. _DeviceStatus = dsConnected;
  61. _DeviceId = devAnnouceMsg->DeviceId;
  62. LONG l;
  63. l = InterlockedIncrement(&_CreateRefCount);
  64. // walk through the mid list that's waiting on the client
  65. // smartcard subsystem comes online and signal them
  66. _MidList.LockShared();
  67. ListEnum = _MidList.First();
  68. while (ListEnum != NULL) {
  69. Mid = (USHORT)ListEnum->Node();
  70. if (_Session->GetExchangeManager().Find(Mid, Exchange)) {
  71. if (MarkBusy(Exchange)) {
  72. DrIoContext *Context = NULL;
  73. PRX_CONTEXT RxContext;
  74. Context = (DrIoContext *)Exchange->_Context;
  75. ASSERT(Context != NULL);
  76. //
  77. // If the IRP was timed out, then we just discard this exchange
  78. //
  79. if (Context->_TimedOut) {
  80. TRC_NRM((TB, "Irp was timed out"));
  81. DiscardBusyExchange(Exchange);
  82. }
  83. else {
  84. RxContext = Context->_RxContext;
  85. if (RxContext != NULL) {
  86. CompleteBusyExchange(Exchange, STATUS_SUCCESS, 0);
  87. } else {
  88. TRC_NRM((TB, "Irp was cancelled"));
  89. DiscardBusyExchange(Exchange);
  90. }
  91. }
  92. }
  93. }
  94. ListEnum = _MidList.Next(ListEnum);
  95. }
  96. _MidList.Unlock();
  97. }
  98. NTSTATUS DrSmartCard::Create(IN OUT PRX_CONTEXT RxContext)
  99. {
  100. NTSTATUS Status = STATUS_SUCCESS;
  101. RxCaptureFcb;
  102. PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
  103. PMRX_SRV_CALL SrvCall = RxContext->Create.pSrvCall;
  104. PMRX_NET_ROOT NetRoot = RxContext->Create.pNetRoot;
  105. SmartPtr<DrSession> Session = _Session;
  106. SmartPtr<DrFile> FileObj;
  107. SmartPtr<DrDevice> Device(this);
  108. BEGIN_FN("DrSmartCard::Create");
  109. ASSERT(RxContext != NULL);
  110. ASSERT(RxContext->MajorFunction == IRP_MJ_CREATE);
  111. //
  112. // Security check the irp.
  113. //
  114. Status = VerifyCreateSecurity(RxContext, Session->GetSessionId());
  115. if (NT_ERROR(Status)) {
  116. return Status;
  117. }
  118. //
  119. // We already have an exclusive lock on the fcb. Finish the create.
  120. //
  121. if (NT_SUCCESS(Status)) {
  122. //
  123. // JC: Worry about this when do buffering
  124. //
  125. SrvOpen->Flags |= SRVOPEN_FLAG_DONTUSE_WRITE_CACHING;
  126. SrvOpen->Flags |= SRVOPEN_FLAG_DONTUSE_READ_CACHING;
  127. RxContext->pFobx = RxCreateNetFobx(RxContext, RxContext->pRelevantSrvOpen);
  128. if (RxContext->pFobx != NULL) {
  129. // Fobx keeps a reference to the device so it won't go away
  130. AddRef();
  131. RxContext->pFobx->Context = (DrDevice *)this;
  132. Status = STATUS_SUCCESS;
  133. } else {
  134. Status = STATUS_INSUFFICIENT_RESOURCES;
  135. }
  136. }
  137. //
  138. // We are using a file object to keep track of file open instance
  139. // and any information stored in the mini-redir for this instance
  140. //
  141. if (NT_SUCCESS(Status)) {
  142. // NOTE: the special FileId agreed upon by both the client
  143. // and server code is used here as the FileId
  144. FileObj = new(NonPagedPool) DrFile(Device, DR_SMARTCARD_FILEID);
  145. if (FileObj) {
  146. //
  147. // Explicit reference the file object here
  148. //
  149. FileObj->AddRef();
  150. RxContext->pFobx->Context2 = (VOID *)(FileObj);
  151. }
  152. else {
  153. Status = STATUS_INSUFFICIENT_RESOURCES;
  154. }
  155. }
  156. //
  157. // We don't send the create request to the client, always return TRUE
  158. //
  159. if (NT_SUCCESS(Status)) {
  160. LONG l;
  161. l = InterlockedIncrement(&_CreateRefCount);
  162. FinishCreate(RxContext);
  163. }
  164. else {
  165. // Release the Device Reference
  166. if (RxContext->pFobx != NULL) {
  167. ((DrDevice *)RxContext->pFobx->Context)->Release();
  168. RxContext->pFobx->Context = NULL;
  169. }
  170. }
  171. return Status;
  172. }
  173. NTSTATUS DrSmartCard::Close(IN OUT PRX_CONTEXT RxContext)
  174. {
  175. NTSTATUS Status = STATUS_SUCCESS;
  176. RxCaptureFcb;
  177. RxCaptureFobx;
  178. PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
  179. PMRX_SRV_CALL SrvCall = NetRoot->pSrvCall;
  180. SmartPtr<DrSession> Session = _Session;
  181. SmartPtr<DrDevice> Device = static_cast<DrDevice*>(this);
  182. BEGIN_FN("DrSmartCard::Close");
  183. //
  184. // Make sure it's okay to access the Client at this time
  185. // This is an optimization, we don't need to acquire the spin lock,
  186. // because it is okay if we're not, we'll just catch it later
  187. //
  188. ASSERT(Session != NULL);
  189. ASSERT(RxContext != NULL);
  190. ASSERT(RxContext->MajorFunction == IRP_MJ_CLOSE);
  191. // Remove the smartcard subsystem if we close the last handle
  192. LONG l;
  193. if ((l = InterlockedDecrement(&_CreateRefCount)) == 0) {
  194. _DeviceStatus = dsDisabled;
  195. Session->GetDevMgr().RemoveDevice(Device);
  196. }
  197. return Status;
  198. }
  199. BOOL DrSmartCard::SupportDiscon()
  200. {
  201. BOOL rc = TRUE;
  202. DrSmartCardState smartcardState;
  203. smartcardState = (DrSmartCardState)InterlockedExchange((long *)&_SmartCardState, dsDisconnected);
  204. if (smartcardState == dsInitialized) {
  205. // Remove the smartcard subsystem if we close the last handle
  206. LONG l;
  207. if ((l = InterlockedDecrement(&_CreateRefCount)) == 0) {
  208. _DeviceStatus = dsDisabled;
  209. rc = FALSE;
  210. }
  211. }
  212. return rc;
  213. }
  214. void DrSmartCard::Disconnect ()
  215. {
  216. BEGIN_FN("DrSmartCard::Disconnect");
  217. _DeviceStatus = dsAvailable;
  218. }
  219. NTSTATUS DrSmartCard::IoControl(IN OUT PRX_CONTEXT RxContext)
  220. {
  221. NTSTATUS Status = STATUS_SUCCESS;
  222. RxCaptureFcb;
  223. RxCaptureFobx;
  224. PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
  225. PMRX_SRV_CALL SrvCall = NetRoot->pSrvCall;
  226. SmartPtr<DrSession> Session = _Session;
  227. DrFile *pFile = (DrFile *)RxContext->pFobx->Context2;
  228. SmartPtr<DrFile> FileObj = pFile;
  229. PRDPDR_IOREQUEST_PACKET pIoPacket;
  230. PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
  231. ULONG cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) +
  232. LowIoContext->ParamsFor.IoCtl.InputBufferLength;
  233. ULONG IoControlCode = LowIoContext->ParamsFor.IoCtl.IoControlCode;
  234. ULONG InputBufferLength = LowIoContext->ParamsFor.IoCtl.InputBufferLength;
  235. ULONG OutputBufferLength = LowIoContext->ParamsFor.IoCtl.OutputBufferLength;
  236. PVOID InputBuffer = LowIoContext->ParamsFor.IoCtl.pInputBuffer;
  237. PVOID OutputBuffer = LowIoContext->ParamsFor.IoCtl.pOutputBuffer;
  238. BEGIN_FN("DrDevice::IoControl");
  239. //
  240. // Make sure it's okay to access the Client at this time
  241. // This is an optimization, we don't need to acquire the spin lock,
  242. // because it is okay if we're not, we'll just catch it later
  243. //
  244. ASSERT(Session != NULL);
  245. ASSERT(RxContext != NULL);
  246. ASSERT(RxContext->MajorFunction == IRP_MJ_DEVICE_CONTROL ||
  247. RxContext->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL ||
  248. RxContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL);
  249. //if (COMPARE_VERSION(Session->GetClientVersion().Minor,
  250. // Session->GetClientVersion().Major, RDPDR_MINOR_VERSION_PORTS,
  251. // RDPDR_MAJOR_VERSION_PORTS) < 0) {
  252. // TRC_ALT((TB, "Failing IoCtl for client that doesn't support it"));
  253. // return STATUS_NOT_IMPLEMENTED;
  254. //}
  255. //
  256. // Make sure the device is still enabled
  257. //
  258. if (_DeviceStatus != dsConnected && IoControlCode != SCARD_IOCTL_SMARTCARD_ONLINE) {
  259. TRC_ALT((TB, "Tried to send IoControl to client device which is not "
  260. "available. State: %ld", _DeviceStatus));
  261. return STATUS_DEVICE_NOT_CONNECTED;
  262. }
  263. //
  264. // Validate the buffer
  265. //
  266. if (RxContext->CurrentIrp->RequestorMode != KernelMode) {
  267. __try {
  268. // If the buffering method is METHOD_NEITHER or METHOD_IN_DIRECT
  269. // then we need to probe the input buffer
  270. if ((IoControlCode & 0x1) &&
  271. InputBuffer != NULL && InputBufferLength != 0) {
  272. ProbeForRead(InputBuffer, InputBufferLength, sizeof(UCHAR));
  273. }
  274. // If the buffering method is METHOD_NEITHER or METHOD_OUT_DIRECT
  275. // then we need to probe the output buffer
  276. if ((IoControlCode & 0x2) &&
  277. OutputBuffer != NULL && OutputBufferLength != 0) {
  278. ProbeForWrite(OutputBuffer, OutputBufferLength, sizeof(UCHAR));
  279. }
  280. }
  281. __except (EXCEPTION_EXECUTE_HANDLER) {
  282. TRC_ERR((TB, "Invalid buffer parameter(s)"));
  283. return STATUS_INVALID_PARAMETER;
  284. }
  285. }
  286. //
  287. // Send the request to the client
  288. //
  289. if (IoControlCode != SCARD_IOCTL_SMARTCARD_ONLINE) {
  290. pIoPacket = (PRDPDR_IOREQUEST_PACKET)new(PagedPool) BYTE[cbPacketSize];
  291. if (pIoPacket != NULL) {
  292. memset(pIoPacket, 0, cbPacketSize);
  293. //
  294. // FS Control uses the same path as IO Control.
  295. //
  296. pIoPacket->Header.Component = RDPDR_CTYP_CORE;
  297. pIoPacket->Header.PacketId = DR_CORE_DEVICE_IOREQUEST;
  298. pIoPacket->IoRequest.DeviceId = _DeviceId;
  299. pIoPacket->IoRequest.FileId = FileObj->GetFileId();
  300. pIoPacket->IoRequest.MajorFunction = IRP_MJ_DEVICE_CONTROL;
  301. pIoPacket->IoRequest.MinorFunction =
  302. LowIoContext->ParamsFor.IoCtl.MinorFunction;
  303. pIoPacket->IoRequest.Parameters.DeviceIoControl.OutputBufferLength =
  304. LowIoContext->ParamsFor.IoCtl.OutputBufferLength;
  305. pIoPacket->IoRequest.Parameters.DeviceIoControl.InputBufferLength =
  306. LowIoContext->ParamsFor.IoCtl.InputBufferLength;
  307. pIoPacket->IoRequest.Parameters.DeviceIoControl.IoControlCode =
  308. LowIoContext->ParamsFor.IoCtl.IoControlCode;
  309. if (LowIoContext->ParamsFor.IoCtl.InputBufferLength != 0) {
  310. TRC_NRM((TB, "DrIoControl inputbufferlength: %lx",
  311. LowIoContext->ParamsFor.IoCtl.InputBufferLength));
  312. RtlCopyMemory(pIoPacket + 1, LowIoContext->ParamsFor.IoCtl.pInputBuffer,
  313. LowIoContext->ParamsFor.IoCtl.InputBufferLength);
  314. } else {
  315. TRC_NRM((TB, "DrIoControl with no inputbuffer"));
  316. }
  317. Status = SendIoRequest(RxContext, pIoPacket, cbPacketSize,
  318. (BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
  319. TRC_NRM((TB, "IoRequestWrite returned to DrIoControl: %lx", Status));
  320. delete pIoPacket;
  321. } else {
  322. TRC_ERR((TB, "DrIoControl unable to allocate packet: %lx", Status));
  323. Status = STATUS_INSUFFICIENT_RESOURCES;
  324. }
  325. }
  326. //
  327. // This is the special IOCTL waiting for client smartcard subsystem come online
  328. // We are already online, so just return
  329. //
  330. else if (_DeviceStatus == dsConnected){
  331. Status = STATUS_SUCCESS;
  332. }
  333. //
  334. // We'll have to wait for client to come online
  335. //
  336. else {
  337. USHORT Mid = INVALID_MID;
  338. BOOL ExchangeCreated = FALSE;
  339. DrIoContext *Context = NULL;
  340. SmartPtr<DrExchange> Exchange;
  341. SmartPtr<DrDevice> Device(this);
  342. Status = STATUS_PENDING;
  343. // Need to keep a list of this.
  344. // on create comes back, signal them
  345. Context = new DrIoContext(RxContext, Device);
  346. if (Context != NULL) {
  347. Status = STATUS_SUCCESS;
  348. } else {
  349. Status = STATUS_INSUFFICIENT_RESOURCES;
  350. }
  351. if (NT_SUCCESS(Status)) {
  352. //
  353. // Set up a mapping so the completion response handler can
  354. // find this context
  355. //
  356. TRC_DBG((TB, "Create the context for this I/O"));
  357. KeClearEvent(&RxContext->SyncEvent);
  358. ExchangeCreated =
  359. _Session->GetExchangeManager().CreateExchange(this, Context, Exchange);
  360. if (ExchangeCreated) {
  361. //
  362. // No need to explicit Refcount for the RxContext
  363. // The place it's been used is the cancel routine.
  364. // Since CreateExchange holds the ref count. we are okay
  365. //
  366. //Exchange->AddRef();
  367. RxContext->MRxContext[MRX_DR_CONTEXT] = (DrExchange *)Exchange;
  368. if (_MidList.CreateEntry((PVOID)Exchange->_Mid)) {
  369. //
  370. // successfully added this entry
  371. //
  372. Status = STATUS_SUCCESS;
  373. }
  374. else {
  375. //
  376. // Unable to add it to the list, clean up
  377. //
  378. Status = STATUS_INSUFFICIENT_RESOURCES;
  379. }
  380. } else {
  381. delete Context;
  382. Status = STATUS_INSUFFICIENT_RESOURCES;
  383. }
  384. }
  385. if (NT_SUCCESS(Status)) {
  386. TRC_DBG((TB, "Setting cancel routine for Io"));
  387. //
  388. // Set this after sending the IO to the client
  389. // if cancel was requested already, we can just call the
  390. // cancel routine ourselves
  391. //
  392. Status = RxSetMinirdrCancelRoutine(RxContext,
  393. MinirdrCancelRoutine);
  394. if (Status == STATUS_CANCELLED) {
  395. TRC_NRM((TB, "Io was already cancelled"));
  396. MinirdrCancelRoutine(RxContext);
  397. Status = STATUS_SUCCESS;
  398. }
  399. }
  400. if ((BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
  401. //
  402. // Some failure is going to prevent our completions routine from
  403. // being called. Do that work now.
  404. //
  405. if (!ExchangeCreated) {
  406. //
  407. // If we couldn't even create the exchange, we need to just
  408. // complete the IO as failed
  409. //
  410. CompleteRxContext(RxContext, Status, 0);
  411. }
  412. else {
  413. LARGE_INTEGER TimeOut;
  414. //
  415. // If we created the exchange and then got a transport failure
  416. // we'll be disconnected, and the the I/O will be completed
  417. // the same way all outstanding I/O is completed when we are
  418. // disconnected.
  419. //
  420. TRC_DBG((TB, "Waiting for IoResult for synchronous request"));
  421. TimeOut = RtlEnlargedIntegerMultiply( 6000000, -1000 );
  422. Status = KeWaitForSingleObject(&RxContext->SyncEvent, UserRequest,
  423. KernelMode, FALSE, &TimeOut);
  424. if (Status == STATUS_TIMEOUT) {
  425. RxContext->IoStatusBlock.Status = Status;
  426. TRC_DBG((TB, "Wait timed out"));
  427. MarkTimedOut(Exchange);
  428. }
  429. else {
  430. Status = RxContext->IoStatusBlock.Status;
  431. }
  432. }
  433. }
  434. else {
  435. TRC_DBG((TB, "Not waiting for IoResult for asynchronous request"));
  436. //
  437. // Some failure is going to prevent our completions routine from
  438. // being called. Do that work now.
  439. //
  440. if (!ExchangeCreated) {
  441. //
  442. // If we couldn't even create the exchange, we need to just
  443. // complete the IO as failed
  444. //
  445. CompleteRxContext(RxContext, Status, 0);
  446. }
  447. else {
  448. //
  449. // If we created the exchange and then got a transport failure
  450. // we'll be disconnected, and the the I/O will be completed
  451. // the same way all outstanding I/O is completed when we are
  452. // disconnected.
  453. //
  454. }
  455. Status = STATUS_PENDING;
  456. }
  457. }
  458. return Status;
  459. }