radsecproxy.c 87.8 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
 *
 * 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.
 */

9
10
11
12
13
/* Code contributions from:
 *
 * Arne Schwabe <schwabe at uni-paderborn.de>
 */

venaas's avatar
   
venaas committed
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/* 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
33
 * Example: With 3 UDP peers and 30 TLS peers, there will be a max of
venaas's avatar
   
venaas committed
34
35
36
 *          1 + (2 + 2 * 3) + (2 * 30) + (2 * 30) = 129 threads
*/

venaas's avatar
venaas committed
37
/* Bugs:
venaas's avatar
venaas committed
38
 * May segfault when dtls connections go down? More testing needed
39
 * Remove expired stuff from clients request list?
venaas's avatar
venaas committed
40
41
 * Multiple outgoing connections if not enough IDs? (multiple servers per conf?)
 * Useful for TCP accounting? Now we require separate server config for alt port
venaas's avatar
venaas committed
42
43
 */

venaas's avatar
venaas committed
44
#include <signal.h>
venaas's avatar
venaas committed
45
46
#include <sys/socket.h>
#include <netinet/in.h>
venaas's avatar
   
venaas committed
47
#include <netdb.h>
venaas's avatar
venaas committed
48
#include <string.h>
venaas's avatar
   
venaas committed
49
#include <unistd.h>
venaas's avatar
venaas committed
50
#include <limits.h>
venaas's avatar
venaas committed
51
52
53
#ifdef SYS_SOLARIS9
#include <fcntl.h>
#endif
venaas's avatar
venaas committed
54
#include <sys/time.h>
55
#include <sys/types.h>
56
#include <ctype.h>
57
#include <sys/wait.h>
58
#include <arpa/inet.h>
venaas's avatar
venaas committed
59
#include <regex.h>
venaas's avatar
venaas committed
60
#include <libgen.h>
venaas's avatar
   
venaas committed
61
62
63
64
65
#include <pthread.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/md5.h>
venaas's avatar
venaas committed
66
#include "debug.h"
venaas's avatar
venaas committed
67
#include "list.h"
68
#include "hash.h"
69
#include "util.h"
70
#include "hostport.h"
venaas's avatar
venaas committed
71
#include "radsecproxy.h"
venaas's avatar
venaas committed
72
#include "udp.h"
venaas's avatar
venaas committed
73
74
#include "tcp.h"
#include "tls.h"
venaas's avatar
venaas committed
75
#include "dtls.h"
venaas's avatar
   
venaas committed
76

77
static struct options options;
venaas's avatar
venaas committed
78
static struct list *clconfs, *srvconfs;
79
80
static struct list *realms;
static struct hash *rewriteconfs;
81

venaas's avatar
venaas committed
82
static pthread_mutex_t *ssl_locks = NULL;
venaas's avatar
   
venaas committed
83
84
85
static long *ssl_lock_count;
extern int optind;
extern char *optarg;
venaas's avatar
venaas committed
86
static const struct protodefs *protodefs[RAD_PROTOCOUNT];
venaas's avatar
   
venaas committed
87

venaas's avatar
venaas committed
88
/* minimum required declarations to avoid reordering code */
89
struct realm *adddynamicrealmserver(struct realm *realm, char *id);
90
int dynamicconfig(struct server *server);
91
int confserver_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *val);
92
93
void freerealm(struct realm *realm);
void freeclsrvconf(struct clsrvconf *conf);
94
95
void freerq(struct request *rq);
void freerqoutdata(struct rqout *rqout);
venaas's avatar
venaas committed
96
void rmclientrq(struct request *rq, uint8_t id);
97

venaas's avatar
venaas committed
98
99
static const struct protodefs *(*protoinits[])(uint8_t) = { udpinit, tlsinit, tcpinit, dtlsinit };

venaas's avatar
venaas committed
100
uint8_t protoname2int(const char *name) {
venaas's avatar
venaas committed
101
    uint8_t i;
venaas's avatar
venaas committed
102

venaas's avatar
venaas committed
103
104
105
106
    for (i = 0; i < RAD_PROTOCOUNT; i++)
	if (protodefs[i] && protodefs[i]->name && !strcasecmp(protodefs[i]->name, name))
	    return i;
    return 255;
venaas's avatar
venaas committed
107
108
}
    
venaas's avatar
   
venaas committed
109
110
111
/* callbacks for making OpenSSL thread safe */
unsigned long ssl_thread_id() {
        return (unsigned long)pthread_self();
venaas's avatar
venaas committed
112
}
venaas's avatar
   
venaas committed
113
114
115
116
117
118
119
120
121

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]);
}

