radsecproxy.c 66.5 KB
Newer Older
venaas's avatar
 
venaas committed
1
/*
venaas's avatar
venaas committed
2
 * Copyright (C) 2006, 2007 Stig Venaas <venaas@uninett.no>
venaas's avatar
 
venaas committed
3
4
5
6
7
8
9
 *
 * 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.
 */

/* TODO:
venaas's avatar
venaas committed
10
11
12
 * accounting
 * radius keep alives (server status)
 * setsockopt(keepalive...), check if openssl has some keepalive feature
venaas's avatar
 
venaas committed
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
*/

/* For UDP there is one server instance consisting of udpserverrd and udpserverth
 *              rd is responsible for init and launching wr
 * For TLS there is a server instance that launches tlsserverrd for each TLS peer
 *          each tlsserverrd launches tlsserverwr
 * For each UDP/TLS peer there is clientrd and clientwr, clientwr is responsible
 *          for init and launching rd
 *
 * serverrd will receive a request, processes it and puts it in the requestq of
 *          the appropriate clientwr
 * clientwr monitors its requestq and sends requests
 * clientrd looks for responses, processes them and puts them in the replyq of
 *          the peer the request came from
 * serverwr monitors its reply and sends replies
 *
 * In addition to the main thread, we have:
 * If UDP peers are configured, there will be 2 + 2 * #peers UDP threads
 * If TLS peers are configured, there will initially be 2 * #peers TLS threads
 * For each TLS peer connecting to us there will be 2 more TLS threads
 *       This is only for connected peers
 * Example: With 3 UDP peer and 30 TLS peers, there will be a max of
 *          1 + (2 + 2 * 3) + (2 * 30) + (2 * 30) = 129 threads
*/

venaas's avatar
venaas committed
38
39
#include <sys/socket.h>
#include <netinet/in.h>
venaas's avatar
 
venaas committed
40
#include <netdb.h>
venaas's avatar
venaas committed
41
#include <string.h>
venaas's avatar
 
venaas committed
42
#include <unistd.h>
venaas's avatar
venaas committed
43
#include <sys/time.h>
venaas's avatar
venaas committed
44
#include <libgen.h>
venaas's avatar
 
venaas committed
45
46
47
48
49
#include <pthread.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/md5.h>
venaas's avatar
venaas committed
50
#include <openssl/hmac.h>
venaas's avatar
venaas committed
51
#include "debug.h"
venaas's avatar
venaas committed
52
#include "radsecproxy.h"
venaas's avatar
 
venaas committed
53

54
static struct options options;
venaas's avatar
venaas committed
55
56
static struct client *clients = NULL;
static struct server *servers = NULL;
57
static struct realm *realms = NULL;
58

59
60
static int client_udp_count = 0;
static int client_tls_count = 0;
61
static int client_count = 0;
62
63
static int server_udp_count = 0;
static int server_tls_count = 0;
64
static int server_count = 0;
65
static int realm_count = 0;
venaas's avatar
 
venaas committed
66

venaas's avatar
venaas committed
67
68
static struct peer *tcp_server_listen;
static struct peer *udp_server_listen;
venaas's avatar
 
venaas committed
69
70
71
72
static struct replyq udp_server_replyq;
static int udp_server_sock = -1;
static pthread_mutex_t *ssl_locks;
static long *ssl_lock_count;
73
static SSL_CTX *ssl_ctx = NULL;
venaas's avatar
 
venaas committed
74
75
76
77
78
79
extern int optind;
extern char *optarg;

/* callbacks for making OpenSSL thread safe */
unsigned long ssl_thread_id() {
        return (unsigned long)pthread_self();
venaas's avatar
venaas committed
80
}
venaas's avatar
 
venaas committed
81
82
83
84
85
86
87
88
89

void ssl_locking_callback(int mode, int type, const char *file, int line) {
    if (mode & CRYPTO_LOCK) {
	pthread_mutex_lock(&ssl_locks[type]);
	ssl_lock_count[type]++;
    } else
	pthread_mutex_unlock(&ssl_locks[type]);
}

venaas's avatar
venaas committed
90
91
92
93
94
95
96
97
static int pem_passwd_cb(char *buf, int size, int rwflag, void *userdata) {
    int pwdlen = strlen(userdata);
    if (rwflag != 0 || pwdlen > size) /* not for decryption or too large */
	return 0;
    memcpy(buf, userdata, pwdlen);
    return pwdlen;
}

98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
static int verify_cb(int ok, X509_STORE_CTX *ctx) {
  char buf[256];
  X509 *err_cert;
  int err, depth;

  err_cert = X509_STORE_CTX_get_current_cert(ctx);
  err = X509_STORE_CTX_get_error(ctx);
  depth = X509_STORE_CTX_get_error_depth(ctx);

  if (depth > MAX_CERT_DEPTH) {
      ok = 0;
      err = X509_V_ERR_CERT_CHAIN_TOO_LONG;
      X509_STORE_CTX_set_error(ctx, err);
  }

  if (!ok) {
      X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
115
      debug(DBG_WARN, "verify error: num=%d:%s:depth=%d:%s", err, X509_verify_cert_error_string(err), depth, buf);
116
117
118
119

      switch (err) {
      case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
	  X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
120
	  debug(DBG_WARN, "\tIssuer=%s", buf);
121
122
123
	  break;
      case X509_V_ERR_CERT_NOT_YET_VALID:
      case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
124
	  debug(DBG_WARN, "\tCertificate not yet valid");
125
126
	  break;
      case X509_V_ERR_CERT_HAS_EXPIRED:
127
	  debug(DBG_WARN, "Certificate has expired");
128
129
	  break;
      case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
130
	  debug(DBG_WARN, "Certificate no longer valid (after notAfter)");
131
132
133
	  break;
      }
  }
134
135
136
#ifdef DEBUG  
  printf("certificate verify returns %d\n", ok);
#endif  
137
138
139
  return ok;
}

