radsecproxy.c 109 KB
Newer Older
venaas's avatar
   
venaas committed
1
/*
2
 * Copyright (C) 2006-2008 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
30
31
 *
 * 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.
 */

/* 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
32
#include <signal.h>
venaas's avatar
venaas committed
33
34
#include <sys/socket.h>
#include <netinet/in.h>
venaas's avatar
   
venaas committed
35
#include <netdb.h>
venaas's avatar
venaas committed
36
#include <string.h>
venaas's avatar
   
venaas committed
37
#include <unistd.h>
venaas's avatar
venaas committed
38
#include <limits.h>
venaas's avatar
venaas committed
39
40
41
#ifdef SYS_SOLARIS9
#include <fcntl.h>
#endif
venaas's avatar
venaas committed
42
#include <sys/time.h>
43
#include <sys/types.h>
44
#include <sys/select.h>
45
#include <ctype.h>
46
#include <sys/wait.h>
47
#include <arpa/inet.h>
venaas's avatar
venaas committed
48
#include <regex.h>
venaas's avatar
venaas committed
49
#include <libgen.h>
venaas's avatar
   
venaas committed
50
51
52
53
54
#include <pthread.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/md5.h>
venaas's avatar
venaas committed
55
#include <openssl/hmac.h>
56
#include <openssl/x509v3.h>
venaas's avatar
venaas committed
57
#include "debug.h"
venaas's avatar
venaas committed
58
#include "list.h"
59
60
#include "util.h"
#include "gconfig.h"
venaas's avatar
venaas committed
61
#include "radsecproxy.h"
venaas's avatar
   
venaas committed
62

63
static struct options options;
64
struct list *clconfs, *srvconfs, *realms, *tlsconfs, *rewriteconfs;
65

66
67
68
69
static int client_udp_count = 0;
static int client_tls_count = 0;
static int server_udp_count = 0;
static int server_tls_count = 0;
venaas's avatar
   
venaas committed
70

71
72
static struct addrinfo *srcudpres = NULL;
static struct addrinfo *srctcpres = NULL;
73

venaas's avatar
venaas committed
74
static struct replyq *udp_server_replyq = NULL;
venaas's avatar
   
venaas committed
75
static int udp_server_sock = -1;
76
static int udp_accserver_sock = -1;
77
78
static int udp_client4_sock = -1;
static int udp_client6_sock = -1;
venaas's avatar
   
venaas committed
79
80
81
82
83
static pthread_mutex_t *ssl_locks;
static long *ssl_lock_count;
extern int optind;
extern char *optarg;

venaas's avatar
venaas committed
84
85
/* minimum required declarations to avoid reordering code */
void adddynamicrealmserver(struct realm *realm, struct clsrvconf *conf, char *id);
86
int dynamicconfig(struct server *server);
87
int confserver_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *val);
88
89
void freerealm(struct realm *realm);
void freeclsrvconf(struct clsrvconf *conf);
90
91
void freerqdata(struct request *rq);

venaas's avatar
   
venaas committed
92
93
94
/* callbacks for making OpenSSL thread safe */
unsigned long ssl_thread_id() {
        return (unsigned long)pthread_self();
venaas's avatar
venaas committed
95
}
venaas's avatar
   
venaas committed
96
97
98
99
100
101
102
103
104

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
105
106
107
108
109
110
111
112
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;
}

113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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);
130
      debug(DBG_WARN, "verify error: num=%d:%s:depth=%d:%s", err, X509_verify_cert_error_string(err), depth, buf);
131
132
133
134

      switch (err) {
      case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
	  X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
135
	  debug(DBG_WARN, "\tIssuer=%s", buf);
136
137
138
	  break;
      case X509_V_ERR_CERT_NOT_YET_VALID:
      case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
139
	  debug(DBG_WARN, "\tCertificate not yet valid");
140
141
	  break;
      case X509_V_ERR_CERT_HAS_EXPIRED:
142
	  debug(DBG_WARN, "Certificate has expired");
143
144
	  break;
      case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
145
	  debug(DBG_WARN, "Certificate no longer valid (after notAfter)");
146
147
148
	  break;
      }
  }
149
150
151
#ifdef DEBUG  
  printf("certificate verify returns %d\n", ok);
#endif  
152
153
154
  return ok;
}

