#include "config.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#ifdef HAVE_SYS_FILIO_H
#include <sys/filio.h>
#endif
#include "wintypes.h"
#include "pcsclite.h"
#include "pcscexport.h"
#include "winscard.h"
#include "debug.h"
#include "winscard_msg.h"
#include "sys_generic.h"
#include <libkern/OSByteOrder.h>
#include <security_utilities/debugging.h>
INTERNAL int32_t SHMClientRead(psharedSegmentMsg msgStruct, uint32_t dwClientID, int32_t blockamount)
{
int rv = SHMMessageReceive(msgStruct, sizeof(*msgStruct), dwClientID, blockamount);
SHSharedSegmentMsgToHostOrder(msgStruct);
return rv;
}
INTERNAL int32_t SHMClientReadMessage(psharedSegmentMsg msgStruct, uint32_t dwClientID, size_t dataSize, int32_t blockamount)
{
size_t headerSize = sizeof(sharedSegmentMsg) - sizeof(msgStruct->data);
Log2(PCSC_LOG_DEBUG, "SHMClientReadMessage: Issuing read for %d bytes (header)", headerSize);
secdebug("pcscd", "SHMClientReadMessage: Issuing read for %ld bytes (header)", headerSize);
int rv = SHMMessageReceive(msgStruct, headerSize, dwClientID, blockamount);
Log3(rv?PCSC_LOG_CRITICAL:PCSC_LOG_DEBUG, "SHMClientReadMessage: read message header error: 0x%08X [0x%08X]", rv, rv);
secdebug("pcscd", "SHMClientReadMessage: read message header error: 0x%08X [0x%08X]", rv, rv);
if (rv)
return rv;
SHSharedSegmentMsgToHostOrder(msgStruct);
if (msgStruct->headerTag != WINSCARD_MSG_HEADER_TAG)
{
Log3(PCSC_LOG_CRITICAL, "Error: read message header tag of: 0x%08X for possible command 0x%08X",
msgStruct->headerTag, msgStruct->command);
secdebug("pcscd", "Error: read message header tag of: 0x%08X for possible command 0x%08X",
msgStruct->headerTag, msgStruct->command);
return SCARD_F_INTERNAL_ERROR;
}
if (dataSize == 0)
dataSize = msgStruct->msgSize - headerSize; else
if (msgStruct->msgSize != (headerSize + dataSize))
{
Log2(PCSC_LOG_CRITICAL, "Error: create on client socket: %s", strerror(errno));
secdebug("pcscd", "Error: create on client socket: %s", strerror(errno));
return SCARD_F_INTERNAL_ERROR;
}
Log2(PCSC_LOG_DEBUG, "SHMClientReadMessage: Issuing read for %d bytes", dataSize);
secdebug("pcscd", "SHMClientReadMessage: Issuing read for %ld bytes", dataSize);
if (blockamount == 0)
blockamount = PCSCLITE_SERVER_ATTEMPTS;
rv = SHMMessageReceive(msgStruct->data, dataSize, dwClientID, blockamount);
Log3(rv?PCSC_LOG_CRITICAL:PCSC_LOG_DEBUG, "SHMClientReadMessage: read message body error: 0x%08X [0x%08X]", rv, rv);
secdebug("pcscd", "SHMClientReadMessage: read message body error: 0x%08X [0x%08X]", rv, rv);
return rv;
}
INTERNAL int SHMClientSetupSession(uint32_t *pdwClientID)
{
struct sockaddr_un svc_addr;
int one;
int ret;
ret = socket(AF_UNIX, SOCK_STREAM, 0);
if (ret < 0)
{
Log2(PCSC_LOG_CRITICAL, "Error: create on client socket: %s",
strerror(errno));
return -1;
}
*pdwClientID = ret;
svc_addr.sun_family = AF_UNIX;
strncpy(svc_addr.sun_path, PCSCLITE_CSOCK_NAME,
sizeof(svc_addr.sun_path));
if (connect(*pdwClientID, (struct sockaddr *) &svc_addr,
sizeof(svc_addr.sun_family) + strlen(svc_addr.sun_path) + 1) < 0)
{
Log3(PCSC_LOG_CRITICAL, "Error: connect to client socket %s: %s",
PCSCLITE_CSOCK_NAME, strerror(errno));
SYS_CloseFile(*pdwClientID);
return -1;
}
one = 1;
if (ioctl(*pdwClientID, FIONBIO, &one) < 0)
{
Log3(PCSC_LOG_CRITICAL, "Error: cannot set socket %s nonblocking: %s",
PCSCLITE_CSOCK_NAME, strerror(errno));
SYS_CloseFile(*pdwClientID);
return -1;
}
return 0;
}
INTERNAL int SHMClientCloseSession(uint32_t dwClientID)
{
SYS_CloseFile(dwClientID);
return 0;
}
INTERNAL size_t SHMCalculateMessageSize(size_t dataSize)
{
return sizeof(sharedSegmentMsg) - PCSCLITE_MAX_MESSAGE_SIZE + dataSize;;
}
INTERNAL int SHMMessageSend(void *buffer_void, uint64_t buffer_size,
int32_t filedes, int32_t blockAmount)
{
char *buffer = (char *)buffer_void;
int retval = 0;
time_t start = time(0);
size_t remaining = buffer_size;
LogXxd(PCSC_LOG_DEBUG, "==> SHMMessageSend:\n", (const unsigned char *)buffer, buffer_size);
while (remaining > 0)
{
fd_set write_fd;
struct timeval timeout;
int selret;
FD_ZERO(&write_fd);
FD_SET(filedes, &write_fd);
timeout.tv_usec = 0;
if ((timeout.tv_sec = start + blockAmount - time(0)) < 0)
{
Log1(PCSC_LOG_ERROR, "SHMMessageReceive: we already timed out");
retval = -1;
break;
}
selret = select(filedes + 1, NULL, &write_fd, NULL, &timeout);
if (selret > 0)
{
int written;
if (!FD_ISSET(filedes, &write_fd))
{
Log1(PCSC_LOG_ERROR, "SHMMessageReceive: very strange situation: !FD_ISSET");
retval = -1;
break;
}
written = write(filedes, buffer, remaining);
if (written > 0)
{
buffer += written;
remaining -= written;
} else if (written == 0)
{
Log1(PCSC_LOG_ERROR, "SHMMessageReceive: peer closed the socket");
retval = -1;
break;
} else
{
if (errno != EINTR && errno != EAGAIN)
{
retval = -1;
break;
}
}
} else if (selret == 0)
{
Log1(PCSC_LOG_ERROR, "SHMMessageReceive: selret == 0 [timeout]");
retval = -1;
break;
} else
{
if (errno != EINTR)
{
Log2(PCSC_LOG_ERROR, "select returns with failure: %s",
strerror(errno));
retval = -1;
break;
}
}
}
if (remaining > 0)
Log3(PCSC_LOG_ERROR, "failure to write all bytes, remaining: %d, err: ", remaining, strerror(errno));
return retval;
}
INTERNAL int SHMMessageReceive(void *buffer_void, uint64_t buffer_size,
int32_t filedes, int32_t blockAmount)
{
char *buffer = (char *)buffer_void;
int retval = 0;
time_t start = time(0);
size_t remaining = buffer_size;
while (remaining > 0)
{
fd_set read_fd;
struct timeval timeout;
int selret;
FD_ZERO(&read_fd);
FD_SET(filedes, &read_fd);
timeout.tv_usec = 0;
if ((timeout.tv_sec = start + blockAmount - time(0)) < 0)
{
Log1(PCSC_LOG_ERROR, "SHMMessageReceive: we already timed out");
retval = -1;
break;
}
selret = select(filedes + 1, &read_fd, NULL, NULL, &timeout);
if (selret > 0)
{
int readed;
if (!FD_ISSET(filedes, &read_fd))
{
Log1(PCSC_LOG_ERROR, "SHMMessageReceive: very strange situation: !FD_ISSET");
retval = -1;
break;
}
readed = read(filedes, buffer, remaining);
if (readed > 0)
{
buffer += readed;
remaining -= readed;
} else if (readed == 0)
{
Log1(PCSC_LOG_ERROR, "SHMMessageReceive: peer closed the socket");
retval = -1;
break;
} else
{
if (errno != EINTR && errno != EAGAIN)
{
retval = -1;
break;
}
}
} else if (selret == 0)
{
Log1(PCSC_LOG_ERROR, "SHMMessageReceive: selret == 0 [timeout]");
retval = -1;
break;
} else
{
if (errno != EINTR)
{
Log2(PCSC_LOG_ERROR, "select returns with failure: %s",
strerror(errno));
retval = -1;
break;
}
}
}
size_t bytesRead = (buffer_size - remaining);
Log3(PCSC_LOG_DEBUG, "SHMMessageReceive errno: 0x%08X: %s", errno, errno?strerror(errno):"no error");
Log3(retval?PCSC_LOG_ERROR:PCSC_LOG_DEBUG, "SHMMessageReceive retval: 0x%08X, bytes read: %d", retval, bytesRead);
LogXxd(PCSC_LOG_DEBUG, "<== SHMMessageReceive:\n", (const unsigned char *)buffer_void, bytesRead);
return retval;
}
INTERNAL int32_t WrapSHMWrite(uint32_t command, uint32_t dwClientID,
uint64_t size, uint32_t blockAmount, void *data_void)
{
char *data = (char *)data_void;
sharedSegmentMsg msgStruct;
int ret;
memset(&msgStruct, 0, sizeof(msgStruct));
msgStruct.headerTag = WINSCARD_MSG_HEADER_TAG;
msgStruct.msgSize = sizeof(sharedSegmentMsg) - sizeof(msgStruct.data) + size;
msgStruct.mtype = (command == CMD_VERSION)?CMD_VERSION:CMD_FUNCTION;
msgStruct.user_id = SYS_GetUID();
msgStruct.group_id = SYS_GetGID();
msgStruct.command = command;
msgStruct.date = time(NULL);
memset(msgStruct.key, 0, sizeof(msgStruct.key));
if ((SCARD_TRANSMIT_EXTENDED == command)
|| (SCARD_CONTROL_EXTENDED == command))
{
size_t sizeToSend = (msgStruct.msgSize <= PCSCLITE_MAX_MESSAGE_SIZE)?msgStruct.msgSize:PCSCLITE_MAX_MESSAGE_SIZE;
size_t sizeRemaining = (msgStruct.msgSize <= PCSCLITE_MAX_MESSAGE_SIZE)?0:
(msgStruct.msgSize - PCSCLITE_MAX_MESSAGE_SIZE);
memcpy(msgStruct.data, data, sizeToSend);
SHSharedSegmentMsgToNetworkOrder(&msgStruct);
ret = SHMMessageSend(&msgStruct, sizeToSend, dwClientID, blockAmount);
if (ret)
return ret;
if (sizeRemaining > sizeof(msgStruct.data))
{
Log2(PCSC_LOG_ERROR, "WrapSHMWrite: cannot send message of size %d", sizeRemaining);
return -1;
}
if (sizeRemaining > 0)
{
memcpy(msgStruct.data, data, sizeRemaining);
ret = SHMMessageSend(&msgStruct, sizeToSend, dwClientID, blockAmount);
if (ret)
return ret;
}
}
else
if (size > sizeof(msgStruct.data))
{
Log3(PCSC_LOG_ERROR, "WrapSHMWrite: cannot send message of size %d with this command: %d", size, command);
return -1;
}
else
{
size_t sizeToSend = msgStruct.msgSize;
memcpy(msgStruct.data, data, size);
SHSharedSegmentMsgToNetworkOrder(&msgStruct);
ret = SHMMessageSend(&msgStruct, sizeToSend, dwClientID, blockAmount);
}
return ret;
}
INTERNAL void SHMCleanupSharedSegment(int sockValue, const char *pcFilePath)
{
SYS_CloseFile(sockValue);
SYS_Unlink((char *)pcFilePath);
}
#pragma mark -------------------- Byte ordering functions --------------------
INTERNAL void SHSharedSegmentMsgToNetworkOrder(psharedSegmentMsg msg)
{
if (msg)
{
msg->headerTag = htonl(msg->headerTag);
msg->msgSize = htonl(msg->msgSize);
msg->mtype = htonl(msg->mtype);
msg->user_id = htonl(msg->user_id);
msg->group_id = htonl(msg->group_id);
msg->command = htonl(msg->command);
msg->date = htonl(msg->date);
}
}
INTERNAL void SHSharedSegmentMsgToHostOrder(psharedSegmentMsg msg)
{
if (msg)
{
msg->headerTag = ntohl(msg->headerTag);
msg->msgSize = ntohl(msg->msgSize);
msg->mtype = ntohl(msg->mtype);
msg->user_id = ntohl(msg->user_id);
msg->group_id = ntohl(msg->group_id);
msg->command = ntohl(msg->command);
msg->date = ntohl(msg->date);
}
}
INTERNAL void htonlControlStructExtended(control_struct_extended *cs)
{
if (cs)
{
cs->hCard = htonl(cs->hCard);
cs->dwControlCode = htonl(cs->dwControlCode);
cs->cbSendLength = htonl(cs->cbSendLength);
cs->cbRecvLength = htonl(cs->cbRecvLength);
cs->size = OSSwapHostToBigInt64(cs->size);
cs->rv = htonl(cs->rv); }
}
INTERNAL void ntohlControlStructExtended(control_struct_extended *cs)
{
if (cs)
{
cs->hCard = ntohl(cs->hCard);
cs->dwControlCode = ntohl(cs->dwControlCode);
cs->cbSendLength = ntohl(cs->cbSendLength);
cs->cbRecvLength = ntohl(cs->cbRecvLength);
cs->size = OSSwapBigToHostInt64(cs->size);
cs->rv = ntohl(cs->rv);
}
}
INTERNAL void htonlTransmitStruct(transmit_struct *ts)
{
if (ts)
{
ts->hCard = htonl(ts->hCard);
ts->pioSendPciProtocol = htonl(ts->pioSendPciProtocol);
ts->pioSendPciLength = htonl(ts->pioSendPciLength);
ts->cbSendLength = htonl(ts->cbSendLength);
ts->pioRecvPciProtocol = htonl(ts->pioRecvPciProtocol);
ts->pioRecvPciLength = htonl(ts->pioRecvPciLength);
ts->pcbRecvLength = htonl(ts->pcbRecvLength);
ts->rv = htonl(ts->rv); }
}
INTERNAL void ntohlTransmitStruct(transmit_struct *ts)
{
if (ts)
{
ts->hCard = ntohl(ts->hCard);
ts->pioSendPciProtocol = ntohl(ts->pioSendPciProtocol);
ts->pioSendPciLength = ntohl(ts->pioSendPciLength);
ts->cbSendLength = ntohl(ts->cbSendLength);
ts->pioRecvPciProtocol = ntohl(ts->pioRecvPciProtocol);
ts->pioRecvPciLength = ntohl(ts->pioRecvPciLength);
ts->pcbRecvLength = ntohl(ts->pcbRecvLength);
ts->rv = ntohl(ts->rv);
}
}
INTERNAL void htonlTransmitStructExtended(transmit_struct_extended *ts)
{
if (ts)
{
ts->hCard = htonl(ts->hCard);
ts->pioSendPciProtocol = htonl(ts->pioSendPciProtocol);
ts->pioSendPciLength = htonl(ts->pioSendPciLength);
ts->cbSendLength = htonl(ts->cbSendLength);
ts->pioRecvPciProtocol = htonl(ts->pioRecvPciProtocol);
ts->pioRecvPciLength = htonl(ts->pioRecvPciLength);
ts->pcbRecvLength = htonl(ts->pcbRecvLength);
ts->size = OSSwapHostToBigInt64(ts->size);
ts->rv = htonl(ts->rv); }
}
INTERNAL void ntohlTransmitStructExtended(transmit_struct_extended *ts)
{
if (ts)
{
ts->hCard = ntohl(ts->hCard);
ts->pioSendPciProtocol = ntohl(ts->pioSendPciProtocol);
ts->pioSendPciLength = ntohl(ts->pioSendPciLength);
ts->cbSendLength = ntohl(ts->cbSendLength);
ts->pioRecvPciLength = ntohl(ts->pioRecvPciLength);
ts->pcbRecvLength = ntohl(ts->pcbRecvLength);
ts->size = OSSwapBigToHostInt64(ts->size);
ts->rv = ntohl(ts->rv);
}
}
INTERNAL void htonlEstablishStruct(establish_struct *es)
{
if (es)
{
es->dwScope = htonl(es->dwScope);
es->phContext = htonl(es->phContext);
es->rv = htonl(es->rv);
}
}
INTERNAL void ntohlEstablishStruct(establish_struct *es)
{
if (es)
{
es->dwScope = ntohl(es->dwScope);
es->phContext = ntohl(es->phContext);
es->rv = ntohl(es->rv);
}
}
INTERNAL void htonlReleaseStruct(release_struct *rs)
{
if (rs)
{
rs->hContext = htonl(rs->hContext);
rs->rv = htonl(rs->rv);
}
}
INTERNAL void ntohlReleaseStruct(release_struct *rs)
{
if (rs)
{
rs->hContext = ntohl(rs->hContext);
rs->rv = ntohl(rs->rv);
}
}
INTERNAL void htonlConnectStruct(connect_struct *cs)
{
if (cs)
{
cs->hContext = htonl(cs->hContext);
cs->dwShareMode = htonl(cs->dwShareMode);
cs->dwPreferredProtocols = htonl(cs->dwPreferredProtocols);
cs->phCard = htonl(cs->phCard);
cs->pdwActiveProtocol = htonl(cs->pdwActiveProtocol);
cs->rv = htonl(cs->rv);
}
}
INTERNAL void ntohlConnectStruct(connect_struct *cs)
{
if (cs)
{
cs->hContext = ntohl(cs->hContext);
cs->dwShareMode = ntohl(cs->dwShareMode);
cs->dwPreferredProtocols = ntohl(cs->dwPreferredProtocols);
cs->phCard = ntohl(cs->phCard);
cs->pdwActiveProtocol = ntohl(cs->pdwActiveProtocol);
cs->rv = ntohl(cs->rv);
}
}
INTERNAL void htonlReconnectStruct(reconnect_struct *rc)
{
if (rc)
{
rc->hCard = htonl(rc->hCard);
rc->dwShareMode = htonl(rc->dwShareMode);
rc->dwPreferredProtocols = htonl(rc->dwPreferredProtocols);
rc->dwInitialization = htonl(rc->dwInitialization);
rc->pdwActiveProtocol = htonl(rc->pdwActiveProtocol);
rc->rv = htonl(rc->rv);
}
}
INTERNAL void ntohlReconnectStruct(reconnect_struct *rc)
{
if (rc)
{
rc->hCard = ntohl(rc->hCard);
rc->dwShareMode = ntohl(rc->dwShareMode);
rc->dwPreferredProtocols = ntohl(rc->dwPreferredProtocols);
rc->dwInitialization = ntohl(rc->dwInitialization);
rc->pdwActiveProtocol = ntohl(rc->pdwActiveProtocol);
rc->rv = ntohl(rc->rv);
}
}
INTERNAL void htonlDisconnectStruct(disconnect_struct *dc)
{
if (dc)
{
dc->hCard = htonl(dc->hCard);
dc->dwDisposition = htonl(dc->dwDisposition);
dc->rv = htonl(dc->rv);
}
}
INTERNAL void ntohlDisconnectStruct(disconnect_struct *dc)
{
if (dc)
{
dc->hCard = ntohl(dc->hCard);
dc->dwDisposition = ntohl(dc->dwDisposition);
dc->rv = ntohl(dc->rv);
}
}
INTERNAL void htonlBeginStruct(begin_struct *bs)
{
if (bs)
{
bs->hCard = htonl(bs->hCard);
bs->rv = htonl(bs->rv);
}
}
INTERNAL void ntohlBeginStruct(begin_struct *bs)
{
if (bs)
{
bs->hCard = ntohl(bs->hCard);
bs->rv = ntohl(bs->rv);
}
}
INTERNAL void htonlCancelStruct(cancel_struct *cs)
{
if (cs)
{
cs->hCard = htonl(cs->hCard);
cs->rv = htonl(cs->rv);
}
}
INTERNAL void ntohlCancelStruct(cancel_struct *cs)
{
if (cs)
{
cs->hCard = ntohl(cs->hCard);
cs->rv = ntohl(cs->rv);
}
}
INTERNAL void htonlEndStruct(end_struct *es)
{
if (es)
{
es->hCard = htonl(es->hCard);
es->dwDisposition = htonl(es->dwDisposition);
es->rv = htonl(es->rv);
}
}
INTERNAL void ntohlEndStruct(end_struct *es)
{
if (es)
{
es->hCard = ntohl(es->hCard);
es->dwDisposition = ntohl(es->dwDisposition);
es->rv = ntohl(es->rv);
}
}
INTERNAL void htonlStatusStruct(status_struct *ss)
{
if (ss)
{
ss->hCard = htonl(ss->hCard);
ss->pcchReaderLen = htonl(ss->pcchReaderLen);
ss->pdwState = htonl(ss->pdwState);
ss->pdwProtocol = htonl(ss->pdwProtocol);
ss->pcbAtrLen = htonl(ss->pcbAtrLen);
ss->rv = htonl(ss->rv);
}
}
INTERNAL void ntohlStatusStruct(status_struct *ss)
{
if (ss)
{
ss->hCard = ntohl(ss->hCard);
ss->pcchReaderLen = ntohl(ss->pcchReaderLen);
ss->pdwState = ntohl(ss->pdwState);
ss->pdwProtocol = ntohl(ss->pdwProtocol);
ss->pcbAtrLen = ntohl(ss->pcbAtrLen);
ss->rv = ntohl(ss->rv);
}
}
INTERNAL void htonlControlStruct(control_struct *cs)
{
if (cs)
{
cs->hCard = htonl(cs->hCard);
cs->dwControlCode = htonl(cs->dwControlCode);
cs->cbSendLength = htonl(cs->cbSendLength);
cs->cbRecvLength = htonl(cs->cbRecvLength);
cs->dwBytesReturned = htonl(cs->dwBytesReturned);
cs->rv = htonl(cs->rv);
}
}
INTERNAL void ntohlControlStruct(control_struct *cs)
{
if (cs)
{
cs->hCard = ntohl(cs->hCard);
cs->dwControlCode = ntohl(cs->dwControlCode);
cs->cbSendLength = ntohl(cs->cbSendLength);
cs->cbRecvLength = ntohl(cs->cbRecvLength);
cs->dwBytesReturned = ntohl(cs->dwBytesReturned);
cs->rv = ntohl(cs->rv);
}
}
INTERNAL void htonlGetSetStruct(getset_struct *gs)
{
if (gs)
{
gs->hCard = htonl(gs->hCard);
gs->dwAttrId = htonl(gs->dwAttrId);
gs->cbAttrLen = htonl(gs->cbAttrLen);
gs->rv = htonl(gs->rv);
}
}
INTERNAL void ntohlGetSetStruct(getset_struct *gs)
{
if (gs)
{
gs->hCard = ntohl(gs->hCard);
gs->dwAttrId = ntohl(gs->dwAttrId);
gs->cbAttrLen = ntohl(gs->cbAttrLen);
gs->rv = ntohl(gs->rv);
}
}
INTERNAL void htonlVersionStruct(version_struct *vs)
{
if (vs)
{
vs->major = htonl(vs->major);
vs->minor = htonl(vs->minor);
vs->rv = htonl(vs->rv);
}
}
INTERNAL void ntohlVersionStruct(version_struct *vs)
{
if (vs)
{
vs->major = ntohl(vs->major);
vs->minor = ntohl(vs->minor);
vs->rv = ntohl(vs->rv);
}
}