122
123
124
/* 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 };
venaas's avatar
venaas committed
125
    uint8_t r, l = len / 8;
126
127
128
129
130
131
132
133
    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]);
}

134
/* returns next config with matching address, or NULL */
135
struct clsrvconf *find_conf(uint8_t type, struct sockaddr *addr, struct list *confs, struct list_node **cur, uint8_t server_p) {
136
137
138
    struct list_node *entry;
    struct clsrvconf *conf;
    
venaas's avatar
venaas committed
139
    for (entry = (cur && *cur ? list_next(*cur) : list_first(confs)); entry; entry = list_next(entry)) {
venaas's avatar
venaas committed
140
	conf = (struct clsrvconf *)entry->data;
141
	if (conf->type == type && addressmatches(conf->hostports, addr, server_p)) {
142
143
144
	    if (cur)
		*cur = entry;
	    return conf;
145
	}
venaas's avatar
venaas committed
146
    }    
venaas's avatar
venaas committed
147
148
149
    return NULL;
}

venaas's avatar
venaas committed
150
struct clsrvconf *find_clconf(uint8_t type, struct sockaddr *addr, struct list_node **cur) {
151
    return find_conf(type, addr, clconfs, cur, 0);
venaas's avatar
venaas committed
152
153
154
}

struct clsrvconf *find_srvconf(uint8_t type, struct sockaddr *addr, struct list_node **cur) {
155
    return find_conf(type, addr, srvconfs, cur, 1);
venaas's avatar
venaas committed
156
157
}

venaas's avatar
venaas committed
158
/* returns next config of given type, or NULL */
venaas's avatar
venaas committed
159
struct clsrvconf *find_clconf_type(uint8_t type, struct list_node **cur) {
venaas's avatar
venaas committed
160
161
162
    struct list_node *entry;
    struct clsrvconf *conf;
    
venaas's avatar
venaas committed
163
    for (entry = (cur && *cur ? list_next(*cur) : list_first(clconfs)); entry; entry = list_next(entry)) {
venaas's avatar
venaas committed
164
165
166
167
168
169
170
171
172
173
	conf = (struct clsrvconf *)entry->data;
	if (conf->type == type) {
	    if (cur)
		*cur = entry;
	    return conf;
	}
    }    
    return NULL;
}

174
175
struct gqueue *newqueue() {
    struct gqueue *q;
176
    
177
    q = malloc(sizeof(struct gqueue));
178
    if (!q)
179
	debugx(1, DBG_ERR, "malloc failed");
180
181
    q->entries = list_create();
    if (!q->entries)
182
	debugx(1, DBG_ERR, "malloc failed");
183
184
185
186
187
    pthread_mutex_init(&q->mutex, NULL);
    pthread_cond_init(&q->cond, NULL);
    return q;
}

188
void removequeue(struct gqueue *q) {
189
    struct list_node *entry;
190
191
192

    if (!q)
	return;
193
194
    pthread_mutex_lock(&q->mutex);
    for (entry = list_first(q->entries); entry; entry = list_next(entry))
venaas's avatar
venaas committed
195
	freerq((struct request *)entry);
196
197
198
199
    list_destroy(q->entries);
    pthread_cond_destroy(&q->cond);
    pthread_mutex_unlock(&q->mutex);
    pthread_mutex_destroy(&q->mutex);
200
    free(q);
201
202
}

203
void freebios(struct gqueue *q) {
204
205
206
207
208
209
210
    BIO *bio;
    
    pthread_mutex_lock(&q->mutex);
    while ((bio = (BIO *)list_shift(q->entries)))
	BIO_free(bio);
    pthread_mutex_unlock(&q->mutex);
    removequeue(q);
211
212
}

213
struct client *addclient(struct clsrvconf *conf, uint8_t lock) {
214
215
216
217
218
    struct client *new = malloc(sizeof(struct client));
    
    if (!new) {
	debug(DBG_ERR, "malloc failed");
	return NULL;
219
    }
220
221
222

    if (lock)
	pthread_mutex_lock(conf->lock);
223
    if (!conf->clients) {
224
225
	conf->clients = list_create();
	if (!conf->clients) {
226
227
	    if (lock)
		pthread_mutex_unlock(conf->lock);
228
229
230
	    debug(DBG_ERR, "malloc failed");
	    return NULL;
	}
231
    }
232
233
234
    
    memset(new, 0, sizeof(struct client));
    new->conf = conf;
venaas's avatar
venaas committed
235
236
237
238
    if (conf->pdef->addclient)
	conf->pdef->addclient(new);
    else
	new->replyq = newqueue();
239
    list_push(conf->clients, new);
240
241
    if (lock)
	pthread_mutex_unlock(conf->lock);
242
243
244
    return new;
}

245
246
247
248
249
250
251
252
253
void removeclientrqs_sendrq_freeserver_lock(uint8_t wantlock) {
    static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

    if (wantlock)
	pthread_mutex_lock(&lock);
    else
	pthread_mutex_unlock(&lock);
}
    
