dtls.c 20.1 KB
Newer Older
1
/*
2
 * Copyright (C) 2008-2009 Stig Venaas <venaas@uninett.no>
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 */

#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#ifdef SYS_SOLARIS9
#include <fcntl.h>
#endif
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#include <ctype.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <regex.h>
#include <pthread.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
29
#include "hash.h"
30
#include "radsecproxy.h"
venaas's avatar
venaas committed
31

32 33 34
#ifdef RADPROT_DTLS
#include "debug.h"
#include "util.h"
35
#include "hostport.h"
36

venaas's avatar
venaas committed
37 38
static void setprotoopts(struct commonprotoopts *opts);
static char **getlistenerargs();
venaas's avatar
venaas committed
39 40 41 42 43
void *udpdtlsserverrd(void *arg);
int dtlsconnect(struct server *server, struct timeval *when, int timeout, char *text);
void *dtlsclientrd(void *arg);
int clientradputdtls(struct server *server, unsigned char *rad);
void addserverextradtls(struct clsrvconf *conf);
venaas's avatar
venaas committed
44
void dtlssetsrcres();
venaas's avatar
venaas committed
45 46 47 48
void initextradtls();

static const struct protodefs protodefs = {
    "dtls",
49
    "radsec", /* secretdefault */
venaas's avatar
venaas committed
50 51 52 53 54 55 56
    SOCK_DGRAM, /* socktype */
    "2083", /* portdefault */
    REQUEST_RETRY_COUNT, /* retrycountdefault */
    10, /* retrycountmax */
    REQUEST_RETRY_INTERVAL, /* retryintervaldefault */
    60, /* retryintervalmax */
    DUPLICATE_INTERVAL, /* duplicateintervaldefault */
venaas's avatar
venaas committed
57 58
    setprotoopts, /* setprotoopts */
    getlistenerargs, /* getlistenerargs */
venaas's avatar
venaas committed
59 60 61 62 63 64 65 66 67
    udpdtlsserverrd, /* listener */
    dtlsconnect, /* connecter */
    dtlsclientrd, /* clientconnreader */
    clientradputdtls, /* clientradput */
    NULL, /* addclient */
    addserverextradtls, /* addserverextra */
    dtlssetsrcres, /* setsrcres */
    initextradtls /* initextra */
};
68

venaas's avatar
venaas committed
69 70
static int client4_sock = -1;
static int client6_sock = -1;
venaas's avatar
venaas committed
71
static struct addrinfo *srcres = NULL;
venaas's avatar
venaas committed
72
static uint8_t handle;
venaas's avatar
venaas committed
73
static struct commonprotoopts *protoopts = NULL;
venaas's avatar
venaas committed
74 75 76 77 78

const struct protodefs *dtlsinit(uint8_t h) {
    handle = h;
    return &protodefs;
}
venaas's avatar
venaas committed
79

venaas's avatar
venaas committed
80 81 82 83 84 85 86 87
static void setprotoopts(struct commonprotoopts *opts) {
    protoopts = opts;
}

static char **getlistenerargs() {
    return protoopts ? protoopts->listenargs : NULL;
}

88 89
struct sessioncacheentry {
    pthread_mutex_t mutex;
90
    struct gqueue *rbios;
91 92 93 94 95 96
    struct timeval expiry;
};

struct dtlsservernewparams {
    struct sessioncacheentry *sesscache;
    int sock;
97
    struct sockaddr_storage addr;
98 99
};

venaas's avatar
venaas committed
100
void dtlssetsrcres() {
venaas's avatar
venaas committed
101
    if (!srcres)
102 103 104
	srcres =
            resolvepassiveaddrinfo(protoopts ? protoopts->sourcearg : NULL,
                                   AF_UNSPEC, NULL, protodefs.socktype);
venaas's avatar
venaas committed
105 106
}

