tls.c 13 KB
Newer Older
venaas's avatar
venaas committed
1
/*
2
 * Copyright (C) 2006-2009 Stig Venaas <venaas@uninett.no>
venaas's avatar
venaas committed
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 29
 *
 * 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>
#include "radsecproxy.h"
30
#include "hostport.h"
venaas's avatar
venaas committed
31

32 33 34 35
#ifdef RADPROT_TLS
#include "debug.h"
#include "util.h"

venaas's avatar
venaas committed
36 37
static void setprotoopts(struct commonprotoopts *opts);
static char **getlistenerargs();
venaas's avatar
venaas committed
38 39 40 41
void *tlslistener(void *arg);
int tlsconnect(struct server *server, struct timeval *when, int timeout, char *text);
void *tlsclientrd(void *arg);
int clientradputtls(struct server *server, unsigned char *rad);
venaas's avatar
venaas committed
42
void tlssetsrcres();
venaas's avatar
venaas committed
43 44 45

static const struct protodefs protodefs = {
    "tls",
46
    "radsec", /* secretdefault */
venaas's avatar
venaas committed
47 48 49 50 51 52 53
    SOCK_STREAM, /* socktype */
    "2083", /* portdefault */
    0, /* retrycountdefault */
    0, /* retrycountmax */
    REQUEST_RETRY_INTERVAL * REQUEST_RETRY_COUNT, /* retryintervaldefault */
    60, /* retryintervalmax */
    DUPLICATE_INTERVAL, /* duplicateintervaldefault */
venaas's avatar
venaas committed
54 55
    setprotoopts, /* setprotoopts */
    getlistenerargs, /* getlistenerargs */
venaas's avatar
venaas committed
56 57 58 59 60 61 62 63 64
    tlslistener, /* listener */
    tlsconnect, /* connecter */
    tlsclientrd, /* clientconnreader */
    clientradputtls, /* clientradput */
    NULL, /* addclient */
    NULL, /* addserverextra */
    tlssetsrcres, /* setsrcres */
    NULL /* initextra */
};
venaas's avatar
venaas committed
65

venaas's avatar
venaas committed
66
static struct addrinfo *srcres = NULL;
venaas's avatar
venaas committed
67
static uint8_t handle;
venaas's avatar
venaas committed
68
static struct commonprotoopts *protoopts = NULL;
venaas's avatar
venaas committed
69 70 71 72 73

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

venaas's avatar
venaas committed
75 76 77 78 79 80 81 82 83
static void setprotoopts(struct commonprotoopts *opts) {
    protoopts = opts;
}

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

void tlssetsrcres() {
venaas's avatar
venaas committed
84
    if (!srcres)
85 86 87
	srcres =
            resolvepassiveaddrinfo(protoopts ? protoopts->sourcearg : NULL,
                                   AF_UNSPEC, NULL, protodefs.socktype);
venaas's avatar
venaas committed
88 89
}

venaas's avatar
venaas committed
90 91 92 93
int tlsconnect(struct server *server, struct timeval *when, int timeout, char *text) {
    struct timeval now;
    time_t elapsed;
    X509 *cert;
94
    SSL_CTX *ctx = NULL;
venaas's avatar
venaas committed
95
    unsigned long error;
96

venaas's avatar
venaas committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
    debug(DBG_DBG, "tlsconnect: 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, "tlsconnect(%s): seems already reconnected", text);
	pthread_mutex_unlock(&server->lock);
	return 1;
    }

    for (;;) {
	gettimeofday(&now, NULL);
	elapsed = now.tv_sec - server->lastconnecttry.tv_sec;
	if (timeout && server->lastconnecttry.tv_sec && elapsed > timeout) {
	    debug(DBG_DBG, "tlsconnect: timeout");
	    if (server->sock >= 0)
		close(server->sock);
	    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, "tlsconnect: sleeping %lds", elapsed);
	    sleep(elapsed);
	} else if (elapsed < 100000) {
	    debug(DBG_INFO, "tlsconnect: sleeping %ds", 60);
	    sleep(60);
	} else
	    server->lastconnecttry.tv_sec = now.tv_sec;  /* no sleep at startup */
131

venaas's avatar
venaas committed
132 133
	if (server->sock >= 0)
	    close(server->sock);
134
	if ((server->sock = connecttcphostlist(server->conf->hostports, srcres)) < 0)
venaas's avatar
venaas committed
135
	    continue;
136

venaas's avatar
venaas committed
137
	SSL_free(server->ssl);
138
	server->ssl = NULL;
venaas's avatar
venaas committed
139
	ctx = tlsgetctx(handle, server->conf->tlsconf);
140 141 142 143 144 145
	if (!ctx)
	    continue;
	server->ssl = SSL_new(ctx);
	if (!server->ssl)
	    continue;

venaas's avatar
venaas committed
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
	SSL_set_fd(server->ssl, server->sock);
	if (SSL_connect(server->ssl) <= 0) {
	    while ((error = ERR_get_error()))
		debug(DBG_ERR, "tlsconnect: TLS: %s", ERR_error_string(error, NULL));
	    continue;
	}
	cert = verifytlscert(server->ssl);
	if (!cert)
	    continue;
	if (verifyconfcert(cert, server->conf)) {
	    X509_free(cert);
	    break;
	}
	X509_free(cert);
    }
