From 39032c8f0d70192bc9be96e0bcc12f569b733005 Mon Sep 17 00:00:00 2001 From: David Gwynne Date: Tue, 16 Aug 2022 10:06:30 +1000 Subject: [PATCH] diff to main for comp3301 2022 assignment 1 --- include/Makefile | 3 +- include/usbcrypt.h | 31 ++ usr.bin/usbcrypt/Makefile | 8 + usr.bin/usbcrypt/usbcrypt.c | 169 ++++++++ usr.sbin/usbcryptd/Makefile | 9 + usr.sbin/usbcryptd/a1.h | 159 ++++++++ usr.sbin/usbcryptd/a1_regs.h | 58 +++ usr.sbin/usbcryptd/client.c | 201 ++++++++++ usr.sbin/usbcryptd/jobs.c | 159 ++++++++ usr.sbin/usbcryptd/log.c | 180 +++++++++ usr.sbin/usbcryptd/log.h | 53 +++ usr.sbin/usbcryptd/usb.c | 697 +++++++++++++++++++++++++++++++++ usr.sbin/usbcryptd/usbcryptd.c | 166 ++++++++ 13 files changed, 1892 insertions(+), 1 deletion(-) create mode 100644 include/usbcrypt.h create mode 100644 usr.bin/usbcrypt/Makefile create mode 100644 usr.bin/usbcrypt/usbcrypt.c create mode 100644 usr.sbin/usbcryptd/Makefile create mode 100644 usr.sbin/usbcryptd/a1.h create mode 100644 usr.sbin/usbcryptd/a1_regs.h create mode 100644 usr.sbin/usbcryptd/client.c create mode 100644 usr.sbin/usbcryptd/jobs.c create mode 100644 usr.sbin/usbcryptd/log.c create mode 100644 usr.sbin/usbcryptd/log.h create mode 100644 usr.sbin/usbcryptd/usb.c create mode 100644 usr.sbin/usbcryptd/usbcryptd.c diff --git a/include/Makefile b/include/Makefile index 21f2c777c..911f3f599 100644 --- a/include/Makefile +++ b/include/Makefile @@ -29,7 +29,8 @@ FILES= a.out.h ar.h asr.h assert.h \ tar.h tgmath.h tib.h time.h ttyent.h \ unistd.h utime.h utmp.h uuid.h \ vis.h \ - wchar.h wctype.h + wchar.h wctype.h \ + usbcrypt.h MFILES= frame.h LFILES= endian.h fcntl.h syslog.h termios.h stdarg.h stdint.h varargs.h diff --git a/include/usbcrypt.h b/include/usbcrypt.h new file mode 100644 index 000000000..1f9e4fcdb --- /dev/null +++ b/include/usbcrypt.h @@ -0,0 +1,31 @@ +#if !defined(_USBCRYPT_H) +#define _USBCRYPT_H + +#include +#include + +#define USBCRYPT_DEFAULT_SOCK "/var/run/usbcryptd.sock" + +enum usbcrypt_op { + USBCRYPT_ENCRYPT = 1, + USBCRYPT_DECRYPT = 2, +}; + +struct usbcrypt_request { + uint8_t ur_op; + uint16_t ur_len; /* big endian */ + /*char ur_data[];*/ +}; + +enum usbcrypt_status { + USBCRYPT_OK = 0, + USBCRYPT_ERROR = 1, +}; + +struct usbcrypt_response { + uint8_t urs_status; + uint16_t urs_len; /* big endian */ + /*char urs_data[];*/ +}; + +#endif diff --git a/usr.bin/usbcrypt/Makefile b/usr.bin/usbcrypt/Makefile new file mode 100644 index 000000000..dbd324525 --- /dev/null +++ b/usr.bin/usbcrypt/Makefile @@ -0,0 +1,8 @@ +PROG= usbcrypt +SRCS= usbcrypt.c +MAN= + +DEBUG=-g +WARNINGS=yes + +.include diff --git a/usr.bin/usbcrypt/usbcrypt.c b/usr.bin/usbcrypt/usbcrypt.c new file mode 100644 index 000000000..9e948069d --- /dev/null +++ b/usr.bin/usbcrypt/usbcrypt.c @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +static void +usage(void) +{ + extern char *__progname; + + fprintf(stderr, "usage: %s [-dq] [-o output] [-p sockpath] [PATH]\n", + __progname); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + FILE *input, *output; + int ch; + int quiet = 0; + int op = USBCRYPT_ENCRYPT; + const char *sockpath = USBCRYPT_DEFAULT_SOCK; + struct sockaddr_un un; + int sock; + struct usbcrypt_request req; + struct usbcrypt_response resp; + char *buf; + size_t buflen, pos; + ssize_t ndone; + struct iovec iov[2]; + + input = stdin; + output = stdout; + + while ((ch = getopt(argc, argv, "o:dqp:")) != -1) { + switch (ch) { + case 'd': + op = USBCRYPT_DECRYPT; + break; + case 'q': + ++quiet; + break; + case 'o': + output = fopen(optarg, "w"); + if (output == NULL) + err(1, "fopen('%s')", optarg); + break; + case 'p': + sockpath = optarg; + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc > 1) { + warnx("too many arguments"); + usage(); + } + + if (argc == 1) { + input = fopen(argv[0], "r"); + if (input == NULL) + err(1, "fopen('%s')", argv[0]); + } + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + err(1, "socket(AF_UNIX)"); + + bzero(&un, sizeof (un)); + un.sun_family = AF_UNIX; + if (strlcpy(un.sun_path, sockpath, sizeof (un.sun_path)) >= + sizeof(un.sun_path)) + errx(1, "socket path is too long"); + + if (connect(sock, (struct sockaddr *)&un, sizeof (un))) + err(1, "connect('%s')", sockpath); + + buflen = 1024; + pos = 0; + buf = malloc(buflen); + if (buf == NULL) + err(1, "malloc"); + + while (!feof(input) && pos < UINT16_MAX) { + ndone = fread(&buf[pos], 1, buflen - pos, input); + if (ferror(input)) + err(1, "fread"); + pos += ndone; + if (pos >= buflen) { + char *nbuf; + buflen *= 2; + nbuf = malloc(buflen); + if (nbuf == NULL) + err(1, "malloc"); + bcopy(buf, nbuf, buflen); + free(buf); + buf = nbuf; + } + } + fclose(input); + + if (pos >= UINT16_MAX) + errx(1, "input data too long (max 64k)"); + + bzero(&req, sizeof (req)); + req.ur_op = op; + req.ur_len = htobe16(pos); + + iov[0].iov_base = &req; + iov[0].iov_len = sizeof(req); + iov[1].iov_base = buf; + iov[1].iov_len = pos; + + ndone = writev(sock, iov, 2); + if (ndone < 0) + err(1, "writev"); + + bzero(&resp, sizeof(resp)); + ndone = read(sock, &resp, sizeof(resp)); + if (ndone < 0) + err(1, "reading response from usbcryptd"); + if (ndone < (ssize_t)sizeof(resp)) + errx(1, "short response from usbcryptd"); + + switch (resp.urs_status) { + case USBCRYPT_OK: + break; + case USBCRYPT_ERROR: + errx(1, "usbcryptd reports a hardware error"); + default: + errx(1, "unknown response type: %02x", resp.urs_status); + } + + pos = be16toh(resp.urs_len); + ndone = read(sock, buf, pos); + if (ndone < 0) + err(1, "reading response data from usbcryptd"); + if (ndone < (ssize_t)pos) + errx(1, "short data from usbcryptd"); + + close(sock); + + ndone = fwrite(buf, pos, 1, output); + if (ndone != 1) { + err(1, "writing response to %s", + (output == stdout) ? "stdout" : "file"); + } + fclose(output); + + return (0); +} diff --git a/usr.sbin/usbcryptd/Makefile b/usr.sbin/usbcryptd/Makefile new file mode 100644 index 000000000..f7b62a381 --- /dev/null +++ b/usr.sbin/usbcryptd/Makefile @@ -0,0 +1,9 @@ +PROG= usbcryptd +SRCS= usbcryptd.c usb.c jobs.c client.c log.c +MAN= +LDADD= -pthread + +DEBUG=-g +WARNINGS=yes + +.include diff --git a/usr.sbin/usbcryptd/a1.h b/usr.sbin/usbcryptd/a1.h new file mode 100644 index 000000000..13e2029d1 --- /dev/null +++ b/usr.sbin/usbcryptd/a1.h @@ -0,0 +1,159 @@ +/* + * Definitions of global structs for A1, plus declarations for functions in + * jobs.c/client.c. + */ + +#if !defined(_A1_H) +#define _A1_H + +#include +#include +#include +#include +#include +#include + +#include + +struct client { + /* Pointer back to owner app_state */ + struct app_state *c_as; + + /* Double-linked list (as_clients) */ + struct client *c_next; + struct client *c_prev; + + /* Worker thread for this client */ + pthread_t c_thread; + /* Socket connection */ + int c_fd; + struct sockpeercred c_pcred; + + /* Buffers for received data and data to be sent */ + struct usbcrypt_request c_req; + struct usbcrypt_response c_resp; + char *c_buf; + size_t c_len; + + /* The job we're waiting on (if any) */ + struct job *c_job; +}; + +/* Attempts to start a new client handling thread, returns errno */ +int client_start(struct app_state *as, int fd); + +enum job_state { + JOB_INIT = 0, + JOB_QUEUED, + JOB_SLOTTED, + JOB_RUNNING, + JOB_COMPLETED +}; + +/* Represents an encryption/decryption job. */ +struct job { + /* Mutex protects all fields of this struct. */ + pthread_mutex_t j_mtx; + + enum job_state j_state; + /* Fires when j_state changes (in job_advance()) */ + pthread_cond_t j_state_change; + + /* Operation type (encrypt/decrypt) */ + enum usbcrypt_op j_op; + /* Data to be encrypted/decrypted */ + char *j_data; + size_t j_len; + + /* queue pointers (if state >= JOB_QUEUED) */ + struct app_state *j_as; + struct job *j_next; + + /* Slot ID (if state >= JOB_SLOTTED) */ + uint j_slot; + + /* Final status (if state >= JOB_COMPLETED) */ + enum usbcrypt_status j_status; +}; + +struct job *job_alloc(enum usbcrypt_op op, const char *data, size_t len); + +void job_enqueue(struct app_state *as, struct job *j); +struct job *job_take_first(struct app_state *as); +void job_enslot(struct job *j, uint slot); +void job_running(struct job *j); +void job_complete(struct job *j, enum usbcrypt_status st); + +void job_wait(struct job *j, enum job_state want); + +void job_free(struct job *j); + +struct app_state { + /* Mutex protects all fields of this struct. */ + pthread_mutex_t as_mtx; + + /* Thread IDs for the USB bulk pipes worker and interrupt worker. */ + pthread_t as_usb_bulk_thread; + pthread_t as_usb_intr_thread; + + /* List of connected clients. */ + struct client *as_clients; + + /* Job queue */ + struct job *as_jobq_head; + struct job *as_jobq_tail; + + /* Jobs currently in slots executing */ + uint as_nslots; + struct job **as_slots; + uint as_next_slot; + + /* Last interrupt transfer */ + struct a1_interrupt as_last_intr; + + /* + * Broadcast when there is work for the usb_bulk_thr: + * - when new jobs are enqueued in job_enqueue() + * - when completions are available, detected by the intr_thread + */ + pthread_cond_t as_bulk_work; + + /* Basic USB device info and config descriptor. */ + uint as_cidx; + struct usb_device_info *as_di; + + struct usb_device_cdesc *as_cdesc; + /* Pointer into as_cdesc */ + const struct usb_config_descriptor *as_cd; + + /* ugen device name */ + char as_devname[PATH_MAX]; + + /* Full descriptor set */ + u_char *as_dbuf; + size_t as_dlen; + + /* Pointers into as_dbuf */ + const struct usb_interface_descriptor *as_ifd; + const struct usb_endpoint_descriptor *as_epd[8]; + const struct a1_class_descriptor *as_clsd; + + /* Endpoint ugen FDs */ + int as_ep_ctrl; + int as_ep_bulk_out; + int as_ep_bulk_in; + int as_ep_intr_in; + + /* Endpoint limits */ + size_t as_max_in; + size_t as_max_out; + size_t as_max_intr; +}; + +int usb_setup(struct app_state *as); +int usb_bulk_start(struct app_state *as); +int usb_intr_start(struct app_state *as); + +extern int usbcrypt_verbose; + +#endif diff --git a/usr.sbin/usbcryptd/a1_regs.h b/usr.sbin/usbcryptd/a1_regs.h new file mode 100644 index 000000000..cc201f75b --- /dev/null +++ b/usr.sbin/usbcryptd/a1_regs.h @@ -0,0 +1,58 @@ +/* + * Definitions of USB-level structures for the A1 device. + */ + +#if !defined(_A1_REGS_H) +#define _A1_REGS_H + +#include +#include +#include + +/* Vendor ID for all COMP3301 devices (pracs and assignments) */ +#define UDVENDOR_COMP3301 0x3301 + +/* Interface subclass for A1 */ +#define UDSUBCLASS_A1 0x31 + +/* the bDescriptorType for a1_class_descriptor */ +#define UDESC_A1_CLASS 0xF0 + +#define A1_MAX_SLOTS 32 + +struct a1_class_descriptor { + uByte bLength; + uByte bDescriptorType; /* UDESC_A1_CLASS */ + uByte bNumSlots; /* number of command slots */ +} __packed; + +enum a1_operations { + OP_ENCRYPT = 0x01, + OP_DECRYPT = 0x02 +}; + +struct a1_command { + uint8_t slot; /* slot index, from zero */ + uint8_t operation; /* OP_ENCRYPT, OP_DECRYPT, ... */ + uint16_t length; /* length of just the data field */ +} __packed; + +enum a1_status { + STATUS_OK = 1, + STATUS_ERROR = 2 +}; + +struct a1_completion { + uint8_t slot; + uint8_t status; /* STATUS_OK, STATUS_ERROR, ... */ + uint16_t length; +} __packed; + +struct a1_interrupt { + uint32_t progress25; + uint32_t progress50; + uint32_t progress75; + uint32_t completion; +} __packed; + +#endif diff --git a/usr.sbin/usbcryptd/client.c b/usr.sbin/usbcryptd/client.c new file mode 100644 index 000000000..2055167c4 --- /dev/null +++ b/usr.sbin/usbcryptd/client.c @@ -0,0 +1,201 @@ +/* + * client.c + * routines for handling client connection traffic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include "a1_regs.h" +#include "a1.h" +#include "log.h" + +#include + +static void +client_close(struct client *c) +{ + struct app_state *as = c->c_as; + + if (as->as_clients == c) + as->as_clients = c->c_next; + if (c->c_next != NULL) + c->c_next->c_prev = c->c_prev; + if (c->c_prev != NULL) + c->c_prev->c_next = c->c_next; + + close(c->c_fd); + + free(c->c_buf); + job_free(c->c_job); + free(c); +} + +static void * +client_thread(void *arg) +{ + struct client *c = arg; + struct app_state *as = c->c_as; + int fd = c->c_fd; + ssize_t nread, nwrote; + socklen_t len; + struct iovec vec[2]; + + len = sizeof(struct sockpeercred); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &c->c_pcred, &len)) { + client_close(c); + return (NULL); + } + + lwarnx("accepted fd %d from pid %u, uid %u, gid %u", fd, + c->c_pcred.pid, c->c_pcred.uid, c->c_pcred.gid); + + while (1) { + /* + * First read in the command header. + */ + nread = read(fd, &c->c_req, sizeof(c->c_req)); + if (nread < 0) { + lwarnx("client on fd %d error %d: %s", + fd, errno, strerror(errno)); + client_close(c); + return (NULL); + } else if (nread == 0) { + lwarnx("client %d closed", fd); + client_close(c); + return (NULL); + } else if (nread < (ssize_t)sizeof(c->c_req)) { + lwarnx("short read (%zd) on fd %d error %d: %s", + nread, fd, errno, strerror(errno)); + client_close(c); + return (NULL); + } + + /* + * Check it looks valid. + */ + switch (c->c_req.ur_op) { + case USBCRYPT_ENCRYPT: + case USBCRYPT_DECRYPT: + break; + default: + lwarnx("invalid op on fd %d", fd); + client_close(c); + return (NULL); + } + + /* Allocate our buffer. */ + c->c_len = be16toh(c->c_req.ur_len); + if (c->c_buf != NULL) + free(c->c_buf); + c->c_buf = malloc(c->c_len); + if (c->c_buf == NULL) + lerr(1, "malloc"); + + /* + * Read the actual data in. + */ + nread = read(fd, c->c_buf, c->c_len); + if (nread < 0) { + lwarnx("client on fd %d error %d: %s", + fd, errno, strerror(errno)); + client_close(c); + return (NULL); + } else if (nread < (ssize_t)c->c_len) { + lwarnx("short read (%zd) on fd %d error %d: %s", + nread, fd, errno, strerror(errno)); + client_close(c); + return (NULL); + } + + /* + * Now build the job and put it on the queue for the USB + * thread to handle. + */ + c->c_job = job_alloc(c->c_req.ur_op, c->c_buf, c->c_len); + if (c->c_job == NULL) { + lwarnx("job_alloc failed for fd %d", fd); + client_close(c); + return (NULL); + } + + job_enqueue(as, c->c_job); + + job_wait(c->c_job, JOB_COMPLETED); + + lwarnx("job for fd %d finished", fd); + + /* + * Prepare our response header and send it, along with the + * data. + */ + bzero(&c->c_resp, sizeof(c->c_resp)); + c->c_resp.urs_status = c->c_job->j_status; + c->c_resp.urs_len = htobe16(c->c_job->j_len); + + vec[0].iov_base = &c->c_resp; + vec[0].iov_len = sizeof(c->c_resp); + vec[1].iov_base = c->c_job->j_data; + vec[1].iov_len = c->c_job->j_len; + + len = vec[0].iov_len + vec[1].iov_len; + + nwrote = writev(fd, vec, 2); + + if (nwrote < 0) { + lwarnx("client on fd %d error %d: %s", + fd, errno, strerror(errno)); + client_close(c); + return (NULL); + } else if (nwrote < (ssize_t)len) { + lwarnx("short read (%zd) on fd %d error %d: %s", + nwrote, fd, errno, strerror(errno)); + client_close(c); + return (NULL); + } + + job_free(c->c_job); + c->c_job = NULL; + } +} + +int +client_start(struct app_state *as, int fd) +{ + struct client *c; + int rc; + + c = calloc(1, sizeof(*c)); + if (c == NULL) + return (errno); + + c->c_as = as; + c->c_fd = fd; + + rc = pthread_create(&c->c_thread, NULL, client_thread, c); + if (rc != 0) { + free(c); + return (rc); + } + + c->c_next = as->as_clients; + if (as->as_clients != NULL) + as->as_clients->c_prev = c; + as->as_clients = c; + + return (0); +} diff --git a/usr.sbin/usbcryptd/jobs.c b/usr.sbin/usbcryptd/jobs.c new file mode 100644 index 000000000..fab76d674 --- /dev/null +++ b/usr.sbin/usbcryptd/jobs.c @@ -0,0 +1,159 @@ +/* + * jobs.c + * routines related to the job queue + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "a1_regs.h" +#include "a1.h" +#include "log.h" + +#include + +struct job * +job_alloc(enum usbcrypt_op op, const char *data, size_t len) +{ + struct job *j; + int rc; + + j = calloc(1, sizeof(*j)); + if (j == NULL) + return (NULL); + + j->j_state = JOB_INIT; + j->j_op = op; + j->j_data = malloc(len); + if (j->j_data == NULL) { + free(j); + return (NULL); + } + bcopy(data, j->j_data, len); + j->j_len = len; + + if ((rc = pthread_mutex_init(&j->j_mtx, NULL))) { + lwarnx("pthread_mutex_init failed: %d (%s)", rc, + strerror(rc)); + free(j); + return (NULL); + } + + if ((rc = pthread_cond_init(&j->j_state_change, NULL))) { + lwarnx("pthread_cond_init failed: %d (%s)", rc, + strerror(rc)); + free(j); + return (NULL); + } + + return (j); +} + +void +job_enqueue(struct app_state *as, struct job *j) +{ + pthread_mutex_lock(&as->as_mtx); + pthread_mutex_lock(&j->j_mtx); + + assert(j->j_state == JOB_INIT); + j->j_as = as; + if (as->as_jobq_tail != NULL) + as->as_jobq_tail->j_next = j; + as->as_jobq_tail = j; + if (as->as_jobq_head == NULL) + as->as_jobq_head = j; + j->j_state = JOB_QUEUED; + pthread_cond_broadcast(&j->j_state_change); + pthread_cond_broadcast(&as->as_bulk_work); + + pthread_mutex_unlock(&j->j_mtx); + pthread_mutex_unlock(&as->as_mtx); +} + +struct job * +job_take_first(struct app_state *as) +{ + struct job *j; + + if (as->as_jobq_head == NULL) + return (NULL); + + j = as->as_jobq_head; + as->as_jobq_head = j->j_next; + j->j_next = NULL; + + if (as->as_jobq_tail == j) + as->as_jobq_tail = NULL; + + return (j); +} + +void +job_enslot(struct job *j, uint slot) +{ + assert(j->j_state == JOB_QUEUED); + j->j_slot = slot; + j->j_state = JOB_SLOTTED; + pthread_cond_broadcast(&j->j_state_change); +} + +void +job_running(struct job *j) +{ + assert(j->j_state == JOB_SLOTTED); + j->j_state = JOB_RUNNING; + pthread_cond_broadcast(&j->j_state_change); +} + +void +job_complete(struct job *j, enum usbcrypt_status st) +{ + assert(j->j_state == JOB_RUNNING || j->j_state == JOB_SLOTTED); + j->j_state = JOB_COMPLETED; + j->j_status = st; + pthread_cond_broadcast(&j->j_state_change); +} + +void +job_wait(struct job *j, enum job_state want) +{ + assert(pthread_mutex_lock(&j->j_mtx) == 0); + while (j->j_state < want) + pthread_cond_wait(&j->j_state_change, &j->j_mtx); + assert(pthread_mutex_unlock(&j->j_mtx) == 0); +} + +void +job_free(struct job *j) +{ + if (j == NULL) + return; + + pthread_mutex_lock(&j->j_mtx); + + assert(j->j_state == JOB_INIT || j->j_state == JOB_COMPLETED); + assert(j->j_next == NULL); + + pthread_mutex_unlock(&j->j_mtx); + + pthread_cond_destroy(&j->j_state_change); + pthread_mutex_destroy(&j->j_mtx); + + free(j->j_data); + + free(j); +} diff --git a/usr.sbin/usbcryptd/log.c b/usr.sbin/usbcryptd/log.c new file mode 100644 index 000000000..49130fb85 --- /dev/null +++ b/usr.sbin/usbcryptd/log.c @@ -0,0 +1,180 @@ + +/* + * Copyright (c) 2008 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" + +static const struct __logger conslogger = { + err, + errx, + errc, + warn, + warnx, + warnc, + warnx, /* info */ + warnx /* debug */ +}; + +__dead static void syslog_err(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +__dead static void syslog_errx(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +__dead static void syslog_errc(int, int, const char *, ...) + __attribute__((__format__ (printf, 3, 4))); +static void syslog_warn(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +static void syslog_warnx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +static void syslog_warnc(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +static void syslog_info(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +static void syslog_debug(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +static void syslog_vstrerror(int, int, const char *, va_list) + __attribute__((__format__ (printf, 3, 0))); + +static const struct __logger syslogger = { + syslog_err, + syslog_errx, + syslog_errc, + syslog_warn, + syslog_warnx, + syslog_warnc, + syslog_info, + syslog_debug +}; + +const struct __logger *__logger = &conslogger; + +void +logger_syslog(const char *progname) +{ + openlog(progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + tzset(); + + __logger = &syslogger; +} + +static void +syslog_vstrerror(int e, int priority, const char *fmt, va_list ap) +{ + char *s; + + if (vasprintf(&s, fmt, ap) == -1) { + syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror"); + exit(1); + } + + syslog(priority, "%s: %s", s, strerror(e)); + + free(s); +} + +static void +syslog_err(int ecode, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + syslog_vstrerror(errno, LOG_CRIT, fmt, ap); + va_end(ap); + + exit(ecode); +} + +static void +syslog_errx(int ecode, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_CRIT, fmt, ap); + va_end(ap); + + exit(ecode); +} + +static void +syslog_errc(int ecode, int error, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + syslog_vstrerror(error, LOG_CRIT, fmt, ap); + va_end(ap); + + exit(ecode); +} + +static void +syslog_warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + syslog_vstrerror(errno, LOG_ERR, fmt, ap); + va_end(ap); +} + +static void +syslog_warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_ERR, fmt, ap); + va_end(ap); +} + +static void +syslog_warnc(int error, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + syslog_vstrerror(error, LOG_ERR, fmt, ap); + va_end(ap); +} + +static void +syslog_info(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_INFO, fmt, ap); + va_end(ap); +} + +static void +syslog_debug(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_DEBUG, fmt, ap); + va_end(ap); +} diff --git a/usr.sbin/usbcryptd/log.h b/usr.sbin/usbcryptd/log.h new file mode 100644 index 000000000..74b207ff3 --- /dev/null +++ b/usr.sbin/usbcryptd/log.h @@ -0,0 +1,53 @@ + +/* + * Copyright (c) 2008 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LOG_H_ +#define _LOG_H_ + +struct __logger { + __dead void (*err)(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); + __dead void (*errx)(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); + __dead void (*errc)(int, int, const char *, ...) + __attribute__((__format__ (printf, 3, 4))); + void (*warn)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); + void (*warnx)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); + void (*warnc)(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); + void (*info)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); + void (*debug)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +}; + +extern const struct __logger *__logger; + +#define lerr(_e, _f...) __logger->err((_e), _f) +#define lerrx(_e, _f...) __logger->errx((_e), _f) +#define lerrc(_e, _f...) __logger->errc((_e), _f) +#define lwarn(_f...) __logger->warn(_f) +#define lwarnx(_f...) __logger->warnx(_f) +#define lwarnc(_f...) __logger->warnc(_f) +#define linfo(_f...) __logger->info(_f) +#define ldebug(_f...) __logger->debug(_f) + +void logger_syslog(const char *); + +#endif /* _LOG_H_ */ diff --git a/usr.sbin/usbcryptd/usb.c b/usr.sbin/usbcryptd/usb.c new file mode 100644 index 000000000..7e1604313 --- /dev/null +++ b/usr.sbin/usbcryptd/usb.c @@ -0,0 +1,697 @@ +/* + * usb.c + * contains routines related to actual USB traffic with the A1 device + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "a1_regs.h" +#include "a1.h" +#include "log.h" + +#include + +/* Opens ugen nodes for each endpoint for the A1 device. */ +static int +open_ugen(struct app_state *as) +{ + char path[PATH_MAX]; + const char *devname = NULL; + const struct usb_endpoint_descriptor *epd; + const struct usb_endpoint_descriptor *outd = NULL, *ind = NULL, + *intrd = NULL; + int fd; + uint i, ep; + int cfg; + uint xfertype, dir; + + for (i = 0; i < USB_MAX_DEVNAMES; ++i) { + if (strncmp("ugen", as->as_di->udi_devnames[i], 4) == 0) { + devname = as->as_di->udi_devnames[i]; + break; + } + } + if (devname == NULL) + return (ENOENT); + strlcpy(as->as_devname, devname, sizeof (as->as_devname)); + + /* + * Now find which endpoint addresses we want. We won't assume they're + * always EP #1, #2, #3, but instead check the descriptors. + */ + for (ep = 0; ep < (sizeof(as->as_epd) / sizeof(*as->as_epd)); ++ep) { + epd = as->as_epd[ep]; + if (epd == NULL) + continue; + + xfertype = UE_GET_XFERTYPE(epd->bmAttributes); + dir = UE_GET_DIR(epd->bEndpointAddress); + + if (xfertype == UE_BULK && dir == UE_DIR_IN) { + ind = epd; + + } else if (xfertype == UE_BULK && dir == UE_DIR_OUT) { + outd = epd; + + } else if (xfertype == UE_INTERRUPT && dir == UE_DIR_IN) { + intrd = epd; + } + } + + if (ind == NULL) { + lwarnx("usb %u/%u has no bulk in endpoint", as->as_cidx, + as->as_di->udi_addr); + return (EINVAL); + } + if (outd == NULL) { + lwarnx("usb %u/%u has no bulk out endpoint", as->as_cidx, + as->as_di->udi_addr); + return (EINVAL); + } + if (intrd == NULL) { + lwarnx("usb %u/%u has no intr in endpoint", as->as_cidx, + as->as_di->udi_addr); + return (EINVAL); + } + + snprintf(path, sizeof (path), "/dev/%s.%02d", devname, 0); + fd = open(path, O_RDWR); + if (fd == -1) + return (errno); + as->as_ep_ctrl = fd; + lwarnx("opened control ep: %s", path); + + cfg = 1; + if (ioctl(as->as_ep_ctrl, USB_SET_CONFIG, &cfg)) + return (errno); + lwarnx("changed to config 1"); + + ep = UE_GET_ADDR(ind->bEndpointAddress); + snprintf(path, sizeof (path), "/dev/%s.%02d", devname, ep); + fd = open(path, O_RDONLY); + if (fd == -1) + return (errno); + as->as_ep_bulk_in = fd; + as->as_max_in = UGETW(&ind->wMaxPacketSize); + lwarnx("opened bulk-in ep: %s", path); + + ep = UE_GET_ADDR(outd->bEndpointAddress); + snprintf(path, sizeof (path), "/dev/%s.%02d", devname, ep); + fd = open(path, O_WRONLY); + if (fd == -1) + return (errno); + as->as_ep_bulk_out = fd; + as->as_max_out = UGETW(&outd->wMaxPacketSize); + lwarnx("opened bulk-out ep: %s", path); + + ep = UE_GET_ADDR(intrd->bEndpointAddress); + snprintf(path, sizeof (path), "/dev/%s.%02d", devname, ep); + fd = open(path, O_RDONLY); + if (fd == -1) + return (errno); + as->as_ep_intr_in = fd; + lwarnx("opened intr-in ep: %s", path); + + return (0); +} + +/* + * Walks all USB devices on the system looking for the first COMP3301 A1 device + */ +static int +find_device(struct app_state *as) +{ + uint cidx, addr; + int fd = -1; + char path[PATH_MAX]; + struct usb_device_info *di = NULL; + struct usb_device_cdesc *cd = NULL; + struct usb_device_ddesc *dd = NULL; + struct usb_device_fdesc fde; + u_char *dbuf = NULL; + size_t dlen, len, off, i; + int rc; + struct usb_descriptor *ud; + struct usb_interface_descriptor *id, *iid; + struct usb_endpoint_descriptor *epd[8]; + size_t nepd = 1; + size_t maxepd = sizeof(as->as_epd) / sizeof(*as->as_epd); + struct a1_class_descriptor *clsd; + + epd[0] = NULL; + + di = malloc(sizeof(*di)); + cd = malloc(sizeof(*cd)); + dd = malloc(sizeof(*dd)); + if (di == NULL || cd == NULL || dd == NULL) { + rc = errno; + goto out; + } + + dlen = 256; + dbuf = malloc(dlen); + if (dbuf == NULL) { + rc = errno; + goto out; + } + + for (cidx = 0; cidx < 10; ++cidx) { + snprintf(path, sizeof(path), "/dev/usb%d", cidx); + fd = open(path, O_RDONLY); + if (fd == -1) { + if (errno != ENOENT && errno != ENXIO) + warn("open(%s)", path); + continue; + } + + for (addr = 1; addr < USB_MAX_DEVICES; ++addr) { + /* + * First, get the deviceinfo to check that it exists, + * and matches the vendor ID + */ + bzero(di, sizeof (*di)); + di->udi_addr = addr; + + if (ioctl(fd, USB_DEVICEINFO, di) == -1) { + if (errno != ENXIO) + warn("usb%u/%u devinfo", cidx, addr); + continue; + } + + if (di->udi_vendorNo != UDVENDOR_COMP3301) + continue; + + /* + * Next, get the device descriptor: we need to check + * the number of configurations + */ + bzero(dd, sizeof (*dd)); + dd->udd_bus = di->udi_bus; + dd->udd_addr = di->udi_addr; + + if (ioctl(fd, USB_DEVICE_GET_DDESC, dd) == -1) { + if (errno != ENXIO) + warn("usb%u/%u ddesc", cidx, addr); + continue; + } + + /* The 3301 device should only have one config */ + if (dd->udd_desc.bNumConfigurations != 1) + continue; + + /* + * To get the length of the full set of descriptors, + * we need to check wTotalLength in the configuration + * descriptor. So we have to fetch just the config + * descriptor itself first. + */ + bzero(cd, sizeof (*cd)); + cd->udc_bus = di->udi_bus; + cd->udc_addr = di->udi_addr; + cd->udc_config_index = 0; + + if (ioctl(fd, USB_DEVICE_GET_CDESC, cd) == -1) { + if (errno != ENXIO) + warn("usb%u/%u cdesc", cidx, addr); + continue; + } + + len = UGETW(&cd->udc_desc.wTotalLength); + + if (len > dlen) { + warn("usb%u/%u descriptors too long: %zu bytes", + cidx, addr, len); + continue; + } + + /* Now fetch the full set of descriptors */ + bzero(&fde, sizeof (fde)); + fde.udf_addr = di->udi_addr; + fde.udf_config_index = 1; + fde.udf_size = len; + fde.udf_data = dbuf; + + if (ioctl(fd, USB_DEVICE_GET_FDESC, &fde) == -1) { + if (errno != ENXIO) + warn("usb%u/%u fdesc", cidx, addr); + continue; + } + + /* + * Iterate over the full set of descriptors for this + * config. At each one, we have to use bLength to + * determine where the next descriptor is located. + */ + off = 0; + nepd = 1; + iid = NULL; + clsd = NULL; + while (off < fde.udf_size) { + ud = (struct usb_descriptor *)&dbuf[off]; + /* Prepare "off" for the next descriptor */ + off += ud->bLength; + + const uint dt = ud->bDescriptorType; + + if (dt == UDESC_INTERFACE) { + /* + * If we already found our class intf, + * then this is the next one and we + * should stop. + */ + if (iid != NULL) + break; + id = (struct usb_interface_descriptor *) + ud; + if (id->bInterfaceClass != + UDCLASS_VENDOR) + continue; + if (id->bInterfaceSubClass != + UDSUBCLASS_A1) + continue; + iid = id; + + } else if (dt == UDESC_A1_CLASS) { + /* + * Only care about first class descr + * directly following our interface. + */ + if (iid == NULL || clsd != NULL) + continue; + clsd = (struct a1_class_descriptor *)ud; + + } else if (dt == UDESC_ENDPOINT) { + /* Only care about EPs in our intf */ + if (iid == NULL) + continue; + if (nepd >= maxepd) + continue; + epd[nepd++] = + (struct usb_endpoint_descriptor *) + ud; + } + } + + /* + * If we didn't find our class interface or the class + * descriptor, this ain't our guy. + */ + if (iid == NULL || clsd == NULL) { + lwarnx("device %u/%u had wrong class", + cidx, addr); + continue; + } + + /* It should be a primary interface, not an alternate */ + if (iid->bAlternateSetting != 0) { + lwarnx("interface %u on %u/%u is an alt", + iid->bInterfaceNumber, cidx, addr); + continue; + } + + lwarnx("using usb device %u/%u (%s %s) " + "(vid %04x, pid %04x)", cidx, addr, di->udi_vendor, + di->udi_product, di->udi_vendorNo, + di->udi_productNo); + + close(fd); + + /* + * Copy all the info into the app_state for the other + * usb functions to use later. + */ + as->as_di = di; + as->as_cidx = cidx; + + as->as_cdesc = cd; + as->as_cd = &cd->udc_desc; + + as->as_dbuf = dbuf; + as->as_dlen = fde.udf_size; + + as->as_ifd = iid; + as->as_clsd = clsd; + + for (i = 0; i < nepd; ++i) + as->as_epd[i] = epd[i]; + + return (0); + } + + close(fd); + fd = -1; + } + + rc = ENOENT; + +out: + if (fd != -1) + close(fd); + free(di); + free(cd); + free(dd); + free(dbuf); + return (rc); +} + +int +usb_setup(struct app_state *as) +{ + int rc; + + if ((rc = find_device(as))) + return (rc); + + if ((rc = open_ugen(as))) + return (rc); + + as->as_nslots = as->as_clsd->bNumSlots; + assert(as->as_nslots < A1_MAX_SLOTS); + as->as_slots = calloc(as->as_nslots, sizeof(struct job *)); + if (as->as_slots == NULL) + return (errno); + + as->as_next_slot = 0; + + return (0); +} + +static int +usb_read_completion(struct app_state *as) +{ + u_char *buf = NULL; + size_t buflen; + struct a1_completion hdr; + ssize_t nread; + size_t pos, take; + struct job *j; + int timeo = 1000; + int shortok = 1; + + if (ioctl(as->as_ep_bulk_in, USB_SET_SHORT_XFER, &shortok)) + lerr(1, "set short xfer: bulk-in ep"); + if (ioctl(as->as_ep_bulk_in, USB_SET_TIMEOUT, &timeo)) + lerr(1, "set timeout: bulk-in ep"); + + /* + * Read the completion header, first. That'll tell us how much data + * to expect after it. + */ + lwarnx("reading completion"); + bzero(&hdr, sizeof (hdr)); + nread = read(as->as_ep_bulk_in, &hdr, sizeof(hdr)); + if (nread < 0) + lerr(1, "read(bulk in)"); + if (nread < (ssize_t)sizeof(hdr)) { + lwarnx("usb_read_completion: short header (%zd bytes)", nread); + return (EINTR); + } + + buflen = le16toh(hdr.length); + if (buflen > 0) { + lwarnx("got %zu data to read", buflen); + /* + * We've got actual data: allocate one big buffer for all of + * it and start doing additional bulk-in transfers. + */ + buf = malloc(buflen); + if (buf == NULL) + return (errno); + + pos = 0; + while (pos < buflen) { + take = buflen - pos; + if (take > as->as_max_in) + take = as->as_max_in; + nread = read(as->as_ep_bulk_in, &buf[pos], take); + if (nread < 0) + lerr(1, "read(bulk in)"); + if (nread == 0) { + /* + * Zero-length transfer: device is aborting + * this completion. + */ + free(buf); + lwarnx("usb_read_completion: short data " + "(%zd bytes)", nread + pos); + return (EINTR); + } + pos += nread; + } + } + + lwarnx("fully read completion for slot %u", hdr.slot); + + pthread_mutex_lock(&as->as_mtx); + j = as->as_slots[hdr.slot]; + assert(hdr.slot == j->j_slot); + + pthread_mutex_lock(&j->j_mtx); + + /* Copy data. */ + take = j->j_len; + if (take > buflen) + take = buflen; + bcopy(buf, j->j_data, take); + j->j_len = buflen; + + /* Mark the job as complete so client picks it up. */ + job_complete(j, + (hdr.status == STATUS_OK) ? USBCRYPT_OK : USBCRYPT_ERROR); + + /* NULL-out the slot entry. */ + as->as_slots[hdr.slot] = NULL; + + /* + * And clear our bit in the interrupt state to make sure we don't + * keep calling this over and over again. + */ + as->as_last_intr.completion &= ~(1 << hdr.slot); + + pthread_mutex_unlock(&j->j_mtx); + + pthread_mutex_unlock(&as->as_mtx); + + return (0); +} + +static int +usb_write_command(struct app_state *as, struct job *j) +{ + struct a1_command hdr; + ssize_t nwrite; + size_t pos, take; + int timeo = 1000; + + bzero(&hdr, sizeof(hdr)); + hdr.slot = j->j_slot; + switch (j->j_op) { + case USBCRYPT_ENCRYPT: + hdr.operation = OP_ENCRYPT; + break; + case USBCRYPT_DECRYPT: + hdr.operation = OP_DECRYPT; + break; + } + hdr.length = htole16(j->j_len); + + if (ioctl(as->as_ep_bulk_out, USB_SET_TIMEOUT, &timeo)) + lerr(1, "set timeout: bulk-out ep"); + + nwrite = write(as->as_ep_bulk_out, &hdr, sizeof(hdr)); + if (nwrite < 0 && errno == ETIMEDOUT) { + lwarnx("usb_write_command: timed out (probably NAK)"); + return (EINVAL); + } else if (nwrite < 0) { + lerr(1, "write(bulk out)"); + } else if (nwrite < (ssize_t)sizeof(hdr)) { + lwarnx("usb_write_command: short write (%zd bytes)", nwrite); + return (EINTR); + } + + pos = 0; + while (pos < j->j_len) { + take = j->j_len - pos; + if (take > as->as_max_out) + take = as->as_max_out; + + nwrite = write(as->as_ep_bulk_out, &j->j_data[pos], take); + if (nwrite < 0) + lerr(1, "write(bulk out)"); + if (nwrite < (ssize_t)take) { + lwarnx("usb_write_command: short write (%zd bytes)", + nwrite); + return (EINTR); + } + pos += nwrite; + } + + return (0); +} + +static void * +usb_bulk_thread(void *arg) +{ + struct app_state *as = arg; + uint n, i; + struct job *j; + int rc; + + pthread_mutex_lock(&as->as_mtx); + + while (1) { + /* + * Sleep on the as_bulk_work cond until either there are + * entries on the jobq, or completions waiting. + */ + while (as->as_jobq_head == NULL && + as->as_last_intr.completion == 0) { + pthread_cond_wait(&as->as_bulk_work, &as->as_mtx); + } + + /* Handle completions first */ + while (as->as_last_intr.completion != 0) { + /* Drop the lock to read the completion in */ + pthread_mutex_unlock(&as->as_mtx); + + (void) usb_read_completion(as); + /* + * usb_read_completion will clear the bit in last_intr + * once it's read the completion in + */ + + /* Take it back and check for any more */ + pthread_mutex_lock(&as->as_mtx); + } + + /* Then jobs */ + if (as->as_jobq_head != NULL) { + /* Search for an empty slot */ + for (n = 0; n < as->as_nslots; ++n) { + i = as->as_next_slot++; + if (i > as->as_nslots) + i = 0; + + if (as->as_slots[i] == NULL) + break; + } + + j = job_take_first(as); + if (j == NULL) + break; + + pthread_mutex_lock(&j->j_mtx); + + lwarnx("scheduling job %p in slot %u", j, i); + + as->as_slots[i] = j; + job_enslot(j, i); + + /* Drop the locks to send it */ + pthread_mutex_unlock(&j->j_mtx); + pthread_mutex_unlock(&as->as_mtx); + + rc = usb_write_command(as, j); + + pthread_mutex_lock(&as->as_mtx); + pthread_mutex_lock(&j->j_mtx); + + if (rc) { + lwarnc(rc, "usb_write_command"); + job_complete(j, USBCRYPT_ERROR); + as->as_slots[i] = NULL; + } + + pthread_mutex_unlock(&j->j_mtx); + } + /* + * If we couldn't schedule everything: sleep until + * we get some completions back. + */ + if (as->as_jobq_head != NULL) { + while (as->as_last_intr.completion == 0) { + pthread_cond_wait(&as->as_bulk_work, + &as->as_mtx); + } + } + } + + return (NULL); +} + +int +usb_bulk_start(struct app_state *as) +{ + int rc; + rc = pthread_create(&as->as_usb_bulk_thread, NULL, usb_bulk_thread, as); + return (rc); +} + +static void * +usb_intr_thread(void *arg) +{ + struct app_state *as = arg; + struct a1_interrupt intr; + ssize_t nread; + int timeo, shortok; + + /* + * This thread just sits in a loop reading interrupt transfers. If it + * detects a completion is waiting, it fires off as_bulk_work to wake + * up the bulk thread. + */ + while (1) { + shortok = 1; + if (ioctl(as->as_ep_intr_in, USB_SET_SHORT_XFER, &shortok)) + lerr(1, "set short xfer: intr-in ep"); + + timeo = 1000; + if (ioctl(as->as_ep_intr_in, USB_SET_TIMEOUT, &timeo)) + lerr(1, "set timeout: intr-in ep"); + + bzero(&intr, sizeof (intr)); + nread = read(as->as_ep_intr_in, &intr, sizeof(intr)); + if (nread < 0) + lerr(1, "read(intr in)"); + if (nread < (ssize_t)sizeof(intr)) + continue; + + /* Convert from device endian (LE) to host, if needed. */ + intr.completion = le32toh(intr.completion); + + pthread_mutex_lock(&as->as_mtx); + if (intr.completion != as->as_last_intr.completion) { + lwarnx("new completions: %08x => %08x", + as->as_last_intr.completion, + intr.completion); + pthread_cond_broadcast(&as->as_bulk_work); + } + as->as_last_intr = intr; + pthread_mutex_unlock(&as->as_mtx); + } + return (NULL); +} + +int +usb_intr_start(struct app_state *as) +{ + int rc; + rc = pthread_create(&as->as_usb_intr_thread, NULL, usb_intr_thread, as); + return (rc); +} diff --git a/usr.sbin/usbcryptd/usbcryptd.c b/usr.sbin/usbcryptd/usbcryptd.c new file mode 100644 index 000000000..33b22833b --- /dev/null +++ b/usr.sbin/usbcryptd/usbcryptd.c @@ -0,0 +1,166 @@ +/* + * usbcryptd.c + * daemon main() and cmdline support + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "a1_regs.h" +#include "a1.h" +#include "log.h" + +#include + +int usbcrypt_verbose = 0; + +static void +usage(void) +{ + fprintf(stderr, "Usage: usbcryptd [-fv] [-p sockpath]\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int ch; + int daemonise = 1; + const char *sockpath = USBCRYPT_DEFAULT_SOCK; + struct sockaddr_un un; + int lsock, sock; + struct app_state *as; + int rc; + + while ((ch = getopt(argc, argv, "fvp:")) != -1) { + switch (ch) { + case 'f': + daemonise = 0; + break; + case 'v': + ++usbcrypt_verbose; + daemonise = 0; + break; + case 'p': + sockpath = optarg; + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc > 0) { + warnx("too many arguments"); + usage(); + } + + /* + * Initialise the app_state struct: this is what we'll share with + * the other threads and pass around. + */ + as = calloc(1, sizeof(struct app_state)); + if (as == NULL) + err(1, "calloc"); + + /* Set some default values. */ + as->as_max_in = 256; + as->as_max_out = 256; + as->as_max_intr = 16; + as->as_nslots = 4; + + if ((rc = pthread_mutex_init(&as->as_mtx, NULL))) + errc(1, rc, "pthread_mutex_init"); + + if ((rc = pthread_cond_init(&as->as_bulk_work, NULL))) + errc(1, rc, "pthread_cond_init"); + + /* Take the lock now: we'll hold it until we're ready to go. */ + assert(pthread_mutex_lock(&as->as_mtx) == 0); + + /* + * Now establish our listening socket. unlink() it first so we delete + * any previous socket file left behind by our past selves + */ + (void) unlink(sockpath); + + lsock = socket(AF_UNIX, SOCK_STREAM, 0); + if (lsock < 0) + err(1, "socket(AF_UNIX)"); + + /* Set up our sockaddr_un */ + bzero(&un, sizeof (un)); + un.sun_family = AF_UNIX; + if (strlcpy(un.sun_path, sockpath, sizeof (un.sun_path)) >= + sizeof(un.sun_path)) + errx(1, "socket path is too long"); + + if (bind(lsock, (struct sockaddr *)&un, sizeof (un))) + err(1, "bind('%s')", sockpath); + if (listen(lsock, 16)) + err(1, "listen"); + + /* + * Set up the USB stack, including opening ugen nodes. This will return + * ENOENT if the device can't be found. + */ + if ((rc = usb_setup(as))) + errc(1, rc, "usb_setup"); + + /* Now we are become daemon, destroyer of worlds. */ + if (daemonise) { + if (daemon(0, 0)) + err(1, "daemon"); + + logger_syslog(getprogname()); + } + /* + * After daemonising we should stop using err(), printf() etc and use + * derr() instead from a1.h. These will check for whether we're + * daemonised (and whether we still have stdout/stderr or not!). + */ + + /* Start the threads which manage USB traffic. */ + if ((rc = usb_bulk_start(as))) + lerrc(1, rc, "usb_bulk_start"); + if ((rc = usb_intr_start(as))) + lerrc(1, rc, "usb_intr_start"); + + /* Release the as_mtx, let the threads fly free */ + assert(pthread_mutex_unlock(&as->as_mtx) == 0); + + /* + * Main thread will now sit here doing accept and spawning threads per + * client that connects. + */ + while (1) { + sock = accept(lsock, NULL, NULL); + if (sock < 0) + lerr(1, "accept"); + + rc = client_start(as, sock); + if (rc != 0) { + lwarnc(rc, "client_start failed"); + close(sock); + continue; + } + } + + return (0); +} -- 2.35.1