140
141
SSL_CTX *ssl_init() {
    SSL_CTX *ctx;
venaas's avatar
 
venaas committed
142
    int i;
143
144
    unsigned long error;
    
venaas's avatar
venaas committed
145
    if (!options.tlscertificatefile || !options.tlscertificatekeyfile)
146
	debugx(1, DBG_ERR, "TLSCertificateFile and TLSCertificateKeyFile must be specified for TLS");
venaas's avatar
venaas committed
147
148

    if (!options.tlscacertificatefile && !options.tlscacertificatepath)
149
	debugx(1, DBG_ERR, "CA Certificate file/path need to be configured");
venaas's avatar
 
venaas committed
150
151
152
153
154
155
156
157
158

    ssl_locks = malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
    ssl_lock_count = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
    for (i = 0; i < CRYPTO_num_locks(); i++) {
	ssl_lock_count[i] = 0;
	pthread_mutex_init(&ssl_locks[i], NULL);
    }
    CRYPTO_set_id_callback(ssl_thread_id);
    CRYPTO_set_locking_callback(ssl_locking_callback);
159
160
161
162
163
164
165
166
167
168
169

    SSL_load_error_strings();
    SSL_library_init();

    while (!RAND_status()) {
	time_t t = time(NULL);
	pid_t pid = getpid();
	RAND_seed((unsigned char *)&t, sizeof(time_t));
        RAND_seed((unsigned char *)&pid, sizeof(pid));
    }

170
    ctx = SSL_CTX_new(TLSv1_method());
venaas's avatar
venaas committed
171
172
173
174
    if (options.tlscertificatekeypassword) {
	SSL_CTX_set_default_passwd_cb_userdata(ctx, options.tlscertificatekeypassword);
	SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb);
    }
venaas's avatar
venaas committed
175
176
177
178
179
180
181
182
183
    if (SSL_CTX_use_certificate_chain_file(ctx, options.tlscertificatefile) &&
	SSL_CTX_use_PrivateKey_file(ctx, options.tlscertificatekeyfile, SSL_FILETYPE_PEM) &&
	SSL_CTX_check_private_key(ctx) &&
	SSL_CTX_load_verify_locations(ctx, options.tlscacertificatefile, options.tlscacertificatepath)) {
	SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb);
	SSL_CTX_set_verify_depth(ctx, MAX_CERT_DEPTH + 1);
	return ctx;
    }

184
    while ((error = ERR_get_error()))
185
	debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL));
venaas's avatar
venaas committed
186
    debug(DBG_ERR, "Error initialising SSL/TLS");
187
    exit(1);
188
}    
venaas's avatar
 
venaas committed
189

190
#ifdef DEBUG
venaas's avatar
venaas committed
191
192
193
194
195
196
197
void printauth(char *s, unsigned char *t) {
    int i;
    printf("%s:", s);
    for (i = 0; i < 16; i++)
	    printf("%02x ", t[i]);
    printf("\n");
}
198
#endif
venaas's avatar
venaas committed
199

venaas's avatar
venaas committed
200
int resolvepeer(struct peer *peer, int ai_flags) {
venaas's avatar
venaas committed
201
    struct addrinfo hints, *addrinfo;
venaas's avatar
 
venaas committed
202
203
    
    memset(&hints, 0, sizeof(hints));
venaas's avatar
venaas committed
204
    hints.ai_socktype = (peer->type == 'T' ? SOCK_STREAM : SOCK_DGRAM);
venaas's avatar
 
venaas committed
205
    hints.ai_family = AF_UNSPEC;
venaas's avatar
venaas committed
206
    hints.ai_flags = ai_flags;
venaas's avatar
venaas committed
207
    if (getaddrinfo(peer->host, peer->port, &hints, &addrinfo)) {
venaas's avatar
venaas committed
208
	debug(DBG_WARN, "resolvepeer: can't resolve %s port %s", peer->host, peer->port);
venaas's avatar
 
venaas committed
209
210
	return 0;
    }
211

venaas's avatar
venaas committed
212
213
214
    if (peer->addrinfo)
	freeaddrinfo(peer->addrinfo);
    peer->addrinfo = addrinfo;
venaas's avatar
 
venaas committed
215
216
217
    return 1;
}	  

218
int connecttoserver(struct addrinfo *addrinfo) {
venaas's avatar
 
venaas committed
219
220
221
    int s;
    struct addrinfo *res;
    
222
    for (res = addrinfo; res; res = res->ai_next) {
venaas's avatar
 
venaas committed
223
224
        s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
        if (s < 0) {
225
            debug(DBG_WARN, "connecttoserver: socket failed");
venaas's avatar
 
venaas committed
226
227
228
229
            continue;
        }
        if (connect(s, res->ai_addr, res->ai_addrlen) == 0)
            break;
230
        debug(DBG_WARN, "connecttoserver: connect failed");
venaas's avatar
 
venaas committed
231
232
233
234
235
236
        close(s);
        s = -1;
    }
    return s;
}	  