107
int udp2bio(int s, struct gqueue *q, int cnt) {
108 109 110 111 112
    unsigned char *buf;
    BIO *rbio;

    if (cnt < 1)
	return 0;
113

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
    buf = malloc(cnt);
    if (!buf) {
	unsigned char err;
	debug(DBG_ERR, "udp2bio: malloc failed");
	recv(s, &err, 1, 0);
	return 0;
    }

    cnt = recv(s, buf, cnt, 0);
    if (cnt < 1) {
	debug(DBG_WARN, "udp2bio: recv failed");
	free(buf);
	return 0;
    }

    rbio = BIO_new_mem_buf(buf, cnt);
    BIO_set_mem_eof_return(rbio, -1);

    pthread_mutex_lock(&q->mutex);
    if (!list_push(q->entries, rbio)) {
	BIO_free(rbio);
	pthread_mutex_unlock(&q->mutex);
	return 0;
    }
    pthread_cond_signal(&q->cond);
    pthread_mutex_unlock(&q->mutex);
    return 1;
}

143
BIO *getrbio(SSL *ssl, struct gqueue *q, int timeout) {
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
    BIO *rbio;
    struct timeval now;
    struct timespec to;

    pthread_mutex_lock(&q->mutex);
    if (!(rbio = (BIO *)list_shift(q->entries))) {
	if (timeout) {
	    gettimeofday(&now, NULL);
	    memset(&to, 0, sizeof(struct timespec));
	    to.tv_sec = now.tv_sec + timeout;
	    pthread_cond_timedwait(&q->cond, &q->mutex, &to);
	} else
	    pthread_cond_wait(&q->cond, &q->mutex);
	rbio = (BIO *)list_shift(q->entries);
    }
    pthread_mutex_unlock(&q->mutex);
    return rbio;
}

163
int dtlsread(SSL *ssl, struct gqueue *q, unsigned char *buf, int num, int timeout) {
164
    int len, cnt;
165
    BIO *rbio;
166

167 168 169 170 171
    for (len = 0; len < num; len += cnt) {
	cnt = SSL_read(ssl, buf + len, num - len);
	if (cnt <= 0)
	    switch (cnt = SSL_get_error(ssl, cnt)) {
	    case SSL_ERROR_WANT_READ:
172 173 174
		rbio = getrbio(ssl, q, timeout);
		if (!rbio)
		    return 0;
175
		BIO_free(ssl->rbio);
176
		ssl->rbio = rbio;
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
		cnt = 0;
		continue;
	    case SSL_ERROR_WANT_WRITE:
		cnt = 0;
		continue;
	    case SSL_ERROR_ZERO_RETURN:
		/* remote end sent close_notify, send one back */
		SSL_shutdown(ssl);
		return -1;
	    default:
		return -1;
	    }
    }
    return num;
}

193
/* accept if acc == 1, else connect */
194
SSL *dtlsacccon(uint8_t acc, SSL_CTX *ctx, int s, struct sockaddr *addr, struct gqueue *rbios) {
195 196 197 198 199 200 201 202
    SSL *ssl;
    int i, res;
    unsigned long error;
    BIO *mem0bio, *wbio;

    ssl = SSL_new(ctx);
    if (!ssl)
	return NULL;
203

204 205 206
    mem0bio = BIO_new(BIO_s_mem());
    BIO_set_mem_eof_return(mem0bio, -1);
    wbio = BIO_new_dgram(s, BIO_NOCLOSE);
207
    i = BIO_dgram_set_peer(wbio, addr); /* i just to avoid warning */
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
    SSL_set_bio(ssl, mem0bio, wbio);

    for (i = 0; i < 5; i++) {
        res = acc ? SSL_accept(ssl) : SSL_connect(ssl);
        if (res > 0)
            return ssl;
        if (res == 0)
            break;
        if (SSL_get_error(ssl, res) == SSL_ERROR_WANT_READ) {
            BIO_free(ssl->rbio);
            ssl->rbio = getrbio(ssl, rbios, 5);
            if (!ssl->rbio)
                break;
        }
        while ((error = ERR_get_error()))
            debug(DBG_ERR, "dtls%st: DTLS: %s", acc ? "accep" : "connec", ERR_error_string(error, NULL));
    }

    SSL_free(ssl);
    return NULL;
}