venaas's avatar
venaas committed
155
int resolvepeer(struct clsrvconf *conf, int ai_flags) {
156
    struct addrinfo hints, *addrinfo, *res;
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
    char *slash, *s;
    int plen;

    slash = conf->host ? strchr(conf->host, '/') : NULL;
    if (slash) {
	s = slash + 1;
	if (!*s) {
	    debug(DBG_WARN, "resolvepeer: prefix length must be specified after the / in %s", conf->host);
	    return 0;
	}
	for (; *s; s++)
	    if (*s < '0' || *s > '9') {
		debug(DBG_WARN, "resolvepeer: %s in %s is not a valid prefix length", slash + 1, conf->host);
		return 0;
	    }
	plen = atoi(slash + 1);
	if (plen < 0 || plen > 128) {
	    debug(DBG_WARN, "resolvepeer: %s in %s is not a valid prefix length", slash + 1, conf->host);
	    return 0;
	}
	*slash = '\0';
    }
venaas's avatar
   
venaas committed
179
    memset(&hints, 0, sizeof(hints));
venaas's avatar
venaas committed
180
    hints.ai_socktype = (conf->type == 'T' ? SOCK_STREAM : SOCK_DGRAM);
venaas's avatar
   
venaas committed
181
    hints.ai_family = AF_UNSPEC;
venaas's avatar
venaas committed
182
    hints.ai_flags = ai_flags;
183
184
185
186
187
188
189
190
191
192
193
194
195
196
    if (!conf->host && !conf->port) {
	/* getaddrinfo() doesn't like host and port to be NULL */
	if (getaddrinfo(conf->host, DEFAULT_UDP_PORT, &hints, &addrinfo)) {
	    debug(DBG_WARN, "resolvepeer: can't resolve (null) port (null)");
	    return 0;
	}
	for (res = addrinfo; res; res = res->ai_next) {
	    switch (res->ai_family) {
	    case AF_INET:
		((struct sockaddr_in *)res->ai_addr)->sin_port = 0;
		break;
	    case AF_INET6:
		((struct sockaddr_in6 *)res->ai_addr)->sin6_port = 0;
		break;
197
	    }
198
199
200
201
202
	}
    } else {
	if (slash)
	    hints.ai_flags |= AI_NUMERICHOST;
	if (getaddrinfo(conf->host, conf->port, &hints, &addrinfo)) {
203
	    debug(DBG_WARN, "resolvepeer: can't resolve %s port %s", conf->host ? conf->host : "(null)", conf->port ? conf->port : "(null)");
204
205
	    return 0;
	}
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
	if (slash) {
	    *slash = '/';
	    switch (addrinfo->ai_family) {
	    case AF_INET:
		if (plen > 32) {
		    debug(DBG_WARN, "resolvepeer: prefix length must be <= 32 in %s", conf->host);
		    freeaddrinfo(addrinfo);
		    return 0;
		}
		break;
	    case AF_INET6:
		break;
	    default:
		debug(DBG_WARN, "resolvepeer: prefix must be IPv4 or IPv6 in %s", conf->host);
		freeaddrinfo(addrinfo);
		return 0;
	    }
	    conf->prefixlen = plen;
	} else
	    conf->prefixlen = 255;
    }
venaas's avatar
venaas committed
227
228
229
    if (conf->addrinfo)
	freeaddrinfo(conf->addrinfo);
    conf->addrinfo = addrinfo;
venaas's avatar
   
venaas committed
230
231
232
    return 1;
}	  

233
234
int bindtoaddr(struct addrinfo *addrinfo, int family, int reuse, int v6only) {
    int s, on = 1;
venaas's avatar
   
venaas committed
235
    struct addrinfo *res;
236
    
237
    for (res = addrinfo; res; res = res->ai_next) {
238
239
	if (family != AF_UNSPEC && family != res->ai_family)
	    continue;
venaas's avatar
   
venaas committed
240
241
        s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
        if (s < 0) {
242
            debug(DBG_WARN, "bindtoaddr: socket failed");
venaas's avatar
   
venaas committed
243
244
            continue;
        }
245
246
247
248
249
250
251
252
253
254
	if (reuse)
	    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
#ifdef IPV6_V6ONLY
	if (v6only)
	    setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
#endif		

	if (!bind(s, res->ai_addr, res->ai_addrlen))
	    return s;
	debug(DBG_WARN, "bindtoaddr: bind failed");
venaas's avatar
   
venaas committed
255
256
        close(s);
    }
257
    return -1;
venaas's avatar
   
venaas committed
258
259
}	  

260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
char *parsehostport(char *s, struct clsrvconf *conf, char *default_port) {
    char *p, *field;
    int ipv6 = 0;

    p = s;
    /* allow literal addresses and port, e.g. [2001:db8::1]:1812 */
    if (*p == '[') {
	p++;
	field = p;
	for (; *p && *p != ']' && *p != ' ' && *p != '\t' && *p != '\n'; p++);
	if (*p != ']')
	    debugx(1, DBG_ERR, "no ] matching initial [");
	ipv6 = 1;
    } else {
	field = p;
	for (; *p && *p != ':' && *p != ' ' && *p != '\t' && *p != '\n'; p++);
    }
    if (field == p)
	debugx(1, DBG_ERR, "missing host/address");

    conf->host = stringcopy(field, p - field);
    if (ipv6) {
	p++;
	if (*p && *p != ':' && *p != ' ' && *p != '\t' && *p != '\n')
	    debugx(1, DBG_ERR, "unexpected character after ]");
    }
    if (*p == ':') {
	    /* port number or service name is specified */;
	    field = ++p;
	    for (; *p && *p != ' ' && *p != '\t' && *p != '\n'; p++);
	    if (field == p)
		debugx(1, DBG_ERR, "syntax error, : but no following port");
	    conf->port = stringcopy(field, p - field);
    } else
	conf->port = default_port ? stringcopy(default_port, 0) : NULL;
    return p;
}

struct clsrvconf *resolve_hostport(char type, char *lconf, char *default_port) {
    struct clsrvconf *conf;

    conf = malloc(sizeof(struct clsrvconf));
    if (!conf)
	debugx(1, DBG_ERR, "malloc failed");
    memset(conf, 0, sizeof(struct clsrvconf));
    conf->type = type;
    if (lconf) {
	parsehostport(lconf, conf, default_port);
	if (!strcmp(conf->host, "*")) {
	    free(conf->host);
	    conf->host = NULL;
	}
    } else
	conf->port = default_port ? stringcopy(default_port, 0) : NULL;
    if (!resolvepeer(conf, AI_PASSIVE))
315
	debugx(1, DBG_ERR, "failed to resolve host %s port %s, exiting", conf->host ? conf->host : "(null)", conf->port ? conf->port : "(null)");
316
317
318
    return conf;
}

319
320
321
322
323
324
325
326
void freeclsrvres(struct clsrvconf *res) {
    free(res->host);
    free(res->port);
    if (res->addrinfo)
	freeaddrinfo(res->addrinfo);
    free(res);
}