venaas's avatar
venaas committed
237
238
239
240
241
242
243
int bindtoaddr(struct addrinfo *addrinfo) {
    int s, on = 1;
    struct addrinfo *res;
    
    for (res = addrinfo; res; res = res->ai_next) {
        s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
        if (s < 0) {
244
            debug(DBG_WARN, "bindtoaddr: socket failed");
venaas's avatar
venaas committed
245
246
247
248
249
            continue;
        }
	setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
	if (!bind(s, res->ai_addr, res->ai_addrlen))
	    return s;
250
	debug(DBG_WARN, "bindtoaddr: bind failed");
venaas's avatar
venaas committed
251
252
253
254
255
        close(s);
    }
    return -1;
}	  

256
257
258
/* returns the client with matching address, or NULL */
/* if client argument is not NULL, we only check that one client */
struct client *find_client(char type, struct sockaddr *addr, struct client *client) {
venaas's avatar
 
venaas committed
259
260
    struct sockaddr_in6 *sa6;
    struct in_addr *a4 = NULL;
261
    struct client *c;
venaas's avatar
 
venaas committed
262
263
264
265
266
267
268
269
270
271
    int i;
    struct addrinfo *res;

    if (addr->sa_family == AF_INET6) {
        sa6 = (struct sockaddr_in6 *)addr;
        if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr))
            a4 = (struct in_addr *)&sa6->sin6_addr.s6_addr[12];
    } else
	a4 = &((struct sockaddr_in *)addr)->sin_addr;

272
273
    c = (client ? client : clients);
    for (i = 0; i < client_count; i++) {
venaas's avatar
venaas committed
274
275
	if (c->peer.type == type)
	    for (res = c->peer.addrinfo; res; res = res->ai_next)
venaas's avatar
 
venaas committed
276
277
278
279
		if ((a4 && res->ai_family == AF_INET &&
		     !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4)) ||
		    (res->ai_family == AF_INET6 &&
		     !memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16)))
280
281
		    return c;
	if (client)
venaas's avatar
 
venaas committed
282
	    break;
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
	c++;
    }
    return NULL;
}

/* returns the server with matching address, or NULL */
/* if server argument is not NULL, we only check that one server */
struct server *find_server(char type, struct sockaddr *addr, struct server *server) {
    struct sockaddr_in6 *sa6;
    struct in_addr *a4 = NULL;
    struct server *s;
    int i;
    struct addrinfo *res;

    if (addr->sa_family == AF_INET6) {
        sa6 = (struct sockaddr_in6 *)addr;
        if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr))
            a4 = (struct in_addr *)&sa6->sin6_addr.s6_addr[12];
    } else
	a4 = &((struct sockaddr_in *)addr)->sin_addr;

    s = (server ? server : servers);
    for (i = 0; i < server_count; i++) {
venaas's avatar
venaas committed
306
307
	if (s->peer.type == type)
	    for (res = s->peer.addrinfo; res; res = res->ai_next)
308
309
310
311
312
313
314
315
		if ((a4 && res->ai_family == AF_INET &&
		     !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4)) ||
		    (res->ai_family == AF_INET6 &&
		     !memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16)))
		    return s;
	if (server)
	    break;
	s++;
venaas's avatar
 
venaas committed
316
317
318
319
    }
    return NULL;
}

320
/* exactly one of client and server must be non-NULL */
venaas's avatar
 
venaas committed
321
322
/* if *peer == NULL we return who we received from, else require it to be from peer */
/* return from in sa if not NULL */
323
unsigned char *radudpget(int s, struct client **client, struct server **server, struct sockaddr_storage *sa) {
venaas's avatar
 
venaas committed
324
    int cnt, len;
325
    void *f;
venaas's avatar
 
venaas committed
326
327
328
329
330
331
332
    unsigned char buf[65536], *rad;
    struct sockaddr_storage from;
    socklen_t fromlen = sizeof(from);

    for (;;) {
	cnt = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)&from, &fromlen);
	if (cnt == -1) {
333
	    debug(DBG_WARN, "radudpget: recv failed");
venaas's avatar
 
venaas committed
334
335
	    continue;
	}
venaas's avatar
venaas committed
336
	debug(DBG_DBG, "radudpget: got %d bytes from %s", cnt, addr2string((struct sockaddr *)&from, fromlen));
venaas's avatar
 
venaas committed
337
338

	if (cnt < 20) {
339
	    debug(DBG_WARN, "radudpget: packet too small");
venaas's avatar
 
venaas committed
340
341
342
343
	    continue;
	}
    
	len = RADLEN(buf);
344
345
346
347
	if (len < 20) {
	    debug(DBG_WARN, "radudpget: length too small");
	    continue;
	}
venaas's avatar
 
venaas committed
348
349

	if (cnt < len) {
350
	    debug(DBG_WARN, "radudpget: packet smaller than length field in radius header");
venaas's avatar
 
venaas committed
351
352
353
	    continue;
	}
	if (cnt > len)
venaas's avatar
venaas committed
354
	    debug(DBG_DBG, "radudpget: packet was padded with %d bytes", cnt - len);
venaas's avatar
 
venaas committed
355

356
357
358
	f = (client
	     ? (void *)find_client('U', (struct sockaddr *)&from, *client)
	     : (void *)find_server('U', (struct sockaddr *)&from, *server));
venaas's avatar
 
venaas committed
359
	if (!f) {
360
	    debug(DBG_WARN, "radudpget: got packet from wrong or unknown UDP peer, ignoring");
venaas's avatar
 
venaas committed
361
362
363
364
365
366
	    continue;
	}

	rad = malloc(len);
	if (rad)
	    break;
