Annotation of embedaddon/strongswan/src/libstrongswan/plugins/agent/agent_private_key.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (C) 2013-2019 Tobias Brunner
        !             3:  * Copyright (C) 2008-2009 Martin Willi
        !             4:  * HSR Hochschule fuer Technik Rapperswil
        !             5:  *
        !             6:  * This program is free software; you can redistribute it and/or modify it
        !             7:  * under the terms of the GNU General Public License as published by the
        !             8:  * Free Software Foundation; either version 2 of the License, or (at your
        !             9:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
        !            10:  *
        !            11:  * This program is distributed in the hope that it will be useful, but
        !            12:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
        !            13:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        !            14:  * for more details.
        !            15:  */
        !            16: 
        !            17: #include "agent_private_key.h"
        !            18: 
        !            19: #include <stdlib.h>
        !            20: #include <unistd.h>
        !            21: #include <sys/types.h>
        !            22: #include <sys/socket.h>
        !            23: #include <sys/un.h>
        !            24: #include <arpa/inet.h>
        !            25: #include <errno.h>
        !            26: 
        !            27: #include <library.h>
        !            28: #include <utils/chunk.h>
        !            29: #include <utils/debug.h>
        !            30: 
        !            31: #ifndef UNIX_PATH_MAX
        !            32: #define UNIX_PATH_MAX 108
        !            33: #endif /* UNIX_PATH_MAX */
        !            34: 
        !            35: typedef struct private_agent_private_key_t private_agent_private_key_t;
        !            36: typedef enum agent_msg_type_t agent_msg_type_t;
        !            37: 
        !            38: /**
        !            39:  * Private data of a agent_private_key_t object.
        !            40:  */
        !            41: struct private_agent_private_key_t {
        !            42:        /**
        !            43:         * Public interface for this signer.
        !            44:         */
        !            45:        agent_private_key_t public;
        !            46: 
        !            47:        /**
        !            48:         * Path to the UNIX socket
        !            49:         */
        !            50:        char *path;
        !            51: 
        !            52:        /**
        !            53:         * public key encoded in SSH format
        !            54:         */
        !            55:        chunk_t key;
        !            56: 
        !            57:        /**
        !            58:         * public key
        !            59:         */
        !            60:        public_key_t *pubkey;
        !            61: 
        !            62:        /**
        !            63:         * keysize in bytes
        !            64:         */
        !            65:        size_t key_size;
        !            66: 
        !            67:        /**
        !            68:         * reference count
        !            69:         */
        !            70:        refcount_t ref;
        !            71: };
        !            72: 
        !            73: /**
        !            74:  * Message types for ssh-agent protocol
        !            75:  */
        !            76: enum agent_msg_type_t {
        !            77:        SSH_AGENT_FAILURE = 5,
        !            78:        SSH_AGENT_SUCCESS =     6,
        !            79:        SSH_AGENT_ID_REQUEST = 11,
        !            80:        SSH_AGENT_ID_RESPONSE = 12,
        !            81:        SSH_AGENT_SIGN_REQUEST = 13,
        !            82:        SSH_AGENT_SIGN_RESPONSE = 14,
        !            83: };
        !            84: 
        !            85: /**
        !            86:  * Flags for signatures
        !            87:  */
        !            88: enum agent_signature_flags_t {
        !            89:        SSH_AGENT_FLAG_SHA2_256 = 2,
        !            90:        SSH_AGENT_FLAG_SHA2_512 = 4,
        !            91: };
        !            92: 
        !            93: /**
        !            94:  * read a byte from a blob
        !            95:  */
        !            96: static u_char read_byte(chunk_t *blob)
        !            97: {
        !            98:        u_char val;
        !            99: 
        !           100:        if (blob->len < sizeof(u_char))
        !           101:        {
        !           102:                return 0;
        !           103:        }
        !           104:        val = *(blob->ptr);
        !           105:        *blob = chunk_skip(*blob, sizeof(u_char));
        !           106:        return val;
        !           107: }
        !           108: 
        !           109: /**
        !           110:  * read a uint32_t from a blob
        !           111:  */
        !           112: static uint32_t read_uint32(chunk_t *blob)
        !           113: {
        !           114:        uint32_t val;
        !           115: 
        !           116:        if (blob->len < sizeof(uint32_t))
        !           117:        {
        !           118:                return 0;
        !           119:        }
        !           120:        val = ntohl(*(uint32_t*)blob->ptr);
        !           121:        *blob = chunk_skip(*blob, sizeof(uint32_t));
        !           122:        return val;
        !           123: }
        !           124: 
        !           125: /**
        !           126:  * read a ssh-agent "string" length/value from a blob
        !           127:  */
        !           128: static chunk_t read_string(chunk_t *blob)
        !           129: {
        !           130:        int len;
        !           131:        chunk_t str;
        !           132: 
        !           133:        len = read_uint32(blob);
        !           134:        if (len > blob->len)
        !           135:        {
        !           136:                return chunk_empty;
        !           137:        }
        !           138:        str = chunk_create(blob->ptr, len);
        !           139:        *blob = chunk_skip(*blob, + len);
        !           140:        return str;
        !           141: }
        !           142: 
        !           143: /**
        !           144:  * open socket connection to the ssh-agent
        !           145:  */
        !           146: static int open_connection(char *path)
        !           147: {
        !           148:        struct sockaddr_un addr;
        !           149:        int s;
        !           150: 
        !           151:        s = socket(AF_UNIX, SOCK_STREAM, 0);
        !           152:        if (s == -1)
        !           153:        {
        !           154:                DBG1(DBG_LIB, "opening ssh-agent socket %s failed: %s:", path,
        !           155:                         strerror(errno));
        !           156:                return -1;
        !           157:        }
        !           158: 
        !           159:        addr.sun_family = AF_UNIX;
        !           160:        addr.sun_path[UNIX_PATH_MAX - 1] = '\0';
        !           161:        strncpy(addr.sun_path, path, UNIX_PATH_MAX - 1);
        !           162: 
        !           163:        if (connect(s, (struct sockaddr*)&addr, SUN_LEN(&addr)) != 0)
        !           164:        {
        !           165:                DBG1(DBG_LIB, "connecting to ssh-agent socket failed: %s",
        !           166:                         strerror(errno));
        !           167:                close(s);
        !           168:                return -1;
        !           169:        }
        !           170:        return s;
        !           171: }
        !           172: 
        !           173: /**
        !           174:  * Get the first usable key from the agent
        !           175:  */
        !           176: static bool read_key(private_agent_private_key_t *this, public_key_t *pubkey)
        !           177: {
        !           178:        int socket, len;
        !           179:        char buf[2048];
        !           180:        chunk_t blob, key;
        !           181:        bool success = FALSE;
        !           182: 
        !           183:        socket = open_connection(this->path);
        !           184:        if (socket < 0)
        !           185:        {
        !           186:                return FALSE;
        !           187:        }
        !           188: 
        !           189:        len = htonl(1);
        !           190:        buf[0] = SSH_AGENT_ID_REQUEST;
        !           191:        if (write(socket, &len, sizeof(len)) != sizeof(len) ||
        !           192:                write(socket, &buf, 1) != 1)
        !           193:        {
        !           194:                DBG1(DBG_LIB, "writing to ssh-agent failed");
        !           195:                goto done;
        !           196:        }
        !           197: 
        !           198:        blob = chunk_create(buf, sizeof(buf));
        !           199:        blob.len = read(socket, blob.ptr, blob.len);
        !           200: 
        !           201:        if (blob.len < sizeof(uint32_t) + sizeof(u_char) ||
        !           202:                read_uint32(&blob) != blob.len ||
        !           203:                read_byte(&blob) != SSH_AGENT_ID_RESPONSE)
        !           204:        {
        !           205:                DBG1(DBG_LIB, "received invalid ssh-agent identity response");
        !           206:                goto done;
        !           207:        }
        !           208:        read_uint32(&blob);
        !           209: 
        !           210:        while (blob.len)
        !           211:        {
        !           212:                key = read_string(&blob);
        !           213:                if (!key.len)
        !           214:                {
        !           215:                        break;
        !           216:                }
        !           217:                this->pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
        !           218:                                                                                  BUILD_BLOB_SSHKEY, key, BUILD_END);
        !           219:                if (!this->pubkey)
        !           220:                {
        !           221:                        continue;
        !           222:                }
        !           223:                if (pubkey && !private_key_belongs_to(&this->public.key, pubkey))
        !           224:                {
        !           225:                        this->pubkey->destroy(this->pubkey);
        !           226:                        this->pubkey = NULL;
        !           227:                        continue;
        !           228:                }
        !           229:                this->key = chunk_clone(key);
        !           230:                success = TRUE;
        !           231:                break;
        !           232:        }
        !           233: done:
        !           234:        close(socket);
        !           235:        return success;
        !           236: }
        !           237: 
        !           238: static bool scheme_supported(private_agent_private_key_t *this,
        !           239:                                                         signature_scheme_t scheme, uint32_t *flags,
        !           240:                                                         char **prefix)
        !           241: {
        !           242:        switch (this->pubkey->get_type(this->pubkey))
        !           243:        {
        !           244:                case KEY_RSA:
        !           245:                        switch (scheme)
        !           246:                        {
        !           247:                                case SIGN_RSA_EMSA_PKCS1_SHA1:
        !           248:                                        *prefix = "ssh-rsa";
        !           249:                                        return TRUE;
        !           250:                                case SIGN_RSA_EMSA_PKCS1_SHA2_256:
        !           251:                                        *flags |= SSH_AGENT_FLAG_SHA2_256;
        !           252:                                        *prefix = "rsa-sha2-256";
        !           253:                                        return TRUE;
        !           254:                                case SIGN_RSA_EMSA_PKCS1_SHA2_512:
        !           255:                                        *flags |= SSH_AGENT_FLAG_SHA2_512;
        !           256:                                        *prefix = "rsa-sha2-512";
        !           257:                                        return TRUE;
        !           258:                                default:
        !           259:                                        break;
        !           260:                        }
        !           261:                        return FALSE;
        !           262:                case KEY_ED25519:
        !           263:                        *prefix = "ssh-ed25519";
        !           264:                        return scheme == SIGN_ED25519;
        !           265:                case KEY_ED448:
        !           266:                        *prefix = "ssh-ed448";
        !           267:                        return scheme == SIGN_ED448;
        !           268:                case KEY_ECDSA:
        !           269:                        return scheme == SIGN_ECDSA_256 ||
        !           270:                                   scheme == SIGN_ECDSA_384 ||
        !           271:                                   scheme == SIGN_ECDSA_521;
        !           272:                default:
        !           273:                        return FALSE;
        !           274:        }
        !           275: }
        !           276: 
        !           277: METHOD(private_key_t, sign, bool,
        !           278:        private_agent_private_key_t *this, signature_scheme_t scheme, void *params,
        !           279:        chunk_t data, chunk_t *signature)
        !           280: {
        !           281:        key_type_t type;
        !           282:        uint32_t len, flags = 0;
        !           283:        char buf[2048], *prefix = NULL;
        !           284:        chunk_t blob;
        !           285:        int socket;
        !           286:        bool success = FALSE;
        !           287: 
        !           288:        if (!scheme_supported(this, scheme, &flags, &prefix))
        !           289:        {
        !           290:                DBG1(DBG_LIB, "signature scheme %N not supported by ssh-agent",
        !           291:                         signature_scheme_names, scheme);
        !           292:                return FALSE;
        !           293:        }
        !           294: 
        !           295:        socket = open_connection(this->path);
        !           296:        if (socket < 0)
        !           297:        {
        !           298:                return FALSE;
        !           299:        }
        !           300: 
        !           301:        len = htonl(1 + sizeof(uint32_t) * 3 + this->key.len + data.len);
        !           302:        buf[0] = SSH_AGENT_SIGN_REQUEST;
        !           303:        if (write(socket, &len, sizeof(len)) != sizeof(len) ||
        !           304:                write(socket, &buf, 1) != 1)
        !           305:        {
        !           306:                DBG1(DBG_LIB, "writing to ssh-agent failed");
        !           307:                goto done;
        !           308:        }
        !           309: 
        !           310:        len = htonl(this->key.len);
        !           311:        if (write(socket, &len, sizeof(len)) != sizeof(len) ||
        !           312:                write(socket, this->key.ptr, this->key.len) != this->key.len)
        !           313:        {
        !           314:                DBG1(DBG_LIB, "writing to ssh-agent failed");
        !           315:                goto done;
        !           316:        }
        !           317: 
        !           318:        len = htonl(data.len);
        !           319:        if (write(socket, &len, sizeof(len)) != sizeof(len) ||
        !           320:                write(socket, data.ptr, data.len) != data.len)
        !           321:        {
        !           322:                DBG1(DBG_LIB, "writing to ssh-agent failed");
        !           323:                goto done;
        !           324:        }
        !           325: 
        !           326:        flags = htonl(flags);
        !           327:        if (write(socket, &flags, sizeof(flags)) != sizeof(flags))
        !           328:        {
        !           329:                DBG1(DBG_LIB, "writing to ssh-agent failed");
        !           330:                goto done;
        !           331:        }
        !           332: 
        !           333:        blob = chunk_create(buf, sizeof(buf));
        !           334:        blob.len = read(socket, blob.ptr, blob.len);
        !           335:        if (blob.len < sizeof(uint32_t) + sizeof(u_char) ||
        !           336:                read_uint32(&blob) != blob.len ||
        !           337:                read_byte(&blob) != SSH_AGENT_SIGN_RESPONSE)
        !           338:        {
        !           339:                DBG1(DBG_LIB, "received invalid ssh-agent signature response");
        !           340:                goto done;
        !           341:        }
        !           342:        /* parse length */
        !           343:        blob = read_string(&blob);
        !           344:        /* verify type */
        !           345:        if (prefix && !chunk_equals(read_string(&blob), chunk_from_str(prefix)))
        !           346:        {
        !           347:                DBG1(DBG_LIB, "ssh-agent didn't return requested %s signature", prefix);
        !           348:                goto done;
        !           349:        }
        !           350:        type = this->pubkey->get_type(this->pubkey);
        !           351:        if (type == KEY_RSA || type == KEY_ED25519 || type == KEY_ED448)
        !           352:        {       /* for RSA/EdDSA, the signature has no special encoding */
        !           353:                blob = read_string(&blob);
        !           354:                if (blob.len)
        !           355:                {
        !           356:                        *signature = chunk_clone(blob);
        !           357:                        success = TRUE;
        !           358:                }
        !           359:        }
        !           360:        else
        !           361:        {       /* parse ECDSA signatures */
        !           362:                blob = read_string(&blob);
        !           363:                if (blob.len)
        !           364:                {
        !           365:                        chunk_t r, s;
        !           366: 
        !           367:                        r = read_string(&blob);
        !           368:                        s = read_string(&blob);
        !           369:                        if (r.len && s.len)
        !           370:                        {
        !           371:                                *signature = chunk_cat("cc", r, s);
        !           372:                                success = TRUE;
        !           373:                        }
        !           374:                }
        !           375:        }
        !           376:        if (!success)
        !           377:        {
        !           378:                DBG1(DBG_LIB, "received invalid ssh-agent signature response");
        !           379:        }
        !           380: 
        !           381: done:
        !           382:        close(socket);
        !           383:        return success;
        !           384: }
        !           385: 
        !           386: METHOD(private_key_t, get_type, key_type_t,
        !           387:        private_agent_private_key_t *this)
        !           388: {
        !           389:        return this->pubkey->get_type(this->pubkey);
        !           390: }
        !           391: 
        !           392: METHOD(private_key_t, decrypt, bool,
        !           393:        private_agent_private_key_t *this, encryption_scheme_t scheme,
        !           394:        chunk_t crypto, chunk_t *plain)
        !           395: {
        !           396:        DBG1(DBG_LIB, "private key decryption not supported by ssh-agent");
        !           397:        return FALSE;
        !           398: }
        !           399: 
        !           400: METHOD(private_key_t, get_keysize, int,
        !           401:        private_agent_private_key_t *this)
        !           402: {
        !           403:        return this->pubkey->get_keysize(this->pubkey);
        !           404: }
        !           405: 
        !           406: /**
        !           407:  * Private data for RSA scheme enumerator
        !           408:  */
        !           409: typedef struct {
        !           410:        enumerator_t public;
        !           411:        int index;
        !           412:        bool reverse;
        !           413: } scheme_enumerator_t;
        !           414: 
        !           415: static signature_params_t rsa_schemes[] = {
        !           416:        { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_256 },
        !           417:        { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_512 },
        !           418: };
        !           419: 
        !           420: METHOD(enumerator_t, enumerate_rsa_scheme, bool,
        !           421:        scheme_enumerator_t *this, va_list args)
        !           422: {
        !           423:        signature_params_t **params;
        !           424: 
        !           425:        VA_ARGS_VGET(args, params);
        !           426: 
        !           427:        if ((this->reverse && --this->index >= 0) ||
        !           428:           (!this->reverse && ++this->index < countof(rsa_schemes)))
        !           429:        {
        !           430:                *params = &rsa_schemes[this->index];
        !           431:                return TRUE;
        !           432:        }
        !           433:        return FALSE;
        !           434: }
        !           435: 
        !           436: /**
        !           437:  * Create an enumerator for the supported RSA signature schemes
        !           438:  */
        !           439: static enumerator_t *create_rsa_enumerator(private_agent_private_key_t *this)
        !           440: {
        !           441:        scheme_enumerator_t *enumerator;
        !           442: 
        !           443:        INIT(enumerator,
        !           444:                .public = {
        !           445:                        .enumerate = enumerator_enumerate_default,
        !           446:                        .venumerate = _enumerate_rsa_scheme,
        !           447:                        .destroy = (void*)free,
        !           448:                },
        !           449:                .index = -1,
        !           450:                .reverse = FALSE,
        !           451:        );
        !           452:        /* propose SHA-512 first for larger keys */
        !           453:        if (get_keysize(this) > 3072)
        !           454:        {
        !           455:                enumerator->index = countof(rsa_schemes);
        !           456:                enumerator->reverse = TRUE;
        !           457:        }
        !           458:        return &enumerator->public;
        !           459: }
        !           460: 
        !           461: METHOD(private_key_t, supported_signature_schemes, enumerator_t*,
        !           462:        private_agent_private_key_t *this)
        !           463: {
        !           464:        key_type_t type = get_type(this);
        !           465: 
        !           466:        switch (type)
        !           467:        {
        !           468:                case KEY_RSA:
        !           469:                        return create_rsa_enumerator(this);
        !           470:                case KEY_ED25519:
        !           471:                case KEY_ED448:
        !           472:                case KEY_ECDSA:
        !           473:                        return signature_schemes_for_key(type, get_keysize(this));
        !           474:                default:
        !           475:                        break;
        !           476:        }
        !           477:        return enumerator_create_empty();
        !           478: }
        !           479: 
        !           480: METHOD(private_key_t, get_public_key, public_key_t*,
        !           481:        private_agent_private_key_t *this)
        !           482: {
        !           483:        return this->pubkey->get_ref(this->pubkey);
        !           484: }
        !           485: 
        !           486: METHOD(private_key_t, get_encoding, bool,
        !           487:        private_agent_private_key_t *this, cred_encoding_type_t type,
        !           488:        chunk_t *encoding)
        !           489: {
        !           490:        return FALSE;
        !           491: }
        !           492: 
        !           493: METHOD(private_key_t, get_fingerprint, bool,
        !           494:        private_agent_private_key_t *this, cred_encoding_type_t type, chunk_t *fp)
        !           495: {
        !           496:        return this->pubkey->get_fingerprint(this->pubkey, type, fp);
        !           497: }
        !           498: 
        !           499: METHOD(private_key_t, get_ref, private_key_t*,
        !           500:        private_agent_private_key_t *this)
        !           501: {
        !           502:        ref_get(&this->ref);
        !           503:        return &this->public.key;
        !           504: }
        !           505: 
        !           506: METHOD(private_key_t, destroy, void,
        !           507:        private_agent_private_key_t *this)
        !           508: {
        !           509:        if (ref_put(&this->ref))
        !           510:        {
        !           511:                chunk_free(&this->key);
        !           512:                DESTROY_IF(this->pubkey);
        !           513:                free(this->path);
        !           514:                free(this);
        !           515:        }
        !           516: }
        !           517: 
        !           518: /**
        !           519:  * See header.
        !           520:  */
        !           521: agent_private_key_t *agent_private_key_open(key_type_t type, va_list args)
        !           522: {
        !           523:        private_agent_private_key_t *this;
        !           524:        public_key_t *pubkey = NULL;
        !           525:        char *path = NULL;
        !           526: 
        !           527:        while (TRUE)
        !           528:        {
        !           529:                switch (va_arg(args, builder_part_t))
        !           530:                {
        !           531:                        case BUILD_AGENT_SOCKET:
        !           532:                                path = va_arg(args, char*);
        !           533:                                continue;
        !           534:                        case BUILD_PUBLIC_KEY:
        !           535:                                pubkey = va_arg(args, public_key_t*);
        !           536:                                continue;
        !           537:                        case BUILD_END:
        !           538:                                break;
        !           539:                        default:
        !           540:                                return NULL;
        !           541:                }
        !           542:                break;
        !           543:        }
        !           544:        if (!path)
        !           545:        {
        !           546:                return NULL;
        !           547:        }
        !           548: 
        !           549:        INIT(this,
        !           550:                .public = {
        !           551:                        .key = {
        !           552:                                .get_type = _get_type,
        !           553:                                .supported_signature_schemes = _supported_signature_schemes,
        !           554:                                .sign = _sign,
        !           555:                                .decrypt = _decrypt,
        !           556:                                .get_keysize = _get_keysize,
        !           557:                                .get_public_key = _get_public_key,
        !           558:                                .belongs_to = private_key_belongs_to,
        !           559:                                .equals = private_key_equals,
        !           560:                                .get_fingerprint = _get_fingerprint,
        !           561:                                .has_fingerprint = private_key_has_fingerprint,
        !           562:                                .get_encoding = _get_encoding,
        !           563:                                .get_ref = _get_ref,
        !           564:                                .destroy = _destroy,
        !           565:                        },
        !           566:                },
        !           567:                .path = strdup(path),
        !           568:                .ref = 1,
        !           569:        );
        !           570: 
        !           571:        if (!read_key(this, pubkey))
        !           572:        {
        !           573:                destroy(this);
        !           574:                return NULL;
        !           575:        }
        !           576:        return &this->public;
        !           577: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>