327
328
int connecttcp(struct addrinfo *addrinfo) {
    int s;
venaas's avatar
venaas committed
329
    struct addrinfo *res;
330
331

    s = -1;
venaas's avatar
venaas committed
332
    for (res = addrinfo; res; res = res->ai_next) {
333
	s = bindtoaddr(srctcpres, res->ai_family, 1, 1);
venaas's avatar
venaas committed
334
        if (s < 0) {
335
            debug(DBG_WARN, "connecttoserver: socket failed");
venaas's avatar
venaas committed
336
337
            continue;
        }
338
339
340
        if (connect(s, res->ai_addr, res->ai_addrlen) == 0)
            break;
        debug(DBG_WARN, "connecttoserver: connect failed");
venaas's avatar
venaas committed
341
        close(s);
342
        s = -1;
venaas's avatar
venaas committed
343
    }
344
    return s;
venaas's avatar
venaas committed
345
346
}	  

347
348
349
350
351
352
353
354
355
356
357
358
/* returns 1 if the len first bits are equal, else 0 */
int prefixmatch(void *a1, void *a2, uint8_t len) {
    static uint8_t mask[] = { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
    int r, l = len / 8;
    if (l && memcmp(a1, a2, l))
	return 0;
    r = len % 8;
    if (!r)
	return 1;
    return (((uint8_t *)a1)[l] & mask[r]) == (((uint8_t *)a2)[l] & mask[r]);
}

359
360
/* check if conf has matching address */
struct clsrvconf *checkconfaddr(char type, struct sockaddr *addr, struct clsrvconf *conf) {
venaas's avatar
venaas committed
361
362
363
    struct sockaddr_in6 *sa6 = NULL;
    struct in_addr *a4 = NULL;
    struct addrinfo *res;
venaas's avatar
venaas committed
364
    
venaas's avatar
venaas committed
365
366
    if (addr->sa_family == AF_INET6) {
        sa6 = (struct sockaddr_in6 *)addr;
367
        if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
venaas's avatar
venaas committed
368
            a4 = (struct in_addr *)&sa6->sin6_addr.s6_addr[12];
369
370
	    sa6 = NULL;
	}
venaas's avatar
venaas committed
371
372
373
    } else
	a4 = &((struct sockaddr_in *)addr)->sin_addr;

374
375
376
377
378
379
380
    if (conf->type == type) {
	if (conf->prefixlen == 255) {
	    for (res = conf->addrinfo; res; res = res->ai_next)
		if ((a4 && res->ai_family == AF_INET &&
		     !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4)) ||
		    (sa6 && res->ai_family == AF_INET6 &&
		     !memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16)))
venaas's avatar
venaas committed
381
		    return conf;
382
383
384
385
386
387
388
389
	} else {
	    res = conf->addrinfo;
	    if (res &&
		((a4 && res->ai_family == AF_INET &&
		  prefixmatch(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, conf->prefixlen)) ||
		 (sa6 && res->ai_family == AF_INET6 &&
		  prefixmatch(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, conf->prefixlen))))
		return conf;
390
	}
venaas's avatar
venaas committed
391
    }
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
    return NULL;
}

/* returns next config with matching address, or NULL */
struct clsrvconf *find_conf(char type, struct sockaddr *addr, struct list *confs, struct list_node **cur) {
    struct sockaddr_in6 *sa6 = NULL;
    struct in_addr *a4 = NULL;
    struct addrinfo *res;
    struct list_node *entry;
    struct clsrvconf *conf;
    
    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];
	    sa6 = NULL;
	}
    } else
	a4 = &((struct sockaddr_in *)addr)->sin_addr;
venaas's avatar
venaas committed
411

venaas's avatar
venaas committed
412
    for (entry = (cur && *cur ? list_next(*cur) : list_first(confs)); entry; entry = list_next(entry)) {
venaas's avatar
venaas committed
413
	conf = (struct clsrvconf *)entry->data;
414
415
416
417
418
419
	if (conf->type == type) {
	    if (conf->prefixlen == 255) {
		for (res = conf->addrinfo; res; res = res->ai_next)
		    if ((a4 && res->ai_family == AF_INET &&
			 !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4)) ||
			(sa6 && res->ai_family == AF_INET6 &&
420
421
422
			 !memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16))) {
			if (cur)
			    *cur = entry;
423
			return conf;
424
		    }
425
426
427
428
429
430
	    } else {
		res = conf->addrinfo;
		if (res &&
		    ((a4 && res->ai_family == AF_INET &&
		      prefixmatch(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, conf->prefixlen)) ||
		     (sa6 && res->ai_family == AF_INET6 &&
431
432
433
		      prefixmatch(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, conf->prefixlen)))) {
		    if (cur)
			*cur = entry;
venaas's avatar
venaas committed
434
		    return conf;
435
		}
436
437
	    }
	}
venaas's avatar
venaas committed
438
    }    
venaas's avatar
venaas committed
439
440
441
    return NULL;
}

442
struct replyq *newreplyq() {
443
444
445
446
447
    struct replyq *replyq;
    
    replyq = malloc(sizeof(struct replyq));
    if (!replyq)
	debugx(1, DBG_ERR, "malloc failed");
448
    replyq->replies = list_create();
449
450
    if (!replyq->replies)
	debugx(1, DBG_ERR, "malloc failed");
451
452
    pthread_mutex_init(&replyq->mutex, NULL);
    pthread_cond_init(&replyq->cond, NULL);
453
454
455
    return replyq;
}

456
457
458
459
460
461
struct client *addclient(struct clsrvconf *conf) {
    struct client *new = malloc(sizeof(struct client));
    