254
255
256
257
258
void removeclientrqs(struct client *client) {
    struct request *rq;
    struct rqout *rqout;
    int i;

259
    removeclientrqs_sendrq_freeserver_lock(1);
260
261
262
263
    for (i = 0; i < MAX_REQUESTS; i++) {
	rq = client->rqs[i];
	if (!rq)
	    continue;
264
265
266
267
268
269
270
	if (rq->to) {
	    rqout = rq->to->requests + rq->newid;
	    pthread_mutex_lock(rqout->lock);
	    if (rqout->rq == rq) /* still pointing to our request */
		freerqoutdata(rqout);
	    pthread_mutex_unlock(rqout->lock);
	}
271
272
	freerq(rq);
    }
273
    removeclientrqs_sendrq_freeserver_lock(0);
274
275
}

venaas's avatar
venaas committed
276
void removelockedclient(struct client *client) {
277
278
279
280
    struct clsrvconf *conf;
    
    conf = client->conf;
    if (conf->clients) {
281
	removeclientrqs(client);
282
	removequeue(client->replyq);
283
	list_removedata(conf->clients, client);
284
285
286
	free(client->addr);
	free(client);
    }
venaas's avatar
venaas committed
287
288
289
290
291
292
293
294
295
296
297
}

void removeclient(struct client *client) {
    struct clsrvconf *conf;
    
    if (!client)
	return;

    conf = client->conf;
    pthread_mutex_lock(conf->lock);
    removelockedclient(client);
298
    pthread_mutex_unlock(conf->lock);
299
300
}

venaas's avatar
venaas committed
301
void freeserver(struct server *server, uint8_t destroymutex) {
302
    struct rqout *rqout, *end;
303

venaas's avatar
venaas committed
304
305
    if (!server)
	return;
306

307
    removeclientrqs_sendrq_freeserver_lock(1);
308
    if (server->requests) {
309
	rqout = server->requests;
310
	for (end = rqout + MAX_REQUESTS; rqout < end; rqout++) {
311
312
	    if (rqout->rq)
		rqout->rq->to = NULL;
313
	    freerqoutdata(rqout);
314
315
316
	    pthread_mutex_destroy(rqout->lock);
	    free(rqout->lock);
	}
317
318
	free(server->requests);
    }
319
320
    if (server->rbios)
	freebios(server->rbios);
321
    free(server->dynamiclookuparg);
322
323
    if (server->ssl)
	SSL_free(server->ssl);
venaas's avatar
venaas committed
324
325
326
327
328
    if (destroymutex) {
	pthread_mutex_destroy(&server->lock);
	pthread_cond_destroy(&server->newrq_cond);
	pthread_mutex_destroy(&server->newrq_mutex);
    }
329
    removeclientrqs_sendrq_freeserver_lock(0);
venaas's avatar
venaas committed
330
331
332
333
    free(server);
}

int addserver(struct clsrvconf *conf) {
334
    int i;
335
    
venaas's avatar
venaas committed
336
337
338
339
    if (conf->servers) {
	debug(DBG_ERR, "addserver: currently works with just one server per conf");
	return 0;
    }
340
    conf->servers = malloc(sizeof(struct server));
venaas's avatar
venaas committed
341
342
343
344
    if (!conf->servers) {
	debug(DBG_ERR, "malloc failed");
	return 0;
    }
345
346
347
    memset(conf->servers, 0, sizeof(struct server));
    conf->servers->conf = conf;

348
349
#ifdef RADPROT_DTLS    
    if (conf->type == RAD_DTLS)
350
	conf->servers->rbios = newqueue();
351
#endif
venaas's avatar
venaas committed
352
    conf->pdef->setsrcres();
venaas's avatar
venaas committed
353

venaas's avatar
venaas committed
354
355
356
    conf->servers->sock = -1;
    if (conf->pdef->addserverextra)
	conf->pdef->addserverextra(conf);
357
    
358
    conf->servers->requests = calloc(MAX_REQUESTS, sizeof(struct rqout));
venaas's avatar
venaas committed
359
360
361
362
    if (!conf->servers->requests) {
	debug(DBG_ERR, "malloc failed");
	goto errexit;
    }
363
364
365
366
367
368
369
370
371
372
373
374
375
    for (i = 0; i < MAX_REQUESTS; i++) {
	conf->servers->requests[i].lock = malloc(sizeof(pthread_mutex_t));
	if (!conf->servers->requests[i].lock) {
	    debug(DBG_ERR, "malloc failed");
	    goto errexit;
	}
	if (pthread_mutex_init(conf->servers->requests[i].lock, NULL)) {
	    debug(DBG_ERR, "mutex init failed");
	    free(conf->servers->requests[i].lock);
	    conf->servers->requests[i].lock = NULL;
	    goto errexit;
	}
    }
venaas's avatar
venaas committed
376
377
378
379
    if (pthread_mutex_init(&conf->servers->lock, NULL)) {
	debug(DBG_ERR, "mutex init failed");
	goto errexit;
    }
380
    conf->servers->newrq = 0;
venaas's avatar
venaas committed
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
    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;
399
400
}