161
    debug(DBG_WARN, "tlsconnect: TLS connection to %s up", server->conf->name);
162
    server->connectionok = 1;
venaas's avatar
venaas committed
163 164 165 166 167 168 169 170 171
    gettimeofday(&server->lastconnecttry, NULL);
    pthread_mutex_unlock(&server->lock);
    return 1;
}

/* timeout in seconds, 0 means no timeout (blocking), returns when num bytes have been read, or timeout */
/* returns 0 on timeout, -1 on error and num if ok */
int sslreadtimeout(SSL *ssl, unsigned char *buf, int num, int timeout) {
    int s, ndesc, cnt, len;
172
    fd_set readfds;
venaas's avatar
venaas committed
173
    struct timeval timer;
174

venaas's avatar
venaas committed
175 176 177 178 179
    s = SSL_get_fd(ssl);
    if (s < 0)
	return -1;
    /* make socket non-blocking? */
    for (len = 0; len < num; len += cnt) {
180 181 182 183 184 185 186 187 188 189
	if (SSL_pending(ssl) == 0) {
            FD_ZERO(&readfds);
            FD_SET(s, &readfds);
            if (timeout) {
                timer.tv_sec = timeout;
                timer.tv_usec = 0;
            }
	    ndesc = select(s + 1, &readfds, NULL, NULL, timeout ? &timer : NULL);
            if (ndesc < 1)
                return ndesc;
venaas's avatar
venaas committed
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
	}

	cnt = SSL_read(ssl, buf + len, num - len);
	if (cnt <= 0)
	    switch (SSL_get_error(ssl, cnt)) {
	    case SSL_ERROR_WANT_READ:
	    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;
}

/* timeout in seconds, 0 means no timeout (blocking) */
unsigned char *radtlsget(SSL *ssl, int timeout) {
    int cnt, len;
    unsigned char buf[4], *rad;

    for (;;) {
	cnt = sslreadtimeout(ssl, buf, 4, timeout);
	if (cnt < 1) {
	    debug(DBG_DBG, cnt ? "radtlsget: connection lost" : "radtlsget: timeout");
	    return NULL;
	}

	len = RADLEN(buf);
	rad = malloc(len);
	if (!rad) {
	    debug(DBG_ERR, "radtlsget: malloc failed");
	    continue;
	}
	memcpy(rad, buf, 4);
229

venaas's avatar
venaas committed
230 231 232 233 234 235
	cnt = sslreadtimeout(ssl, rad + 4, len - 4, timeout);
	if (cnt < 1) {
	    debug(DBG_DBG, cnt ? "radtlsget: connection lost" : "radtlsget: timeout");
	    free(rad);
	    return NULL;
	}
236

venaas's avatar
venaas committed
237 238
	if (len >= 20)
	    break;
239

venaas's avatar
venaas committed
240 241 242
	free(rad);
	debug(DBG_WARN, "radtlsget: packet smaller than minimum radius size");
    }
243

venaas's avatar
venaas committed
244 245 246 247 248 249 250 251 252
    debug(DBG_DBG, "radtlsget: got %d bytes", len);
    return rad;
}

int clientradputtls(struct server *server, unsigned char *rad) {
    int cnt;
    size_t len;
    unsigned long error;
    struct clsrvconf *conf = server->conf;
253 254 255

    if (!server->connectionok)
	return 0;
venaas's avatar
venaas committed
256
    len = RADLEN(rad);
257
    if ((cnt = SSL_write(server->ssl, rad, len)) <= 0) {
venaas's avatar
venaas committed
258 259
	while ((error = ERR_get_error()))
	    debug(DBG_ERR, "clientradputtls: TLS: %s", ERR_error_string(error, NULL));
260
	return 0;
venaas's avatar
venaas committed
261 262
    }

263
    debug(DBG_DBG, "clientradputtls: Sent %d bytes, Radius packet of length %d to TLS peer %s", cnt, len, conf->name);
venaas's avatar
venaas committed
264 265 266 267 268 269 270
    return 1;
}

void *tlsclientrd(void *arg) {
    struct server *server = (struct server *)arg;
    unsigned char *buf;
    struct timeval now, lastconnecttry;
271

venaas's avatar
venaas committed
272 273 274 275 276 277 278 279 280 281 282
    for (;;) {
	/* yes, lastconnecttry is really necessary */
	lastconnecttry = server->lastconnecttry;
	buf = radtlsget(server->ssl, server->dynamiclookuparg ? IDLE_TIMEOUT : 0);
	if (!buf) {
	    if (server->dynamiclookuparg)
		break;
	    tlsconnect(server, &lastconnecttry, 0, "tlsclientrd");
	    continue;
	}

venaas's avatar
venaas committed
283 284
	replyh(server, buf);

venaas's avatar
venaas committed
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
	if (server->dynamiclookuparg) {
	    gettimeofday(&now, NULL);
	    if (now.tv_sec - server->lastreply.tv_sec > IDLE_TIMEOUT) {
		debug(DBG_INFO, "tlsclientrd: idle timeout for %s", server->conf->name);
		break;
	    }
	}
    }
    ERR_remove_state(0);
    server->clientrdgone = 1;
    return NULL;
}

void *tlsserverwr(void *arg) {
    int cnt;
    unsigned long error;
    struct client *client = (struct client *)arg;
302
    struct gqueue *replyq;
venaas's avatar
venaas committed
303
    struct request *reply;
304

305
    debug(DBG_DBG, "tlsserverwr: starting for %s", addr2string(client->addr));
venaas's avatar
venaas committed
306 307 308 309
    replyq = client->replyq;
    for (;;) {
	pthread_mutex_lock(&replyq->mutex);
	while (!list_first(replyq->entries)) {
310
	    if (client->ssl) {
venaas's avatar
venaas committed
311 312 313 314 315 316 317 318 319 320 321 322
		debug(DBG_DBG, "tlsserverwr: waiting for signal");
		pthread_cond_wait(&replyq->cond, &replyq->mutex);
		debug(DBG_DBG, "tlsserverwr: got signal");
	    }
	    if (!client->ssl) {
		/* ssl might have changed while waiting */
		pthread_mutex_unlock(&replyq->mutex);
		debug(DBG_DBG, "tlsserverwr: exiting as requested");
		ERR_remove_state(0);
		pthread_exit(NULL);
	    }
	}
venaas's avatar
venaas committed
323
	reply = (struct request *)list_shift(replyq->entries);
venaas's avatar
venaas committed
324
	pthread_mutex_unlock(&replyq->mutex);
venaas's avatar
venaas committed
325
	cnt = SSL_write(client->ssl, reply->replybuf, RADLEN(reply->replybuf));
venaas's avatar
venaas committed
326
	if (cnt > 0)
327 328
	    debug(DBG_DBG, "tlsserverwr: sent %d bytes, Radius packet of length %d to %s",
		  cnt, RADLEN(reply->replybuf), addr2string(client->addr));
venaas's avatar
venaas committed
329 330 331
	else
	    while ((error = ERR_get_error()))
		debug(DBG_ERR, "tlsserverwr: SSL: %s", ERR_error_string(error, NULL));
venaas's avatar
venaas committed
332
	freerq(reply);
venaas's avatar
venaas committed
333 334 335 336
    }
}

void tlsserverrd(struct client *client) {
337 338
    struct request *rq;
    uint8_t *buf;
venaas's avatar
venaas committed
339
    pthread_t tlsserverwrth;
340

341
    debug(DBG_DBG, "tlsserverrd: starting for %s", addr2string(client->addr));
342

343
    if (pthread_create(&tlsserverwrth, &pthread_attr, tlsserverwr, (void *)client)) {
venaas's avatar
venaas committed
344 345 346 347 348
	debug(DBG_ERR, "tlsserverrd: pthread_create failed");
	return;
    }

    for (;;) {
349 350
	buf = radtlsget(client->ssl, 0);
	if (!buf) {
351
	    debug(DBG_ERR, "tlsserverrd: connection from %s lost", addr2string(client->addr));
venaas's avatar
venaas committed
352 353
	    break;
	}
354
	debug(DBG_DBG, "tlsserverrd: got Radius message from %s", addr2string(client->addr));
355 356 357 358 359 360 361 362
	rq = newrequest();
	if (!rq) {
	    free(buf);
	    continue;
	}
	rq->buf = buf;
	rq->from = client;
	if (!radsrv(rq)) {
363
	    debug(DBG_ERR, "tlsserverrd: message authentication/validation failed, closing connection from %s", addr2string(client->addr));
venaas's avatar
venaas committed
364 365 366
	    break;
	}
    }
367

venaas's avatar
venaas committed
368 369 370 371 372 373 374
    /* 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, "tlsserverrd: waiting for writer to end");
    pthread_join(tlsserverwrth, NULL);
375
    debug(DBG_DBG, "tlsserverrd: reader for %s exiting", addr2string(client->addr));
venaas's avatar
venaas committed
376 377 378 379 380
}

void *tlsservernew(void *arg) {
    int s;
    struct sockaddr_storage from;
venaas's avatar
venaas committed
381
    socklen_t fromlen = sizeof(from);
venaas's avatar
venaas committed
382 383 384 385
    struct clsrvconf *conf;
    struct list_node *cur = NULL;
    SSL *ssl = NULL;
    X509 *cert = NULL;
386
    SSL_CTX *ctx = NULL;
venaas's avatar
venaas committed
387 388
    unsigned long error;
    struct client *client;
389
    struct tls *accepted_tls = NULL;
venaas's avatar
venaas committed
390 391 392 393 394 395

    s = *(int *)arg;
    if (getpeername(s, (struct sockaddr *)&from, &fromlen)) {
	debug(DBG_DBG, "tlsservernew: getpeername failed, exiting");
	goto exit;
    }
396
    debug(DBG_WARN, "tlsservernew: incoming TLS connection from %s", addr2string((struct sockaddr *)&from));
venaas's avatar
venaas committed
397

venaas's avatar
venaas committed
398
    conf = find_clconf(handle, (struct sockaddr *)&from, &cur);
venaas's avatar
venaas committed
399
    if (conf) {
venaas's avatar
venaas committed
400
	ctx = tlsgetctx(handle, conf->tlsconf);
401 402 403 404 405
	if (!ctx)
	    goto exit;
	ssl = SSL_new(ctx);
	if (!ssl)
	    goto exit;
venaas's avatar
venaas committed
406 407 408 409 410 411 412 413 414 415 416
	SSL_set_fd(ssl, s);

	if (SSL_accept(ssl) <= 0) {
	    while ((error = ERR_get_error()))
		debug(DBG_ERR, "tlsservernew: SSL: %s", ERR_error_string(error, NULL));
	    debug(DBG_ERR, "tlsservernew: SSL_accept failed");
	    goto exit;
	}
	cert = verifytlscert(ssl);
	if (!cert)
	    goto exit;
417
        accepted_tls = conf->tlsconf;
venaas's avatar
venaas committed
418
    }
419

venaas's avatar
venaas committed
420
    while (conf) {
421 422 423 424 425 426 427 428 429 430 431 432 433
        if (accepted_tls == conf->tlsconf && verifyconfcert(cert, conf)) {
            X509_free(cert);
            client = addclient(conf, 1);
            if (client) {
                client->ssl = ssl;
                client->addr = addr_copy((struct sockaddr *)&from);
                tlsserverrd(client);
                removeclient(client);
            } else
                debug(DBG_WARN, "tlsservernew: failed to create new client instance");
            goto exit;
        }
        conf = find_clconf(handle, (struct sockaddr *)&from, &cur);
venaas's avatar
venaas committed
434 435 436 437 438
    }
    debug(DBG_WARN, "tlsservernew: ignoring request, no matching TLS client");
    if (cert)
	X509_free(cert);

439
exit:
440 441 442 443
    if (ssl) {
	SSL_shutdown(ssl);
	SSL_free(ssl);
    }
venaas's avatar
venaas committed
444 445 446 447 448 449 450 451 452 453
    ERR_remove_state(0);
    shutdown(s, SHUT_RDWR);
    close(s);
    pthread_exit(NULL);
}

void *tlslistener(void *arg) {
    pthread_t tlsserverth;
    int s, *sp = (int *)arg;
    struct sockaddr_storage from;
venaas's avatar
venaas committed
454
    socklen_t fromlen = sizeof(from);
venaas's avatar
venaas committed
455 456 457 458 459 460 461 462 463

    listen(*sp, 0);

    for (;;) {
	s = accept(*sp, (struct sockaddr *)&from, &fromlen);
	if (s < 0) {
	    debug(DBG_WARN, "accept failed");
	    continue;
	}
464
	if (pthread_create(&tlsserverth, &pthread_attr, tlsservernew, (void *)&s)) {
venaas's avatar
venaas committed
465 466 467 468 469 470 471 472 473 474
	    debug(DBG_ERR, "tlslistener: pthread_create failed");
	    shutdown(s, SHUT_RDWR);
	    close(s);
	    continue;
	}
	pthread_detach(tlsserverth);
    }
    free(sp);
    return NULL;
}
475 476 477 478 479
#else
const struct protodefs *tlsinit(uint8_t h) {
    return NULL;
}
#endif
480 481 482 483

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