230
unsigned char *raddtlsget(SSL *ssl, struct gqueue *rbios, int timeout) {
231 232 233 234
    int cnt, len;
    unsigned char buf[4], *rad;

    for (;;) {
235
        cnt = dtlsread(ssl, rbios, buf, 4, timeout);
236
        if (cnt < 1) {
237
            debug(DBG_DBG, cnt ? "raddtlsget: connection lost" : "raddtlsget: timeout");
238 239 240 241
            return NULL;
        }

	len = RADLEN(buf);
242 243 244 245
	if (len < 4) {
	    debug(DBG_ERR, "raddtlsget: length too small");
	    continue;
	}
246 247 248 249 250 251
	rad = malloc(len);
	if (!rad) {
	    debug(DBG_ERR, "raddtlsget: malloc failed");
	    continue;
	}
	memcpy(rad, buf, 4);
252

253
	cnt = dtlsread(ssl, rbios, rad + 4, len - 4, timeout);
254
        if (cnt < 1) {
255
            debug(DBG_DBG, cnt ? "raddtlsget: connection lost" : "raddtlsget: timeout");
256 257 258
            free(rad);
            return NULL;
        }
259

260 261
        if (len >= 20)
            break;
262

263 264 265
        free(rad);
        debug(DBG_WARN, "raddtlsget: packet smaller than minimum radius size");
    }
266

267 268 269 270
    debug(DBG_DBG, "raddtlsget: got %d bytes", len);
    return rad;
}

271 272 273 274
void *dtlsserverwr(void *arg) {
    int cnt;
    unsigned long error;
    struct client *client = (struct client *)arg;
275
    struct gqueue *replyq;
venaas's avatar
venaas committed
276
    struct request *reply;
277

278
    debug(DBG_DBG, "dtlsserverwr: starting for %s", addr2string(client->addr));
279 280 281 282
    replyq = client->replyq;
    for (;;) {
	pthread_mutex_lock(&replyq->mutex);
	while (!list_first(replyq->entries)) {
283
	    if (client->ssl) {
284 285 286 287 288 289 290 291 292 293 294 295
		debug(DBG_DBG, "dtlsserverwr: waiting for signal");
		pthread_cond_wait(&replyq->cond, &replyq->mutex);
		debug(DBG_DBG, "dtlsserverwr: got signal");
	    }
	    if (!client->ssl) {
		/* ssl might have changed while waiting */
		pthread_mutex_unlock(&replyq->mutex);
		debug(DBG_DBG, "dtlsserverwr: exiting as requested");
		ERR_remove_state(0);
		pthread_exit(NULL);
	    }
	}
venaas's avatar
venaas committed
296
	reply = (struct request *)list_shift(replyq->entries);
297
	pthread_mutex_unlock(&replyq->mutex);
venaas's avatar
venaas committed
298
	cnt = SSL_write(client->ssl, reply->replybuf, RADLEN(reply->replybuf));
299
	if (cnt > 0)
300 301
	    debug(DBG_DBG, "dtlsserverwr: sent %d bytes, Radius packet of length %d to %s",
		  cnt, RADLEN(reply->replybuf), addr2string(client->addr));
302 303 304
	else
	    while ((error = ERR_get_error()))
		debug(DBG_ERR, "dtlsserverwr: SSL: %s", ERR_error_string(error, NULL));
venaas's avatar
venaas committed
305
	freerq(reply);
306 307 308
    }
}

309
void dtlsserverrd(struct client *client) {
310 311
    struct request *rq;
    uint8_t *buf;
312
    pthread_t dtlsserverwrth;
313

314
    debug(DBG_DBG, "dtlsserverrd: starting for %s", addr2string(client->addr));
315

316
    if (pthread_create(&dtlsserverwrth, &pthread_attr, dtlsserverwr, (void *)client)) {
317 318 319 320 321
	debug(DBG_ERR, "dtlsserverrd: pthread_create failed");
	return;
    }

    for (;;) {
322 323
	buf = raddtlsget(client->ssl, client->rbios, IDLE_TIMEOUT);
	if (!buf) {
324
	    debug(DBG_ERR, "dtlsserverrd: connection from %s lost", addr2string(client->addr));
325 326
	    break;
	}
327
	debug(DBG_DBG, "dtlsserverrd: got Radius message from %s", addr2string(client->addr));
328 329 330 331 332 333 334 335
	rq = newrequest();
	if (!rq) {
	    free(buf);
	    continue;
	}
	rq->buf = buf;
	rq->from = client;
	if (!radsrv(rq)) {
336
	    debug(DBG_ERR, "dtlsserverrd: message authentication/validation failed, closing connection from %s", addr2string(client->addr));
337 338 339
	    break;
	}
    }
340

341 342 343 344 345 346 347 348
    /* stop writer by setting ssl to NULL and give signal in case waiting for data */
    client->ssl = NULL;

    pthread_mutex_lock(&client->replyq->mutex);
    pthread_cond_signal(&client->replyq->cond);
    pthread_mutex_unlock(&client->replyq->mutex);
    debug(DBG_DBG, "dtlsserverrd: waiting for writer to end");
    pthread_join(dtlsserverwrth, NULL);
349
    debug(DBG_DBG, "dtlsserverrd: reader for %s exiting", addr2string(client->addr));
350 351 352
}