401
unsigned char *attrget(unsigned char *attrs, int length, uint8_t type) {
402
    while (length > 1) {
venaas's avatar
venaas committed
403
	if (ATTRTYPE(attrs) == type)
404
	    return attrs;
venaas's avatar
venaas committed
405
406
	length -= ATTRLEN(attrs);
	attrs += ATTRLEN(attrs);
407
408
409
410
    }
    return NULL;
}

venaas's avatar
venaas committed
411
struct request *newrqref(struct request *rq) {
412
413
    if (rq)
	rq->refcount++;
venaas's avatar
venaas committed
414
415
416
    return rq;
}

417
418
419
420
421
422
void freerq(struct request *rq) {
    if (!rq)
	return;
    debug(DBG_DBG, "freerq: called with refcount %d", rq->refcount);
    if (--rq->refcount)
	return;
venaas's avatar
venaas committed
423
424
425
426
    if (rq->origusername)
	free(rq->origusername);
    if (rq->buf)
	free(rq->buf);
venaas's avatar
venaas committed
427
428
429
    if (rq->replybuf)
	free(rq->replybuf);
    if (rq->msg)
venaas's avatar
venaas committed
430
	radmsg_free(rq->msg);
431
432
433
434
435
436
    free(rq);
}

void freerqoutdata(struct rqout *rqout) {
    if (!rqout)
	return;
437
    if (rqout->rq) {
438
439
440
441
	if (rqout->rq->buf) {
	    free(rqout->rq->buf);
	    rqout->rq->buf = NULL;
	}
442
	freerq(rqout->rq);
443
444
445
446
	rqout->rq = NULL;
    }
    rqout->tries = 0;
    memset(&rqout->expiry, 0, sizeof(struct timeval));
447
448
}

449
void sendrq(struct request *rq) {
450
    int i, start;
451
452
453
454
455
456
    struct server *to;

    removeclientrqs_sendrq_freeserver_lock(1);
    to = rq->to;
    if (!to)
	goto errexit;
457
    
458
    start = to->conf->statusserver ? 1 : 0;
venaas's avatar
   
venaas committed
459
    pthread_mutex_lock(&to->newrq_mutex);
460
461
462
463
    if (start && rq->msg->code == RAD_Status_Server) {
	pthread_mutex_lock(to->requests[0].lock);
	if (to->requests[0].rq) {
	    pthread_mutex_unlock(to->requests[0].lock);
464
	    debug(DBG_INFO, "sendrq: status server already in queue, dropping request");
465
	    goto errexit;
466
	}
467
468
469
470
471
472
	i = 0;
    } else {
	if (!to->nextid)
	    to->nextid = start;
	/* might simplify if only try nextid, might be ok */
	for (i = to->nextid; i < MAX_REQUESTS; i++) {
venaas's avatar
venaas committed
473
	    if (!to->requests[i].rq) {
474
		pthread_mutex_lock(to->requests[i].lock);
venaas's avatar
venaas committed
475
		if (!to->requests[i].rq)
476
477
478
479
		    break;
		pthread_mutex_unlock(to->requests[i].lock);
	    }
	}
480
481
482
483
484
485
486
487
488
489
	if (i == MAX_REQUESTS) {
	    for (i = start; i < to->nextid; i++) {
		if (!to->requests[i].rq) {
		    pthread_mutex_lock(to->requests[i].lock);
		    if (!to->requests[i].rq)
			break;
		    pthread_mutex_unlock(to->requests[i].lock);
		}
	    }
	    if (i == to->nextid) {
490
		debug(DBG_INFO, "sendrq: no room in queue, dropping request");
491
492
		goto errexit;
	    }
venaas's avatar
venaas committed
493
	}
venaas's avatar
   
venaas committed
494
    }
495
    rq->newid = (uint8_t)i;
venaas's avatar
venaas committed
496
497
498
    rq->msg->id = (uint8_t)i;
    rq->buf = radmsg2buf(rq->msg, (uint8_t *)to->conf->secret);
    if (!rq->buf) {
499
	pthread_mutex_unlock(to->requests[i].lock);
venaas's avatar
venaas committed
500
	debug(DBG_ERR, "sendrq: radmsg2buf failed");
501
	goto errexit;
venaas's avatar
venaas committed
502
503
    }
    
504
    debug(DBG_DBG, "sendrq: inserting packet with id %d in queue for %s", i, to->conf->name);
venaas's avatar
venaas committed
505
    to->requests[i].rq = rq;
506
    pthread_mutex_unlock(to->requests[i].lock);
507
508
    if (i >= start) /* i is not reserved for statusserver */
	to->nextid = i + 1;
venaas's avatar
   
venaas committed
509
510
511

    if (!to->newrq) {
	to->newrq = 1;
venaas's avatar
venaas committed
512
	debug(DBG_DBG, "sendrq: signalling client writer");
venaas's avatar
   
venaas committed
513
514
	pthread_cond_signal(&to->newrq_cond);
    }
venaas's avatar
venaas committed
515

516
    pthread_mutex_unlock(&to->newrq_mutex);
517
    removeclientrqs_sendrq_freeserver_lock(0);
518
519
520
    return;

 errexit:
521
522
    if (rq->from)
	rmclientrq(rq, rq->msg->id);
523
    freerq(rq);
venaas's avatar
   
venaas committed
524
    pthread_mutex_unlock(&to->newrq_mutex);
525
    removeclientrqs_sendrq_freeserver_lock(0);
venaas's avatar
   
venaas committed
526
527
}

