123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- /* $OpenBSD: ssh-pkcs11-helper.c,v 1.23 2020/03/06 18:26:21 markus Exp $ */
- /*
- * Copyright (c) 2010 Markus Friedl. All rights reserved.
- *
- * 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 "includes.h"
- #include <sys/types.h>
- #ifdef HAVE_SYS_TIME_H
- # include <sys/time.h>
- #endif
- #include "openbsd-compat/sys-queue.h"
- #include <stdlib.h>
- #include <errno.h>
- #ifdef HAVE_POLL_H
- #include <poll.h>
- #endif
- #include <stdarg.h>
- #include <string.h>
- #include <unistd.h>
- #include "xmalloc.h"
- #include "sshbuf.h"
- #include "log.h"
- #include "misc.h"
- #include "sshkey.h"
- #include "authfd.h"
- #include "ssh-pkcs11.h"
- #include "ssherr.h"
- #ifdef ENABLE_PKCS11
- #ifdef WITH_OPENSSL
- /* borrows code from sftp-server and ssh-agent */
- struct pkcs11_keyinfo {
- struct sshkey *key;
- char *providername, *label;
- TAILQ_ENTRY(pkcs11_keyinfo) next;
- };
- TAILQ_HEAD(, pkcs11_keyinfo) pkcs11_keylist;
- #define MAX_MSG_LENGTH 10240 /*XXX*/
- /* input and output queue */
- struct sshbuf *iqueue;
- struct sshbuf *oqueue;
- static void
- add_key(struct sshkey *k, char *name, char *label)
- {
- struct pkcs11_keyinfo *ki;
- ki = xcalloc(1, sizeof(*ki));
- ki->providername = xstrdup(name);
- ki->key = k;
- ki->label = xstrdup(label);
- TAILQ_INSERT_TAIL(&pkcs11_keylist, ki, next);
- }
- static void
- del_keys_by_name(char *name)
- {
- struct pkcs11_keyinfo *ki, *nxt;
- for (ki = TAILQ_FIRST(&pkcs11_keylist); ki; ki = nxt) {
- nxt = TAILQ_NEXT(ki, next);
- if (!strcmp(ki->providername, name)) {
- TAILQ_REMOVE(&pkcs11_keylist, ki, next);
- free(ki->providername);
- free(ki->label);
- sshkey_free(ki->key);
- free(ki);
- }
- }
- }
- /* lookup matching 'private' key */
- static struct sshkey *
- lookup_key(struct sshkey *k)
- {
- struct pkcs11_keyinfo *ki;
- TAILQ_FOREACH(ki, &pkcs11_keylist, next) {
- debug("check %p %s %s", ki, ki->providername, ki->label);
- if (sshkey_equal(k, ki->key))
- return (ki->key);
- }
- return (NULL);
- }
- static void
- send_msg(struct sshbuf *m)
- {
- int r;
- if ((r = sshbuf_put_stringb(oqueue, m)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
- }
- static void
- process_add(void)
- {
- char *name, *pin;
- struct sshkey **keys = NULL;
- int r, i, nkeys;
- u_char *blob;
- size_t blen;
- struct sshbuf *msg;
- char **labels = NULL;
- if ((msg = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
- if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 ||
- (r = sshbuf_get_cstring(iqueue, &pin, NULL)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
- if ((nkeys = pkcs11_add_provider(name, pin, &keys, &labels)) > 0) {
- if ((r = sshbuf_put_u8(msg,
- SSH2_AGENT_IDENTITIES_ANSWER)) != 0 ||
- (r = sshbuf_put_u32(msg, nkeys)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
- for (i = 0; i < nkeys; i++) {
- if ((r = sshkey_to_blob(keys[i], &blob, &blen)) != 0) {
- debug("%s: sshkey_to_blob: %s",
- __func__, ssh_err(r));
- continue;
- }
- if ((r = sshbuf_put_string(msg, blob, blen)) != 0 ||
- (r = sshbuf_put_cstring(msg, labels[i])) != 0)
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
- free(blob);
- add_key(keys[i], name, labels[i]);
- free(labels[i]);
- }
- } else {
- if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
- if ((r = sshbuf_put_u32(msg, -nkeys)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
- }
- free(labels);
- free(keys); /* keys themselves are transferred to pkcs11_keylist */
- free(pin);
- free(name);
- send_msg(msg);
- sshbuf_free(msg);
- }
- static void
- process_del(void)
- {
- char *name, *pin;
- struct sshbuf *msg;
- int r;
- if ((msg = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
- if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 ||
- (r = sshbuf_get_cstring(iqueue, &pin, NULL)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
- del_keys_by_name(name);
- if ((r = sshbuf_put_u8(msg, pkcs11_del_provider(name) == 0 ?
- SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
- free(pin);
- free(name);
- send_msg(msg);
- sshbuf_free(msg);
- }
- static void
- process_sign(void)
- {
- u_char *blob, *data, *signature = NULL;
- size_t blen, dlen, slen = 0;
- int r, ok = -1;
- struct sshkey *key, *found;
- struct sshbuf *msg;
- /* XXX support SHA2 signature flags */
- if ((r = sshbuf_get_string(iqueue, &blob, &blen)) != 0 ||
- (r = sshbuf_get_string(iqueue, &data, &dlen)) != 0 ||
- (r = sshbuf_get_u32(iqueue, NULL)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
- if ((r = sshkey_from_blob(blob, blen, &key)) != 0)
- error("%s: sshkey_from_blob: %s", __func__, ssh_err(r));
- else {
- if ((found = lookup_key(key)) != NULL) {
- #ifdef WITH_OPENSSL
- int ret;
- if (key->type == KEY_RSA) {
- slen = RSA_size(key->rsa);
- signature = xmalloc(slen);
- ret = RSA_private_encrypt(dlen, data, signature,
- found->rsa, RSA_PKCS1_PADDING);
- if (ret != -1) {
- slen = ret;
- ok = 0;
- }
- #ifdef OPENSSL_HAS_ECC
- } else if (key->type == KEY_ECDSA) {
- u_int xslen = ECDSA_size(key->ecdsa);
- signature = xmalloc(xslen);
- /* "The parameter type is ignored." */
- ret = ECDSA_sign(-1, data, dlen, signature,
- &xslen, found->ecdsa);
- if (ret != 0)
- ok = 0;
- else
- error("%s: ECDSA_sign"
- " returns %d", __func__, ret);
- slen = xslen;
- #endif /* OPENSSL_HAS_ECC */
- } else
- error("%s: don't know how to sign with key "
- "type %d", __func__, (int)key->type);
- #endif /* WITH_OPENSSL */
- }
- sshkey_free(key);
- }
- if ((msg = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
- if (ok == 0) {
- if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE)) != 0 ||
- (r = sshbuf_put_string(msg, signature, slen)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
- } else {
- if ((r = sshbuf_put_u8(msg, SSH2_AGENT_FAILURE)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
- }
- free(data);
- free(blob);
- free(signature);
- send_msg(msg);
- sshbuf_free(msg);
- }
- static void
- process(void)
- {
- u_int msg_len;
- u_int buf_len;
- u_int consumed;
- u_char type;
- const u_char *cp;
- int r;
- buf_len = sshbuf_len(iqueue);
- if (buf_len < 5)
- return; /* Incomplete message. */
- cp = sshbuf_ptr(iqueue);
- msg_len = get_u32(cp);
- if (msg_len > MAX_MSG_LENGTH) {
- error("bad message len %d", msg_len);
- cleanup_exit(11);
- }
- if (buf_len < msg_len + 4)
- return;
- if ((r = sshbuf_consume(iqueue, 4)) != 0 ||
- (r = sshbuf_get_u8(iqueue, &type)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
- buf_len -= 4;
- switch (type) {
- case SSH_AGENTC_ADD_SMARTCARD_KEY:
- debug("process_add");
- process_add();
- break;
- case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
- debug("process_del");
- process_del();
- break;
- case SSH2_AGENTC_SIGN_REQUEST:
- debug("process_sign");
- process_sign();
- break;
- default:
- error("Unknown message %d", type);
- break;
- }
- /* discard the remaining bytes from the current packet */
- if (buf_len < sshbuf_len(iqueue)) {
- error("iqueue grew unexpectedly");
- cleanup_exit(255);
- }
- consumed = buf_len - sshbuf_len(iqueue);
- if (msg_len < consumed) {
- error("msg_len %d < consumed %d", msg_len, consumed);
- cleanup_exit(255);
- }
- if (msg_len > consumed) {
- if ((r = sshbuf_consume(iqueue, msg_len - consumed)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
- }
- }
- void
- cleanup_exit(int i)
- {
- /* XXX */
- _exit(i);
- }
- int
- main(int argc, char **argv)
- {
- int r, ch, in, out, log_stderr = 0;
- ssize_t len;
- SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
- LogLevel log_level = SYSLOG_LEVEL_ERROR;
- char buf[4*4096];
- extern char *__progname;
- struct pollfd pfd[2];
- __progname = ssh_get_progname(argv[0]);
- seed_rng();
- TAILQ_INIT(&pkcs11_keylist);
- log_init(__progname, log_level, log_facility, log_stderr);
- while ((ch = getopt(argc, argv, "v")) != -1) {
- switch (ch) {
- case 'v':
- log_stderr = 1;
- if (log_level == SYSLOG_LEVEL_ERROR)
- log_level = SYSLOG_LEVEL_DEBUG1;
- else if (log_level < SYSLOG_LEVEL_DEBUG3)
- log_level++;
- break;
- default:
- fprintf(stderr, "usage: %s [-v]\n", __progname);
- exit(1);
- }
- }
- log_init(__progname, log_level, log_facility, log_stderr);
- pkcs11_init(0);
- in = STDIN_FILENO;
- out = STDOUT_FILENO;
- if ((iqueue = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
- if ((oqueue = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
- while (1) {
- memset(pfd, 0, sizeof(pfd));
- pfd[0].fd = in;
- pfd[1].fd = out;
- /*
- * Ensure that we can read a full buffer and handle
- * the worst-case length packet it can generate,
- * otherwise apply backpressure by stopping reads.
- */
- if ((r = sshbuf_check_reserve(iqueue, sizeof(buf))) == 0 &&
- (r = sshbuf_check_reserve(oqueue, MAX_MSG_LENGTH)) == 0)
- pfd[0].events = POLLIN;
- else if (r != SSH_ERR_NO_BUFFER_SPACE)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
- if (sshbuf_len(oqueue) > 0)
- pfd[1].events = POLLOUT;
- if ((r = poll(pfd, 2, -1 /* INFTIM */)) <= 0) {
- if (r == 0 || errno == EINTR)
- continue;
- fatal("poll: %s", strerror(errno));
- }
- /* copy stdin to iqueue */
- if ((pfd[0].revents & (POLLIN|POLLERR)) != 0) {
- len = read(in, buf, sizeof buf);
- if (len == 0) {
- debug("read eof");
- cleanup_exit(0);
- } else if (len < 0) {
- error("read: %s", strerror(errno));
- cleanup_exit(1);
- } else if ((r = sshbuf_put(iqueue, buf, len)) != 0) {
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
- }
- }
- /* send oqueue to stdout */
- if ((pfd[1].revents & (POLLOUT|POLLHUP)) != 0) {
- len = write(out, sshbuf_ptr(oqueue),
- sshbuf_len(oqueue));
- if (len < 0) {
- error("write: %s", strerror(errno));
- cleanup_exit(1);
- } else if ((r = sshbuf_consume(oqueue, len)) != 0) {
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
- }
- }
- /*
- * Process requests from client if we can fit the results
- * into the output buffer, otherwise stop processing input
- * and let the output queue drain.
- */
- if ((r = sshbuf_check_reserve(oqueue, MAX_MSG_LENGTH)) == 0)
- process();
- else if (r != SSH_ERR_NO_BUFFER_SPACE)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
- }
- }
- #else /* WITH_OPENSSL */
- void
- cleanup_exit(int i)
- {
- _exit(i);
- }
- int
- main(int argc, char **argv)
- {
- fprintf(stderr, "PKCS#11 code is not enabled\n");
- return 1;
- }
- #endif /* WITH_OPENSSL */
- #else /* ENABLE_PKCS11 */
- int
- main(int argc, char **argv)
- {
- extern char *__progname;
- __progname = ssh_get_progname(argv[0]);
- log_init(__progname, SYSLOG_LEVEL_ERROR, SYSLOG_FACILITY_AUTH, 0);
- fatal("PKCS#11 support disabled at compile time");
- }
- #endif /* ENABLE_PKCS11 */
|