void *dtlsservernew(void *arg) {
353 354
    struct dtlsservernewparams *params = (struct dtlsservernewparams *)arg;
    struct client *client;
355 356 357
    struct clsrvconf *conf;
    struct list_node *cur = NULL;
    SSL *ssl = NULL;
358
    X509 *cert = NULL;
359
    SSL_CTX *ctx = NULL;
360
    uint8_t delay = 60;
361
    struct tls *accepted_tls = NULL;
362 363

    debug(DBG_DBG, "dtlsservernew: starting");
venaas's avatar
venaas committed
364
    conf = find_clconf(handle, (struct sockaddr *)&params->addr, NULL);
365
    if (conf) {
venaas's avatar
venaas committed
366
	ctx = tlsgetctx(handle, conf->tlsconf);
367 368 369
	if (!ctx)
	    goto exit;
	ssl = dtlsacccon(1, ctx, params->sock, (struct sockaddr *)&params->addr, params->sesscache->rbios);
370 371 372 373 374
	if (!ssl)
	    goto exit;
	cert = verifytlscert(ssl);
        if (!cert)
            goto exit;
375
        accepted_tls = conf->tlsconf;
376 377 378
    }

    while (conf) {
379
	if (accepted_tls == conf->tlsconf && verifyconfcert(cert, conf)) {
380
	    X509_free(cert);
381
	    client = addclient(conf, 1);
382
	    if (client) {
383
		client->sock = params->sock;
384
		client->addr = addr_copy((struct sockaddr *)&params->addr);
385
		client->rbios = params->sesscache->rbios;
386 387 388
		client->ssl = ssl;
		dtlsserverrd(client);
		removeclient(client);
389
		delay = 0;
390 391 392 393 394
	    } else {
		debug(DBG_WARN, "dtlsservernew: failed to create new client instance");
	    }
	    goto exit;
	}
venaas's avatar
venaas committed
395
	conf = find_clconf(handle, (struct sockaddr *)&params->addr, &cur);
396 397
    }
    debug(DBG_WARN, "dtlsservernew: ignoring request, no matching TLS client");
398 399 400 401

    if (cert)
	X509_free(cert);

402
exit:
403 404 405 406
    if (ssl) {
	SSL_shutdown(ssl);
	SSL_free(ssl);
    }
407 408 409 410 411 412 413
    pthread_mutex_lock(&params->sesscache->mutex);
    freebios(params->sesscache->rbios);
    params->sesscache->rbios = NULL;
    gettimeofday(&params->sesscache->expiry, NULL);
    params->sesscache->expiry.tv_sec += delay;
    pthread_mutex_unlock(&params->sesscache->mutex);
    free(params);
414 415
    ERR_remove_state(0);
    pthread_exit(NULL);
416 417 418 419 420 421 422
    debug(DBG_DBG, "dtlsservernew: exiting");
}

void cacheexpire(struct hash *cache, struct timeval *last) {
    struct timeval now;
    struct hash_entry *he;
    struct sessioncacheentry *e;
423

424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
    gettimeofday(&now, NULL);
    if (now.tv_sec - last->tv_sec < 19)
	return;

    for (he = hash_first(cache); he; he = hash_next(he)) {
	e = (struct sessioncacheentry *)he->data;
	pthread_mutex_lock(&e->mutex);
	if (!e->expiry.tv_sec || e->expiry.tv_sec > now.tv_sec) {
	    pthread_mutex_unlock(&e->mutex);
	    continue;
	}
	debug(DBG_DBG, "cacheexpire: freeing entry");
	hash_extract(cache, he->key, he->keylen);
	if (e->rbios) {
	    freebios(e->rbios);
	    e->rbios = NULL;
	}
	pthread_mutex_unlock(&e->mutex);
	pthread_mutex_destroy(&e->mutex);
    }
    last->tv_sec = now.tv_sec;
}