venaas's avatar
venaas committed
528
void sendreply(struct request *rq) {
529
    uint8_t first;
venaas's avatar
venaas committed
530
531
532
533
534
535
536
537
    struct client *to = rq->from;
    
    if (!rq->replybuf)
	rq->replybuf = radmsg2buf(rq->msg, (uint8_t *)to->conf->secret);
    radmsg_free(rq->msg);
    rq->msg = NULL;
    if (!rq->replybuf) {
	freerq(rq);
venaas's avatar
venaas committed
538
	debug(DBG_ERR, "sendreply: radmsg2buf failed");
venaas's avatar
venaas committed
539
540
	return;
    }
541
542

    pthread_mutex_lock(&to->replyq->mutex);
543
    first = list_first(to->replyq->entries) == NULL;
544
    
venaas's avatar
venaas committed
545
    if (!list_push(to->replyq->entries, rq)) {
546
	pthread_mutex_unlock(&to->replyq->mutex);
venaas's avatar
venaas committed
547
	freerq(rq);
548
549
550
551
552
	debug(DBG_ERR, "sendreply: malloc failed");
	return;
    }
    
    if (first) {
553
	debug(DBG_DBG, "signalling server writer");
554
	pthread_cond_signal(&to->replyq->cond);
venaas's avatar
   
venaas committed
555
    }
556
    pthread_mutex_unlock(&to->replyq->mutex);
venaas's avatar
   
venaas committed
557
558
}

venaas's avatar
venaas committed
559
int pwdencrypt(uint8_t *in, uint8_t len, char *shared, uint8_t sharedlen, uint8_t *auth) {
venaas's avatar
venaas committed
560
561
562
563
564
    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
565
    uint8_t i, offset = 0, out[128];
venaas's avatar
venaas committed
566
567
568
569
570
571
572
573
574
575
    
    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
576
	    !EVP_DigestUpdate(&mdctx, (uint8_t *)shared, sharedlen) ||
venaas's avatar
venaas committed
577
578
579
580
581
582
583
584
585
586
587
588
589
	    !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
590
    memcpy(in, out, len);
venaas's avatar
venaas committed
591
592
593
594
    pthread_mutex_unlock(&lock);
    return 1;
}

venaas's avatar
venaas committed
595
int pwddecrypt(uint8_t *in, uint8_t len, char *shared, uint8_t sharedlen, uint8_t *auth) {
venaas's avatar
   
venaas committed
596
597
598
599
600
    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
601
    uint8_t i, offset = 0, out[128];
venaas's avatar
   
venaas committed
602
603
604
605
606
607
608
609
610
611
    
    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
612
	    !EVP_DigestUpdate(&mdctx, (uint8_t *)shared, sharedlen) ||
venaas's avatar
   
venaas committed
613
614
615
616
617
618
619
	    !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
620
621
	    out[offset + i] = hash[i] ^ in[offset + i];
	input = in + offset;
venaas's avatar
   
venaas committed
622
	offset += 16;
venaas's avatar
venaas committed
623
	if (offset == len)
venaas's avatar
   
venaas committed
624
625
	    break;
    }
venaas's avatar
venaas committed
626
    memcpy(in, out, len);
venaas's avatar
   
venaas committed
627
628
629
630
    pthread_mutex_unlock(&lock);
    return 1;
}

venaas's avatar
venaas committed
631
632
633
634
635
636
637
638
639
640
641
642
643
644
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;
    }

645
646
647
648
#if 0
    printfchars(NULL, "msppencrypt auth in", "%02x ", auth, 16);
    printfchars(NULL, "msppencrypt salt in", "%02x ", salt, 2);
    printfchars(NULL, "msppencrypt in", "%02x ", text, len);
venaas's avatar
venaas committed
649
650
651
652
653
654
655
656
657
658
659
660
#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    
661
    printfchars(NULL, "msppencrypt hash", "%02x ", hash, 16);
venaas's avatar
venaas committed
662
663
664
665
666
667
#endif
    
    for (i = 0; i < 16; i++)
	text[i] ^= hash[i];
    
    for (offset = 16; offset < len; offset += 16) {
venaas's avatar
venaas committed
668
#if 0	
venaas's avatar
venaas committed
669
	printf("text + offset - 16 c(%d): ", offset / 16);
670
	printfchars(NULL, NULL, "%02x ", text + offset - 16, 16);
venaas's avatar
venaas committed
671
#endif
venaas's avatar
venaas committed
672
673
674
675
676
677
678
679
	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;
	}
