/* * Copyright (c) 2000-2002 Apple Computer, Inc. All Rights Reserved. * The contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please * obtain a copy of the License at http://www.apple.com/publicsource and * read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please * see the License for the specific language governing rights and * limitations under the License. */ /****************************************************************** MUSCLE SmartCard Development ( http://www.linuxnet.com ) Title : winscard_svc.c Package: pcsc lite Author : David Corcoran Date : 03/30/01 License: Copyright (C) 2001 David Corcoran Purpose: This demarshalls functions over the message queue and keeps track of clients and their handles. ********************************************************************/ #include #include #include #include #include #include #include #include #include "config.h" #include "wintypes.h" #include "pcsclite.h" #include "winscard.h" #include "winscard_msg.h" #include "winscard_svc.h" #include "debuglog.h" #include "sys_generic.h" #include #ifndef WIN32 #include #endif /* The standard ccidDriver.bundle that we shipped on Panther writes 10 bytes beyond the legal end of the passed in recieve buffer. */ static const unsigned char receive_buffer_padding[] = { 0xCC, 0x1D, 0xCC, 0x1D, 0xCC, 0x1D, 0xCC, 0x1D, 0xCC, 0x1D }; static int commonSocket = 0; struct _clientSockets { int sd; }; static struct _clientSockets clientSockets[PCSCLITE_MAX_APPLICATIONS]; static struct _psChannelMap { SCARDCONTEXT hContext; SCARDHANDLE hCard[PCSCLITE_MAX_CONTEXTS]; DWORD dwClientID; DWORD dwHandleID; } psChannelMap[PCSCLITE_MAX_CHANNELS]; LONG MSGCheckHandleAssociation(DWORD, SCARDHANDLE); void MSGFixByteOrder(request_object *requestObj); /* * A list of local functions used to keep track of clients and their * connections */ int MSGServerSetupCommonChannel() { int i; static struct sockaddr_un serv_adr; for (i = 0; i < PCSCLITE_MAX_APPLICATIONS; i++) { clientSockets[i].sd = -1; } /* * Create the common shared connection socket */ if ((commonSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { DebugLogB ("SHMInitializeSharedSegment: Unable to create common socket: %s", strerror(errno)); return -1; } serv_adr.sun_family = AF_UNIX; strncpy(serv_adr.sun_path, PCSCLITE_CSOCK_NAME, sizeof(serv_adr.sun_path)); unlink(PCSCLITE_CSOCK_NAME); if (bind(commonSocket, (struct sockaddr *) &serv_adr, sizeof(serv_adr.sun_family) + strlen(serv_adr.sun_path) + 1) < 0) { DebugLogB ("SHMInitializeSharedSegment: Unable to bind common socket: %s", strerror(errno)); MSGServerCleanupCommonChannel(commonSocket, PCSCLITE_CSOCK_NAME); return -1; } if (listen(commonSocket, 1) < 0) { DebugLogB ("SHMInitializeSharedSegment: Unable to listen common socket: %s", strerror(errno)); MSGServerCleanupCommonChannel(commonSocket, PCSCLITE_CSOCK_NAME); return -1; } /* * Chmod the public entry channel */ SYS_Chmod(PCSCLITE_CSOCK_NAME, S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP | S_IRUSR | S_IWUSR); return 0; } void MSGServerCleanupCommonChannel(int sockValue, char *pcFilePath) { SYS_CloseFile(sockValue); SYS_Unlink(pcFilePath); } int MSGServerProcessCommonChannelRequest() { int i; socklen_t clnt_len; int new_sock; struct sockaddr_un clnt_addr; int one; clnt_len = sizeof(clnt_addr); if ((new_sock = accept(commonSocket, (struct sockaddr *) &clnt_addr, &clnt_len)) < 0) { DebugLogB ("MSGServerProcessCommonChannelRequest: ER: Accept on common socket: %s", strerror(errno)); return -1; } for (i = 0; i < PCSCLITE_MAX_APPLICATIONS; i++) { if (clientSockets[i].sd == -1) { break; } } if (i == PCSCLITE_MAX_APPLICATIONS) { SYS_CloseFile(new_sock); return -1; } clientSockets[i].sd = new_sock; one = 1; if (ioctl(clientSockets[i].sd, FIONBIO, &one) < 0) { DebugLogB("SHMInitializeSharedSegment: Error: cannot set socket " "nonblocking: %s", strerror(errno)); SYS_CloseFile(clientSockets[i].sd); clientSockets[i].sd = -1; return -1; } return 0; } int MSGServerProcessEvents(request_object *requestObj, int blockAmount) { static fd_set read_fd; int i, selret, largeSock, rv; largeSock = 0; FD_ZERO(&read_fd); /* * Set up the bit masks for select */ FD_SET(commonSocket, &read_fd); largeSock = commonSocket; for (i = 0; i < PCSCLITE_MAX_APPLICATIONS; i++) { if (clientSockets[i].sd != -1) { FD_SET(clientSockets[i].sd, &read_fd); if (clientSockets[i].sd > largeSock) { largeSock = clientSockets[i].sd; } } } selret = select(largeSock + 1, &read_fd, (fd_set *) NULL, (fd_set *) NULL, NULL); if (selret < 0) { DebugLogB("MSGServerProcessEvents: Select returns with failure: %s", strerror(errno)); return -1; } if (selret == 0) // timeout return 2; /* * A common pipe packet has arrived - it could be a new application or * it could be a reader event packet coming from another thread */ if (FD_ISSET(commonSocket, &read_fd)) { DebugLogA("MSGServerProcessEvents: Common channel packet arrival"); if (MSGServerProcessCommonChannelRequest() == -1) { return -1; } else { return 0; } } for (i = 0; i < PCSCLITE_MAX_APPLICATIONS; i++) { if (clientSockets[i].sd != -1) { if (FD_ISSET(clientSockets[i].sd, &read_fd)) { request_header *header = &requestObj->message.header; /* Read the request header from the client socket */ rv = MSGRecieveData(clientSockets[i].sd, PCSCLITE_SERVER_ATTEMPTS, header, sizeof(*header)); /* Fix byte order of received data */ header->size = ntohl(header->size); header->additional_data_size = ntohl(header->additional_data_size); header->command = ntohl(header->command); if (!rv) { /* Receive the remainder of the request. */ size_t bytes_left = header->size - sizeof(*header); if (bytes_left > sizeof(requestObj->message)) { /* The client is sending bogus data, pretend it died */ rv = -1; } else { rv = MSGRecieveData(clientSockets[i].sd, PCSCLITE_SERVER_ATTEMPTS, header + 1, bytes_left); if (!rv && header->additional_data_size) { requestObj->additional_data = malloc(header->additional_data_size); rv = MSGRecieveData(clientSockets[i].sd, PCSCLITE_SERVER_ATTEMPTS, requestObj->additional_data, header->additional_data_size); } else { requestObj->additional_data = NULL; header->additional_data_size = 0; } } } /* * Set the identifier handle */ requestObj->socket = clientSockets[i].sd; if (rv == -1) { /* The client has died */ requestObj->mtype = CMD_CLIENT_DIED; requestObj->additional_data = NULL; header->command = 0; SYS_CloseFile(clientSockets[i].sd); clientSockets[i].sd = -1; return 0; } requestObj->mtype = CMD_FUNCTION; /* Fix up the byte orders */ MSGFixByteOrder(requestObj); return 1; } } } return -1; } LONG MSGFunctionDemarshall(const request_object *request, reply_object *reply) { const request_message *requestm = &request->message; const request_header *requesth = &requestm->header; reply_message *replym = &reply->message; reply_header *replyh = &replym->header; //LONG rv; /* * Zero out everything */ //rv = 0; switch (requesth->command) { case SCARD_ESTABLISH_CONTEXT: if (requesth->size != sizeof(requestm->establish)) return -1; replyh->size = sizeof(replym->establish); replyh->rv = SCardEstablishContext(requestm->establish.dwScope, 0, 0, &replym->establish.phContext); if (replyh->rv == SCARD_S_SUCCESS) replyh->rv = MSGAddContext(replym->establish.phContext, request->socket); /* Byte ordering */ replym->establish.phContext = htonl(replym->establish.phContext); break; case SCARD_RELEASE_CONTEXT: if (requesth->size != sizeof(requestm->release)) return -1; replyh->size = sizeof(replym->release); replyh->rv = SCardReleaseContext(requestm->release.hContext); if (replyh->rv == SCARD_S_SUCCESS) replyh->rv = MSGRemoveContext(requestm->release.hContext, request->socket); break; case SCARD_CONNECT: if (requesth->size != sizeof(requestm->connect)) return -1; replyh->size = sizeof(replym->connect); replyh->rv = SCardConnect(requestm->connect.hContext, requestm->connect.szReader, requestm->connect.dwShareMode, requestm->connect.dwPreferredProtocols, &replym->connect.phCard, &replym->connect.pdwActiveProtocol); if (replyh->rv == SCARD_S_SUCCESS) replyh->rv = MSGAddHandle(requestm->connect.hContext, request->socket, replym->connect.phCard); /* Byte ordering */ replym->connect.phCard = htonl(replym->connect.phCard); replym->connect.pdwActiveProtocol = htonl(replym->connect.pdwActiveProtocol); break; case SCARD_RECONNECT: if (requesth->size != sizeof(requestm->reconnect)) return -1; replyh->size = sizeof(replym->reconnect); replyh->rv = MSGCheckHandleAssociation(request->socket, requestm->reconnect.hCard); if (replyh->rv != 0) return 0; replyh->rv = SCardReconnect(requestm->reconnect.hCard, requestm->reconnect.dwShareMode, requestm->reconnect.dwPreferredProtocols, requestm->reconnect.dwInitialization, &replym->reconnect.pdwActiveProtocol); /* Byte ordering */ replym->reconnect.pdwActiveProtocol = htonl(replym->reconnect.pdwActiveProtocol); break; case SCARD_DISCONNECT: if (requesth->size != sizeof(requestm->disconnect)) return -1; replyh->size = sizeof(replym->disconnect); replyh->rv = MSGCheckHandleAssociation(request->socket, requestm->disconnect.hCard); if (replyh->rv != 0) return 0; replyh->rv = SCardDisconnect(requestm->disconnect.hCard, requestm->disconnect.dwDisposition); if (replyh->rv == SCARD_S_SUCCESS) replyh->rv = MSGRemoveHandle(0, request->socket, requestm->disconnect.hCard); break; case SCARD_BEGIN_TRANSACTION: if (requesth->size != sizeof(requestm->begin)) return -1; replyh->size = sizeof(replym->begin); replyh->rv = MSGCheckHandleAssociation(request->socket, requestm->begin.hCard); if (replyh->rv != 0) return 0; replyh->rv = SCardBeginTransaction(requestm->begin.hCard); break; case SCARD_END_TRANSACTION: if (requesth->size != sizeof(requestm->end)) return -1; replyh->size = sizeof(replym->end); replyh->rv = MSGCheckHandleAssociation(request->socket, requestm->end.hCard); if (replyh->rv != 0) return 0; replyh->rv = SCardEndTransaction(requestm->end.hCard, requestm->end.dwDisposition); break; case SCARD_CANCEL_TRANSACTION: if (requesth->size != sizeof(requestm->cancel)) return -1; replyh->size = sizeof(replym->cancel); replyh->rv = MSGCheckHandleAssociation(request->socket, requestm->cancel.hCard); if (replyh->rv != 0) return 0; replyh->rv = SCardCancelTransaction(requestm->cancel.hCard); break; case SCARD_STATUS: if (requesth->size != sizeof(requestm->status)) return -1; replyh->size = sizeof(replym->status); replyh->rv = MSGCheckHandleAssociation(request->socket, requestm->status.hCard); if (replyh->rv != 0) return 0; replyh->rv = SCardStatus(requestm->status.hCard, replym->status.mszReaderNames, &replym->status.pcchReaderLen, &replym->status.pdwState, &replym->status.pdwProtocol, replym->status.pbAtr, &replym->status.pcbAtrLen); /* Byte ordering */ replym->status.pcchReaderLen = htonl(replym->status.pcchReaderLen); replym->status.pdwState = htonl(replym->status.pdwState); replym->status.pdwProtocol = htonl(replym->status.pdwProtocol); replym->status.pcbAtrLen = htonl(replym->status.pcbAtrLen); break; case SCARD_TRANSMIT: if (requesth->size != sizeof(requestm->transmit)) return -1; replyh->size = sizeof(replym->transmit); replyh->rv = MSGCheckHandleAssociation(request->socket, requestm->transmit.hCard); if (replyh->rv != 0) return 0; reply->additional_data = malloc(requestm->transmit.cbMaxRecvLength + sizeof(receive_buffer_padding)); if (!reply->additional_data) replym->transmit.cbRecvLength = 0; else { /* Fill the padding part of the rcv buffer with RCV_BUFFER_PADDING */ memcpy(((char *)reply->additional_data) + requestm->transmit.cbMaxRecvLength, receive_buffer_padding, sizeof(receive_buffer_padding)); replym->transmit.cbRecvLength = requestm->transmit.cbMaxRecvLength; } replyh->rv = SCardTransmit(requestm->transmit.hCard, &requestm->transmit.pioSendPci, request->additional_data, requesth->additional_data_size, &replym->transmit.pioRecvPci, reply->additional_data, &replym->transmit.cbRecvLength); /* Check to make sure the RCV_BUFFER_PADDING was untouched. */ if (memcmp(((char *)reply->additional_data) + requestm->transmit.cbMaxRecvLength, receive_buffer_padding, sizeof(receive_buffer_padding))) { DebugLogA("SCardTransmit: wrote past end of receive buffer"); #ifndef WIN32 syslog(LOG_WARNING, "SCardTransmit: wrote past end receive buffer"); #endif } if (replyh->rv == SCARD_S_SUCCESS) replyh->additional_data_size = replym->transmit.cbRecvLength; /* Byte ordering */ replym->transmit.pioRecvPci.dwProtocol = htonl(replym->transmit.pioRecvPci.dwProtocol); replym->transmit.pioRecvPci.cbPciLength = htonl(replym->transmit.pioRecvPci.cbPciLength); replym->transmit.cbRecvLength = htonl(replym->transmit.cbRecvLength); break; default: return -1; } /* Do byte reversals in reply header prior to sending through the socket. Any command specific reversals (i.e. non-header) need to be done above */ replyh->size = htonl(replyh->size); replyh->additional_data_size = htonl(replyh->additional_data_size); replyh->rv = htonl(replyh->rv); return 0; } LONG MSGAddContext(SCARDCONTEXT hContext, DWORD dwClientID) { int i; for (i = 0; i < PCSCLITE_MAX_CHANNELS; i++) { if (psChannelMap[i].dwClientID == 0) { psChannelMap[i].hContext = hContext; psChannelMap[i].dwClientID = dwClientID; break; } } if (i == PCSCLITE_MAX_CHANNELS) { return SCARD_F_INTERNAL_ERROR; } else { return SCARD_S_SUCCESS; } } LONG MSGRemoveContext(SCARDCONTEXT hContext, DWORD dwClientID) { int i, j; LONG rv; for (i = 0; i < PCSCLITE_MAX_CHANNELS; i++) { if (psChannelMap[i].hContext == hContext && psChannelMap[i].dwClientID == dwClientID) { for (j = 0; j < PCSCLITE_MAX_CONTEXTS; j++) { /* * Disconnect each of these just in case */ if (psChannelMap[i].hCard[j] != 0) { /* * We will use SCardStatus to see if the card has been * reset there is no need to reset each time * Disconnect is called */ rv = SCardStatus(psChannelMap[i].hCard[j], 0, 0, 0, 0, 0, 0); if (rv == SCARD_W_RESET_CARD || rv == SCARD_W_REMOVED_CARD) { SCardDisconnect(psChannelMap[i].hCard[j], SCARD_LEAVE_CARD); } else { SCardDisconnect(psChannelMap[i].hCard[j], SCARD_RESET_CARD); } psChannelMap[i].hCard[j] = 0; } psChannelMap[i].hContext = 0; psChannelMap[i].dwClientID = 0; } SCardReleaseContext(hContext); return SCARD_S_SUCCESS; } } return SCARD_E_INVALID_VALUE; } LONG MSGAddHandle(SCARDCONTEXT hContext, DWORD dwClientID, SCARDHANDLE hCard) { int i, j; for (i = 0; i < PCSCLITE_MAX_CHANNELS; i++) { if (psChannelMap[i].hContext == hContext && psChannelMap[i].dwClientID == dwClientID) { /* * Find an empty spot to put the hCard value */ for (j = 0; j < PCSCLITE_MAX_CONTEXTS; j++) { if (psChannelMap[i].hCard[j] == 0) { psChannelMap[i].hCard[j] = hCard; break; } } if (j == PCSCLITE_MAX_CONTEXTS) { return SCARD_F_INTERNAL_ERROR; } else { return SCARD_S_SUCCESS; } } } /* End of for */ return SCARD_E_INVALID_VALUE; } LONG MSGRemoveHandle(SCARDCONTEXT hContext, DWORD dwClientID, SCARDHANDLE hCard) { int i, j; for (i = 0; i < PCSCLITE_MAX_CHANNELS; i++) { if (psChannelMap[i].dwClientID == dwClientID) { for (j = 0; j < PCSCLITE_MAX_CONTEXTS; j++) { if (psChannelMap[i].hCard[j] == hCard) { psChannelMap[i].hCard[j] = 0; return SCARD_S_SUCCESS; } } } } return SCARD_E_INVALID_VALUE; } LONG MSGCheckHandleAssociation(DWORD dwClientID, SCARDHANDLE hCard) { int i, j; for (i = 0; i < PCSCLITE_MAX_CHANNELS; i++) { if (psChannelMap[i].dwClientID == dwClientID) { for (j = 0; j < PCSCLITE_MAX_CONTEXTS; j++) { if (psChannelMap[i].hCard[j] == hCard) { return 0; } } } } /* Must be a rogue client, debug log and sleep a couple of seconds */ DebugLogA("MSGCheckHandleAssociation: Client failed to authenticate\n"); SYS_Sleep(2); return SCARD_E_INVALID_HANDLE; } LONG MSGCleanupClient(request_object *request) { int i; for (i = 0; i < PCSCLITE_MAX_CHANNELS; i++) { if (psChannelMap[i].dwClientID == request->socket) { MSGRemoveContext(psChannelMap[i].hContext, request->socket); } } return 0; } void MSGFixByteOrder(request_object *requestObj) { /* Do any necessary byte swapping on a response */ // requestObj->mtype = ntohl(requestObj->mtype); /* One of enum pcsc_adm_commands */ // requestObj->socket = ntohl(requestObj->socket); if (requestObj->mtype != CMD_FUNCTION) return; switch (requestObj->message.header.command) { case SCARD_ESTABLISH_CONTEXT: if (requestObj->message.header.size == sizeof(establish_request)) requestObj->message.establish.dwScope = ntohl(requestObj->message.establish.dwScope); break; case SCARD_RELEASE_CONTEXT: if (requestObj->message.header.size == sizeof(release_request)) requestObj->message.release.hContext = ntohl(requestObj->message.release.hContext); break; case SCARD_CONNECT: if (requestObj->message.header.size == sizeof(connect_request)) { requestObj->message.connect.hContext = ntohl(requestObj->message.connect.hContext); requestObj->message.connect.dwShareMode = ntohl(requestObj->message.connect.dwShareMode); requestObj->message.connect.dwPreferredProtocols = ntohl(requestObj->message.connect.dwPreferredProtocols); } break; case SCARD_RECONNECT: if (requestObj->message.header.size == sizeof(reconnect_request)) { requestObj->message.reconnect.hCard = ntohl(requestObj->message.reconnect.hCard); requestObj->message.reconnect.dwShareMode = ntohl(requestObj->message.reconnect.dwShareMode); requestObj->message.reconnect.dwPreferredProtocols = ntohl(requestObj->message.reconnect.dwPreferredProtocols); requestObj->message.reconnect.dwInitialization = ntohl(requestObj->message.reconnect.dwInitialization); } break; case SCARD_DISCONNECT: if (requestObj->message.header.size == sizeof(disconnect_request)) { requestObj->message.disconnect.hCard = ntohl(requestObj->message.disconnect.hCard); requestObj->message.disconnect.dwDisposition = ntohl(requestObj->message.disconnect.dwDisposition); } break; case SCARD_BEGIN_TRANSACTION: if (requestObj->message.header.size == sizeof(begin_request)) requestObj->message.begin.hCard = ntohl(requestObj->message.begin.hCard); break; case SCARD_END_TRANSACTION: if (requestObj->message.header.size == sizeof(end_request)) { requestObj->message.end.hCard = ntohl(requestObj->message.end.hCard); requestObj->message.end.dwDisposition = ntohl(requestObj->message.end.dwDisposition); } break; case SCARD_CANCEL_TRANSACTION: if (requestObj->message.header.size == sizeof(cancel_request)) requestObj->message.cancel.hCard = ntohl(requestObj->message.cancel.hCard); break; case SCARD_STATUS: if (requestObj->message.header.size == sizeof(status_request)) { requestObj->message.status.hCard = ntohl(requestObj->message.status.hCard); requestObj->message.status.cbMaxAtrLen = ntohl(requestObj->message.status.cbMaxAtrLen); } break; case SCARD_TRANSMIT: if (requestObj->message.header.size == sizeof(transmit_request)) { requestObj->message.transmit.hCard = ntohl(requestObj->message.transmit.hCard); requestObj->message.transmit.cbMaxRecvLength = ntohl(requestObj->message.transmit.cbMaxRecvLength); } break; } }