Annotation of embedaddon/strongswan/src/libcharon/plugins/eap_sim/eap_sim_server.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:  * Copyright (C) 2007-2009 Martin Willi
                      3:  * HSR Hochschule fuer Technik Rapperswil
                      4:  *
                      5:  * This program is free software; you can redistribute it and/or modify it
                      6:  * under the terms of the GNU General Public License as published by the
                      7:  * Free Software Foundation; either version 2 of the License, or (at your
                      8:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
                      9:  *
                     10:  * This program is distributed in the hope that it will be useful, but
                     11:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
                     12:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
                     13:  * for more details.
                     14:  */
                     15: 
                     16: #include "eap_sim_server.h"
                     17: 
                     18: #include <daemon.h>
                     19: 
                     20: #include <simaka_message.h>
                     21: #include <simaka_crypto.h>
                     22: #include <simaka_manager.h>
                     23: 
                     24: /* number of triplets for one authentication */
                     25: #define TRIPLET_COUNT 3
                     26: 
                     27: /** length of the AT_NONCE_S value */
                     28: #define NONCE_LEN 16
                     29: 
                     30: typedef struct private_eap_sim_server_t private_eap_sim_server_t;
                     31: 
                     32: /**
                     33:  * Private data of an eap_sim_server_t object.
                     34:  */
                     35: struct private_eap_sim_server_t {
                     36: 
                     37:        /**
                     38:         * Public authenticator_t interface.
                     39:         */
                     40:        eap_sim_server_t public;
                     41: 
                     42:        /**
                     43:         * SIM backend manager
                     44:         */
                     45:        simaka_manager_t *mgr;
                     46: 
                     47:        /**
                     48:         * permanent ID of peer
                     49:         */
                     50:        identification_t *permanent;
                     51: 
                     52:        /**
                     53:         * pseudonym ID of peer
                     54:         */
                     55:        identification_t *pseudonym;
                     56: 
                     57:        /**
                     58:         * reauthentication ID of peer
                     59:         */
                     60:        identification_t *reauth;
                     61: 
                     62:        /**
                     63:         * EAP-SIM/AKA crypto helper
                     64:         */
                     65:        simaka_crypto_t *crypto;
                     66: 
                     67:        /**
                     68:         * unique EAP identifier
                     69:         */
                     70:        uint8_t identifier;
                     71: 
                     72:        /**
                     73:         * concatenated SRES values
                     74:         */
                     75:        chunk_t sreses;
                     76: 
                     77:        /**
                     78:         * Nonce value used in AT_NONCE_S
                     79:         */
                     80:        chunk_t nonce;
                     81: 
                     82:        /**
                     83:         * Counter value negotiated, network order
                     84:         */
                     85:        chunk_t counter;
                     86: 
                     87:        /**
                     88:         * MSK, used for EAP-SIM based IKEv2 authentication
                     89:         */
                     90:        chunk_t msk;
                     91: 
                     92:        /**
                     93:         * Do we request fast reauthentication?
                     94:         */
                     95:        bool use_reauth;
                     96: 
                     97:        /**
                     98:         * Do we request pseudonym identities?
                     99:         */
                    100:        bool use_pseudonym;
                    101: 
                    102:        /**
                    103:         * Do we request permanent identities?
                    104:         */
                    105:        bool use_permanent;
                    106: 
                    107:        /**
                    108:         * EAP-SIM message we have initiated
                    109:         */
                    110:        simaka_subtype_t pending;
                    111: };
                    112: 
                    113: /* version of SIM protocol we speak */
                    114: static chunk_t version = chunk_from_chars(0x00,0x01);
                    115: 
                    116: /**
                    117:  * Generate a payload from a message, destroy message
                    118:  */
                    119: static bool generate_payload(simaka_message_t *message, chunk_t data,
                    120:                                                         eap_payload_t **out)
                    121: {
                    122:        chunk_t chunk;
                    123:        bool ok;
                    124: 
                    125:        ok = message->generate(message, data, &chunk);
                    126:        if (ok)
                    127:        {
                    128:                *out = eap_payload_create_data_own(chunk);
                    129:        }
                    130:        message->destroy(message);
                    131:        return ok;
                    132: }
                    133: 
                    134: METHOD(eap_method_t, initiate, status_t,
                    135:        private_eap_sim_server_t *this, eap_payload_t **out)
                    136: {
                    137:        simaka_message_t *message;
                    138: 
                    139:        message = simaka_message_create(TRUE, this->identifier++, EAP_SIM,
                    140:                                                                        SIM_START, this->crypto);
                    141:        message->add_attribute(message, AT_VERSION_LIST, version);
                    142:        if (this->use_reauth)
                    143:        {
                    144:                message->add_attribute(message, AT_ANY_ID_REQ, chunk_empty);
                    145:        }
                    146:        else if (this->use_pseudonym)
                    147:        {
                    148:                message->add_attribute(message, AT_FULLAUTH_ID_REQ, chunk_empty);
                    149:        }
                    150:        else if (this->use_permanent)
                    151:        {
                    152:                message->add_attribute(message, AT_PERMANENT_ID_REQ, chunk_empty);
                    153:        }
                    154:        if (!generate_payload(message, chunk_empty, out))
                    155:        {
                    156:                return FAILED;
                    157:        }
                    158:        this->pending = SIM_START;
                    159:        return NEED_MORE;
                    160: }
                    161: 
                    162: /**
                    163:  * Initiate  EAP-SIM/Request/Re-authentication message
                    164:  */
                    165: static status_t reauthenticate(private_eap_sim_server_t *this,
                    166:                                                           char mk[HASH_SIZE_SHA1], uint16_t counter,
                    167:                                                           eap_payload_t **out)
                    168: {
                    169:        simaka_message_t *message;
                    170:        identification_t *next;
                    171:        chunk_t mkc;
                    172:        rng_t *rng;
                    173: 
                    174:        DBG1(DBG_IKE, "initiating EAP-SIM reauthentication");
                    175: 
                    176:        rng = this->crypto->get_rng(this->crypto);
                    177:        if (!rng->allocate_bytes(rng, NONCE_LEN, &this->nonce))
                    178:        {
                    179:                return FAILED;
                    180:        }
                    181: 
                    182:        mkc = chunk_create(mk, HASH_SIZE_SHA1);
                    183:        counter = htons(counter);
                    184:        this->counter = chunk_clone(chunk_create((char*)&counter, sizeof(counter)));
                    185: 
                    186:        if (!this->crypto->derive_keys_reauth(this->crypto, mkc) ||
                    187:                !this->crypto->derive_keys_reauth_msk(this->crypto,
                    188:                                        this->reauth, this->counter, this->nonce, mkc, &this->msk))
                    189:        {
                    190:                return FAILED;
                    191:        }
                    192: 
                    193:        message = simaka_message_create(TRUE, this->identifier++, EAP_SIM,
                    194:                                                                        SIM_REAUTHENTICATION, this->crypto);
                    195:        message->add_attribute(message, AT_COUNTER, this->counter);
                    196:        message->add_attribute(message, AT_NONCE_S, this->nonce);
                    197:        next = this->mgr->provider_gen_reauth(this->mgr, this->permanent, mk);
                    198:        if (next)
                    199:        {
                    200:                message->add_attribute(message, AT_NEXT_REAUTH_ID,
                    201:                                                           next->get_encoding(next));
                    202:                next->destroy(next);
                    203:        }
                    204:        if (!generate_payload(message, chunk_empty, out))
                    205:        {
                    206:                return FAILED;
                    207:        }
                    208:        this->pending = SIM_REAUTHENTICATION;
                    209:        return NEED_MORE;
                    210: }
                    211: 
                    212: /**
                    213:  * process an EAP-SIM/Response/Reauthentication message
                    214:  */
                    215: static status_t process_reauthentication(private_eap_sim_server_t *this,
                    216:                                                                        simaka_message_t *in, eap_payload_t **out)
                    217: {
                    218:        enumerator_t *enumerator;
                    219:        simaka_attribute_t type;
                    220:        chunk_t data, counter = chunk_empty;
                    221:        bool too_small = FALSE;
                    222: 
                    223:        if (this->pending != SIM_REAUTHENTICATION)
                    224:        {
                    225:                DBG1(DBG_IKE, "received %N, but not expected",
                    226:                         simaka_subtype_names, SIM_REAUTHENTICATION);
                    227:                return FAILED;
                    228:        }
                    229:        /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_S"  */
                    230:        if (!in->verify(in, this->nonce))
                    231:        {
                    232:                return FAILED;
                    233:        }
                    234: 
                    235:        enumerator = in->create_attribute_enumerator(in);
                    236:        while (enumerator->enumerate(enumerator, &type, &data))
                    237:        {
                    238:                switch (type)
                    239:                {
                    240:                        case AT_COUNTER:
                    241:                                counter = data;
                    242:                                break;
                    243:                        case AT_COUNTER_TOO_SMALL:
                    244:                                too_small = TRUE;
                    245:                                break;
                    246:                        default:
                    247:                                if (!simaka_attribute_skippable(type))
                    248:                                {
                    249:                                        enumerator->destroy(enumerator);
                    250:                                        return FAILED;
                    251:                                }
                    252:                                break;
                    253:                }
                    254:        }
                    255:        enumerator->destroy(enumerator);
                    256: 
                    257:        if (too_small)
                    258:        {
                    259:                DBG1(DBG_IKE, "received %N, initiating full authentication",
                    260:                         simaka_attribute_names, AT_COUNTER_TOO_SMALL);
                    261:                this->use_reauth = FALSE;
                    262:                this->crypto->clear_keys(this->crypto);
                    263:                return initiate(this, out);
                    264:        }
                    265:        if (!chunk_equals_const(counter, this->counter))
                    266:        {
                    267:                DBG1(DBG_IKE, "received counter does not match");
                    268:                return FAILED;
                    269:        }
                    270:        return SUCCESS;
                    271: }
                    272: 
                    273: /**
                    274:  * process an EAP-SIM/Response/Start message
                    275:  */
                    276: static status_t process_start(private_eap_sim_server_t *this,
                    277:                                                          simaka_message_t *in, eap_payload_t **out)
                    278: {
                    279:        simaka_message_t *message;
                    280:        enumerator_t *enumerator;
                    281:        simaka_attribute_t type;
                    282:        chunk_t data, identity = chunk_empty, nonce = chunk_empty, mk;
                    283:        chunk_t rands, rand, kcs, kc, sreses, sres;
                    284:        bool supported = FALSE;
                    285:        identification_t *id;
                    286:        int i;
                    287: 
                    288:        if (this->pending != SIM_START)
                    289:        {
                    290:                DBG1(DBG_IKE, "received %N, but not expected",
                    291:                         simaka_subtype_names, SIM_START);
                    292:                return FAILED;
                    293:        }
                    294: 
                    295:        enumerator = in->create_attribute_enumerator(in);
                    296:        while (enumerator->enumerate(enumerator, &type, &data))
                    297:        {
                    298:                switch (type)
                    299:                {
                    300:                        case AT_NONCE_MT:
                    301:                                nonce = data;
                    302:                                break;
                    303:                        case AT_SELECTED_VERSION:
                    304:                                if (chunk_equals(data, version))
                    305:                                {
                    306:                                        supported = TRUE;
                    307:                                }
                    308:                                break;
                    309:                        case AT_IDENTITY:
                    310:                                identity = data;
                    311:                                break;
                    312:                        default:
                    313:                                if (!simaka_attribute_skippable(type))
                    314:                                {
                    315:                                        enumerator->destroy(enumerator);
                    316:                                        return FAILED;
                    317:                                }
                    318:                                break;
                    319:                }
                    320:        }
                    321:        enumerator->destroy(enumerator);
                    322: 
                    323:        if (identity.len)
                    324:        {
                    325:                identification_t *permanent;
                    326: 
                    327:                id = identification_create_from_data(identity);
                    328:                if (this->use_reauth && !nonce.len)
                    329:                {
                    330:                        char mk[HASH_SIZE_SHA1];
                    331:                        uint16_t counter;
                    332: 
                    333:                        permanent = this->mgr->provider_is_reauth(this->mgr, id,
                    334:                                                                                                          mk, &counter);
                    335:                        if (permanent)
                    336:                        {
                    337:                                this->permanent->destroy(this->permanent);
                    338:                                this->permanent = permanent;
                    339:                                this->reauth = id;
                    340:                                return reauthenticate(this, mk, counter, out);
                    341:                        }
                    342:                        DBG1(DBG_IKE, "received unknown reauthentication identity '%Y', "
                    343:                                 "initiating full authentication", id);
                    344:                        this->use_reauth = FALSE;
                    345:                        id->destroy(id);
                    346:                        return initiate(this, out);
                    347:                }
                    348:                if (this->use_pseudonym)
                    349:                {
                    350:                        permanent = this->mgr->provider_is_pseudonym(this->mgr, id);
                    351:                        if (permanent)
                    352:                        {
                    353:                                this->permanent->destroy(this->permanent);
                    354:                                this->permanent = permanent;
                    355:                                this->pseudonym = id->clone(id);
                    356:                                /* we already have a new permanent identity now */
                    357:                                this->use_permanent = FALSE;
                    358:                        }
                    359:                }
                    360:                if (!this->pseudonym && this->use_permanent)
                    361:                {
                    362:                        DBG1(DBG_IKE, "received %spermanent identity '%Y'",
                    363:                                 this->use_pseudonym ? "pseudonym or " : "", id);
                    364:                        this->permanent->destroy(this->permanent);
                    365:                        this->permanent = id->clone(id);
                    366:                }
                    367:                id->destroy(id);
                    368:        }
                    369: 
                    370:        if (!supported || !nonce.len)
                    371:        {
                    372:                DBG1(DBG_IKE, "received incomplete EAP-SIM/Response/Start");
                    373:                return FAILED;
                    374:        }
                    375: 
                    376:        /* read triplets from provider */
                    377:        rand = rands = chunk_alloca(SIM_RAND_LEN * TRIPLET_COUNT);
                    378:        kc = kcs = chunk_alloca(SIM_KC_LEN * TRIPLET_COUNT);
                    379:        sres = sreses = chunk_alloca(SIM_SRES_LEN * TRIPLET_COUNT);
                    380:        rands.len = kcs.len = sreses.len = 0;
                    381:        for (i = 0; i < TRIPLET_COUNT; i++)
                    382:        {
                    383:                if (!this->mgr->provider_get_triplet(this->mgr, this->permanent,
                    384:                                                                                         rand.ptr, sres.ptr, kc.ptr))
                    385:                {
                    386:                        if (this->use_pseudonym)
                    387:                        {
                    388:                                /* probably received a pseudonym we couldn't map */
                    389:                                DBG1(DBG_IKE, "failed to map pseudonym identity '%Y', "
                    390:                                         "fallback to permanent identity request", this->permanent);
                    391:                                this->use_pseudonym = FALSE;
                    392:                                DESTROY_IF(this->pseudonym);
                    393:                                this->pseudonym = NULL;
                    394:                                return initiate(this, out);
                    395:                        }
                    396:                        return FAILED;
                    397:                }
                    398:                rands.len += SIM_RAND_LEN;
                    399:                sreses.len += SIM_SRES_LEN;
                    400:                kcs.len += SIM_KC_LEN;
                    401:                rand = chunk_skip(rand, SIM_RAND_LEN);
                    402:                sres = chunk_skip(sres, SIM_SRES_LEN);
                    403:                kc = chunk_skip(kc, SIM_KC_LEN);
                    404:        }
                    405:        free(this->sreses.ptr);
                    406:        this->sreses = chunk_clone(sreses);
                    407: 
                    408:        data = chunk_cata("cccc", kcs, nonce, version, version);
                    409:        free(this->msk.ptr);
                    410:        id = this->permanent;
                    411:        if (this->pseudonym)
                    412:        {
                    413:                id = this->pseudonym;
                    414:        }
                    415:        if (!this->crypto->derive_keys_full(this->crypto, id, data, &mk, &this->msk))
                    416:        {
                    417:                return FAILED;
                    418:        }
                    419: 
                    420:        /* build response with AT_MAC, built over "EAP packet | NONCE_MT" */
                    421:        message = simaka_message_create(TRUE, this->identifier++, EAP_SIM,
                    422:                                                                        SIM_CHALLENGE, this->crypto);
                    423:        message->add_attribute(message, AT_RAND, rands);
                    424:        id = this->mgr->provider_gen_reauth(this->mgr, this->permanent, mk.ptr);
                    425:        free(mk.ptr);
                    426:        if (id)
                    427:        {
                    428:                message->add_attribute(message, AT_NEXT_REAUTH_ID,
                    429:                                                           id->get_encoding(id));
                    430:                id->destroy(id);
                    431:        }
                    432:        id = this->mgr->provider_gen_pseudonym(this->mgr, this->permanent);
                    433:        if (id)
                    434:        {
                    435:                message->add_attribute(message, AT_NEXT_PSEUDONYM,
                    436:                                                           id->get_encoding(id));
                    437:                id->destroy(id);
                    438:        }
                    439:        if (!generate_payload(message, nonce, out))
                    440:        {
                    441:                return FAILED;
                    442:        }
                    443:        this->pending = SIM_CHALLENGE;
                    444:        return NEED_MORE;
                    445: }
                    446: 
                    447: /**
                    448:  * process an EAP-SIM/Response/Challenge message
                    449:  */
                    450: static status_t process_challenge(private_eap_sim_server_t *this,
                    451:                                                                  simaka_message_t *in, eap_payload_t **out)
                    452: {
                    453:        enumerator_t *enumerator;
                    454:        simaka_attribute_t type;
                    455:        chunk_t data;
                    456: 
                    457:        if (this->pending != SIM_CHALLENGE)
                    458:        {
                    459:                DBG1(DBG_IKE, "received %N, but not expected",
                    460:                         simaka_subtype_names, SIM_CHALLENGE);
                    461:                return FAILED;
                    462:        }
                    463:        /* verify AT_MAC attribute, signature is over "EAP packet | n*SRES"  */
                    464:        if (!in->verify(in, this->sreses))
                    465:        {
                    466:                return FAILED;
                    467:        }
                    468: 
                    469:        enumerator = in->create_attribute_enumerator(in);
                    470:        while (enumerator->enumerate(enumerator, &type, &data))
                    471:        {
                    472:                if (!simaka_attribute_skippable(type))
                    473:                {
                    474:                        enumerator->destroy(enumerator);
                    475:                        return FAILED;
                    476:                }
                    477:        }
                    478:        enumerator->destroy(enumerator);
                    479: 
                    480:        return SUCCESS;
                    481: }
                    482: 
                    483: /**
                    484:  * EAP-SIM/Response/ClientErrorCode message
                    485:  */
                    486: static status_t process_client_error(private_eap_sim_server_t *this,
                    487:                                                                         simaka_message_t *in)
                    488: {
                    489:        enumerator_t *enumerator;
                    490:        simaka_attribute_t type;
                    491:        chunk_t data;
                    492: 
                    493:        enumerator = in->create_attribute_enumerator(in);
                    494:        while (enumerator->enumerate(enumerator, &type, &data))
                    495:        {
                    496:                if (type == AT_CLIENT_ERROR_CODE)
                    497:                {
                    498:                        uint16_t code;
                    499: 
                    500:                        memcpy(&code, data.ptr, sizeof(code));
                    501:                        DBG1(DBG_IKE, "received EAP-SIM client error '%N'",
                    502:                                 simaka_client_error_names, ntohs(code));
                    503:                }
                    504:                else if (!simaka_attribute_skippable(type))
                    505:                {
                    506:                        break;
                    507:                }
                    508:        }
                    509:        enumerator->destroy(enumerator);
                    510:        return FAILED;
                    511: }
                    512: 
                    513: METHOD(eap_method_t, process, status_t,
                    514:        private_eap_sim_server_t *this, eap_payload_t *in, eap_payload_t **out)
                    515: {
                    516:        simaka_message_t *message;
                    517:        status_t status;
                    518: 
                    519:        message = simaka_message_create_from_payload(in->get_data(in), this->crypto);
                    520:        if (!message)
                    521:        {
                    522:                return FAILED;
                    523:        }
                    524:        if (!message->parse(message))
                    525:        {
                    526:                message->destroy(message);
                    527:                return FAILED;
                    528:        }
                    529:        switch (message->get_subtype(message))
                    530:        {
                    531:                case SIM_START:
                    532:                        status = process_start(this, message, out);
                    533:                        break;
                    534:                case SIM_CHALLENGE:
                    535:                        status = process_challenge(this, message, out);
                    536:                        break;
                    537:                case SIM_REAUTHENTICATION:
                    538:                        status = process_reauthentication(this, message, out);
                    539:                        break;
                    540:                case SIM_CLIENT_ERROR:
                    541:                        status = process_client_error(this, message);
                    542:                        break;
                    543:                default:
                    544:                        DBG1(DBG_IKE, "unable to process EAP-SIM subtype %N",
                    545:                                 simaka_subtype_names, message->get_subtype(message));
                    546:                        status = FAILED;
                    547:                        break;
                    548:        }
                    549:        message->destroy(message);
                    550:        return status;
                    551: }
                    552: 
                    553: METHOD(eap_method_t, get_type, eap_type_t,
                    554:        private_eap_sim_server_t *this, uint32_t *vendor)
                    555: {
                    556:        *vendor = 0;
                    557:        return EAP_SIM;
                    558: }
                    559: 
                    560: METHOD(eap_method_t, get_msk, status_t,
                    561:        private_eap_sim_server_t *this, chunk_t *msk)
                    562: {
                    563:        if (this->msk.ptr)
                    564:        {
                    565:                *msk = this->msk;
                    566:                return SUCCESS;
                    567:        }
                    568:        return FAILED;
                    569: }
                    570: 
                    571: METHOD(eap_method_t, get_identifier, uint8_t,
                    572:        private_eap_sim_server_t *this)
                    573: {
                    574:        return this->identifier;
                    575: }
                    576: 
                    577: METHOD(eap_method_t, set_identifier, void,
                    578:        private_eap_sim_server_t *this, uint8_t identifier)
                    579: {
                    580:        this->identifier = identifier;
                    581: }
                    582: 
                    583: METHOD(eap_method_t, is_mutual, bool,
                    584:        private_eap_sim_server_t *this)
                    585: {
                    586:        return TRUE;
                    587: }
                    588: 
                    589: METHOD(eap_method_t, destroy, void,
                    590:        private_eap_sim_server_t *this)
                    591: {
                    592:        this->crypto->destroy(this->crypto);
                    593:        this->permanent->destroy(this->permanent);
                    594:        DESTROY_IF(this->pseudonym);
                    595:        DESTROY_IF(this->reauth);
                    596:        free(this->sreses.ptr);
                    597:        free(this->nonce.ptr);
                    598:        free(this->msk.ptr);
                    599:        free(this->counter.ptr);
                    600:        free(this);
                    601: }
                    602: 
                    603: /*
                    604:  * Described in header.
                    605:  */
                    606: eap_sim_server_t *eap_sim_server_create(identification_t *server,
                    607:                                                                                identification_t *peer)
                    608: {
                    609:        private_eap_sim_server_t *this;
                    610: 
                    611:        INIT(this,
                    612:                .public = {
                    613:                        .interface = {
                    614:                                .initiate = _initiate,
                    615:                                .process = _process,
                    616:                                .get_type = _get_type,
                    617:                                .is_mutual = _is_mutual,
                    618:                                .get_msk = _get_msk,
                    619:                                .get_identifier = _get_identifier,
                    620:                                .set_identifier = _set_identifier,
                    621:                                .destroy = _destroy,
                    622:                        },
                    623:                },
                    624:                .crypto = simaka_crypto_create(EAP_SIM),
                    625:                .mgr = lib->get(lib, "sim-manager"),
                    626:        );
                    627: 
                    628:        if (!this->crypto)
                    629:        {
                    630:                free(this);
                    631:                return NULL;
                    632:        }
                    633: 
                    634:        this->permanent = peer->clone(peer);
                    635:        this->use_reauth = this->use_pseudonym = this->use_permanent =
                    636:                lib->settings->get_bool(lib->settings,
                    637:                                                                "%s.plugins.eap-sim.request_identity", TRUE,
                    638:                                                                lib->ns);
                    639: 
                    640:        /* generate a non-zero identifier */
                    641:        do {
                    642:                this->identifier = random();
                    643:        } while (!this->identifier);
                    644: 
                    645:        return &this->public;
                    646: }

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