680
681
#if 0
	printfchars(NULL, "msppencrypt hash", "%02x ", hash, 16);
venaas's avatar
venaas committed
682
683
684
685
686
687
688
#endif    
	
	for (i = 0; i < 16; i++)
	    text[offset + i] ^= hash[i];
    }
    
#if 0
689
    printfchars(NULL, "msppencrypt out", "%02x ", text, len);
venaas's avatar
venaas committed
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
#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;
    }

711
712
713
714
#if 0
    printfchars(NULL, "msppdecrypt auth in", "%02x ", auth, 16);
    printfchars(NULL, "msppdecrypt salt in", "%02x ", salt, 2);
    printfchars(NULL, "msppdecrypt in", "%02x ", text, len);
venaas's avatar
venaas committed
715
716
717
718
719
720
721
722
723
724
725
726
#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    
727
    printfchars(NULL, "msppdecrypt hash", "%02x ", hash, 16);
venaas's avatar
venaas committed
728
729
730
731
732
733
734
735
#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);
736
	printfchars(NULL, NULL, "%02x ", text + offset - 16, 16);
venaas's avatar
venaas committed
737
738
739
740
741
742
743
744
745
#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;
	}
746
747
#if 0
	printfchars(NULL, "msppdecrypt hash", "%02x ", hash, 16);
venaas's avatar
venaas committed
748
749
#endif    

750
751
	for (i = 0; i < 16; i++)
	    plain[offset + i] = text[offset + i] ^ hash[i];
venaas's avatar
venaas committed
752
753
754
755
    }

    memcpy(text, plain, len);
#if 0
756
    printfchars(NULL, "msppdecrypt out", "%02x ", text, len);
venaas's avatar
venaas committed
757
758
759
760
761
762
#endif

    pthread_mutex_unlock(&lock);
    return 1;
}

763
764
765
766
767
768
769
struct realm *newrealmref(struct realm *r) {
    if (r)
	r->refcount++;
    return r;
}

/* returns with lock on realm */
venaas's avatar
venaas committed
770
struct realm *id2realm(struct list *realmlist, char *id) {
venaas's avatar
venaas committed
771
    struct list_node *entry;
772
    struct realm *realm, *subrealm;
venaas's avatar
venaas committed
773
774
775

    /* need to do locking for subrealms and check subrealm timers */
    for (entry = list_first(realmlist); entry; entry = list_next(entry)) {
venaas's avatar
venaas committed
776
777
	realm = (struct realm *)entry->data;
	if (!regexec(&realm->regex, id, 0, NULL, 0)) {
778
779
	    pthread_mutex_lock(&realm->mutex);
	    if (realm->subrealms) {
venaas's avatar
venaas committed
780
		subrealm = id2realm(realm->subrealms, id);
781
782
783
784
785
786
		if (subrealm) {
		    pthread_mutex_unlock(&realm->mutex);
		    return subrealm;
		}
	    }
	    return newrealmref(realm);
venaas's avatar
venaas committed
787
	}
venaas's avatar
venaas committed
788
    }
venaas's avatar
venaas committed
789
    return NULL;
venaas's avatar
venaas committed
790
791
}

792
793
794
795
796
797
798
799
800
int hasdynamicserver(struct list *srvconfs) {
    struct list_node *entry;

    for (entry = list_first(srvconfs); entry; entry = list_next(entry))
	if (((struct clsrvconf *)entry->data)->dynamiclookupcommand)
	    return 1;
    return 0;
}

801
802
/* helper function, only used by removeserversubrealms() */
void _internal_removeserversubrealms(struct list *realmlist, struct clsrvconf *srv) {
803
    struct list_node *entry, *entry2;
804
    struct realm *realm;
805

806
    for (entry = list_first(realmlist); entry;) {
807
808
	realm = newrealmref((struct realm *)entry->data);
	pthread_mutex_lock(&realm->mutex);
809
	entry = list_next(entry);
810

811
	if (realm->srvconfs) {
812
813
814
	    for (entry2 = list_first(realm->srvconfs); entry2; entry2 = list_next(entry2))
		if (entry2->data == srv)
		    freerealm(realm);
815
816
817
	    list_removedata(realm->srvconfs, srv);
	}
	if (realm->accsrvconfs) {
818
819
820
	    for (entry2 = list_first(realm->accsrvconfs); entry2; entry2 = list_next(entry2))
		if (entry2->data == srv)
		    freerealm(realm);
821
822
823
	    list_removedata(realm->accsrvconfs, srv);
	}

824
825
826
827
828
829
830
831
832
833
	/* remove subrealm if no dynamic servers left */
	if (!hasdynamicserver(realm->srvconfs) && !hasdynamicserver(realm->accsrvconfs)) {
	    while (list_shift(realm->srvconfs))
		freerealm(realm);
	    list_destroy(realm->srvconfs);
	    realm->srvconfs = NULL;
	    while (list_shift(realm->accsrvconfs))
		freerealm(realm);
	    list_destroy(realm->accsrvconfs);
	    realm->accsrvconfs = NULL;
834
	    list_removedata(realmlist, realm);
835
	}
836
837
	pthread_mutex_unlock(&realm->mutex);
	freerealm(realm);
838
839
840
841
842
843
844
845
846
    }
}