367
	debug(DBG_ERR, "radudpget: malloc failed");
venaas's avatar
 
venaas committed
368
369
    }
    memcpy(rad, buf, len);
370
371
372
373
    if (client)
	*client = (struct client *)f; /* only need this if *client == NULL, but if not NULL *client == f here */
    else
	*server = (struct server *)f; /* only need this if *server == NULL, but if not NULL *server == f here */
venaas's avatar
 
venaas committed
374
375
376
377
378
    if (sa)
	*sa = from;
    return rad;
}

379
int tlsverifycert(struct peer *peer) {
380
    int l, loc;
381
382
383
384
385
386
387
    X509 *cert;
    X509_NAME *nm;
    X509_NAME_ENTRY *e;
    unsigned char *v;
    unsigned long error;

    if (SSL_get_verify_result(peer->ssl) != X509_V_OK) {
388
	debug(DBG_ERR, "tlsverifycert: basic validation failed");
389
	while ((error = ERR_get_error()))
390
	    debug(DBG_ERR, "tlsverifycert: TLS: %s", ERR_error_string(error, NULL));
391
392
	return 0;
    }
393

394
395
    cert = SSL_get_peer_certificate(peer->ssl);
    if (!cert) {
396
	debug(DBG_ERR, "tlsverifycert: failed to obtain certificate");
397
398
399
400
401
402
403
404
405
406
407
408
	return 0;
    }
    nm = X509_get_subject_name(cert);
    loc = -1;
    for (;;) {
	loc = X509_NAME_get_index_by_NID(nm, NID_commonName, loc);
	if (loc == -1)
	    break;
	e = X509_NAME_get_entry(nm, loc);
	l = ASN1_STRING_to_UTF8(&v, X509_NAME_ENTRY_get_data(e));
	if (l < 0)
	    continue;
409
410
411
412
413
414
415
416
417
#ifdef DEBUG
	{
	    int i;
	    printf("cn: ");
	    for (i = 0; i < l; i++)
		printf("%c", v[i]);
	    printf("\n");
	}
#endif	
venaas's avatar
venaas committed
418
	if (l == strlen(peer->host) && !strncasecmp(peer->host, (char *)v, l)) {
venaas's avatar
venaas committed
419
	    debug(DBG_DBG, "tlsverifycert: Found cn matching host %s, All OK", peer->host);
420
421
	    return 1;
	}
422
	debug(DBG_ERR, "tlsverifycert: cn not matching host %s", peer->host);
423
424
425
426
427
    }
    X509_free(cert);
    return 0;
}

428
void tlsconnect(struct server *server, struct timeval *when, char *text) {
venaas's avatar
venaas committed
429
430
    struct timeval now;
    time_t elapsed;
venaas's avatar
venaas committed
431

venaas's avatar
venaas committed
432
    debug(DBG_DBG, "tlsconnect called from %s", text);
433
434
    pthread_mutex_lock(&server->lock);
    if (when && memcmp(&server->lastconnecttry, when, sizeof(struct timeval))) {
venaas's avatar
 
venaas committed
435
	/* already reconnected, nothing to do */
venaas's avatar
venaas committed
436
	debug(DBG_DBG, "tlsconnect(%s): seems already reconnected", text);
437
	pthread_mutex_unlock(&server->lock);
venaas's avatar
 
venaas committed
438
439
440
	return;
    }

venaas's avatar
venaas committed
441
    debug(DBG_DBG, "tlsconnect %s", text);
venaas's avatar
venaas committed
442

venaas's avatar
 
venaas committed
443
    for (;;) {
venaas's avatar
venaas committed
444
	gettimeofday(&now, NULL);
445
446
447
	elapsed = now.tv_sec - server->lastconnecttry.tv_sec;
	if (server->connectionok) {
	    server->connectionok = 0;
venaas's avatar
venaas committed
448
449
450
	    sleep(10);
	} else if (elapsed < 5)
	    sleep(10);
venaas's avatar
venaas committed
451
	else if (elapsed < 300) {
venaas's avatar
venaas committed
452
	    debug(DBG_INFO, "tlsconnect: sleeping %lds", elapsed);
venaas's avatar
venaas committed
453
	    sleep(elapsed);
venaas's avatar
venaas committed
454
455
456
	} else if (elapsed < 100000) {
	    debug(DBG_INFO, "tlsconnect: sleeping %ds", 600);
	    sleep(600);
venaas's avatar
venaas committed
457
	} else
venaas's avatar
venaas committed
458
	    server->lastconnecttry.tv_sec = now.tv_sec;  /* no sleep at startup */
459
	debug(DBG_WARN, "tlsconnect: trying to open TLS connection to %s port %s", server->peer.host, server->peer.port);
460
461
	if (server->sock >= 0)
	    close(server->sock);
462
463
	if ((server->sock = connecttoserver(server->peer.addrinfo)) < 0) {
	    debug(DBG_ERR, "tlsconnect: connecttoserver failed");
venaas's avatar
venaas committed
464
	    continue;
465
466
	}
	
venaas's avatar
venaas committed
467
	SSL_free(server->peer.ssl);
468
	server->peer.ssl = SSL_new(ssl_ctx);
venaas's avatar
venaas committed
469
	SSL_set_fd(server->peer.ssl, server->sock);
470
	if (SSL_connect(server->peer.ssl) > 0 && tlsverifycert(&server->peer))
venaas's avatar
 
venaas committed
471
472
	    break;
    }
473
    debug(DBG_WARN, "tlsconnect: TLS connection to %s port %s up", server->peer.host, server->peer.port);
venaas's avatar
venaas committed
474
    gettimeofday(&server->lastconnecttry, NULL);
475
    pthread_mutex_unlock(&server->lock);
venaas's avatar
 
venaas committed
476
477
478
479
480
481
482
483
484
485
}

