Annotation of embedaddon/strongswan/src/scepclient/scep.c, revision 1.1.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>