void removeserversubrealms(struct list *realmlist, struct clsrvconf *srv) {
    struct list_node *entry;
    struct realm *realm;
    
    for (entry = list_first(realmlist); entry; entry = list_next(entry)) {
	realm = (struct realm *)entry->data;
847
	pthread_mutex_lock(&realm->mutex);
848
849
850
851
852
853
854
	if (realm->subrealms) {
	    _internal_removeserversubrealms(realm->subrealms, srv);
	    if (!list_first(realm->subrealms)) {
		list_destroy(realm->subrealms);
		realm->subrealms = NULL;
	    }
	}
855
	pthread_mutex_unlock(&realm->mutex);
856
857
858
    }
}
			
859
860
int attrvalidate(unsigned char *attrs, int length) {
    while (length > 1) {
venaas's avatar
venaas committed
861
	if (ATTRLEN(attrs) < 2) {
862
	    debug(DBG_INFO, "attrvalidate: invalid attribute length %d", ATTRLEN(attrs));
863
864
	    return 0;
	}
venaas's avatar
venaas committed
865
	length -= ATTRLEN(attrs);
866
	if (length < 0) {
867
	    debug(DBG_INFO, "attrvalidate: attribute length %d exceeds packet length", ATTRLEN(attrs));
868
869
	    return 0;
	}
venaas's avatar
venaas committed
870
	attrs += ATTRLEN(attrs);
871
872
    }
    if (length)
873
	debug(DBG_INFO, "attrvalidate: malformed packet? remaining byte after last attribute");
874
875
876
    return 1;
}

venaas's avatar
venaas committed
877
878
879
880
881
882
883
884
885
886
887
int pwdrecrypt(uint8_t *pwd, uint8_t len, char *oldsecret, char *newsecret, uint8_t *oldauth, uint8_t *newauth) {
    if (len < 16 || len > 128 || len % 16) {
	debug(DBG_WARN, "pwdrecrypt: invalid password length");
	return 0;
    }
	
    if (!pwddecrypt(pwd, len, oldsecret, strlen(oldsecret), oldauth)) {
	debug(DBG_WARN, "pwdrecrypt: cannot decrypt password");
	return 0;
    }
#ifdef DEBUG
888
    printfchars(NULL, "pwdrecrypt: password", "%02x ", pwd, len);
venaas's avatar
venaas committed
889
890
891
892
893
894
895
896
#endif	
    if (!pwdencrypt(pwd, len, newsecret, strlen(newsecret), newauth)) {
	debug(DBG_WARN, "pwdrecrypt: cannot encrypt password");
	return 0;
    }
    return 1;
}

897
int msmpprecrypt(uint8_t *msmpp, uint8_t len, char *oldsecret, char *newsecret, uint8_t *oldauth, uint8_t *newauth) {
venaas's avatar
venaas committed
898
899
    if (len < 18)
	return 0;
900
    if (!msmppdecrypt(msmpp + 2, len - 2, (uint8_t *)oldsecret, strlen(oldsecret), oldauth, msmpp)) {
venaas's avatar
venaas committed
901
902
903
	debug(DBG_WARN, "msmpprecrypt: failed to decrypt msppe key");
	return 0;
    }
904
    if (!msmppencrypt(msmpp + 2, len - 2, (uint8_t *)newsecret, strlen(newsecret), newauth, msmpp)) {
venaas's avatar
venaas committed
905
906
907
908
909
910
	debug(DBG_WARN, "msmpprecrypt: failed to encrypt msppe key");
	return 0;
    }
    return 1;
}

venaas's avatar
venaas committed
911
int msmppe(unsigned char *attrs, int length, uint8_t type, char *attrtxt, struct request *rq,
venaas's avatar
venaas committed
912
913
914
915
916
	   char *oldsecret, char *newsecret) {
    unsigned char *attr;
    
    for (attr = attrs; (attr = attrget(attr, length - (attr - attrs), type)); attr += ATTRLEN(attr)) {
	debug(DBG_DBG, "msmppe: Got %s", attrtxt);
917
	if (!msmpprecrypt(ATTRVAL(attr), ATTRVALLEN(attr), oldsecret, newsecret, rq->buf + 4, rq->rqauth))
venaas's avatar
venaas committed
918
919
920
921
922
	    return 0;
    }
    return 1;
}

923
int findvendorsubattr(uint32_t *attrs, uint32_t vendor, uint32_t subattr) {
venaas's avatar
venaas committed
924
925
926
927
928
929
930
931
932
    if (!attrs)
	return 0;
    
    for (; attrs[0]; attrs += 2)
	if (attrs[0] == vendor && attrs[1] == subattr)
	    return 1;
    return 0;
}

