Annotation of embedaddon/strongswan/src/libradius/radius_socket.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:  * Copyright (C) 2010 Martin Willi
                      3:  * Copyright (C) 2010 revosec AG
                      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: /*
                     17:  * Copyright (C) 2015 Thom Troy
                     18:  *
                     19:  * Permission is hereby granted, free of charge, to any person obtaining a copy
                     20:  * of this software and associated documentation files (the "Software"), to deal
                     21:  * in the Software without restriction, including without limitation the rights
                     22:  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                     23:  * copies of the Software, and to permit persons to whom the Software is
                     24:  * furnished to do so, subject to the following conditions:
                     25:  *
                     26:  * The above copyright notice and this permission notice shall be included in
                     27:  * all copies or substantial portions of the Software.
                     28:  *
                     29:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                     30:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                     31:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                     32:  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                     33:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                     34:  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
                     35:  * THE SOFTWARE.
                     36:  */
                     37: 
                     38: #include "radius_socket.h"
                     39: #include "radius_mppe.h"
                     40: 
                     41: #include <errno.h>
                     42: #include <unistd.h>
                     43: #include <math.h>
                     44: 
                     45: #include <pen/pen.h>
                     46: #include <utils/debug.h>
                     47: 
                     48: typedef struct private_radius_socket_t private_radius_socket_t;
                     49: 
                     50: /**
                     51:  * Private data of an radius_socket_t object.
                     52:  */
                     53: struct private_radius_socket_t {
                     54: 
                     55:        /**
                     56:         * Public radius_socket_t interface.
                     57:         */
                     58:        radius_socket_t public;
                     59: 
                     60:        /**
                     61:         * Server port for authentication
                     62:         */
                     63:        uint16_t auth_port;
                     64: 
                     65:        /**
                     66:         * socket file descriptor for authentication
                     67:         */
                     68:        int auth_fd;
                     69: 
                     70:        /**
                     71:         * Server port for accounting
                     72:         */
                     73:        uint16_t acct_port;
                     74: 
                     75:        /**
                     76:         * socket file descriptor for accounting
                     77:         */
                     78:        int acct_fd;
                     79: 
                     80:        /**
                     81:         * Server address
                     82:         */
                     83:        char *address;
                     84: 
                     85:        /**
                     86:         * current RADIUS identifier
                     87:         */
                     88:        uint8_t identifier;
                     89: 
                     90:        /**
                     91:         * hasher to use for response verification
                     92:         */
                     93:        hasher_t *hasher;
                     94: 
                     95:        /**
                     96:         * HMAC-MD5 signer to build Message-Authenticator attribute
                     97:         */
                     98:        signer_t *signer;
                     99: 
                    100:        /**
                    101:         * random number generator for RADIUS request authenticator
                    102:         */
                    103:        rng_t *rng;
                    104: 
                    105:        /**
                    106:         * RADIUS secret
                    107:         */
                    108:        chunk_t secret;
                    109: 
                    110:        /**
                    111:         * Number of times we retransmit messages before giving up
                    112:         */
                    113:        u_int retransmit_tries;
                    114: 
                    115:        /**
                    116:         * Retransmission timeout
                    117:         */
                    118:        double retransmit_timeout;
                    119: 
                    120:        /**
                    121:         * Base to calculate retransmission timeout
                    122:         */
                    123:        double retransmit_base;
                    124: };
                    125: 
                    126: /**
                    127:  * Check or establish RADIUS connection
                    128:  */
                    129: static bool check_connection(private_radius_socket_t *this,
                    130:                                                         int *fd, uint16_t port)
                    131: {
                    132:        if (*fd == -1)
                    133:        {
                    134:                host_t *server;
                    135: 
                    136:                server = host_create_from_dns(this->address, AF_UNSPEC, port);
                    137:                if (!server)
                    138:                {
                    139:                        DBG1(DBG_CFG, "resolving RADIUS server address '%s' failed",
                    140:                                 this->address);
                    141:                        return FALSE;
                    142:                }
                    143:                *fd = socket(server->get_family(server), SOCK_DGRAM, IPPROTO_UDP);
                    144:                if (*fd == -1)
                    145:                {
                    146:                        DBG1(DBG_CFG, "opening RADIUS socket for %#H failed: %s",
                    147:                                 server, strerror(errno));
                    148:                        server->destroy(server);
                    149:                        return FALSE;
                    150:                }
                    151:                if (connect(*fd, server->get_sockaddr(server),
                    152:                                        *server->get_sockaddr_len(server)) < 0)
                    153:                {
                    154:                        DBG1(DBG_CFG, "connecting RADIUS socket to %#H failed: %s",
                    155:                                 server, strerror(errno));
                    156:                        server->destroy(server);
                    157:                        close(*fd);
                    158:                        *fd = -1;
                    159:                        return FALSE;
                    160:                }
                    161:                server->destroy(server);
                    162:        }
                    163:        return TRUE;
                    164: }
                    165: 
                    166: /**
                    167:  * Receive the response to the message with the given ID
                    168:  */
                    169: static status_t receive_response(int fd, int timeout, uint8_t id,
                    170:                                                                 radius_message_t **response)
                    171: {
                    172:        radius_message_t *msg;
                    173:        char buf[4096];
                    174:        int res;
                    175:        struct pollfd pfd = {
                    176:                .fd = fd,
                    177:                .events = POLLIN,
                    178:        };
                    179: 
                    180:        while (TRUE)
                    181:        {
                    182:                res = poll(&pfd, 1, timeout);
                    183:                if (res < 0)
                    184:                {
                    185:                        DBG1(DBG_CFG, "waiting for RADIUS message failed: %s",
                    186:                                 strerror(errno));
                    187:                        return FAILED;
                    188:                }
                    189:                if (res == 0)
                    190:                {       /* timeout */
                    191:                        return OUT_OF_RES;
                    192:                }
                    193:                res = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
                    194:                if (res <= 0)
                    195:                {
                    196:                        DBG1(DBG_CFG, "receiving RADIUS message failed: %s",
                    197:                                 strerror(errno));
                    198:                        return FAILED;
                    199:                }
                    200:                msg = radius_message_parse(chunk_create(buf, res));
                    201:                if (!msg)
                    202:                {
                    203:                        DBG1(DBG_CFG, "received invalid RADIUS message, ignored");
                    204:                        return FAILED;
                    205:                }
                    206:                if (id != msg->get_identifier(msg))
                    207:                {
                    208:                        /* we haven't received the response to our current request, but
                    209:                         * perhaps one for an earlier request for which we didn't wait
                    210:                         * long enough */
                    211:                        DBG1(DBG_CFG, "received RADIUS message with unexpected ID %d "
                    212:                                 "[%d expected], ignored", msg->get_identifier(msg), id);
                    213:                        msg->destroy(msg);
                    214:                        continue;
                    215:                }
                    216:                *response = msg;
                    217:                return SUCCESS;
                    218:        }
                    219: }
                    220: 
                    221: METHOD(radius_socket_t, request, radius_message_t*,
                    222:        private_radius_socket_t *this, radius_message_t *request)
                    223: {
                    224:        radius_message_t *response;
                    225:        chunk_t data;
                    226:        int *fd, retransmit = 0, timeout;
                    227:        uint16_t port;
                    228:        rng_t *rng = NULL;
                    229: 
                    230:        if (request->get_code(request) == RMC_ACCOUNTING_REQUEST)
                    231:        {
                    232:                fd = &this->acct_fd;
                    233:                port = this->acct_port;
                    234:        }
                    235:        else
                    236:        {
                    237:                fd = &this->auth_fd;
                    238:                port = this->auth_port;
                    239:                rng = this->rng;
                    240:        }
                    241: 
                    242:        /* set Message Identifier */
                    243:        request->set_identifier(request, this->identifier++);
                    244:        /* sign the request */
                    245:        if (!request->sign(request, NULL, this->secret, this->hasher, this->signer,
                    246:                                           rng, rng != NULL))
                    247:        {
                    248:                return NULL;
                    249:        }
                    250: 
                    251:        if (!check_connection(this, fd, port))
                    252:        {
                    253:                return NULL;
                    254:        }
                    255: 
                    256:        data = request->get_encoding(request);
                    257:        DBG3(DBG_CFG, "%B", &data);
                    258: 
                    259:        while (retransmit < this->retransmit_tries)
                    260:        {
                    261:                timeout = (int)(this->retransmit_timeout * 1000.0 *
                    262:                                                pow(this->retransmit_base, retransmit));
                    263:                if (retransmit)
                    264:                {
                    265:                        DBG1(DBG_CFG, "retransmit %d of RADIUS %N (timeout: %.1fs)",
                    266:                                 retransmit, radius_message_code_names,
                    267:                                 request->get_code(request), timeout/1000.0);
                    268:                }
                    269:                if (send(*fd, data.ptr, data.len, 0) != data.len)
                    270:                {
                    271:                        DBG1(DBG_CFG, "sending RADIUS message failed: %s", strerror(errno));
                    272:                        return NULL;
                    273:                }
                    274:                switch (receive_response(*fd, timeout, request->get_identifier(request),
                    275:                                                                 &response))
                    276:                {
                    277:                        case SUCCESS:
                    278:                                break;
                    279:                        case OUT_OF_RES:
                    280:                                retransmit++;
                    281:                                continue;
                    282:                        default:
                    283:                                return NULL;
                    284:                }
                    285:                if (response->verify(response, request->get_authenticator(request),
                    286:                                                         this->secret, this->hasher, this->signer))
                    287:                {
                    288:                        return response;
                    289:                }
                    290:                response->destroy(response);
                    291:                return NULL;
                    292:        }
                    293: 
                    294:        DBG1(DBG_CFG, "RADIUS %N timed out after %d attempts",
                    295:                 radius_message_code_names, request->get_code(request), retransmit);
                    296:        return NULL;
                    297: }
                    298: 
                    299: /**
                    300:  * Decrypt a MS-MPPE-Send/Recv-Key
                    301:  */
                    302: static chunk_t decrypt_mppe_key(private_radius_socket_t *this, uint16_t salt,
                    303:                                                                chunk_t C, radius_message_t *request)
                    304: {
                    305:        chunk_t decrypted;
                    306: 
                    307:        decrypted = chunk_alloca(C.len);
                    308:        if (!request->crypt(request, chunk_from_thing(salt), C, decrypted,
                    309:                                                this->secret, this->hasher) ||
                    310:                decrypted.ptr[0] >= decrypted.len)
                    311:        {       /* decryption failed? */
                    312:                return chunk_empty;
                    313:        }
                    314:        /* remove truncation, first byte is key length */
                    315:        return chunk_clone(chunk_create(decrypted.ptr + 1, decrypted.ptr[0]));
                    316: }
                    317: 
                    318: METHOD(radius_socket_t, decrypt_msk, chunk_t,
                    319:        private_radius_socket_t *this, radius_message_t *request,
                    320:        radius_message_t *response)
                    321: {
                    322:        mppe_key_t *mppe_key;
                    323:        enumerator_t *enumerator;
                    324:        chunk_t data, send = chunk_empty, recv = chunk_empty;
                    325:        int type;
                    326: 
                    327:        enumerator = response->create_enumerator(response);
                    328:        while (enumerator->enumerate(enumerator, &type, &data))
                    329:        {
                    330:                if (type == RAT_VENDOR_SPECIFIC && data.len > sizeof(mppe_key_t))
                    331:                {
                    332:                        mppe_key = (mppe_key_t*)data.ptr;
                    333:                        if (ntohl(mppe_key->id) == PEN_MICROSOFT &&
                    334:                                mppe_key->length == data.len - sizeof(mppe_key->id))
                    335:                        {
                    336:                                data = chunk_create(mppe_key->key, data.len - sizeof(mppe_key_t));
                    337:                                if (mppe_key->type == MS_MPPE_SEND_KEY)
                    338:                                {
                    339:                                        send = decrypt_mppe_key(this, mppe_key->salt, data, request);
                    340:                                }
                    341:                                if (mppe_key->type == MS_MPPE_RECV_KEY)
                    342:                                {
                    343:                                        recv = decrypt_mppe_key(this, mppe_key->salt, data, request);
                    344:                                }
                    345:                        }
                    346:                }
                    347:        }
                    348:        enumerator->destroy(enumerator);
                    349:        if (send.ptr && recv.ptr)
                    350:        {
                    351:                chunk_t pad = chunk_empty;
                    352: 
                    353:                if ((send.len + recv.len) < 64)
                    354:                {       /* zero-pad MSK to at least 64 bytes */
                    355:                        pad = chunk_alloca(64 - send.len - recv.len);
                    356:                        memset(pad.ptr, 0, pad.len);
                    357:                }
                    358:                return chunk_cat("mmc", recv, send, pad);
                    359:        }
                    360:        chunk_clear(&send);
                    361:        chunk_clear(&recv);
                    362:        return chunk_empty;
                    363: }
                    364: 
                    365: METHOD(radius_socket_t, destroy, void,
                    366:        private_radius_socket_t *this)
                    367: {
                    368:        DESTROY_IF(this->hasher);
                    369:        DESTROY_IF(this->signer);
                    370:        DESTROY_IF(this->rng);
                    371:        if (this->auth_fd != -1)
                    372:        {
                    373:                close(this->auth_fd);
                    374:        };
                    375:        if (this->acct_fd != -1)
                    376:        {
                    377:                close(this->acct_fd);
                    378:        }
                    379:        free(this);
                    380: }
                    381: 
                    382: /**
                    383:  * See header
                    384:  */
                    385: radius_socket_t *radius_socket_create(char *address, uint16_t auth_port,
                    386:                                                                          uint16_t acct_port, chunk_t secret,
                    387:                                                                          u_int tries, double timeout, double base)
                    388: {
                    389:        private_radius_socket_t *this;
                    390: 
                    391:        INIT(this,
                    392:                .public = {
                    393:                        .request = _request,
                    394:                        .decrypt_msk = _decrypt_msk,
                    395:                        .destroy = _destroy,
                    396:                },
                    397:                .address = address,
                    398:                .auth_port = auth_port,
                    399:                .auth_fd = -1,
                    400:                .acct_port = acct_port,
                    401:                .acct_fd = -1,
                    402:                .hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5),
                    403:                .signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128),
                    404:                .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK),
                    405:                .retransmit_tries = tries,
                    406:                .retransmit_timeout = timeout,
                    407:                .retransmit_base = base,
                    408:        );
                    409: 
                    410:        if (!this->hasher || !this->signer || !this->rng ||
                    411:                !this->signer->set_key(this->signer, secret))
                    412:        {
                    413:                DBG1(DBG_CFG, "RADIUS initialization failed, HMAC/MD5/RNG required");
                    414:                destroy(this);
                    415:                return NULL;
                    416:        }
                    417:        this->secret = secret;
                    418:        /* we use a random identifier, helps if we restart often */
                    419:        this->identifier = random();
                    420: 
                    421:        return &this->public;
                    422: }

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