unsigned char *radtlsget(SSL *ssl) {
    int cnt, total, len;
    unsigned char buf[4], *rad;

    for (;;) {
	for (total = 0; total < 4; total += cnt) {
	    cnt = SSL_read(ssl, buf + total, 4 - total);
	    if (cnt <= 0) {
486
		debug(DBG_ERR, "radtlsget: connection lost");
487
		if (SSL_get_error(ssl, cnt) == SSL_ERROR_ZERO_RETURN) {
venaas's avatar
venaas committed
488
		    /* remote end sent close_notify, send one back */
489
490
		    SSL_shutdown(ssl);
		}
venaas's avatar
 
venaas committed
491
492
493
494
495
496
497
		return NULL;
	    }
	}

	len = RADLEN(buf);
	rad = malloc(len);
	if (!rad) {
498
	    debug(DBG_ERR, "radtlsget: malloc failed");
venaas's avatar
 
venaas committed
499
500
501
502
503
504
505
	    continue;
	}
	memcpy(rad, buf, 4);

	for (; total < len; total += cnt) {
	    cnt = SSL_read(ssl, rad + total, len - total);
	    if (cnt <= 0) {
506
		debug(DBG_ERR, "radtlsget: connection lost");
507
		if (SSL_get_error(ssl, cnt) == SSL_ERROR_ZERO_RETURN) {
venaas's avatar
venaas committed
508
		    /* remote end sent close_notify, send one back */
509
510
		    SSL_shutdown(ssl);
		}
venaas's avatar
 
venaas committed
511
512
513
514
515
516
517
518
519
		free(rad);
		return NULL;
	    }
	}
    
	if (total >= 20)
	    break;
	
	free(rad);
520
	debug(DBG_WARN, "radtlsget: packet smaller than minimum radius size");
venaas's avatar
 
venaas committed
521
522
    }
    
venaas's avatar
venaas committed
523
    debug(DBG_DBG, "radtlsget: got %d bytes", total);
venaas's avatar
 
venaas committed
524
525
526
    return rad;
}

527
int clientradput(struct server *server, unsigned char *rad) {
venaas's avatar
venaas committed
528
    int cnt;
venaas's avatar
 
venaas committed
529
530
    size_t len;
    unsigned long error;
venaas's avatar
venaas committed
531
532
    struct timeval lastconnecttry;
    
venaas's avatar
 
venaas committed
533
    len = RADLEN(rad);
venaas's avatar
venaas committed
534
    if (server->peer.type == 'U') {
535
	if (send(server->sock, rad, len, 0) >= 0) {
venaas's avatar
venaas committed
536
	    debug(DBG_DBG, "clienradput: sent UDP of length %d to %s port %s", len, server->peer.host, server->peer.port);
venaas's avatar
 
venaas committed
537
538
	    return 1;
	}
539
	debug(DBG_WARN, "clientradput: send failed");
venaas's avatar
 
venaas committed
540
541
542
	return 0;
    }

543
    lastconnecttry = server->lastconnecttry;
venaas's avatar
venaas committed
544
    while ((cnt = SSL_write(server->peer.ssl, rad, len)) <= 0) {
venaas's avatar
 
venaas committed
545
	while ((error = ERR_get_error()))
546
	    debug(DBG_ERR, "clientradput: TLS: %s", ERR_error_string(error, NULL));
547
548
	tlsconnect(server, &lastconnecttry, "clientradput");
	lastconnecttry = server->lastconnecttry;
venaas's avatar
 
venaas committed
549
    }
venaas's avatar
venaas committed
550

551
    server->connectionok = 1;
venaas's avatar
venaas committed
552
    debug(DBG_DBG, "clientradput: Sent %d bytes, Radius packet of length %d to TLS peer %s",
venaas's avatar
venaas committed
553
	   cnt, len, server->peer.host);
venaas's avatar
 
venaas committed
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
    return 1;
}

int radsign(unsigned char *rad, unsigned char *sec) {
    static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    static unsigned char first = 1;
    static EVP_MD_CTX mdctx;
    unsigned int md_len;
    int result;
    
    pthread_mutex_lock(&lock);
    if (first) {
	EVP_MD_CTX_init(&mdctx);
	first = 0;
    }

    result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
	EVP_DigestUpdate(&mdctx, rad, RADLEN(rad)) &&
venaas's avatar
venaas committed
572
	EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
venaas's avatar
 
venaas committed
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
	EVP_DigestFinal_ex(&mdctx, rad + 4, &md_len) &&
	md_len == 16);
    pthread_mutex_unlock(&lock);
    return result;
}