933
934
935
936
/* returns 1 if entire element is to be removed, else 0 */
int dovendorrewriterm(struct tlv *attr, uint32_t *removevendorattrs) {
    uint8_t alen, sublen;
    uint32_t vendor;
venaas's avatar
venaas committed
937
938
939
940
941
    uint8_t *subattrs;
    
    if (!removevendorattrs)
	return 0;

942
943
    memcpy(&vendor, attr->v, 4);
    vendor = ntohl(vendor);
venaas's avatar
venaas committed
944
945
946
947
948
    while (*removevendorattrs && *removevendorattrs != vendor)
	removevendorattrs += 2;
    if (!*removevendorattrs)
	return 0;
    
949
    if (findvendorsubattr(removevendorattrs, vendor, 256))
950
	return 1; /* remove entire vendor attribute */
venaas's avatar
venaas committed
951

952
953
    sublen = attr->l - 4;
    subattrs = attr->v + 4;
venaas's avatar
venaas committed
954
955
    
    if (!attrvalidate(subattrs, sublen)) {
956
	debug(DBG_INFO, "dovendorrewrite: vendor attribute validation failed, no rewrite");
venaas's avatar
venaas committed
957
958
959
960
961
962
963
	return 0;
    }

    while (sublen > 1) {
	alen = ATTRLEN(subattrs);
	sublen -= alen;
	if (findvendorsubattr(removevendorattrs, vendor, ATTRTYPE(subattrs))) {
964
965
	    memmove(subattrs, subattrs + alen, sublen);
	    attr->l -= alen;
venaas's avatar
venaas committed
966
967
968
	} else
	    subattrs += alen;
    }
969
    return 0;
venaas's avatar
venaas committed
970
971
}

972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
void dorewriterm(struct radmsg *msg, uint8_t *rmattrs, uint32_t *rmvattrs) {
    struct list_node *n, *p;
    struct tlv *attr;

    p = NULL;
    n = list_first(msg->attrs);
    while (n) {
	attr = (struct tlv *)n->data;
	if ((rmattrs && strchr((char *)rmattrs, attr->t)) ||
	    (rmvattrs && attr->t == RAD_Attr_Vendor_Specific && dovendorrewriterm(attr, rmvattrs))) {
	    list_removedata(msg->attrs, attr);
	    freetlv(attr);
	    n = p ? list_next(p) : list_first(msg->attrs);
	} else
	    p = n;
            n = list_next(n);
988
989
990
    }
}

991
int dorewriteadd(struct radmsg *msg, struct list *addattrs) {
venaas's avatar
venaas committed
992
    struct list_node *n;
993
    struct tlv *a;
venaas's avatar
venaas committed
994
995
    
    for (n = list_first(addattrs); n; n = list_next(n)) {
996
997
998
999
1000
1001
1002
1003
	a = copytlv((struct tlv *)n->data);
	if (!a)
	    return 0;
	if (!radmsg_add(msg, a)) {
	    freetlv(a);
	    return 0;
	}
    }
venaas's avatar
venaas committed
1004
1005
1006
    return 1;
}

venaas's avatar
venaas committed
1007
int resizeattr(struct tlv *attr, uint8_t newlen) {
1008
    uint8_t *newv;
1009
    
1010
1011
1012
1013
1014
1015
    if (newlen != attr->l) {
	newv = realloc(attr->v, newlen);
	if (!newv)
	    return 0;
	attr->v = newv;
	attr->l = newlen;
1016
    }
1017
    return 1;
1018
}
1019

1020
int dorewritemodattr(struct tlv *attr, struct modattr *modattr) {
1021
1022
1023
    size_t nmatch = 10, reslen = 0, start = 0;
    regmatch_t pmatch[10], *pfield;
    int i;
1024
1025
    char *in, *out;

1026
    in = stringcopy((char *)attr->v, attr->l);
1027
1028
    if (!in)
	return 0;
1029
    
1030
1031
    if (regexec(modattr->regex, in, nmatch, pmatch, 0)) {
	free(in);
1032
	return 1;
1033
1034
    }
    
1035
    out = modattr->replacement;
1036
    
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
    for (i = start; out[i]; i++) {
	if (out[i] == '\\' && out[i + 1] >= '1' && out[i + 1] <= '9') {
	    pfield = &pmatch[out[i + 1] - '0'];
	    if (pfield->rm_so >= 0) {
		reslen += i - start + pfield->rm_eo - pfield->rm_so;
		start = i + 2;
	    }
	    i++;
	}
    }
    reslen += i - start;
1048
    if (reslen > 253) {
1049
	debug(DBG_INFO, "rewritten attribute length would be %d, max possible is 253, discarding message", reslen);
1050
1051
1052
	free(in);
	return 0;
    }
1053

venaas's avatar
venaas committed
1054
    if (!resizeattr(attr, reslen)) {
1055
	free(in);
1056
	return 0;
1057
    }
1058

venaas's avatar
venaas committed
1059
    start = 0;