Annotation of src/usr.bin/ssh/ssh-pkcs11-helper.c, Revision 1.1
1.1 ! markus 1: /*
! 2: * Copyright (c) 2010 Markus Friedl. All rights reserved.
! 3: *
! 4: * Permission to use, copy, modify, and distribute this software for any
! 5: * purpose with or without fee is hereby granted, provided that the above
! 6: * copyright notice and this permission notice appear in all copies.
! 7: *
! 8: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 9: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 10: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 11: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 12: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 13: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 14: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 15: */
! 16:
! 17: #include <sys/queue.h>
! 18: #include <sys/types.h>
! 19: #include <sys/time.h>
! 20:
! 21: #include <stdarg.h>
! 22: #include <string.h>
! 23: #include <unistd.h>
! 24: #include <errno.h>
! 25:
! 26: #include "xmalloc.h"
! 27: #include "buffer.h"
! 28: #include "log.h"
! 29: #include "misc.h"
! 30: #include "key.h"
! 31: #include "authfd.h"
! 32: #include "ssh-pkcs11.h"
! 33:
! 34: /* borrows code from sftp-server and ssh-agent */
! 35:
! 36: struct pkcs11_keyinfo {
! 37: Key *key;
! 38: char *providername;
! 39: TAILQ_ENTRY(pkcs11_keyinfo) next;
! 40: };
! 41:
! 42: TAILQ_HEAD(, pkcs11_keyinfo) pkcs11_keylist;
! 43:
! 44: #define MAX_MSG_LENGTH 10240 /*XXX*/
! 45:
! 46: /* helper */
! 47: #define get_int() buffer_get_int(&iqueue);
! 48: #define get_string(lenp) buffer_get_string(&iqueue, lenp);
! 49:
! 50: /* input and output queue */
! 51: Buffer iqueue;
! 52: Buffer oqueue;
! 53:
! 54: static void
! 55: add_key(Key *k, char *name)
! 56: {
! 57: struct pkcs11_keyinfo *ki;
! 58:
! 59: ki = xcalloc(1, sizeof(*ki));
! 60: ki->providername = xstrdup(name);
! 61: ki->key = k;
! 62: TAILQ_INSERT_TAIL(&pkcs11_keylist, ki, next);
! 63: }
! 64:
! 65: static void
! 66: del_keys_by_name(char *name)
! 67: {
! 68: struct pkcs11_keyinfo *ki, *nxt;
! 69:
! 70: for (ki = TAILQ_FIRST(&pkcs11_keylist); ki; ki = nxt) {
! 71: nxt = TAILQ_NEXT(ki, next);
! 72: if (!strcmp(ki->providername, name)) {
! 73: TAILQ_REMOVE(&pkcs11_keylist, ki, next);
! 74: xfree(ki->providername);
! 75: key_free(ki->key);
! 76: free(ki);
! 77: }
! 78: }
! 79: }
! 80:
! 81: /* lookup matching 'private' key */
! 82: static Key *
! 83: lookup_key(Key *k)
! 84: {
! 85: struct pkcs11_keyinfo *ki;
! 86:
! 87: TAILQ_FOREACH(ki, &pkcs11_keylist, next) {
! 88: debug("check %p %s", ki, ki->providername);
! 89: if (key_equal(k, ki->key))
! 90: return (ki->key);
! 91: }
! 92: return (NULL);
! 93: }
! 94:
! 95: static void
! 96: send_msg(Buffer *m)
! 97: {
! 98: int mlen = buffer_len(m);
! 99:
! 100: buffer_put_int(&oqueue, mlen);
! 101: buffer_append(&oqueue, buffer_ptr(m), mlen);
! 102: buffer_consume(m, mlen);
! 103: }
! 104:
! 105: static void
! 106: process_add(void)
! 107: {
! 108: char *name, *pin;
! 109: Key **keys;
! 110: int i, nkeys;
! 111: u_char *blob;
! 112: u_int blen;
! 113: Buffer msg;
! 114:
! 115: buffer_init(&msg);
! 116: name = get_string(NULL);
! 117: pin = get_string(NULL);
! 118: if ((nkeys = pkcs11_add_provider(name, pin, &keys)) > 0) {
! 119: buffer_put_char(&msg, SSH2_AGENT_IDENTITIES_ANSWER);
! 120: buffer_put_int(&msg, nkeys);
! 121: for (i = 0; i < nkeys; i++) {
! 122: key_to_blob(keys[i], &blob, &blen);
! 123: buffer_put_string(&msg, blob, blen);
! 124: buffer_put_cstring(&msg, name);
! 125: xfree(blob);
! 126: add_key(keys[i], name);
! 127: }
! 128: xfree(keys);
! 129: } else {
! 130: buffer_put_char(&msg, SSH_AGENT_FAILURE);
! 131: }
! 132: xfree(pin);
! 133: xfree(name);
! 134: send_msg(&msg);
! 135: buffer_free(&msg);
! 136: }
! 137:
! 138: static void
! 139: process_del(void)
! 140: {
! 141: char *name, *pin;
! 142: Buffer msg;
! 143:
! 144: buffer_init(&msg);
! 145: name = get_string(NULL);
! 146: pin = get_string(NULL);
! 147: del_keys_by_name(name);
! 148: if (pkcs11_del_provider(name) == 0)
! 149: buffer_put_char(&msg, SSH_AGENT_SUCCESS);
! 150: else
! 151: buffer_put_char(&msg, SSH_AGENT_FAILURE);
! 152: xfree(pin);
! 153: xfree(name);
! 154: send_msg(&msg);
! 155: buffer_free(&msg);
! 156: }
! 157:
! 158: static void
! 159: process_sign(void)
! 160: {
! 161: u_char *blob, *data, *signature = NULL;
! 162: u_int blen, dlen, slen = 0;
! 163: int ok = -1, flags, ret;
! 164: Key *key, *found;
! 165: Buffer msg;
! 166:
! 167: blob = get_string(&blen);
! 168: data = get_string(&dlen);
! 169: flags = get_int(); /* XXX ignore */
! 170:
! 171: if ((key = key_from_blob(blob, blen)) != NULL) {
! 172: if ((found = lookup_key(key)) != NULL) {
! 173: slen = RSA_size(key->rsa);
! 174: signature = xmalloc(slen);
! 175: if ((ret = RSA_private_encrypt(dlen, data, signature,
! 176: found->rsa, RSA_PKCS1_PADDING)) != -1) {
! 177: slen = ret;
! 178: ok = 0;
! 179: }
! 180: }
! 181: key_free(key);
! 182: }
! 183: buffer_init(&msg);
! 184: if (ok == 0) {
! 185: buffer_put_char(&msg, SSH2_AGENT_SIGN_RESPONSE);
! 186: buffer_put_string(&msg, signature, slen);
! 187: } else {
! 188: buffer_put_char(&msg, SSH_AGENT_FAILURE);
! 189: }
! 190: xfree(data);
! 191: xfree(blob);
! 192: if (signature != NULL)
! 193: xfree(signature);
! 194: send_msg(&msg);
! 195: buffer_free(&msg);
! 196: }
! 197:
! 198: static void
! 199: process(void)
! 200: {
! 201: u_int msg_len;
! 202: u_int buf_len;
! 203: u_int consumed;
! 204: u_int type;
! 205: u_char *cp;
! 206:
! 207: buf_len = buffer_len(&iqueue);
! 208: if (buf_len < 5)
! 209: return; /* Incomplete message. */
! 210: cp = buffer_ptr(&iqueue);
! 211: msg_len = get_u32(cp);
! 212: if (msg_len > MAX_MSG_LENGTH) {
! 213: error("bad message len %d", msg_len);
! 214: cleanup_exit(11);
! 215: }
! 216: if (buf_len < msg_len + 4)
! 217: return;
! 218: buffer_consume(&iqueue, 4);
! 219: buf_len -= 4;
! 220: type = buffer_get_char(&iqueue);
! 221: switch (type) {
! 222: case SSH_AGENTC_ADD_SMARTCARD_KEY:
! 223: debug("process_add");
! 224: process_add();
! 225: break;
! 226: case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
! 227: debug("process_del");
! 228: process_del();
! 229: break;
! 230: case SSH2_AGENTC_SIGN_REQUEST:
! 231: debug("process_sign");
! 232: process_sign();
! 233: break;
! 234: default:
! 235: error("Unknown message %d", type);
! 236: break;
! 237: }
! 238: /* discard the remaining bytes from the current packet */
! 239: if (buf_len < buffer_len(&iqueue)) {
! 240: error("iqueue grew unexpectedly");
! 241: cleanup_exit(255);
! 242: }
! 243: consumed = buf_len - buffer_len(&iqueue);
! 244: if (msg_len < consumed) {
! 245: error("msg_len %d < consumed %d", msg_len, consumed);
! 246: cleanup_exit(255);
! 247: }
! 248: if (msg_len > consumed)
! 249: buffer_consume(&iqueue, msg_len - consumed);
! 250: }
! 251:
! 252: void
! 253: cleanup_exit(int i)
! 254: {
! 255: /* XXX */
! 256: _exit(i);
! 257: }
! 258:
! 259: int
! 260: main(int argc, char **argv)
! 261: {
! 262: fd_set *rset, *wset;
! 263: int in, out, max, log_stderr = 0;
! 264: ssize_t len, olen, set_size;
! 265: SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
! 266: LogLevel log_level = SYSLOG_LEVEL_ERROR;
! 267: char buf[4*4096];
! 268:
! 269: TAILQ_INIT(&pkcs11_keylist);
! 270: pkcs11_init(0);
! 271:
! 272: extern char *optarg;
! 273: extern char *__progname;
! 274:
! 275: log_init(__progname, log_level, log_facility, log_stderr);
! 276:
! 277: in = STDIN_FILENO;
! 278: out = STDOUT_FILENO;
! 279:
! 280: max = 0;
! 281: if (in > max)
! 282: max = in;
! 283: if (out > max)
! 284: max = out;
! 285:
! 286: buffer_init(&iqueue);
! 287: buffer_init(&oqueue);
! 288:
! 289: set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
! 290: rset = (fd_set *)xmalloc(set_size);
! 291: wset = (fd_set *)xmalloc(set_size);
! 292:
! 293: for (;;) {
! 294: memset(rset, 0, set_size);
! 295: memset(wset, 0, set_size);
! 296:
! 297: /*
! 298: * Ensure that we can read a full buffer and handle
! 299: * the worst-case length packet it can generate,
! 300: * otherwise apply backpressure by stopping reads.
! 301: */
! 302: if (buffer_check_alloc(&iqueue, sizeof(buf)) &&
! 303: buffer_check_alloc(&oqueue, MAX_MSG_LENGTH))
! 304: FD_SET(in, rset);
! 305:
! 306: olen = buffer_len(&oqueue);
! 307: if (olen > 0)
! 308: FD_SET(out, wset);
! 309:
! 310: if (select(max+1, rset, wset, NULL, NULL) < 0) {
! 311: if (errno == EINTR)
! 312: continue;
! 313: error("select: %s", strerror(errno));
! 314: cleanup_exit(2);
! 315: }
! 316:
! 317: /* copy stdin to iqueue */
! 318: if (FD_ISSET(in, rset)) {
! 319: len = read(in, buf, sizeof buf);
! 320: if (len == 0) {
! 321: debug("read eof");
! 322: cleanup_exit(0);
! 323: } else if (len < 0) {
! 324: error("read: %s", strerror(errno));
! 325: cleanup_exit(1);
! 326: } else {
! 327: buffer_append(&iqueue, buf, len);
! 328: }
! 329: }
! 330: /* send oqueue to stdout */
! 331: if (FD_ISSET(out, wset)) {
! 332: len = write(out, buffer_ptr(&oqueue), olen);
! 333: if (len < 0) {
! 334: error("write: %s", strerror(errno));
! 335: cleanup_exit(1);
! 336: } else {
! 337: buffer_consume(&oqueue, len);
! 338: }
! 339: }
! 340:
! 341: /*
! 342: * Process requests from client if we can fit the results
! 343: * into the output buffer, otherwise stop processing input
! 344: * and let the output queue drain.
! 345: */
! 346: if (buffer_check_alloc(&oqueue, MAX_MSG_LENGTH))
! 347: process();
! 348: }
! 349: }