    if (!new) {
	debug(DBG_ERR, "malloc failed");
	return NULL;
462
463
    }
    if (!conf->clients) {
464
465
466
467
468
	conf->clients = list_create();
	if (!conf->clients) {
	    debug(DBG_ERR, "malloc failed");
	    return NULL;
	}
469
    }
470
471
472
473
474
475
476
477
478
479
    
    memset(new, 0, sizeof(struct client));
    new->conf = conf;
    new->replyq = conf->type == 'T' ? newreplyq() : udp_server_replyq;

    list_push(conf->clients, new);
    return new;
}

void removeclient(struct client *client) {
480
481
    struct list_node *entry;
    
482
483
484
    if (!client || !client->conf->clients)
	return;

485
486
487
488
    pthread_mutex_lock(&client->replyq->mutex);
    for (entry = list_first(client->replyq->replies); entry; entry = list_next(entry))
	free(((struct reply *)entry)->buf);
    list_destroy(client->replyq->replies);
venaas's avatar
venaas committed
489
    pthread_cond_destroy(&client->replyq->cond);
490
    pthread_mutex_unlock(&client->replyq->mutex);
venaas's avatar
venaas committed
491
    pthread_mutex_destroy(&client->replyq->mutex);
492
493
    list_removedata(client->conf->clients, client);
    free(client);
494
495
}

496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
void removeclientrqs(struct client *client) {
    struct list_node *entry;
    struct server *server;
    struct request *rq;
    int i;
    
    for (entry = list_first(srvconfs); entry; entry = list_next(entry)) {
	server = ((struct clsrvconf *)entry->data)->servers;
	pthread_mutex_lock(&server->newrq_mutex);
	for (i = 0; i < MAX_REQUESTS; i++) {
	    rq = server->requests + i;
	    if (rq->from == client)
		rq->from = NULL;
	}
	pthread_mutex_unlock(&server->newrq_mutex);
    }
}
venaas's avatar
venaas committed
513
514

void freeserver(struct server *server, uint8_t destroymutex) {
515
516
    struct request *rq, *end;

venaas's avatar
venaas committed
517
518
    if (!server)
	return;
519
520
521
522
523
524
525

    if(server->requests) {
	rq = server->requests;
	for (end = rq + MAX_REQUESTS; rq < end; rq++)
	    freerqdata(rq);
	free(server->requests);
    }
526
    free(server->dynamiclookuparg);
venaas's avatar
venaas committed
527
528
529
530
531
532
533
534
535
536
    if (destroymutex) {
	pthread_mutex_destroy(&server->lock);
	pthread_cond_destroy(&server->newrq_cond);
	pthread_mutex_destroy(&server->newrq_mutex);
    }
    free(server);
}

int addserver(struct clsrvconf *conf) {
    struct clsrvconf *res;
537
    
venaas's avatar
venaas committed
538
539
540
541
    if (conf->servers) {
	debug(DBG_ERR, "addserver: currently works with just one server per conf");
	return 0;
    }
542
    conf->servers = malloc(sizeof(struct server));
venaas's avatar
venaas committed
543
544
545
546
    if (!conf->servers) {
	debug(DBG_ERR, "malloc failed");
	return 0;
    }
547
548
549
    memset(conf->servers, 0, sizeof(struct server));
    conf->servers->conf = conf;

550
    if (conf->type == 'U') {
551
552
553
554
555
556
	if (!srcudpres) {
	    res = resolve_hostport('U', options.sourceudp, NULL);
	    srcudpres = res->addrinfo;
	    res->addrinfo = NULL;
	    freeclsrvres(res);
	}
557
558
559
	switch (conf->addrinfo->ai_family) {
	case AF_INET:
	    if (udp_client4_sock < 0) {
560
		udp_client4_sock = bindtoaddr(srcudpres, AF_INET, 0, 1);
561
562
563
564
		if (udp_client4_sock < 0)
		    debugx(1, DBG_ERR, "addserver: failed to create client socket for server %s", conf->host);
	    }
	    conf->servers->sock = udp_client4_sock;
565
	    break;
566
567
	case AF_INET6:
	    if (udp_client6_sock < 0) {
568
		udp_client6_sock = bindtoaddr(srcudpres, AF_INET6, 0, 1);
569
570
571
572
		if (udp_client6_sock < 0)
		    debugx(1, DBG_ERR, "addserver: failed to create client socket for server %s", conf->host);
	    }
	    conf->servers->sock = udp_client6_sock;
573
	    break;
574
575
576
577
	default:
	    debugx(1, DBG_ERR, "addserver: unsupported address family");
	}
	
578
    } else {
579
580
581
582
583
584
	if (!srctcpres) {
	    res = resolve_hostport('T', options.sourcetcp, NULL);
	    srctcpres = res->addrinfo;
	    res->addrinfo = NULL;
	    freeclsrvres(res);
	}
585
	conf->servers->sock = -1;
586
    }
587
    
588
    conf->servers->requests = calloc(MAX_REQUESTS, sizeof(struct request));
venaas's avatar
venaas committed
589
590
591
592
593
594
595
596
    if (!conf->servers->requests) {
	debug(DBG_ERR, "malloc failed");
	goto errexit;
    }
    if (pthread_mutex_init(&conf->servers->lock, NULL)) {
	debug(DBG_ERR, "mutex init failed");
	goto errexit;
    }
597
    conf->servers->newrq = 0;
venaas's avatar
venaas committed
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
    if (pthread_mutex_init(&conf->servers->newrq_mutex, NULL)) {
	debug(DBG_ERR, "mutex init failed");
	pthread_mutex_destroy(&conf->servers->lock);
	goto errexit;
    }
    if (pthread_cond_init(&conf->servers->newrq_cond, NULL)) {
	debug(DBG_ERR, "mutex init failed");
	pthread_mutex_destroy(&conf->servers->newrq_mutex);
	pthread_mutex_destroy(&conf->servers->lock);
	goto errexit;
    }

    return 1;
    
 errexit:
    freeserver(conf->servers, 0);
    conf->servers = NULL;
    return 0;
616
617
}

