[BACK]Return to ssh-pkcs11-helper.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

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: }