void *udpdtlsserverrd(void *arg) {
    int ndesc, cnt, s = *(int *)arg;
    unsigned char buf[4];
    struct sockaddr_storage from;
    socklen_t fromlen = sizeof(from);
    struct dtlsservernewparams *params;
    fd_set readfds;
    struct timeval timeout, lastexpiry;
    pthread_t dtlsserverth;
    struct hash *sessioncache;
    struct sessioncacheentry *cacheentry;
458

459 460 461 462
    sessioncache = hash_create();
    if (!sessioncache)
	debugx(1, DBG_ERR, "udpdtlsserverrd: malloc failed");
    gettimeofday(&lastexpiry, NULL);
463

464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
    for (;;) {
	FD_ZERO(&readfds);
        FD_SET(s, &readfds);
	memset(&timeout, 0, sizeof(struct timeval));
	timeout.tv_sec = 60;
	ndesc = select(s + 1, &readfds, NULL, NULL, &timeout);
	if (ndesc < 1) {
	    cacheexpire(sessioncache, &lastexpiry);
	    continue;
	}
	cnt = recvfrom(s, buf, 4, MSG_PEEK | MSG_TRUNC, (struct sockaddr *)&from, &fromlen);
	if (cnt == -1) {
	    debug(DBG_WARN, "udpdtlsserverrd: recv failed");
	    cacheexpire(sessioncache, &lastexpiry);
	    continue;
	}
	cacheentry = hash_read(sessioncache, &from, fromlen);
	if (cacheentry) {
	    debug(DBG_DBG, "udpdtlsserverrd: cache hit");
	    pthread_mutex_lock(&cacheentry->mutex);
	    if (cacheentry->rbios) {
		if (udp2bio(s, cacheentry->rbios, cnt))
486
		    debug(DBG_DBG, "udpdtlsserverrd: got DTLS in UDP from %s", addr2string((struct sockaddr *)&from));
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
	    } else
		recv(s, buf, 1, 0);
	    pthread_mutex_unlock(&cacheentry->mutex);
	    cacheexpire(sessioncache, &lastexpiry);
	    continue;
	}

	/* from new source */
	debug(DBG_DBG, "udpdtlsserverrd: cache miss");
	params = malloc(sizeof(struct dtlsservernewparams));
	if (!params) {
	    cacheexpire(sessioncache, &lastexpiry);
	    recv(s, buf, 1, 0);
	    continue;
	}
	memset(params, 0, sizeof(struct dtlsservernewparams));
	params->sesscache = malloc(sizeof(struct sessioncacheentry));
	if (!params->sesscache) {
	    free(params);
	    cacheexpire(sessioncache, &lastexpiry);
	    recv(s, buf, 1, 0);
	    continue;
	}
	memset(params->sesscache, 0, sizeof(struct sessioncacheentry));
	pthread_mutex_init(&params->sesscache->mutex, NULL);
	params->sesscache->rbios = newqueue();
	if (hash_insert(sessioncache, &from, fromlen, params->sesscache)) {
	    params->sock = s;
	    memcpy(&params->addr, &from, fromlen);

	    if (udp2bio(s, params->sesscache->rbios, cnt)) {
518
		debug(DBG_DBG, "udpdtlsserverrd: got DTLS in UDP from %s", addr2string((struct sockaddr *)&from));
519
		if (!pthread_create(&dtlsserverth, &pthread_attr, dtlsservernew, (void *)params)) {
520 521 522 523 524 525 526 527 528 529 530 531 532 533
		    pthread_detach(dtlsserverth);
		    cacheexpire(sessioncache, &lastexpiry);
		    continue;
		}
		debug(DBG_ERR, "udpdtlsserverrd: pthread_create failed");
	    }
	    hash_extract(sessioncache, &from, fromlen);
	}
	freebios(params->sesscache->rbios);
	pthread_mutex_destroy(&params->sesscache->mutex);
	free(params->sesscache);
	free(params);
	cacheexpire(sessioncache, &lastexpiry);
    }
534 535 536 537 538 539
}