618
/* exactly one of client and server must be non-NULL */
venaas's avatar
venaas committed
619
/* should probably take peer list (client(s) or server(s)) as argument instead */
venaas's avatar
   
venaas committed
620
621
/* if *peer == NULL we return who we received from, else require it to be from peer */
/* return from in sa if not NULL */
622
unsigned char *radudpget(int s, struct client **client, struct server **server, struct sockaddr_storage *sa) {
venaas's avatar
venaas committed
623
    int cnt, len;
venaas's avatar
   
venaas committed
624
625
626
    unsigned char buf[65536], *rad;
    struct sockaddr_storage from;
    socklen_t fromlen = sizeof(from);
venaas's avatar
venaas committed
627
    struct clsrvconf *p;
venaas's avatar
venaas committed
628
629
    struct list_node *node;
    
venaas's avatar
   
venaas committed
630
631
632
    for (;;) {
	cnt = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)&from, &fromlen);
	if (cnt == -1) {
633
	    debug(DBG_WARN, "radudpget: recv failed");
venaas's avatar
   
venaas committed
634
635
	    continue;
	}
venaas's avatar
venaas committed
636
	debug(DBG_DBG, "radudpget: got %d bytes from %s", cnt, addr2string((struct sockaddr *)&from, fromlen));
venaas's avatar
   
venaas committed
637
638

	if (cnt < 20) {
639
	    debug(DBG_WARN, "radudpget: packet too small");
venaas's avatar
   
venaas committed
640
641
642
643
	    continue;
	}
    
	len = RADLEN(buf);
644
645
646
647
	if (len < 20) {
	    debug(DBG_WARN, "radudpget: length too small");
	    continue;
	}
venaas's avatar
   
venaas committed
648
649

	if (cnt < len) {
650
	    debug(DBG_WARN, "radudpget: packet smaller than length field in radius header");
venaas's avatar
   
venaas committed
651
652
653
	    continue;
	}
	if (cnt > len)
venaas's avatar
venaas committed
654
	    debug(DBG_DBG, "radudpget: packet was padded with %d bytes", cnt - len);
venaas's avatar
   
venaas committed
655

venaas's avatar
venaas committed
656
	if (client)
venaas's avatar
venaas committed
657
	    if (*client)
658
		p = checkconfaddr('U', (struct sockaddr *)&from, (*client)->conf);
venaas's avatar
venaas committed
659
	    else
660
		p = find_conf('U', (struct sockaddr *)&from, clconfs, NULL);
venaas's avatar
venaas committed
661
	else
venaas's avatar
venaas committed
662
	    if (*server)
663
		p = checkconfaddr('U', (struct sockaddr *)&from, (*server)->conf);
venaas's avatar
venaas committed
664
	    else
665
		p = find_conf('U', (struct sockaddr *)&from, srvconfs, NULL);
venaas's avatar
venaas committed
666
667

	if (!p) {
668
	    debug(DBG_WARN, "radudpget: got packet from wrong or unknown UDP peer %s, ignoring", addr2string((struct sockaddr *)&from, fromlen));
venaas's avatar
   
venaas committed
669
670
	    continue;
	}
671
	
venaas's avatar
   
venaas committed
672
	rad = malloc(len);
673
674
675
676
677
678
	if (!rad) {
	    debug(DBG_ERR, "radudpget: malloc failed");
	    continue;
	}
	
	if (client && !*client) {
679
	    node = list_first(p->clients);
venaas's avatar
venaas committed
680
	    *client = node ? (struct client *)node->data : addclient(p);
681
	    if (!*client) {
682
683
684
685
686
687
688
		free(rad);
		continue;
	    }
	} else if (server && !*server)
	    *server = p->servers;
	
	break;
venaas's avatar
   
venaas committed
689
690
691
692
693
694
695
    }
    memcpy(rad, buf, len);
    if (sa)
	*sa = from;
    return rad;
}

696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
int subjectaltnameaddr(X509 *cert, int family, struct in6_addr *addr) {
    int loc, i, l, n, r = 0;
    char *v;
    X509_EXTENSION *ex;
    STACK_OF(GENERAL_NAME) *alt;
    GENERAL_NAME *gn;
    
    debug(DBG_DBG, "subjectaltnameaddr");
    
    loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1);
    if (loc < 0)
	return r;
    
    ex = X509_get_ext(cert, loc);
    alt = X509V3_EXT_d2i(ex);
    if (!alt)
	return r;
    
    n = sk_GENERAL_NAME_num(alt);
    for (i = 0; i < n; i++) {
	gn = sk_GENERAL_NAME_value(alt, i);
	if (gn->type != GEN_IPADD)
	    continue;
	r = -1;
	v = (char *)ASN1_STRING_data(gn->d.ia5);
	l = ASN1_STRING_length(gn->d.ia5);
	if (((family == AF_INET && l == sizeof(struct in_addr)) || (family == AF_INET6 && l == sizeof(struct in6_addr)))
	    && !memcmp(v, &addr, l)) {
	    r = 1;
	    break;
	}
    }
    GENERAL_NAMES_free(alt);
    return r;
}

