tls.c 13.4 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
	}

	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);
223
224
225
226
	if (len < 4) {
	    debug(DBG_ERR, "radtlsget: length too small");
	    continue;
	}
venaas's avatar
venaas committed
227
228
229
230
231
232
	rad = malloc(len);
	if (!rad) {
	    debug(DBG_ERR, "radtlsget: malloc failed");
	    continue;
	}
	memcpy(rad, buf, 4);
233

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

venaas's avatar
venaas committed
241
242
	if (len >= 20)
	    break;
243

venaas's avatar
venaas committed
244
245
246
	free(rad);
	debug(DBG_WARN, "radtlsget: packet smaller than minimum radius size");
    }
247

venaas's avatar
venaas committed
248
249
250
251
252
253
254
255
256
    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;
257
258
259

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

267
    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
268
269
270
271
272
273
274
    return 1;
}

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

venaas's avatar
venaas committed
276
277
278
279
280
281
282
283
284
285
286
    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
287
288
	replyh(server, buf);

venaas's avatar
venaas committed
289
290
291
292
293
294
295
296
	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;
	    }
	}
    }
297
    debug(DBG_INFO, "tlsclientrd: exiting for %s", server->conf->name);
venaas's avatar
venaas committed
298
    ERR_remove_state(0);
299
300
301
302
303
    SSL_shutdown(server->ssl);
    shutdown(server->sock, SHUT_RDWR);
    close(server->sock);

    /* Wake up clientwr(). */
venaas's avatar
venaas committed
304
    server->clientrdgone = 1;
305
306
307
    pthread_mutex_lock(&server->newrq_mutex);
    pthread_cond_signal(&server->newrq_cond);
    pthread_mutex_unlock(&server->newrq_mutex);
venaas's avatar
venaas committed
308
309
310
311
312
313
314
    return NULL;
}

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

318
    debug(DBG_DBG, "tlsserverwr: starting for %s", addr2string(client->addr));
venaas's avatar
venaas committed
319
320
321
322
    replyq = client->replyq;
    for (;;) {
	pthread_mutex_lock(&replyq->mutex);
	while (!list_first(replyq->entries)) {
323
	    if (client->ssl) {
venaas's avatar
venaas committed
324
325
326
327
328
329
330
331
332
333
334
335
		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
336
	reply = (struct request *)list_shift(replyq->entries);
venaas's avatar
venaas committed
337
	pthread_mutex_unlock(&replyq->mutex);
venaas's avatar
venaas committed
338
	cnt = SSL_write(client->ssl, reply->replybuf, RADLEN(reply->replybuf));
venaas's avatar
venaas committed
339
	if (cnt > 0)
340
341
	    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
342
343
344
	else
	    while ((error = ERR_get_error()))
		debug(DBG_ERR, "tlsserverwr: SSL: %s", ERR_error_string(error, NULL));
venaas's avatar
venaas committed
345
	freerq(reply);
venaas's avatar
venaas committed
346
347
348
349
    }
}

void tlsserverrd(struct client *client) {
350
351
    struct request *rq;
    uint8_t *buf;
venaas's avatar
venaas committed
352
    pthread_t tlsserverwrth;
353

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

356
    if (pthread_create(&tlsserverwrth, &pthread_attr, tlsserverwr, (void *)client)) {
venaas's avatar
venaas committed
357
358
359
360
361
	debug(DBG_ERR, "tlsserverrd: pthread_create failed");
	return;
    }

    for (;;) {
362
	buf = radtlsget(client->ssl, IDLE_TIMEOUT * 3);
363
	if (!buf) {
364
	    debug(DBG_ERR, "tlsserverrd: connection from %s lost", addr2string(client->addr));
venaas's avatar
venaas committed
365
366
	    break;
	}
367
	debug(DBG_DBG, "tlsserverrd: got Radius message from %s", addr2string(client->addr));
368
369
370
371
372
373
374
375
	rq = newrequest();
	if (!rq) {
	    free(buf);
	    continue;
	}
	rq->buf = buf;
	rq->from = client;
	if (!radsrv(rq)) {
376
	    debug(DBG_ERR, "tlsserverrd: message authentication/validation failed, closing connection from %s", addr2string(client->addr));
venaas's avatar
venaas committed
377
378
379
	    break;
	}
    }
380

venaas's avatar
venaas committed
381
382
383
384
385
386
387
    /* 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);
388
    debug(DBG_DBG, "tlsserverrd: reader for %s exiting", addr2string(client->addr));
venaas's avatar
venaas committed
389
390
391
392
393
}

void *tlsservernew(void *arg) {
    int s;
    struct sockaddr_storage from;
venaas's avatar
venaas committed
394
    socklen_t fromlen = sizeof(from);
venaas's avatar
venaas committed
395
396
397
398
    struct clsrvconf *conf;
    struct list_node *cur = NULL;
    SSL *ssl = NULL;
    X509 *cert = NULL;
399
    SSL_CTX *ctx = NULL;
venaas's avatar
venaas committed
400
401
    unsigned long error;
    struct client *client;
402
    struct tls *accepted_tls = NULL;
venaas's avatar
venaas committed
403
404
405
406
407
408

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

venaas's avatar
venaas committed
411
    conf = find_clconf(handle, (struct sockaddr *)&from, &cur);
venaas's avatar
venaas committed
412
    if (conf) {
venaas's avatar
venaas committed
413
	ctx = tlsgetctx(handle, conf->tlsconf);
414
415
416
417
418
	if (!ctx)
	    goto exit;
	ssl = SSL_new(ctx);
	if (!ssl)
	    goto exit;
venaas's avatar
venaas committed
419
420
421
422
423
424
425
426
427
428
429
	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;
430
        accepted_tls = conf->tlsconf;
venaas's avatar
venaas committed
431
    }
432

venaas's avatar
venaas committed
433
    while (conf) {
434
435
436
437
438
439
440
441
442
443
444
445
446
        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
447
448
449
450
451
    }
    debug(DBG_WARN, "tlsservernew: ignoring request, no matching TLS client");
    if (cert)
	X509_free(cert);

452
exit:
453
454
455
456
    if (ssl) {
	SSL_shutdown(ssl);
	SSL_free(ssl);
    }
venaas's avatar
venaas committed
457
458
459
460
461
462
463
464
465
466
    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
467
    socklen_t fromlen = sizeof(from);
venaas's avatar
venaas committed
468
469
470
471
472
473
474
475
476

    listen(*sp, 0);

    for (;;) {
	s = accept(*sp, (struct sockaddr *)&from, &fromlen);
	if (s < 0) {
	    debug(DBG_WARN, "accept failed");
	    continue;
	}
477
	if (pthread_create(&tlsserverth, &pthread_attr, tlsservernew, (void *)&s)) {
venaas's avatar
venaas committed
478
479
480
481
482
483
484
485
486
487
	    debug(DBG_ERR, "tlslistener: pthread_create failed");
	    shutdown(s, SHUT_RDWR);
	    close(s);
	    continue;
	}
	pthread_detach(tlsserverth);
    }
    free(sp);
    return NULL;
}
488
489
490
491
492
#else
const struct protodefs *tlsinit(uint8_t h) {
    return NULL;
}
#endif
493
494
495
496

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