/*
* This program is provided under the terms of the Common Public License,
* version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
* software constitutes recipient's acceptance of CPL-1.0 terms which can be
* found in the file LICENSE file or at
* https://opensource.org/licenses/cpl1.0.php
*/
/* (C) COPYRIGHT Google Inc. 2013 */
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <grp.h>
#include "log.h"
#include "slotmgr.h"
#include "pkcsslotd.h"
#include "apictl.h"
// Creates the daemon's listener socket, to which clients will connect and
// retrieve slot information through. Returns the file descriptor of the
// created socket.
int CreateListenerSocket(void)
{
struct sockaddr_un address;
struct group *grp;
int socketfd;
socketfd = socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (socketfd < 0) {
ErrLog("Failed to create listener socket, errno 0x%X.", errno);
return -1;
}
if (unlink(SOCKET_FILE_PATH) && errno != ENOENT) {
ErrLog("Failed to unlink socket file, errno 0x%X.", errno);
close(socketfd);
return -1;
}
memset(&address, 0, sizeof(struct sockaddr_un));
address.sun_family = AF_UNIX;
strcpy(address.sun_path, SOCKET_FILE_PATH);
if (bind(socketfd,
(struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0) {
ErrLog("Failed to bind to socket, errno 0x%X.", errno);
close(socketfd);
return -1;
}
// make socket file part of the pkcs11 group, and write accessable
// for that group
grp = getgrnam("pkcs11");
if (!grp) {
ErrLog("Group PKCS#11 does not exist");
DetachSocketListener(socketfd);
return -1;
}
if (chown(SOCKET_FILE_PATH, 0, grp->gr_gid)) {
ErrLog("Could not change file group on socket, errno 0x%X.", errno);
DetachSocketListener(socketfd);
return -1;
}
if (chmod(SOCKET_FILE_PATH,
S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP | S_IXUSR | S_IXGRP)) {
ErrLog("Could not change file permissions on socket, errno 0x%X.",
errno);
DetachSocketListener(socketfd);
return -1;
}
if (listen(socketfd, 20) != 0) {
ErrLog("Failed to listen to socket, errno 0x%X.", errno);
DetachSocketListener(socketfd);
return -1;
}
return socketfd;
}
int InitSocketData(Slot_Mgr_Socket_t *socketData)
{
unsigned int processed = 0;
PopulateCKInfo(&(socketData->ck_info));
socketData->num_slots = NumberSlotsInDB;
PopulateSlotInfo(socketData->slot_info, &processed);
/* check that we read in correct amount of slots */
if (processed != NumberSlotsInDB) {
ErrLog("Failed to populate slot info.\n");
return FALSE;
}
return TRUE;
}
int SocketConnectionHandler(int socketfd, int timeout_secs)
{
int returnVal;
fd_set set;
struct timeval timeout;
FD_ZERO(&set);
FD_SET(socketfd, &set);
timeout.tv_sec = timeout_secs;
timeout.tv_usec = 0;
returnVal = select(socketfd + 1, &set, NULL, NULL, &timeout);
if (returnVal == -1) {
ErrLog("select failed on socket connection, errno 0x%X.", errno);
return FALSE;
} else if (returnVal == 0) {
// select call timed out, return
return FALSE;
} else {
struct sockaddr_un address;
socklen_t address_length = sizeof(address);
int connectionfd = accept(socketfd,
(struct sockaddr *) &address,
&address_length);
if (connectionfd < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
/* These errors are allowed since
* socket is non-blocking
*/
ErrLog("Failed to accept socket connection, errno 0x%X.",
errno);
}
return FALSE;
}
if (write(connectionfd, &socketData, sizeof(socketData)) !=
sizeof(socketData)) {
ErrLog("Failed to write socket data, errno 0x%X.", errno);
close(connectionfd);
return FALSE;
}
close(connectionfd);
return TRUE;
}
}
void DetachSocketListener(int socketfd)
{
close(socketfd);
unlink(SOCKET_FILE_PATH);
}