venaas's avatar
venaas committed
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
int cnregexp(X509 *cert, char *exact, regex_t *regex) {
    int loc, l;
    char *v, *s;
    X509_NAME *nm;
    X509_NAME_ENTRY *e;
    ASN1_STRING *t;

    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);
	t = X509_NAME_ENTRY_get_data(e);
	v = (char *) ASN1_STRING_data(t);
	l = ASN1_STRING_length(t);
	if (l < 0)
	    continue;
	if (exact) {
	    if (l == strlen(exact) && !strncasecmp(exact, v, l))
		return 1;
	} else {
	    s = stringcopy((char *)v, l);
	    if (!s) {
		debug(DBG_ERR, "malloc failed");
		continue;
	    }
	    if (regexec(regex, s, 0, NULL, 0)) {
		free(s);
		continue;
	    }
	    free(s);
	    return 1;
	}
    }
    return 0;
}

771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
int subjectaltnameregexp(X509 *cert, int type, char *exact,  regex_t *regex) {
    int loc, i, l, n, r = 0;
    char *s, *v;
    X509_EXTENSION *ex;
    STACK_OF(GENERAL_NAME) *alt;
    GENERAL_NAME *gn;
    
    debug(DBG_DBG, "subjectaltnameregexp");
    
    loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1);
    if (loc < 0)
	return r;
    
    ex = X509_get_ext(cert, loc);
    alt = X509V3_EXT_d2i(ex);
    if (!alt)
	return r;
    
    n = sk_GENERAL_NAME_num(alt);
    for (i = 0; i < n; i++) {
	gn = sk_GENERAL_NAME_value(alt, i);
	if (gn->type != type)
	    continue;
	r = -1;
	v = (char *)ASN1_STRING_data(gn->d.ia5);
	l = ASN1_STRING_length(gn->d.ia5);
	if (l <= 0)
	    continue;
#ifdef DEBUG
	printfchars(NULL, gn->type == GEN_DNS ? "dns" : "uri", NULL, v, l);
#endif	
	if (exact) {
	    if (memcmp(v, exact, l))
		continue;
	} else {
	    s = stringcopy((char *)v, l);
	    if (!s) {
		debug(DBG_ERR, "malloc failed");
		continue;
	    }
	    if (regexec(regex, s, 0, NULL, 0)) {
		free(s);
		continue;
	    }
	    free(s);
	}
	r = 1;
	break;
    }
    GENERAL_NAMES_free(alt);
    return r;
}

venaas's avatar
venaas committed
824
X509 *verifytlscert(SSL *ssl) {
825
826
    X509 *cert;
    unsigned long error;
827
    
venaas's avatar
venaas committed
828
    if (SSL_get_verify_result(ssl) != X509_V_OK) {
venaas's avatar
venaas committed
829
	debug(DBG_ERR, "verifytlscert: basic validation failed");
830
	while ((error = ERR_get_error()))
venaas's avatar
venaas committed
831
832
	    debug(DBG_ERR, "verifytlscert: TLS: %s", ERR_error_string(error, NULL));
	return NULL;
833
    }
834

venaas's avatar
venaas committed
835
    cert = SSL_get_peer_certificate(ssl);
venaas's avatar
venaas committed
836
837
838
839
840
841
842
843
844
845
    if (!cert)
	debug(DBG_ERR, "verifytlscert: failed to obtain certificate");
    return cert;
}
    
int verifyconfcert(X509 *cert, struct clsrvconf *conf) {
    int r;
    uint8_t type = 0; /* 0 for DNS, AF_INET for IPv4, AF_INET6 for IPv6 */
    struct in6_addr addr;
    
846
    if (conf->certnamecheck && conf->prefixlen == 255) {
847
848
849
850
851
852
853
854
	if (inet_pton(AF_INET, conf->host, &addr))
	    type = AF_INET;
	else if (inet_pton(AF_INET6, conf->host, &addr))
	    type = AF_INET6;

	r = type ? subjectaltnameaddr(cert, type, &addr) : subjectaltnameregexp(cert, GEN_DNS, conf->host, NULL);
	if (r) {
	    if (r < 0) {
venaas's avatar
venaas committed
855
		debug(DBG_WARN, "verifyconfcert: No subjectaltname matching %s %s", type ? "address" : "host", conf->host);
venaas's avatar
venaas committed
856
		return 0;
857
	    }
venaas's avatar
venaas committed
858
	    debug(DBG_DBG, "verifyconfcert: Found subjectaltname matching %s %s", type ? "address" : "host", conf->host);
859
	} else {
venaas's avatar
venaas committed
860
	    if (!cnregexp(cert, conf->host, NULL)) {
venaas's avatar
venaas committed
861
		debug(DBG_WARN, "verifyconfcert: cn not matching host %s", conf->host);
venaas's avatar
venaas committed
862
		return 0;
venaas's avatar
venaas committed
863
	    }		
venaas's avatar
venaas committed
864
	    debug(DBG_DBG, "verifyconfcert: Found cn matching host %s", conf->host);
venaas's avatar
venaas committed
865
866
867
868
	}
    }
    if (conf->certcnregex) {
	if (cnregexp(cert, NULL, conf->certcnregex) < 1) {
venaas's avatar
venaas committed
869
	    debug(DBG_WARN, "verifyconfcert: CN not matching regex");
venaas's avatar
venaas committed
870
	    return 0;
871
	}
venaas's avatar
venaas committed
872
	debug(DBG_DBG, "verifyconfcert: CN matching regex");
873
874
    }
    if (conf->certuriregex) {
venaas's avatar
venaas committed
875
	if (subjectaltnameregexp(cert, GEN_URI, NULL, conf->certuriregex) < 1) {
venaas's avatar
venaas committed
876
	    debug(DBG_WARN, "verifyconfcert: subjectaltname URI not matching regex");
venaas's avatar
venaas committed
877
	    return 0;
878
	}
venaas's avatar
venaas committed
879
	debug(DBG_DBG, "verifyconfcert: subjectaltname URI matching regex");
880
    }
881
    return 1;
882
883
}