int validauth(unsigned char *rad, unsigned char *reqauth, unsigned char *sec) {
    static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    static unsigned char first = 1;
    static EVP_MD_CTX mdctx;
    unsigned char hash[EVP_MAX_MD_SIZE];
    unsigned int len;
    int result;
    
    pthread_mutex_lock(&lock);
    if (first) {
	EVP_MD_CTX_init(&mdctx);
	first = 0;
    }

    len = RADLEN(rad);
    
    result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
	      EVP_DigestUpdate(&mdctx, rad, 4) &&
	      EVP_DigestUpdate(&mdctx, reqauth, 16) &&
	      (len <= 20 || EVP_DigestUpdate(&mdctx, rad + 20, len - 20)) &&
venaas's avatar
venaas committed
599
	      EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
venaas's avatar
 
venaas committed
600
601
602
603
604
605
606
	      EVP_DigestFinal_ex(&mdctx, hash, &len) &&
	      len == 16 &&
	      !memcmp(hash, rad + 4, 16));
    pthread_mutex_unlock(&lock);
    return result;
}
	      
venaas's avatar
venaas committed
607
int checkmessageauth(unsigned char *rad, uint8_t *authattr, char *secret) {
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
    static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    static unsigned char first = 1;
    static HMAC_CTX hmacctx;
    unsigned int md_len;
    uint8_t auth[16], hash[EVP_MAX_MD_SIZE];
    
    pthread_mutex_lock(&lock);
    if (first) {
	HMAC_CTX_init(&hmacctx);
	first = 0;
    }

    memcpy(auth, authattr, 16);
    memset(authattr, 0, 16);
    md_len = 0;
    HMAC_Init_ex(&hmacctx, secret, strlen(secret), EVP_md5(), NULL);
    HMAC_Update(&hmacctx, rad, RADLEN(rad));
    HMAC_Final(&hmacctx, hash, &md_len);
venaas's avatar
venaas committed
626
    memcpy(authattr, auth, 16);
627
    if (md_len != 16) {
628
	debug(DBG_WARN, "message auth computation failed");
629
630
631
632
633
	pthread_mutex_unlock(&lock);
	return 0;
    }

    if (memcmp(auth, hash, 16)) {
634
	debug(DBG_WARN, "message authenticator, wrong value");
635
636
637
638
639
640
641
642
	pthread_mutex_unlock(&lock);
	return 0;
    }	
	
    pthread_mutex_unlock(&lock);
    return 1;
}

venaas's avatar
venaas committed
643
int createmessageauth(unsigned char *rad, unsigned char *authattrval, char *secret) {
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
    static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    static unsigned char first = 1;
    static HMAC_CTX hmacctx;
    unsigned int md_len;

    if (!authattrval)
	return 1;
    
    pthread_mutex_lock(&lock);
    if (first) {
	HMAC_CTX_init(&hmacctx);
	first = 0;
    }

    memset(authattrval, 0, 16);
    md_len = 0;
    HMAC_Init_ex(&hmacctx, secret, strlen(secret), EVP_md5(), NULL);
    HMAC_Update(&hmacctx, rad, RADLEN(rad));
    HMAC_Final(&hmacctx, authattrval, &md_len);
    if (md_len != 16) {
664
	debug(DBG_WARN, "message auth computation failed");
665
666
667
668
669
670
671
672
	pthread_mutex_unlock(&lock);
	return 0;
    }

    pthread_mutex_unlock(&lock);
    return 1;
}

673
674
675
676
677
678
679
680
681
682
683
684
685
unsigned char *attrget(unsigned char *attrs, int length, uint8_t type, uint8_t *len) {
    while (length > 1) {
	if (attrs[RAD_Attr_Type] == type) {
	    if (len)
		*len = attrs[RAD_Attr_Length] - 2;
	    return &attrs[RAD_Attr_Value];
	}
	length -= attrs[RAD_Attr_Length];
	attrs += attrs[RAD_Attr_Length];
    }
    return NULL;
}

686
void sendrq(struct server *to, struct client *from, struct request *rq) {
venaas's avatar
 
venaas committed
687
    int i;
688
    uint8_t *attrval;
689
    
venaas's avatar
 
venaas committed
690
    pthread_mutex_lock(&to->newrq_mutex);
venaas's avatar
venaas committed
691
692
    /* might simplify if only try nextid, might be ok */
    for (i = to->nextid; i < MAX_REQUESTS; i++)
venaas's avatar
 
venaas committed
693
694
695
	if (!to->requests[i].buf)
	    break;
    if (i == MAX_REQUESTS) {
venaas's avatar
venaas committed
696
697
698
699
	for (i = 0; i < to->nextid; i++)
	    if (!to->requests[i].buf)
		break;
	if (i == to->nextid) {
700
	    debug(DBG_WARN, "No room in queue, dropping request");
venaas's avatar
venaas committed
701
702
703
	    pthread_mutex_unlock(&to->newrq_mutex);
	    return;
	}
venaas's avatar
 
venaas committed
704
    }
venaas's avatar
venaas committed
705
706
    
    to->nextid = i + 1;
venaas's avatar
 
venaas committed
707
    rq->buf[1] = (char)i;
venaas's avatar
venaas committed
708
    debug(DBG_DBG, "sendrq: inserting packet with id %d in queue for %s", i, to->peer.host);
709
710
711

    attrval = attrget(rq->buf + 20, RADLEN(rq->buf) - 20, RAD_Attr_Message_Authenticator, NULL);
    if (attrval && !createmessageauth(rq->buf, attrval, to->peer.secret))
712
	return;
venaas's avatar
venaas committed
713

venaas's avatar
 
venaas committed
714
715
716
717
    to->requests[i] = *rq;

    if (!to->newrq) {
	to->newrq = 1;
venaas's avatar
venaas committed
718
	debug(DBG_DBG, "signalling client writer");
venaas's avatar
 
venaas committed
719
720
721
722
723
	pthread_cond_signal(&to->newrq_cond);
    }
    pthread_mutex_unlock(&to->newrq_mutex);
}