int dtlsconnect(struct server *server, struct timeval *when, int timeout, char *text) {
    struct timeval now;
    time_t elapsed;
    X509 *cert;
540
    SSL_CTX *ctx = NULL;
541
    struct hostportres *hp;
542

543 544 545 546 547 548 549 550 551
    debug(DBG_DBG, "dtlsconnect: called from %s", text);
    pthread_mutex_lock(&server->lock);
    if (when && memcmp(&server->lastconnecttry, when, sizeof(struct timeval))) {
	/* already reconnected, nothing to do */
	debug(DBG_DBG, "dtlsconnect(%s): seems already reconnected", text);
	pthread_mutex_unlock(&server->lock);
	return 1;
    }

552
    hp = (struct hostportres *)list_first(server->conf->hostports)->data;
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
    for (;;) {
	gettimeofday(&now, NULL);
	elapsed = now.tv_sec - server->lastconnecttry.tv_sec;

	if (timeout && server->lastconnecttry.tv_sec && elapsed > timeout) {
	    debug(DBG_DBG, "dtlsconnect: timeout");
	    SSL_free(server->ssl);
	    server->ssl = NULL;
	    pthread_mutex_unlock(&server->lock);
	    return 0;
	}

	if (server->connectionok) {
	    server->connectionok = 0;
	    sleep(2);
	} else if (elapsed < 1)
	    sleep(2);
	else if (elapsed < 60) {
	    debug(DBG_INFO, "dtlsconnect: sleeping %lds", elapsed);
	    sleep(elapsed);
	} else if (elapsed < 100000) {
	    debug(DBG_INFO, "dtlsconnect: sleeping %ds", 60);
	    sleep(60);
	} else
	    server->lastconnecttry.tv_sec = now.tv_sec;  /* no sleep at startup */
578
	debug(DBG_WARN, "dtlsconnect: trying to open DTLS connection to %s port %s", hp->host, hp->port);
579 580

	SSL_free(server->ssl);
581
	server->ssl = NULL;
venaas's avatar
venaas committed
582
	ctx = tlsgetctx(handle, server->conf->tlsconf);
583 584
	if (!ctx)
	    continue;
585
	server->ssl = dtlsacccon(0, ctx, server->sock, hp->addrinfo->ai_addr, server->rbios);
586 587 588
	if (!server->ssl)
	    continue;
	debug(DBG_DBG, "dtlsconnect: DTLS: ok");
589

590 591 592
	cert = verifytlscert(server->ssl);
	if (!cert)
	    continue;
593

594 595 596 597 598
	if (verifyconfcert(cert, server->conf))
	    break;
	X509_free(cert);
    }
    X509_free(cert);
599
    debug(DBG_WARN, "dtlsconnect: DTLS connection to %s port %s up", hp->host, hp->port);
600
    server->connectionok = 1;
601 602 603 604 605 606 607 608 609 610
    gettimeofday(&server->lastconnecttry, NULL);
    pthread_mutex_unlock(&server->lock);
    return 1;
}

int clientradputdtls(struct server *server, unsigned char *rad) {
    int cnt;
    size_t len;
    unsigned long error;
    struct clsrvconf *conf = server->conf;
611

612
    if (!server->connectionok)
613
	return 0;
614
    len = RADLEN(rad);
615
    if ((cnt = SSL_write(server->ssl, rad, len)) <= 0) {
616 617
	while ((error = ERR_get_error()))
	    debug(DBG_ERR, "clientradputdtls: DTLS: %s", ERR_error_string(error, NULL));
618
	return 0;
619
    }
620
    debug(DBG_DBG, "clientradputdtls: Sent %d bytes, Radius packet of length %d to DTLS peer %s", cnt, len, conf->name);
621 622 623 624 625 626 627 628 629 630 631
    return 1;
}