884
int tlsconnect(struct server *server, struct timeval *when, int timeout, char *text) {
venaas's avatar
venaas committed
885
886
    struct timeval now;
    time_t elapsed;
venaas's avatar
venaas committed
887
888
    X509 *cert;
    
venaas's avatar
venaas committed
889
    debug(DBG_DBG, "tlsconnect called from %s", text);
890
891
    pthread_mutex_lock(&server->lock);
    if (when && memcmp(&server->lastconnecttry, when, sizeof(struct timeval))) {
venaas's avatar
   
venaas committed
892
	/* already reconnected, nothing to do */
venaas's avatar
venaas committed
893
	debug(DBG_DBG, "tlsconnect(%s): seems already reconnected", text);
894
	pthread_mutex_unlock(&server->lock);
895
	return 1;
venaas's avatar
   
venaas committed
896
897
    }

venaas's avatar
venaas committed
898
    debug(DBG_DBG, "tlsconnect %s", text);
venaas's avatar
venaas committed
899

venaas's avatar
   
venaas committed
900
    for (;;) {
venaas's avatar
venaas committed
901
	gettimeofday(&now, NULL);
902
	elapsed = now.tv_sec - server->lastconnecttry.tv_sec;
903
904
905
906
907
	if (timeout && server->lastconnecttry.tv_sec && elapsed > timeout) {
	    debug(DBG_DBG, "tlsconnect: timeout");
	    if (server->sock >= 0)
		close(server->sock);
	    SSL_free(server->ssl);
908
	    server->ssl = NULL;
909
910
911
	    pthread_mutex_unlock(&server->lock);
	    return 0;
	}
912
913
	if (server->connectionok) {
	    server->connectionok = 0;
914
915
916
917
	    sleep(2);
	} else if (elapsed < 1)
	    sleep(2);
	else if (elapsed < 60) {
venaas's avatar
venaas committed
918
	    debug(DBG_INFO, "tlsconnect: sleeping %lds", elapsed);
venaas's avatar
venaas committed
919
	    sleep(elapsed);
venaas's avatar
venaas committed
920
	} else if (elapsed < 100000) {
921
922
	    debug(DBG_INFO, "tlsconnect: sleeping %ds", 60);
	    sleep(60);
venaas's avatar
venaas committed
923
	} else
venaas's avatar
venaas committed
924
	    server->lastconnecttry.tv_sec = now.tv_sec;  /* no sleep at startup */
venaas's avatar
venaas committed
925
	debug(DBG_WARN, "tlsconnect: trying to open TLS connection to %s port %s", server->conf->host, server->conf->port);
926
927
	if (server->sock >= 0)
	    close(server->sock);
928
929
	if ((server->sock = connecttcp(server->conf->addrinfo)) < 0) {
	    debug(DBG_ERR, "tlsconnect: connecttcp failed");
venaas's avatar
venaas committed
930
	    continue;
931
932
	}
	
venaas's avatar
venaas committed
933
	SSL_free(server->ssl);
venaas's avatar
venaas committed
934
	server->ssl = SSL_new(server->conf->ssl_ctx);
venaas's avatar
venaas committed
935
	SSL_set_fd(server->ssl, server->sock);
venaas's avatar
venaas committed
936
937
938
939
940
941
942
	if (SSL_connect(server->ssl) <= 0)
	    continue;
	cert = verifytlscert(server->ssl);
	if (!cert)
	    continue;
	if (verifyconfcert(cert, server->conf)) {
	    X509_free(cert);
venaas's avatar
   
venaas committed
943
	    break;
venaas's avatar
venaas committed
944
945
	}
	X509_free(cert);
venaas's avatar
   
venaas committed
946
    }
venaas's avatar
venaas committed
947
    debug(DBG_WARN, "tlsconnect: TLS connection to %s port %s up", server->conf->host, server->conf->port);
venaas's avatar
venaas committed
948
    gettimeofday(&server->lastconnecttry, NULL);
949
    pthread_mutex_unlock(&server->lock);
950
    return 1;
venaas's avatar
   
venaas committed
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
994
995
/* 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;
    fd_set readfds, writefds;
    struct timeval timer;
    
    s = SSL_get_fd(ssl);
    if (s < 0)
	return -1;
    /* make socket non-blocking? */
    for (len = 0; len < num; len += cnt) {
	FD_ZERO(&readfds);
	FD_SET(s, &readfds);
	writefds = readfds;
	if (timeout) {
	    timer.tv_sec = timeout;
	    timer.tv_usec = 0;
	}
	ndesc = select(s + 1, &readfds, &writefds, NULL, timeout ? &timer : NULL);
	if (ndesc < 1)
	    return ndesc;

	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);
		/* fall through */
	    default:
		return -1;
	    }
    }
    return num;
}