venaas's avatar
venaas committed
724
void sendreply(struct client *to, struct server *from, unsigned char *buf, struct sockaddr_storage *tosa) {
venaas's avatar
 
venaas committed
725
726
727
728
    struct replyq *replyq = to->replyq;
    
    pthread_mutex_lock(&replyq->count_mutex);
    if (replyq->count == replyq->size) {
729
	debug(DBG_WARN, "No room in queue, dropping request");
venaas's avatar
 
venaas committed
730
731
732
733
734
735
736
737
738
739
	pthread_mutex_unlock(&replyq->count_mutex);
	return;
    }

    replyq->replies[replyq->count].buf = buf;
    if (tosa)
	replyq->replies[replyq->count].tosa = *tosa;
    replyq->count++;

    if (replyq->count == 1) {
venaas's avatar
venaas committed
740
	debug(DBG_DBG, "signalling client writer");
venaas's avatar
 
venaas committed
741
742
743
744
745
	pthread_cond_signal(&replyq->count_cond);
    }
    pthread_mutex_unlock(&replyq->count_mutex);
}

venaas's avatar
venaas committed
746
int pwdencrypt(uint8_t *in, uint8_t len, char *shared, uint8_t sharedlen, uint8_t *auth) {
venaas's avatar
venaas committed
747
748
749
750
751
    static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    static unsigned char first = 1;
    static EVP_MD_CTX mdctx;
    unsigned char hash[EVP_MAX_MD_SIZE], *input;
    unsigned int md_len;
venaas's avatar
venaas committed
752
    uint8_t i, offset = 0, out[128];
venaas's avatar
venaas committed
753
754
755
756
757
758
759
760
761
762
    
    pthread_mutex_lock(&lock);
    if (first) {
	EVP_MD_CTX_init(&mdctx);
	first = 0;
    }

    input = auth;
    for (;;) {
	if (!EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) ||
venaas's avatar
venaas committed
763
	    !EVP_DigestUpdate(&mdctx, (uint8_t *)shared, sharedlen) ||
venaas's avatar
venaas committed
764
765
766
767
768
769
770
771
772
773
774
775
776
	    !EVP_DigestUpdate(&mdctx, input, 16) ||
	    !EVP_DigestFinal_ex(&mdctx, hash, &md_len) ||
	    md_len != 16) {
	    pthread_mutex_unlock(&lock);
	    return 0;
	}
	for (i = 0; i < 16; i++)
	    out[offset + i] = hash[i] ^ in[offset + i];
	input = out + offset - 16;
	offset += 16;
	if (offset == len)
	    break;
    }
venaas's avatar
venaas committed
777
    memcpy(in, out, len);
venaas's avatar
venaas committed
778
779
780
781
    pthread_mutex_unlock(&lock);
    return 1;
}

venaas's avatar
venaas committed
782
int pwddecrypt(uint8_t *in, uint8_t len, char *shared, uint8_t sharedlen, uint8_t *auth) {
venaas's avatar
 
venaas committed
783
784
785
786
787
    static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    static unsigned char first = 1;
    static EVP_MD_CTX mdctx;
    unsigned char hash[EVP_MAX_MD_SIZE], *input;
    unsigned int md_len;
venaas's avatar
venaas committed
788
    uint8_t i, offset = 0, out[128];
venaas's avatar
 
venaas committed
789
790
791
792
793
794
795
796
797
798
    
    pthread_mutex_lock(&lock);
    if (first) {
	EVP_MD_CTX_init(&mdctx);
	first = 0;
    }

    input = auth;
    for (;;) {
	if (!EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) ||
venaas's avatar
venaas committed
799
	    !EVP_DigestUpdate(&mdctx, (uint8_t *)shared, sharedlen) ||
venaas's avatar
 
venaas committed
800
801
802
803
804
805
806
	    !EVP_DigestUpdate(&mdctx, input, 16) ||
	    !EVP_DigestFinal_ex(&mdctx, hash, &md_len) ||
	    md_len != 16) {
	    pthread_mutex_unlock(&lock);
	    return 0;
	}
	for (i = 0; i < 16; i++)
venaas's avatar
venaas committed
807
808
	    out[offset + i] = hash[i] ^ in[offset + i];
	input = in + offset;
venaas's avatar
 
venaas committed
809
	offset += 16;
venaas's avatar
venaas committed
810
	if (offset == len)
venaas's avatar
 
venaas committed
811
812
	    break;
    }
venaas's avatar
venaas committed
813
    memcpy(in, out, len);
venaas's avatar
 
venaas committed
814
815
816
817
    pthread_mutex_unlock(&lock);
    return 1;
}

venaas's avatar
venaas committed
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
int msmppencrypt(uint8_t *text, uint8_t len, uint8_t *shared, uint8_t sharedlen, uint8_t *auth, uint8_t *salt) {
    static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    static unsigned char first = 1;
    static EVP_MD_CTX mdctx;
    unsigned char hash[EVP_MAX_MD_SIZE];
    unsigned int md_len;
    uint8_t i, offset;
    
    pthread_mutex_lock(&lock);
    if (first) {
	EVP_MD_CTX_init(&mdctx);
	first = 0;
    }

#if 0    
    printf("msppencrypt auth in: ");
    for (i = 0; i < 16; i++)
	printf("%02x ", auth[i]);
    printf("\n");
    
    printf("msppencrypt salt in: ");
    for (i = 0; i < 2; i++)
	printf("%02x ", salt[i]);
    printf("\n");
    
    printf("msppencrypt in: ");
    for (i = 0; i < len; i++)
	printf("%02x ", text[i]);
    printf("\n");
#endif
    
    if (!EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) ||
	!EVP_DigestUpdate(&mdctx, shared, sharedlen) ||
	!EVP_DigestUpdate(&mdctx, auth, 16) ||
	!EVP_DigestUpdate(&mdctx, salt, 2) ||
	!EVP_DigestFinal_ex(&mdctx, hash, &md_len)) {
	pthread_mutex_unlock(&lock);
	return 0;
    }

