Annotation of embedaddon/strongswan/src/scepclient/scep.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (C) 2012 Tobias Brunner
        !             3:  * Copyright (C) 2005 Jan Hutter, 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 <string.h>
        !            18: #include <stdlib.h>
        !            19: 
        !            20: #include <library.h>
        !            21: #include <utils/debug.h>
        !            22: #include <asn1/asn1.h>
        !            23: #include <asn1/asn1_parser.h>
        !            24: #include <asn1/oid.h>
        !            25: #include <crypto/rngs/rng.h>
        !            26: #include <crypto/hashers/hasher.h>
        !            27: 
        !            28: #include "scep.h"
        !            29: 
        !            30: static const char *pkiStatus_values[] = { "0", "2", "3" };
        !            31: 
        !            32: static const char *pkiStatus_names[] = {
        !            33:        "SUCCESS",
        !            34:        "FAILURE",
        !            35:        "PENDING",
        !            36:        "UNKNOWN"
        !            37: };
        !            38: 
        !            39: static const char *msgType_values[] = { "3", "19", "20", "21", "22" };
        !            40: 
        !            41: static const char *msgType_names[] = {
        !            42:        "CertRep",
        !            43:        "PKCSReq",
        !            44:        "GetCertInitial",
        !            45:        "GetCert",
        !            46:        "GetCRL",
        !            47:        "Unknown"
        !            48: };
        !            49: 
        !            50: static const char *failInfo_reasons[] = {
        !            51:        "badAlg - unrecognized or unsupported algorithm identifier",
        !            52:        "badMessageCheck - integrity check failed",
        !            53:        "badRequest - transaction not permitted or supported",
        !            54:        "badTime - Message time field was not sufficiently close to the system time",
        !            55:        "badCertId - No certificate could be identified matching the provided criteria"
        !            56: };
        !            57: 
        !            58: const scep_attributes_t empty_scep_attributes = {
        !            59:        SCEP_Unknown_MSG   , /* msgType */
        !            60:        SCEP_UNKNOWN       , /* pkiStatus */
        !            61:        SCEP_unknown_REASON, /* failInfo */
        !            62:        { NULL, 0 }        , /* transID */
        !            63:        { NULL, 0 }        , /* senderNonce */
        !            64:        { NULL, 0 }        , /* recipientNonce */
        !            65: };
        !            66: 
        !            67: /**
        !            68:  * Extract X.501 attributes
        !            69:  */
        !            70: void extract_attributes(pkcs7_t *pkcs7, enumerator_t *enumerator,
        !            71:                                                scep_attributes_t *attrs)
        !            72: {
        !            73:        chunk_t attr;
        !            74: 
        !            75:        if (pkcs7->get_attribute(pkcs7, OID_PKI_MESSAGE_TYPE, enumerator, &attr))
        !            76:        {
        !            77:                scep_msg_t m;
        !            78: 
        !            79:                for (m = SCEP_CertRep_MSG; m < SCEP_Unknown_MSG; m++)
        !            80:                {
        !            81:                        if (strncmp(msgType_values[m], attr.ptr, attr.len) == 0)
        !            82:                        {
        !            83:                                attrs->msgType = m;
        !            84:                        }
        !            85:                }
        !            86:                DBG2(DBG_APP, "messageType:  %s", msgType_names[attrs->msgType]);
        !            87:                free(attr.ptr);
        !            88:        }
        !            89:        if (pkcs7->get_attribute(pkcs7, OID_PKI_STATUS, enumerator, &attr))
        !            90:        {
        !            91:                pkiStatus_t s;
        !            92: 
        !            93:                for (s = SCEP_SUCCESS; s < SCEP_UNKNOWN; s++)
        !            94:                {
        !            95:                        if (strncmp(pkiStatus_values[s], attr.ptr, attr.len) == 0)
        !            96:                        {
        !            97:                                attrs->pkiStatus = s;
        !            98:                        }
        !            99:                }
        !           100:                DBG2(DBG_APP, "pkiStatus:    %s", pkiStatus_names[attrs->pkiStatus]);
        !           101:                free(attr.ptr);
        !           102:        }
        !           103:        if (pkcs7->get_attribute(pkcs7, OID_PKI_FAIL_INFO, enumerator, &attr))
        !           104:        {
        !           105:                if (attr.len == 1 && *attr.ptr >= '0' && *attr.ptr <= '4')
        !           106:                {
        !           107:                        attrs->failInfo = (failInfo_t)(*attr.ptr - '0');
        !           108:                }
        !           109:                if (attrs->failInfo != SCEP_unknown_REASON)
        !           110:                {
        !           111:                        DBG1(DBG_APP, "failInfo:     %s", failInfo_reasons[attrs->failInfo]);
        !           112:                }
        !           113:                free(attr.ptr);
        !           114:        }
        !           115: 
        !           116:        pkcs7->get_attribute(pkcs7, OID_PKI_SENDER_NONCE, enumerator,
        !           117:                                                 &attrs->senderNonce);
        !           118:        pkcs7->get_attribute(pkcs7, OID_PKI_RECIPIENT_NONCE, enumerator,
        !           119:                                                 &attrs->recipientNonce);
        !           120:        pkcs7->get_attribute(pkcs7, OID_PKI_TRANS_ID, enumerator,
        !           121:                                                 &attrs->transID);
        !           122: }
        !           123: 
        !           124: /**
        !           125:  * Generates a unique fingerprint of the pkcs10 request
        !           126:  * by computing an MD5 hash over it
        !           127:  */
        !           128: chunk_t scep_generate_pkcs10_fingerprint(chunk_t pkcs10)
        !           129: {
        !           130:        chunk_t digest = chunk_alloca(HASH_SIZE_MD5);
        !           131:        hasher_t *hasher;
        !           132: 
        !           133:        hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
        !           134:        if (!hasher || !hasher->get_hash(hasher, pkcs10, digest.ptr))
        !           135:        {
        !           136:                DESTROY_IF(hasher);
        !           137:                return chunk_empty;
        !           138:        }
        !           139:        hasher->destroy(hasher);
        !           140: 
        !           141:        return chunk_to_hex(digest, NULL, FALSE);
        !           142: }
        !           143: 
        !           144: /**
        !           145:  * Generate a transaction id as the MD5 hash of an public key
        !           146:  * the transaction id is also used as a unique serial number
        !           147:  */
        !           148: void scep_generate_transaction_id(public_key_t *key, chunk_t *transID,
        !           149:                                                                  chunk_t *serialNumber)
        !           150: {
        !           151:        chunk_t digest = chunk_alloca(HASH_SIZE_MD5);
        !           152:        chunk_t keyEncoding = chunk_empty, keyInfo;
        !           153:        hasher_t *hasher;
        !           154:        int zeros = 0, msb_set = 0;
        !           155: 
        !           156:        key->get_encoding(key, PUBKEY_ASN1_DER, &keyEncoding);
        !           157: 
        !           158:        keyInfo = asn1_wrap(ASN1_SEQUENCE, "mm",
        !           159:                                                asn1_algorithmIdentifier(OID_RSA_ENCRYPTION),
        !           160:                                                asn1_bitstring("m", keyEncoding));
        !           161: 
        !           162:        hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
        !           163:        if (!hasher || !hasher->get_hash(hasher, keyInfo, digest.ptr))
        !           164:        {
        !           165:                memset(digest.ptr, 0, digest.len);
        !           166:        }
        !           167:        DESTROY_IF(hasher);
        !           168:        free(keyInfo.ptr);
        !           169: 
        !           170:        /* the serialNumber should be valid ASN1 integer content:
        !           171:         * remove leading zeros, add one if MSB is set (two's complement) */
        !           172:        while (zeros < digest.len)
        !           173:        {
        !           174:                if (digest.ptr[zeros])
        !           175:                {
        !           176:                        if (digest.ptr[zeros] & 0x80)
        !           177:                        {
        !           178:                                msb_set = 1;
        !           179:                        }
        !           180:                        break;
        !           181:                }
        !           182:                zeros++;
        !           183:        }
        !           184:        *serialNumber = chunk_alloc(digest.len - zeros + msb_set);
        !           185:        if (msb_set)
        !           186:        {
        !           187:                serialNumber->ptr[0] = 0x00;
        !           188:        }
        !           189:        memcpy(serialNumber->ptr + msb_set, digest.ptr + zeros,
        !           190:                   digest.len - zeros);
        !           191: 
        !           192:        /* the transaction id is the serial number in hex format */
        !           193:        *transID = chunk_to_hex(digest, NULL, TRUE);
        !           194: }
        !           195: 
        !           196: /**
        !           197:  * Builds a pkcs7 enveloped and signed scep request
        !           198:  */
        !           199: chunk_t scep_build_request(chunk_t data, chunk_t transID, scep_msg_t msg,
        !           200:                                        certificate_t *enc_cert, encryption_algorithm_t enc_alg,
        !           201:                                        size_t key_size, certificate_t *signer_cert,
        !           202:                                        hash_algorithm_t digest_alg, private_key_t *private_key)
        !           203: {
        !           204:        chunk_t request;
        !           205:        container_t *container;
        !           206:        char nonce[16];
        !           207:        rng_t *rng;
        !           208:        chunk_t senderNonce, msgType;
        !           209: 
        !           210:        /* generate senderNonce */
        !           211:        rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
        !           212:        if (!rng || !rng->get_bytes(rng, sizeof(nonce), nonce))
        !           213:        {
        !           214:                DESTROY_IF(rng);
        !           215:                return chunk_empty;
        !           216:        }
        !           217:        rng->destroy(rng);
        !           218: 
        !           219:        /* encrypt data in enveloped-data PKCS#7 */
        !           220:        container = lib->creds->create(lib->creds,
        !           221:                                        CRED_CONTAINER, CONTAINER_PKCS7_ENVELOPED_DATA,
        !           222:                                        BUILD_BLOB, data,
        !           223:                                        BUILD_CERT, enc_cert,
        !           224:                                        BUILD_ENCRYPTION_ALG, enc_alg,
        !           225:                                        BUILD_KEY_SIZE, (int)key_size,
        !           226:                                        BUILD_END);
        !           227:        if (!container)
        !           228:        {
        !           229:                return chunk_empty;
        !           230:        }
        !           231:        if (!container->get_encoding(container, &request))
        !           232:        {
        !           233:                container->destroy(container);
        !           234:                return chunk_empty;
        !           235:        }
        !           236:        container->destroy(container);
        !           237: 
        !           238:        /* sign enveloped-data in a signed-data PKCS#7 */
        !           239:        senderNonce = asn1_wrap(ASN1_OCTET_STRING, "c", chunk_from_thing(nonce));
        !           240:        transID = asn1_wrap(ASN1_PRINTABLESTRING, "c", transID);
        !           241:        msgType = asn1_wrap(ASN1_PRINTABLESTRING, "c",
        !           242:                                                chunk_create((char*)msgType_values[msg],
        !           243:                                                                         strlen(msgType_values[msg])));
        !           244: 
        !           245:        container = lib->creds->create(lib->creds,
        !           246:                                        CRED_CONTAINER, CONTAINER_PKCS7_SIGNED_DATA,
        !           247:                                        BUILD_BLOB, request,
        !           248:                                        BUILD_SIGNING_CERT, signer_cert,
        !           249:                                        BUILD_SIGNING_KEY, private_key,
        !           250:                                        BUILD_DIGEST_ALG, digest_alg,
        !           251:                                        BUILD_PKCS7_ATTRIBUTE, OID_PKI_SENDER_NONCE, senderNonce,
        !           252:                                        BUILD_PKCS7_ATTRIBUTE, OID_PKI_TRANS_ID, transID,
        !           253:                                        BUILD_PKCS7_ATTRIBUTE, OID_PKI_MESSAGE_TYPE, msgType,
        !           254:                                        BUILD_END);
        !           255: 
        !           256:        free(request.ptr);
        !           257:        free(senderNonce.ptr);
        !           258:        free(transID.ptr);
        !           259:        free(msgType.ptr);
        !           260: 
        !           261:        if (!container)
        !           262:        {
        !           263:                return chunk_empty;
        !           264:        }
        !           265:        if (!container->get_encoding(container, &request))
        !           266:        {
        !           267:                container->destroy(container);
        !           268:                return chunk_empty;
        !           269:        }
        !           270:        container->destroy(container);
        !           271: 
        !           272:        return request;
        !           273: }
        !           274: 
        !           275: /**
        !           276:  * Converts a binary request to base64 with 64 characters per line
        !           277:  * newline and '+' characters are escaped by %0A and %2B, respectively
        !           278:  */
        !           279: static char* escape_http_request(chunk_t req)
        !           280: {
        !           281:        char *escaped_req = NULL;
        !           282:        char *p1, *p2;
        !           283:        int lines = 0;
        !           284:        int plus  = 0;
        !           285:        int n     = 0;
        !           286: 
        !           287:        /* compute and allocate the size of the base64-encoded request */
        !           288:        int len = 1 + 4 * ((req.len + 2) / 3);
        !           289:        char *encoded_req = malloc(len);
        !           290: 
        !           291:        /* do the base64 conversion */
        !           292:        chunk_t base64 = chunk_to_base64(req, encoded_req);
        !           293:        len = base64.len + 1;
        !           294: 
        !           295:        /* compute newline characters to be inserted every 64 characters */
        !           296:        lines = (len - 2) / 64;
        !           297: 
        !           298:        /* count number of + characters to be escaped */
        !           299:        p1 = encoded_req;
        !           300:        while (*p1 != '\0')
        !           301:        {
        !           302:                if (*p1++ == '+')
        !           303:                {
        !           304:                        plus++;
        !           305:                }
        !           306:        }
        !           307: 
        !           308:        escaped_req = malloc(len + 3 * (lines + plus));
        !           309: 
        !           310:        /* escape special characters in the request */
        !           311:        p1 = encoded_req;
        !           312:        p2 = escaped_req;
        !           313:        while (*p1 != '\0')
        !           314:        {
        !           315:                if (n == 64)
        !           316:                {
        !           317:                        memcpy(p2, "%0A", 3);
        !           318:                        p2 += 3;
        !           319:                        n = 0;
        !           320:                }
        !           321:                if (*p1 == '+')
        !           322:                {
        !           323:                        memcpy(p2, "%2B", 3);
        !           324:                        p2 += 3;
        !           325:                }
        !           326:                else
        !           327:                {
        !           328:                        *p2++ = *p1;
        !           329:                }
        !           330:                p1++;
        !           331:                n++;
        !           332:        }
        !           333:        *p2 = '\0';
        !           334:        free(encoded_req);
        !           335:        return escaped_req;
        !           336: }
        !           337: 
        !           338: /**
        !           339:  * Send a SCEP request via HTTP and wait for a response
        !           340:  */
        !           341: bool scep_http_request(const char *url, chunk_t msg, scep_op_t op,
        !           342:                                           bool http_get_request, u_int timeout, char *src,
        !           343:                                           chunk_t *response)
        !           344: {
        !           345:        int len;
        !           346:        status_t status;
        !           347:        char *complete_url = NULL;
        !           348:        host_t *srcip = NULL;
        !           349: 
        !           350:        /* initialize response */
        !           351:        *response = chunk_empty;
        !           352: 
        !           353:        if (src)
        !           354:        {
        !           355:                srcip = host_create_from_string(src, 0);
        !           356:        }
        !           357: 
        !           358:        DBG2(DBG_APP, "sending scep request to '%s'", url);
        !           359: 
        !           360:        if (op == SCEP_PKI_OPERATION)
        !           361:        {
        !           362:                const char operation[] = "PKIOperation";
        !           363: 
        !           364:                if (http_get_request)
        !           365:                {
        !           366:                        char *escaped_req = escape_http_request(msg);
        !           367: 
        !           368:                        /* form complete url */
        !           369:                        len = strlen(url) + 20 + strlen(operation) + strlen(escaped_req) + 1;
        !           370:                        complete_url = malloc(len);
        !           371:                        snprintf(complete_url, len, "%s?operation=%s&message=%s"
        !           372:                                        , url, operation, escaped_req);
        !           373:                        free(escaped_req);
        !           374: 
        !           375:                        status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
        !           376:                                                                                 FETCH_HTTP_VERSION_1_0,
        !           377:                                                                                 FETCH_TIMEOUT, timeout,
        !           378:                                                                                 FETCH_REQUEST_HEADER, "Pragma:",
        !           379:                                                                                 FETCH_REQUEST_HEADER, "Host:",
        !           380:                                                                                 FETCH_REQUEST_HEADER, "Accept:",
        !           381:                                                                                 FETCH_SOURCEIP, srcip,
        !           382:                                                                                 FETCH_END);
        !           383:                }
        !           384:                else /* HTTP_POST */
        !           385:                {
        !           386:                        /* form complete url */
        !           387:                        len = strlen(url) + 11 + strlen(operation) + 1;
        !           388:                        complete_url = malloc(len);
        !           389:                        snprintf(complete_url, len, "%s?operation=%s", url, operation);
        !           390: 
        !           391:                        status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
        !           392:                                                                                 FETCH_HTTP_VERSION_1_0,
        !           393:                                                                                 FETCH_TIMEOUT, timeout,
        !           394:                                                                                 FETCH_REQUEST_DATA, msg,
        !           395:                                                                                 FETCH_REQUEST_TYPE, "",
        !           396:                                                                                 FETCH_REQUEST_HEADER, "Expect:",
        !           397:                                                                                 FETCH_SOURCEIP, srcip,
        !           398:                                                                                 FETCH_END);
        !           399:                }
        !           400:        }
        !           401:        else  /* SCEP_GET_CA_CERT */
        !           402:        {
        !           403:                const char operation[] = "GetCACert";
        !           404:                int i;
        !           405: 
        !           406:                /* escape spaces, TODO: complete URL escape */
        !           407:                for (i = 0; i < msg.len; i++)
        !           408:                {
        !           409:                        if (msg.ptr[i] == ' ')
        !           410:                        {
        !           411:                                msg.ptr[i] = '+';
        !           412:                        }
        !           413:                }
        !           414: 
        !           415:                /* form complete url */
        !           416:                len = strlen(url) + 32 + strlen(operation) + msg.len + 1;
        !           417:                complete_url = malloc(len);
        !           418:                snprintf(complete_url, len, "%s?operation=%s&message=%.*s",
        !           419:                                 url, operation, (int)msg.len, msg.ptr);
        !           420: 
        !           421:                status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
        !           422:                                                                         FETCH_HTTP_VERSION_1_0,
        !           423:                                                                         FETCH_TIMEOUT, timeout,
        !           424:                                                                         FETCH_SOURCEIP, srcip,
        !           425:                                                                         FETCH_END);
        !           426:        }
        !           427: 
        !           428:        DESTROY_IF(srcip);
        !           429:        free(complete_url);
        !           430:        return (status == SUCCESS);
        !           431: }
        !           432: 
        !           433: err_t scep_parse_response(chunk_t response, chunk_t transID,
        !           434:                                                  container_t **out, scep_attributes_t *attrs)
        !           435: {
        !           436:        enumerator_t *enumerator;
        !           437:        bool verified = FALSE;
        !           438:        container_t *container;
        !           439:        auth_cfg_t *auth;
        !           440: 
        !           441:        container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7,
        !           442:                                                                   BUILD_BLOB_ASN1_DER, response, BUILD_END);
        !           443:        if (!container)
        !           444:        {
        !           445:                return "error parsing the scep response";
        !           446:        }
        !           447:        if (container->get_type(container) != CONTAINER_PKCS7_SIGNED_DATA)
        !           448:        {
        !           449:                container->destroy(container);
        !           450:                return "scep response is not PKCS#7 signed-data";
        !           451:        }
        !           452: 
        !           453:        enumerator = container->create_signature_enumerator(container);
        !           454:        while (enumerator->enumerate(enumerator, &auth))
        !           455:        {
        !           456:                verified = TRUE;
        !           457:                extract_attributes((pkcs7_t*)container, enumerator, attrs);
        !           458:                if (!chunk_equals(transID, attrs->transID))
        !           459:                {
        !           460:                        enumerator->destroy(enumerator);
        !           461:                        container->destroy(container);
        !           462:                        return "transaction ID of scep response does not match";
        !           463:                }
        !           464:        }
        !           465:        enumerator->destroy(enumerator);
        !           466:        if (!verified)
        !           467:        {
        !           468:                container->destroy(container);
        !           469:                return "unable to verify PKCS#7 container";
        !           470:        }
        !           471:        *out = container;
        !           472:        return NULL;
        !           473: }

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