/* timeout in seconds, 0 means no timeout (blocking) */
unsigned char *radtlsget(SSL *ssl, int timeout) {
venaas's avatar
venaas committed
996
    int cnt, len;
venaas's avatar
   
venaas committed
997
998
999
    unsigned char buf[4], *rad;

    for (;;) {
1000
1001
1002
1003
	cnt = sslreadtimeout(ssl, buf, 4, timeout);
	if (cnt < 1) {
	    debug(DBG_DBG, cnt ? "radtlsget: connection lost" : "radtlsget: timeout");
	    return NULL;
venaas's avatar
   
venaas committed
1004
1005
1006
1007
1008
	}

	len = RADLEN(buf);
	rad = malloc(len);
	if (!rad) {
1009
	    debug(DBG_ERR, "radtlsget: malloc failed");
venaas's avatar
   
venaas committed
1010
1011
1012
	    continue;
	}
	memcpy(rad, buf, 4);
1013
1014
1015
1016
1017
1018
	
	cnt = sslreadtimeout(ssl, rad + 4, len - 4, timeout);
	if (cnt < 1) {
	    debug(DBG_DBG, cnt ? "radtlsget: connection lost" : "radtlsget: timeout");
	    free(rad);
	    return NULL;
venaas's avatar
   
venaas committed
1019
	}
1020
1021
	
	if (len >= 20)
venaas's avatar
   
venaas committed
1022
1023
1024
	    break;
	
	free(rad);
1025
	debug(DBG_WARN, "radtlsget: packet smaller than minimum radius size");
venaas's avatar
   
venaas committed
1026
1027
    }
    
venaas's avatar
venaas committed
1028
    debug(DBG_DBG, "radtlsget: got %d bytes", len);
venaas's avatar
   
venaas committed
1029
1030
1031
    return rad;
}

1032
int clientradputudp(struct server *server, unsigned char *rad) {
venaas's avatar
   
venaas committed
1033
    size_t len;
1034
1035
    struct sockaddr_storage sa;
    struct sockaddr *sap;
1036
    struct clsrvconf *conf = server->conf;
1037
    in_port_t *port = NULL;
venaas's avatar
venaas committed
1038
    
venaas's avatar
   
venaas committed
1039
    len = RADLEN(rad);
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
    
    if (*rad == RAD_Accounting_Request) {
	sap = (struct sockaddr *)&sa;
	memcpy(sap, conf->addrinfo->ai_addr, conf->addrinfo->ai_addrlen);
    } else
	sap = conf->addrinfo->ai_addr;
    
    switch (sap->sa_family) {
    case AF_INET:
	port = &((struct sockaddr_in *)sap)->sin_port;
	break;
    case AF_INET6:
	port = &((struct sockaddr_in6 *)sap)->sin6_port;
	break;
    default:
venaas's avatar
   
venaas committed
1055
1056
1057
	return 0;
    }

1058
    if (*rad == RAD_Accounting_Request)
venaas's avatar
venaas committed
1059
	*port = htons(ntohs(*port) + 1);
1060
1061
    
    if (sendto(server->sock, rad, len, 0, sap, conf->addrinfo->ai_addrlen) >= 0) {
venaas's avatar
venaas committed
1062
	debug(DBG_DBG, "clienradputudp: sent UDP of length %d to %s port %d", len, conf->host, ntohs(*port));
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
	return 1;
    }

    debug(DBG_WARN, "clientradputudp: send failed");
    return 0;
}

int clientradputtls(struct server *server, unsigned char *rad) {
    int cnt;
    size_t len;
    unsigned long error;
    struct timeval lastconnecttry;
    struct clsrvconf *conf = server->conf;
    
    len = RADLEN(rad);
1078
    lastconnecttry = server->lastconnecttry;
venaas's avatar
venaas committed
1079
    while ((cnt = SSL_write(server->ssl, rad, len)) <= 0) {
venaas's avatar
   
venaas committed
1080
	while ((error = ERR_get_error()))
1081
	    debug(DBG_ERR, "clientradputtls: TLS: %s", ERR_error_string(error, NULL));
1082
1083
	if (server->dynamiclookuparg)
	    return 0;
1084
	tlsconnect(server, &lastconnecttry, 0, "clientradputtls");
1085
	lastconnecttry = server->lastconnecttry;
venaas's avatar
   
venaas committed
1086
    }
venaas's avatar
venaas committed
1087

1088
    server->connectionok = 1;
1089
    debug(DBG_DBG, "clientradputtls: Sent %d bytes, Radius packet of length %d to TLS peer %s", cnt, len, conf->host);
venaas's avatar
   
venaas committed
1090
1091
1092
    return 1;
}

1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
int clientradput(struct server *server, unsigned char *rad) {
    switch (server->conf->type) {
    case 'U':
	return clientradputudp(server, rad);
    case 'T':
	return clientradputtls(server, rad);
    }
    return 0;
}

venaas's avatar
   
venaas committed
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
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
1118
	EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
venaas's avatar
   
venaas committed
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
	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
1145
	      EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
venaas's avatar
   
venaas committed
1146
1147
1148
1149
1150
1151
1152
	      EVP_DigestFinal_ex(&mdctx, hash, &len) &&
	      len == 16 &&
	      !memcmp(hash, rad + 4, 16));
    pthread_mutex_unlock(&lock);
    return result;
}
	      
venaas's avatar
venaas committed
1153
int checkmessageauth(unsigned char *rad, uint8_t *authattr, char *secret) {
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
    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
1172
    memcpy(authattr, auth, 16);
1173
    if (md_len != 16) {
1174
	debug(DBG_WARN, "message auth computation failed");
1175
1176
1177
1178
1179
	pthread_mutex_unlock(&lock);
	return 0;
    }

    if (memcmp(auth, hash, 16)) {
1180
	debug(DBG_WARN, "message authenticator, wrong value");
1181
1182
1183
1184
1185
1186
1187
1188
	pthread_mutex_unlock(&lock);
	return 0;
    }	
	
    pthread_mutex_unlock(&lock);
    return 1;
}

venaas's avatar
venaas committed
1189
int createmessageauth(unsigned char *rad, unsigned char *authattrval, char *secret) {
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
    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) {
1210
	debug(DBG_WARN, "message auth computation failed");