/** * FreeRDP: A Remote Desktop Protocol Implementation * Smartcard Device Service Virtual Channel * * Copyright 2011 O.S. Systems Software Ltda. * Copyright 2011 Eduardo Fiss Beloni * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __SCARD_MAIN_H #define __SCARD_MAIN_H #include #include #include #include #include /* * When using Windows Server 2008 R2 as the Terminal Services (TS) * server, and with a smart card reader connected to the TS client machine * and used to authenticate to an existing login session, the TS server * will initiate the protocol initialization of MS-RDPEFS, Section 1.3.1, * twice as it re-establishes a connection. The TS server starts both * initializations with a "Server Announce Request" message. * When the TS client receives this message, as per Section 3.2.5.1.2, * * The client SHOULD treat this packet as the beginning * of a new sequence. The client SHOULD also cancel all * outstanding requests and release previous references to * all devices. * * As of this writing, the code does not cancel all outstanding requests. * This leads to a problem where, after the first MS-RDPEFS initialization, * the TS server sends an SCARD_IOCTL_GETSTATUSCHANGEx control in a message * that uses an available "CompletionID". The * TS client doesn't respond immediately because it is blocking while * waiting for a change in the smart card's status in the reader. * Then the TS server initiates a second MS-RDPEFS initialization sequence. * As noted above, this should cancel the outstanding * SCARD_IOCTL_GETSTATUSCHANGEx request, but it does not. * At this point, the TS server is free to reuse the previously used * "CompletionID", and it does reuse it for other SCARD_IOCTLs. * Therefore, when the user removes (for example) the card from the reader, * the TS client sends an "IOCompetion" message in response to the * GETSTATUSCHANGEx using the original "CompletionID". The TS server does not * expect this "CompletionID" and so, as per Section 3.1.5.2 of MS-RDPEFS, * it treats that "IOCompletion" message as an error and terminates the * virtual channel. * * The following structure is part of a work-around for this missing * capability of canceling outstanding requests. This work-around * allows the TS client to send an "IOCompletion" back to the * TS server for the second (and subsequent) SCARD_IOCTLs that use * the same "CompletionID" as the still outstanding * SCARD_IOCTL_GETSTATUSCHANGEx. The work-around in the TS client * prevents the client from sending the "IOCompletion" back (when * the user removes the card) for the SCARD_IOCTL_GETSTATUSCHANGEx. * * This TS client expects the responses from the PCSC daemon for the second * and subsequent SCARD_IOCTLs that use the same "CompletionID" * to arrive at the TS client before the daemon's response to the * SCARD_IOCTL_GETSTATUSCHANGEx. This is a race condition. * * The "CompletionIDs" are a global pool of IDs across all "DeviceIDs". * However, this problem of duplicate "CompletionIDs" only affects smart cards. * * This structure tracks outstanding Terminal Services server "CompletionIDs" * used by the redirected smart card device. */ struct _COMPLETIONIDINFO { UINT32 ID; /* CompletionID */ BOOL duplicate; /* Indicates whether or not this * CompletionID is a duplicate of an * earlier, outstanding, CompletionID. */ }; typedef struct _COMPLETIONIDINFO COMPLETIONIDINFO; struct _SCARD_DEVICE { DEVICE device; char* name; char* path; PSLIST_HEADER pIrpList; freerdp_thread* thread; LIST* CompletionIds; HANDLE CompletionIdsMutex; }; typedef struct _SCARD_DEVICE SCARD_DEVICE; #ifdef WITH_DEBUG_SCARD #define DEBUG_SCARD(fmt, ...) DEBUG_CLASS(SCARD, fmt, ## __VA_ARGS__) #else #define DEBUG_SCARD(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__) #endif BOOL scard_async_op(IRP*); void scard_device_control(SCARD_DEVICE*, IRP*); #endif