/* reads UDP containing DTLS and passes it on to dtlsclientrd */
void *udpdtlsclientrd(void *arg) {
    int cnt, s = *(int *)arg;
    unsigned char buf[4];
    struct sockaddr_storage from;
    socklen_t fromlen = sizeof(from);
    struct clsrvconf *conf;
    fd_set readfds;
632

633 634 635 636 637 638 639 640 641 642
    for (;;) {
	FD_ZERO(&readfds);
        FD_SET(s, &readfds);
	if (select(s + 1, &readfds, NULL, NULL, NULL) < 1)
	    continue;
	cnt = recvfrom(s, buf, 4, MSG_PEEK | MSG_TRUNC, (struct sockaddr *)&from, &fromlen);
	if (cnt == -1) {
	    debug(DBG_WARN, "udpdtlsclientrd: recv failed");
	    continue;
	}
643

venaas's avatar
venaas committed
644
	conf = find_srvconf(handle, (struct sockaddr *)&from, NULL);
645
	if (!conf) {
646
	    debug(DBG_WARN, "udpdtlsclientrd: got packet from wrong or unknown DTLS peer %s, ignoring", addr2string((struct sockaddr *)&from));
647 648 649 650
	    recv(s, buf, 4, 0);
	    continue;
	}
	if (udp2bio(s, conf->servers->rbios, cnt))
651
	    debug(DBG_DBG, "radudpget: got DTLS in UDP from %s", addr2string((struct sockaddr *)&from));
652 653 654 655 656 657 658
    }
}

void *dtlsclientrd(void *arg) {
    struct server *server = (struct server *)arg;
    unsigned char *buf;
    struct timeval lastconnecttry;
659
    int secs;
660

661 662 663
    for (;;) {
	/* yes, lastconnecttry is really necessary */
	lastconnecttry = server->lastconnecttry;
664
	for (secs = 0; !(buf = raddtlsget(server->ssl, server->rbios, 10)) && !server->lostrqs && secs < IDLE_TIMEOUT; secs += 10);
665 666 667 668
	if (!buf) {
	    dtlsconnect(server, &lastconnecttry, 0, "dtlsclientrd");
	    continue;
	}
venaas's avatar
venaas committed
669
	replyh(server, buf);
670
    }
671 672 673
    ERR_remove_state(0);
    server->clientrdgone = 1;
    return NULL;
674
}
venaas's avatar
venaas committed
675 676

void addserverextradtls(struct clsrvconf *conf) {
677
    switch (((struct hostportres *)list_first(conf->hostports)->data)->addrinfo->ai_family) {
venaas's avatar
venaas committed
678 679
    case AF_INET:
	if (client4_sock < 0) {
venaas's avatar
venaas committed
680
	    client4_sock = bindtoaddr(srcres, AF_INET, 0, 1);
venaas's avatar
venaas committed
681
	    if (client4_sock < 0)
682
		debugx(1, DBG_ERR, "addserver: failed to create client socket for server %s", conf->name);
venaas's avatar
venaas committed
683 684 685 686 687
	}
	conf->servers->sock = client4_sock;
	break;
    case AF_INET6:
	if (client6_sock < 0) {
venaas's avatar
venaas committed
688
	    client6_sock = bindtoaddr(srcres, AF_INET6, 0, 1);
venaas's avatar
venaas committed
689
	    if (client6_sock < 0)
690
		debugx(1, DBG_ERR, "addserver: failed to create client socket for server %s", conf->name);
venaas's avatar
venaas committed
691 692 693 694 695 696 697 698 699 700
	}
	conf->servers->sock = client6_sock;
	break;
    default:
	debugx(1, DBG_ERR, "addserver: unsupported address family");
    }
}

void initextradtls() {
    pthread_t cl4th, cl6th;
venaas's avatar
venaas committed
701 702 703 704 705

    if (srcres) {
	freeaddrinfo(srcres);
	srcres = NULL;
    }
706

venaas's avatar
venaas committed
707
    if (client4_sock >= 0)
708
	if (pthread_create(&cl4th, &pthread_attr, udpdtlsclientrd, (void *)&client4_sock))
venaas's avatar
venaas committed
709 710
	    debugx(1, DBG_ERR, "pthread_create failed");
    if (client6_sock >= 0)
711
	if (pthread_create(&cl6th, &pthread_attr, udpdtlsclientrd, (void *)&client6_sock))
venaas's avatar
venaas committed
712 713
	    debugx(1, DBG_ERR, "pthread_create failed");
}
714 715 716 717 718
#else
const struct protodefs *dtlsinit(uint8_t h) {
    return NULL;
}
#endif
719 720 721 722

/* Local Variables: */
/* c-file-style: "stroustrup" */
/* End: */