#if 0    
    printf("msppencrypt hash: ");
    for (i = 0; i < 16; i++)
	printf("%02x ", hash[i]);
    printf("\n");
#endif
    
    for (i = 0; i < 16; i++)
	text[i] ^= hash[i];
    
    for (offset = 16; offset < len; offset += 16) {
venaas's avatar
venaas committed
869
#if 0	
venaas's avatar
venaas committed
870
871
872
873
	printf("text + offset - 16 c(%d): ", offset / 16);
	for (i = 0; i < 16; i++)
	    printf("%02x ", (text + offset - 16)[i]);
	printf("\n");
venaas's avatar
venaas committed
874
#endif
venaas's avatar
venaas committed
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
	if (!EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) ||
	    !EVP_DigestUpdate(&mdctx, shared, sharedlen) ||
	    !EVP_DigestUpdate(&mdctx, text + offset - 16, 16) ||
	    !EVP_DigestFinal_ex(&mdctx, hash, &md_len) ||
	    md_len != 16) {
	    pthread_mutex_unlock(&lock);
	    return 0;
	}
#if 0	
	printf("msppencrypt hash: ");
	for (i = 0; i < 16; i++)
	    printf("%02x ", hash[i]);
	printf("\n");
#endif    
	
	for (i = 0; i < 16; i++)
	    text[offset + i] ^= hash[i];
    }
    
#if 0
    printf("msppencrypt out: ");
    for (i = 0; i < len; i++)
	printf("%02x ", text[i]);
    printf("\n");
#endif

    pthread_mutex_unlock(&lock);
    return 1;
}

int msmppdecrypt(uint8_t *text, uint8_t len, uint8_t *shared, uint8_t sharedlen, uint8_t *auth, uint8_t *salt) {
    static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    static unsigned char first = 1;
    static EVP_MD_CTX mdctx;
    unsigned char hash[EVP_MAX_MD_SIZE];
    unsigned int md_len;
    uint8_t i, offset;
    char plain[255];
    
    pthread_mutex_lock(&lock);
    if (first) {
	EVP_MD_CTX_init(&mdctx);
	first = 0;
    }

#if 0    
    printf("msppdecrypt auth in: ");
    for (i = 0; i < 16; i++)
	printf("%02x ", auth[i]);
    printf("\n");
    
    printf("msppedecrypt salt in: ");
    for (i = 0; i < 2; i++)
	printf("%02x ", salt[i]);
    printf("\n");
    
    printf("msppedecrypt in: ");
    for (i = 0; i < len; i++)
	printf("%02x ", text[i]);
    printf("\n");
#endif
    
    if (!EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) ||
	!EVP_DigestUpdate(&mdctx, shared, sharedlen) ||
	!EVP_DigestUpdate(&mdctx, auth, 16) ||
	!EVP_DigestUpdate(&mdctx, salt, 2) ||
	!EVP_DigestFinal_ex(&mdctx, hash, &md_len)) {
	pthread_mutex_unlock(&lock);
	return 0;
    }

#if 0    
    printf("msppedecrypt hash: ");
    for (i = 0; i < 16; i++)
	printf("%02x ", hash[i]);
    printf("\n");
#endif
    
    for (i = 0; i < 16; i++)
	plain[i] = text[i] ^ hash[i];
    
    for (offset = 16; offset < len; offset += 16) {
#if 0 	
	printf("text + offset - 16 c(%d): ", offset / 16);
	for (i = 0; i < 16; i++)
	    printf("%02x ", (text + offset - 16)[i]);
	printf("\n");
#endif
	if (!EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) ||
	    !EVP_DigestUpdate(&mdctx, shared, sharedlen) ||
	    !EVP_DigestUpdate(&mdctx, text + offset - 16, 16) ||
	    !EVP_DigestFinal_ex(&mdctx, hash, &md_len) ||
	    md_len != 16) {
	    pthread_mutex_unlock(&lock);
	    return 0;
	}
#if 0	
    printf("msppedecrypt hash: ");
    for (i = 0; i < 16; i++)
	printf("%02x ", hash[i]);
    printf("\n");
#endif    

    for (i = 0; i < 16; i++)
	plain[offset + i] = text[offset + i] ^ hash[i];
    }

    memcpy(text, plain, len);
#if 0
    printf("msppedecrypt out: ");
    for (i = 0; i < len; i++)
	printf("%02x ", text[i]);
    printf("\n");
#endif

    pthread_mutex_unlock(&lock);
    return 1;
}

994
struct server *id2server(char *id, uint8_t len) {
venaas's avatar
venaas committed
995
    int i;
996
997
998
    char *idrealm;
    struct server *deflt = NULL;
    
venaas's avatar
venaas committed
999
    idrealm = strchr(id, '@');
venaas's avatar
venaas committed
1000
    if (idrealm) {