radsecproxy.c 90.3 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
 *
 * 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
33
34
35
36
/* 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
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 <sys/select.h>
57
#include <ctype.h>
58
#include <sys/wait.h>
59
#include <arpa/inet.h>
venaas's avatar
venaas committed
60
#include <regex.h>
venaas's avatar
venaas committed
61
#include <libgen.h>
venaas's avatar
   
venaas committed
62
63
64
65
66
#include <pthread.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/md5.h>
venaas's avatar
venaas committed
67
#include <openssl/hmac.h>
68
#include <openssl/x509v3.h>
venaas's avatar
venaas committed
69
#include "debug.h"
venaas's avatar
venaas committed
70
#include "list.h"
71
#include "hash.h"
72
#include "util.h"
venaas's avatar
venaas committed
73
#include "radsecproxy.h"
venaas's avatar
venaas committed
74
#include "udp.h"
venaas's avatar
venaas committed
75
76
#include "tcp.h"
#include "tls.h"
venaas's avatar
venaas committed
77
#include "dtls.h"
venaas's avatar
   
venaas committed
78

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

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

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

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

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

venaas's avatar
venaas committed
105
106
107
108
    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
109
110
}
    
venaas's avatar
   
venaas committed
111
112
113
/* callbacks for making OpenSSL thread safe */
unsigned long ssl_thread_id() {
        return (unsigned long)pthread_self();
venaas's avatar
venaas committed
114
}
venaas's avatar
   
venaas committed
115
116
117
118
119
120
121
122
123

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
124
int resolvepeer(struct clsrvconf *conf, int ai_flags) {
125
    struct addrinfo hints, *addrinfo, *res;
126
    char *slash, *s;
venaas's avatar
venaas committed
127
    int plen = 0;
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

    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
148
    memset(&hints, 0, sizeof(hints));
venaas's avatar
venaas committed
149
    hints.ai_socktype = conf->pdef->socktype;
venaas's avatar
   
venaas committed
150
    hints.ai_family = AF_UNSPEC;
venaas's avatar
venaas committed
151
    hints.ai_flags = ai_flags;
152
153
    if (!conf->host && !conf->port) {
	/* getaddrinfo() doesn't like host and port to be NULL */
venaas's avatar
venaas committed
154
	if (getaddrinfo(conf->host, conf->pdef->portdefault, &hints, &addrinfo)) {
155
156
157
	    debug(DBG_WARN, "resolvepeer: can't resolve (null) port (null)");
	    return 0;
	}
venaas's avatar
venaas committed
158
159
	for (res = addrinfo; res; res = res->ai_next)
	    port_set(res->ai_addr, 0);
160
161
162
163
    } else {
	if (slash)
	    hints.ai_flags |= AI_NUMERICHOST;
	if (getaddrinfo(conf->host, conf->port, &hints, &addrinfo)) {
164
	    debug(DBG_WARN, "resolvepeer: can't resolve %s port %s", conf->host ? conf->host : "(null)", conf->port ? conf->port : "(null)");
165
166
	    return 0;
	}
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
	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;
	    }
venaas's avatar
venaas committed
184
	    conf->prefixlen = (uint8_t)plen;
185
186
187
	} else
	    conf->prefixlen = 255;
    }
venaas's avatar
venaas committed
188
189
190
    if (conf->addrinfo)
	freeaddrinfo(conf->addrinfo);
    conf->addrinfo = addrinfo;
venaas's avatar
   
venaas committed
191
192
193
    return 1;
}	  

194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
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;
}

venaas's avatar
venaas committed
232
struct clsrvconf *resolve_hostport(uint8_t type, char *lconf, char *default_port) {
233
234
235
236
237
238
239
    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;
venaas's avatar
venaas committed
240
    conf->pdef = protodefs[conf->type];
241
242
243
244
245
246
247
248
249
    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))
250
	debugx(1, DBG_ERR, "failed to resolve host %s port %s, exiting", conf->host ? conf->host : "(null)", conf->port ? conf->port : "(null)");
251
252
253
    return conf;
}

254
255
256
257
258
259
260
261
void freeclsrvres(struct clsrvconf *res) {
    free(res->host);
    free(res->port);
    if (res->addrinfo)
	freeaddrinfo(res->addrinfo);
    free(res);
}

venaas's avatar
venaas committed
262
263
264
265
266
267
268
269
270
271
272
struct addrinfo *resolve_hostport_addrinfo(uint8_t type, char *hostport) {
    struct addrinfo *ai;
    struct clsrvconf *res;

    res = resolve_hostport(type, hostport, NULL);
    ai = res->addrinfo;
    res->addrinfo = NULL;
    freeclsrvres(res);
    return ai;
}
    
273
274
275
/* 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
276
    uint8_t r, l = len / 8;
277
278
279
280
281
282
283
284
    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]);
}

285
/* returns next config with matching address, or NULL */
venaas's avatar
venaas committed
286
struct clsrvconf *find_conf(uint8_t type, struct sockaddr *addr, struct list *confs, struct list_node **cur) {
287
288
289
290
291
292
293
294
295
296
297
298
299
300
    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
301

venaas's avatar
venaas committed
302
    for (entry = (cur && *cur ? list_next(*cur) : list_first(confs)); entry; entry = list_next(entry)) {
venaas's avatar
venaas committed
303
	conf = (struct clsrvconf *)entry->data;
304
305
306
307
308
309
	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 &&
310
311
312
			 !memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16))) {
			if (cur)
			    *cur = entry;
313
			return conf;
314
		    }
315
316
317
318
319
320
	    } 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 &&
321
322
323
		      prefixmatch(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, conf->prefixlen)))) {
		    if (cur)
			*cur = entry;
venaas's avatar
venaas committed
324
		    return conf;
325
		}
326
327
	    }
	}
venaas's avatar
venaas committed
328
    }    
venaas's avatar
venaas committed
329
330
331
    return NULL;
}

venaas's avatar
venaas committed
332
333
334
335
336
337
338
339
struct clsrvconf *find_clconf(uint8_t type, struct sockaddr *addr, struct list_node **cur) {
    return find_conf(type, addr, clconfs, cur);
}

struct clsrvconf *find_srvconf(uint8_t type, struct sockaddr *addr, struct list_node **cur) {
    return find_conf(type, addr, srvconfs, cur);
}

venaas's avatar
venaas committed
340
/* returns next config of given type, or NULL */
venaas's avatar
venaas committed
341
struct clsrvconf *find_clconf_type(uint8_t type, struct list_node **cur) {
venaas's avatar
venaas committed
342
343
344
    struct list_node *entry;
    struct clsrvconf *conf;
    
venaas's avatar
venaas committed
345
    for (entry = (cur && *cur ? list_next(*cur) : list_first(clconfs)); entry; entry = list_next(entry)) {
venaas's avatar
venaas committed
346
347
348
349
350
351
352
353
354
355
	conf = (struct clsrvconf *)entry->data;
	if (conf->type == type) {
	    if (cur)
		*cur = entry;
	    return conf;
	}
    }    
    return NULL;
}

356
357
struct queue *newqueue() {
    struct queue *q;
358
    
359
360
    q = malloc(sizeof(struct queue));
    if (!q)
361
	debugx(1, DBG_ERR, "malloc failed");
362
363
    q->entries = list_create();
    if (!q->entries)
364
	debugx(1, DBG_ERR, "malloc failed");
365
366
367
368
369
370
371
    pthread_mutex_init(&q->mutex, NULL);
    pthread_cond_init(&q->cond, NULL);
    return q;
}

void removequeue(struct queue *q) {
    struct list_node *entry;
372
373
374

    if (!q)
	return;
375
376
    pthread_mutex_lock(&q->mutex);
    for (entry = list_first(q->entries); entry; entry = list_next(entry))
venaas's avatar
venaas committed
377
	freerq((struct request *)entry);
378
379
380
381
    list_destroy(q->entries);
    pthread_cond_destroy(&q->cond);
    pthread_mutex_unlock(&q->mutex);
    pthread_mutex_destroy(&q->mutex);
382
    free(q);
383
384
385
386
387
388
389
390
391
392
}

void freebios(struct queue *q) {
    BIO *bio;
    
    pthread_mutex_lock(&q->mutex);
    while ((bio = (BIO *)list_shift(q->entries)))
	BIO_free(bio);
    pthread_mutex_unlock(&q->mutex);
    removequeue(q);
393
394
}

395
struct client *addclient(struct clsrvconf *conf, uint8_t lock) {
396
397
398
399
400
    struct client *new = malloc(sizeof(struct client));
    
    if (!new) {
	debug(DBG_ERR, "malloc failed");
	return NULL;
401
    }
402
403
404

    if (lock)
	pthread_mutex_lock(conf->lock);
405
    if (!conf->clients) {
406
407
	conf->clients = list_create();
	if (!conf->clients) {
408
409
	    if (lock)
		pthread_mutex_unlock(conf->lock);
410
411
412
	    debug(DBG_ERR, "malloc failed");
	    return NULL;
	}
413
    }
414
415
416
    
    memset(new, 0, sizeof(struct client));
    new->conf = conf;
venaas's avatar
venaas committed
417
418
419
420
    if (conf->pdef->addclient)
	conf->pdef->addclient(new);
    else
	new->replyq = newqueue();
421
    list_push(conf->clients, new);
422
423
    if (lock)
	pthread_mutex_unlock(conf->lock);
424
425
426
    return new;
}

427
428
429
430
431
432
433
434
435
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);
}
    
436
437
438
439
440
void removeclientrqs(struct client *client) {
    struct request *rq;
    struct rqout *rqout;
    int i;

441
    removeclientrqs_sendrq_freeserver_lock(1);
442
443
444
445
    for (i = 0; i < MAX_REQUESTS; i++) {
	rq = client->rqs[i];
	if (!rq)
	    continue;
446
447
448
449
450
451
452
	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);
	}
453
454
	freerq(rq);
    }
455
    removeclientrqs_sendrq_freeserver_lock(0);
456
457
}

venaas's avatar
venaas committed
458
void removelockedclient(struct client *client) {
459
460
461
462
    struct clsrvconf *conf;
    
    conf = client->conf;
    if (conf->clients) {
463
	removeclientrqs(client);
464
	removequeue(client->replyq);
465
	list_removedata(conf->clients, client);
466
467
468
	free(client->addr);
	free(client);
    }
venaas's avatar
venaas committed
469
470
471
472
473
474
475
476
477
478
479
}

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

    conf = client->conf;
    pthread_mutex_lock(conf->lock);
    removelockedclient(client);
480
    pthread_mutex_unlock(conf->lock);
481
482
}

venaas's avatar
venaas committed
483
void freeserver(struct server *server, uint8_t destroymutex) {
484
    struct rqout *rqout, *end;
485

venaas's avatar
venaas committed
486
487
    if (!server)
	return;
488

489
    removeclientrqs_sendrq_freeserver_lock(1);
490
    if (server->requests) {
491
	rqout = server->requests;
492
	for (end = rqout + MAX_REQUESTS; rqout < end; rqout++) {
493
494
	    if (rqout->rq)
		rqout->rq->to = NULL;
495
	    freerqoutdata(rqout);
496
497
498
	    pthread_mutex_destroy(rqout->lock);
	    free(rqout->lock);
	}
499
500
	free(server->requests);
    }
501
502
    if (server->rbios)
	freebios(server->rbios);
503
    free(server->dynamiclookuparg);
504
505
    if (server->ssl)
	SSL_free(server->ssl);
venaas's avatar
venaas committed
506
507
508
509
510
    if (destroymutex) {
	pthread_mutex_destroy(&server->lock);
	pthread_cond_destroy(&server->newrq_cond);
	pthread_mutex_destroy(&server->newrq_mutex);
    }
511
    removeclientrqs_sendrq_freeserver_lock(0);
venaas's avatar
venaas committed
512
513
514
515
    free(server);
}

int addserver(struct clsrvconf *conf) {
516
    int i;
517
    
venaas's avatar
venaas committed
518
519
520
521
    if (conf->servers) {
	debug(DBG_ERR, "addserver: currently works with just one server per conf");
	return 0;
    }
522
    conf->servers = malloc(sizeof(struct server));
venaas's avatar
venaas committed
523
524
525
526
    if (!conf->servers) {
	debug(DBG_ERR, "malloc failed");
	return 0;
    }
527
528
529
    memset(conf->servers, 0, sizeof(struct server));
    conf->servers->conf = conf;

530
531
#ifdef RADPROT_DTLS    
    if (conf->type == RAD_DTLS)
532
	conf->servers->rbios = newqueue();
533
#endif
venaas's avatar
venaas committed
534
    conf->pdef->setsrcres();
venaas's avatar
venaas committed
535

venaas's avatar
venaas committed
536
537
538
    conf->servers->sock = -1;
    if (conf->pdef->addserverextra)
	conf->pdef->addserverextra(conf);
539
    
540
    conf->servers->requests = calloc(MAX_REQUESTS, sizeof(struct rqout));
venaas's avatar
venaas committed
541
542
543
544
    if (!conf->servers->requests) {
	debug(DBG_ERR, "malloc failed");
	goto errexit;
    }
545
546
547
548
549
550
551
552
553
554
555
556
557
    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
558
559
560
561
    if (pthread_mutex_init(&conf->servers->lock, NULL)) {
	debug(DBG_ERR, "mutex init failed");
	goto errexit;
    }
562
    conf->servers->newrq = 0;
venaas's avatar
venaas committed
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
    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;
581
582
}

583
unsigned char *attrget(unsigned char *attrs, int length, uint8_t type) {
584
    while (length > 1) {
venaas's avatar
venaas committed
585
	if (ATTRTYPE(attrs) == type)
586
	    return attrs;
venaas's avatar
venaas committed
587
588
	length -= ATTRLEN(attrs);
	attrs += ATTRLEN(attrs);
589
590
591
592
    }
    return NULL;
}

venaas's avatar
venaas committed
593
struct request *newrqref(struct request *rq) {
594
595
    if (rq)
	rq->refcount++;
venaas's avatar
venaas committed
596
597
598
    return rq;
}

599
600
601
602
603
604
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
605
606
607
608
    if (rq->origusername)
	free(rq->origusername);
    if (rq->buf)
	free(rq->buf);
venaas's avatar
venaas committed
609
610
611
    if (rq->replybuf)
	free(rq->replybuf);
    if (rq->msg)
venaas's avatar
venaas committed
612
	radmsg_free(rq->msg);
613
614
615
616
617
618
    free(rq);
}

void freerqoutdata(struct rqout *rqout) {
    if (!rqout)
	return;
619
    if (rqout->rq) {
620
621
622
623
	if (rqout->rq->buf) {
	    free(rqout->rq->buf);
	    rqout->rq->buf = NULL;
	}
624
	freerq(rqout->rq);
625
626
627
628
	rqout->rq = NULL;
    }
    rqout->tries = 0;
    memset(&rqout->expiry, 0, sizeof(struct timeval));
629
630
}

631
void sendrq(struct request *rq) {
632
    int i, start;
633
634
635
636
637
638
    struct server *to;

    removeclientrqs_sendrq_freeserver_lock(1);
    to = rq->to;
    if (!to)
	goto errexit;
639
    
640
    start = to->conf->statusserver ? 1 : 0;
venaas's avatar
   
venaas committed
641
    pthread_mutex_lock(&to->newrq_mutex);
642
643
644
645
646
647
    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);
	    debug(DBG_WARN, "sendrq: status server already in queue, dropping request");
	    goto errexit;
648
	}
649
650
651
652
653
654
	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
655
	    if (!to->requests[i].rq) {
656
		pthread_mutex_lock(to->requests[i].lock);
venaas's avatar
venaas committed
657
		if (!to->requests[i].rq)
658
659
660
661
		    break;
		pthread_mutex_unlock(to->requests[i].lock);
	    }
	}
662
663
664
665
666
667
668
669
670
671
672
673
674
	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) {
		debug(DBG_WARN, "sendrq: no room in queue, dropping request");
		goto errexit;
	    }
venaas's avatar
venaas committed
675
	}
venaas's avatar
   
venaas committed
676
    }
677
    rq->newid = (uint8_t)i;
venaas's avatar
venaas committed
678
679
680
    rq->msg->id = (uint8_t)i;
    rq->buf = radmsg2buf(rq->msg, (uint8_t *)to->conf->secret);
    if (!rq->buf) {
681
	pthread_mutex_unlock(to->requests[i].lock);
venaas's avatar
venaas committed
682
	debug(DBG_ERR, "sendrq: radmsg2buf failed");
683
	goto errexit;
venaas's avatar
venaas committed
684
685
    }
    
venaas's avatar
venaas committed
686
    debug(DBG_DBG, "sendrq: inserting packet with id %d in queue for %s", i, to->conf->host);
venaas's avatar
venaas committed
687
    to->requests[i].rq = rq;
688
    pthread_mutex_unlock(to->requests[i].lock);
689
690
    if (i >= start) /* i is not reserved for statusserver */
	to->nextid = i + 1;
venaas's avatar
   
venaas committed
691
692
693

    if (!to->newrq) {
	to->newrq = 1;
venaas's avatar
venaas committed
694
	debug(DBG_DBG, "sendrq: signalling client writer");
venaas's avatar
   
venaas committed
695
696
	pthread_cond_signal(&to->newrq_cond);
    }
venaas's avatar
venaas committed
697

698
    pthread_mutex_unlock(&to->newrq_mutex);
699
    removeclientrqs_sendrq_freeserver_lock(0);
700
701
702
    return;

 errexit:
703
704
    if (rq->from)
	rmclientrq(rq, rq->msg->id);
705
    freerq(rq);
venaas's avatar
   
venaas committed
706
    pthread_mutex_unlock(&to->newrq_mutex);
707
    removeclientrqs_sendrq_freeserver_lock(0);
venaas's avatar
   
venaas committed
708
709
}

venaas's avatar
venaas committed
710
void sendreply(struct request *rq) {
711
    uint8_t first;
venaas's avatar
venaas committed
712
713
714
715
716
717
718
719
    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
720
	debug(DBG_ERR, "sendreply: radmsg2buf failed");
venaas's avatar
venaas committed
721
722
	return;
    }
723
724

    pthread_mutex_lock(&to->replyq->mutex);
725
    first = list_first(to->replyq->entries) == NULL;
726
    
venaas's avatar
venaas committed
727
    if (!list_push(to->replyq->entries, rq)) {
728
	pthread_mutex_unlock(&to->replyq->mutex);
venaas's avatar
venaas committed
729
	freerq(rq);
730
731
732
733
734
	debug(DBG_ERR, "sendreply: malloc failed");
	return;
    }
    
    if (first) {
735
	debug(DBG_DBG, "signalling server writer");
736
	pthread_cond_signal(&to->replyq->cond);
venaas's avatar
   
venaas committed
737
    }
738
    pthread_mutex_unlock(&to->replyq->mutex);
venaas's avatar
   
venaas committed
739
740
}

venaas's avatar
venaas committed
741
int pwdencrypt(uint8_t *in, uint8_t len, char *shared, uint8_t sharedlen, uint8_t *auth) {
venaas's avatar
venaas committed
742
743
744
745
746
    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
747
    uint8_t i, offset = 0, out[128];
venaas's avatar
venaas committed
748
749
750
751
752
753
754
755
756
757
    
    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
758
	    !EVP_DigestUpdate(&mdctx, (uint8_t *)shared, sharedlen) ||
venaas's avatar
venaas committed
759
760
761
762
763
764
765
766
767
768
769
770
771
	    !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
772
    memcpy(in, out, len);
venaas's avatar
venaas committed
773
774
775
776
    pthread_mutex_unlock(&lock);
    return 1;
}

venaas's avatar
venaas committed
777
int pwddecrypt(uint8_t *in, uint8_t len, char *shared, uint8_t sharedlen, uint8_t *auth) {
venaas's avatar
   
venaas committed
778
779
780
781
782
    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
783
    uint8_t i, offset = 0, out[128];
venaas's avatar
   
venaas committed
784
785
786
787
788
789
790
791
792
793
    
    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
794
	    !EVP_DigestUpdate(&mdctx, (uint8_t *)shared, sharedlen) ||
venaas's avatar
   
venaas committed
795
796
797
798
799
800
801
	    !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
802
803
	    out[offset + i] = hash[i] ^ in[offset + i];
	input = in + offset;
venaas's avatar
   
venaas committed
804
	offset += 16;
venaas's avatar
venaas committed
805
	if (offset == len)
venaas's avatar
   
venaas committed
806
807
	    break;
    }
venaas's avatar
venaas committed
808
    memcpy(in, out, len);
venaas's avatar
   
venaas committed
809
810
811
812
    pthread_mutex_unlock(&lock);
    return 1;
}

venaas's avatar
venaas committed
813
814
815
816
817
818
819
820
821
822
823
824
825
826
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;
    }

827
828
829
830
#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
831
832
833
834
835
836
837
838
839
840
841
842
#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    
843
    printfchars(NULL, "msppencrypt hash", "%02x ", hash, 16);
venaas's avatar
venaas committed
844
845
846
847
848
849
#endif
    
    for (i = 0; i < 16; i++)
	text[i] ^= hash[i];
    
    for (offset = 16; offset < len; offset += 16) {
venaas's avatar
venaas committed
850
#if 0	
venaas's avatar
venaas committed
851
	printf("text + offset - 16 c(%d): ", offset / 16);
852
	printfchars(NULL, NULL, "%02x ", text + offset - 16, 16);
venaas's avatar
venaas committed
853
#endif
venaas's avatar
venaas committed
854
855
856
857
858
859
860
861
	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;
	}
862
863
#if 0
	printfchars(NULL, "msppencrypt hash", "%02x ", hash, 16);
venaas's avatar
venaas committed
864
865
866
867
868
869
870
#endif    
	
	for (i = 0; i < 16; i++)
	    text[offset + i] ^= hash[i];
    }
    
#if 0
871
    printfchars(NULL, "msppencrypt out", "%02x ", text, len);
venaas's avatar
venaas committed
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
#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;
    }

893
894
895
896
#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
897
898
899
900
901
902
903
904
905
906
907
908
#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    
909
    printfchars(NULL, "msppdecrypt hash", "%02x ", hash, 16);
venaas's avatar
venaas committed
910
911
912
913
914
915
916
917
#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);
918
	printfchars(NULL, NULL, "%02x ", text + offset - 16, 16);
venaas's avatar
venaas committed
919
920
921
922
923
924
925
926
927
#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;
	}
928
929
#if 0
	printfchars(NULL, "msppdecrypt hash", "%02x ", hash, 16);
venaas's avatar
venaas committed
930
931
#endif    

932
933
	for (i = 0; i < 16; i++)
	    plain[offset + i] = text[offset + i] ^ hash[i];
venaas's avatar
venaas committed
934
935
936
937
    }

    memcpy(text, plain, len);
#if 0
938
    printfchars(NULL, "msppdecrypt out", "%02x ", text, len);
venaas's avatar
venaas committed
939
940
941
942
943
944
#endif

    pthread_mutex_unlock(&lock);
    return 1;
}

945
946
947
948
949
950
951
struct realm *newrealmref(struct realm *r) {
    if (r)
	r->refcount++;
    return r;
}

/* returns with lock on realm */
venaas's avatar
venaas committed
952
struct realm *id2realm(struct list *realmlist, char *id) {
venaas's avatar
venaas committed
953
    struct list_node *entry;
954
    struct realm *realm, *subrealm;
venaas's avatar
venaas committed
955
956
957

    /* 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
958
959
	realm = (struct realm *)entry->data;
	if (!regexec(&realm->regex, id, 0, NULL, 0)) {
960
961
	    pthread_mutex_lock(&realm->mutex);
	    if (realm->subrealms) {
venaas's avatar
venaas committed
962
		subrealm = id2realm(realm->subrealms, id);
963
964
965
966
967
968
		if (subrealm) {
		    pthread_mutex_unlock(&realm->mutex);
		    return subrealm;
		}
	    }
	    return newrealmref(realm);
venaas's avatar
venaas committed
969
	}
venaas's avatar
venaas committed
970
    }
venaas's avatar
venaas committed
971
    return NULL;
venaas's avatar
venaas committed
972
973
}

974
975
/* helper function, only used by removeserversubrealms() */
void _internal_removeserversubrealms(struct list *realmlist, struct clsrvconf *srv) {
976
    struct list_node *entry, *entry2;
977
    struct realm *realm;
978

979
    for (entry = list_first(realmlist); entry;) {
980
981
	realm = newrealmref((struct realm *)entry->data);
	pthread_mutex_lock(&realm->mutex);
982
	entry = list_next(entry);
983

984
	if (realm->srvconfs) {
985
986
987
	    for (entry2 = list_first(realm->srvconfs); entry2; entry2 = list_next(entry2))
		if (entry2->data == srv)
		    freerealm(realm);
988
989
990
991
992
993
994
	    list_removedata(realm->srvconfs, srv);
	    if (!list_first(realm->srvconfs)) {
		list_destroy(realm->srvconfs);
		realm->srvconfs = NULL;
	    }
	}
	if (realm->accsrvconfs) {
995
996
997
	    for (entry2 = list_first(realm->accsrvconfs); entry2; entry2 = list_next(entry2))
		if (entry2->data == srv)
		    freerealm(realm);
998
999
1000
1001
1002
1003
1004
1005
	    list_removedata(realm->accsrvconfs, srv);
	    if (!list_first(realm->accsrvconfs)) {
		list_destroy(realm->accsrvconfs);
		realm->accsrvconfs = NULL;
	    }
	}

	/* remove subrealm if no servers */
1006
	if (!realm->srvconfs && !realm->accsrvconfs)
1007
	    list_removedata(realmlist, realm);
1008
1009
1010

	pthread_mutex_unlock(&realm->mutex);
	freerealm(realm);
1011
1012
1013
1014
1015
1016
1017
1018
1019
    }
}

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;
1020
	pthread_mutex_lock(&realm->mutex);
1021
1022
1023
1024
1025
1026
1027
	if (realm->subrealms) {
	    _internal_removeserversubrealms(realm->subrealms, srv);
	    if (!list_first(realm->subrealms)) {
		list_destroy(realm->subrealms);
		realm->subrealms = NULL;
	    }
	}
1028
	pthread_mutex_unlock(&realm->mutex);
1029
1030
1031
    }
}
			
1032
1033
int attrvalidate(unsigned char *attrs, int length) {
    while (length > 1) {
venaas's avatar
venaas committed
1034
1035
	if (ATTRLEN(attrs) < 2) {
	    debug(DBG_WARN, "attrvalidate: invalid attribute length %d", ATTRLEN(attrs));
1036
1037
	    return 0;
	}
venaas's avatar
venaas committed
1038
	length -= ATTRLEN(attrs);
1039
	if (length < 0) {
venaas's avatar
venaas committed
1040
	    debug(DBG_WARN, "attrvalidate: attribute length %d exceeds packet length", ATTRLEN(attrs));
1041
1042
	    return 0;
	}
venaas's avatar
venaas committed
1043
	attrs += ATTRLEN(attrs);
1044
1045
1046
1047
1048
1049
    }
    if (length)
	debug(DBG_WARN, "attrvalidate: malformed packet? remaining byte after last attribute");
    return 1;
}

venaas's avatar
venaas committed
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
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
1061
    printfchars(NULL, "pwdrecrypt: password", "%02x ", pwd, len);
venaas's avatar
venaas committed
1062
1063
1064
1065
1066
1067
1068
1069
#endif	
    if (!pwdencrypt(pwd, len, newsecret, strlen(newsecret), newauth)) {
	debug(DBG_WARN, "pwdrecrypt: cannot encrypt password");
	return 0;
    }
    return 1;
}

1070
int msmpprecrypt(uint8_t *msmpp, uint8_t len, char *oldsecret, char *newsecret, uint8_t *oldauth, uint8_t *newauth) {
venaas's avatar
venaas committed
1071
1072
    if (len < 18)
	return 0;
1073
    if (!msmppdecrypt(msmpp + 2, len - 2, (uint8_t *)oldsecret, strlen(oldsecret), oldauth, msmpp)) {
venaas's avatar
venaas committed
1074
1075
1076
	debug(DBG_WARN, "msmpprecrypt: failed to decrypt msppe key");
	return 0;
    }
1077
    if (!msmppencrypt(msmpp + 2, len - 2, (uint8_t *)newsecret, strlen(newsecret), newauth, msmpp)) {
venaas's avatar
venaas committed
1078
1079
1080
1081
1082
1083
	debug(DBG_WARN, "msmpprecrypt: failed to encrypt msppe key");
	return 0;
    }
    return 1;
}

venaas's avatar
venaas committed
1084
int msmppe(unsigned char *attrs, int length, uint8_t type, char *attrtxt, struct request *rq,
venaas's avatar
venaas committed
1085
1086
1087
1088
1089
	   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);
1090
	if (!msmpprecrypt(ATTRVAL(attr), ATTRVALLEN(attr), oldsecret, newsecret, rq->buf + 4, rq->rqauth))
venaas's avatar
venaas committed
1091
1092
1093
1094
1095
	    return 0;
    }
    return 1;
}

1096
int findvendorsubattr(uint32_t *attrs, uint32_t vendor, uint32_t subattr) {
venaas's avatar
venaas committed
1097
1098
1099
1100
1101
1102
1103
1104
1105
    if (!attrs)
	return 0;
    
    for (; attrs[0]; attrs += 2)
	if (attrs[0] == vendor && attrs[1] == subattr)
	    return 1;
    return 0;
}

1106
1107
1108
1109
/* 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
1110
1111
1112
1113
1114
    uint8_t *subattrs;
    
    if (!removevendorattrs)
	return 0;

1115
1116
    memcpy(&vendor, attr->v, 4);
    vendor = ntohl(vendor);
venaas's avatar
venaas committed
1117
1118
1119
1120
1121
    while (*removevendorattrs && *removevendorattrs != vendor)
	removevendorattrs += 2;
    if (!*removevendorattrs)
	return 0;
    
1122
    if (findvendorsubattr(removevendorattrs, vendor, 256))
1123
	return 1; /* remove entire vendor attribute */
venaas's avatar
venaas committed
1124

1125
1126
    sublen = attr->l - 4;
    subattrs = attr->v + 4;
venaas's avatar
venaas committed
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
    
    if (!attrvalidate(subattrs, sublen)) {
	debug(DBG_WARN, "dovendorrewrite: vendor attribute validation failed, no rewrite");
	return 0;
    }

    while (sublen > 1) {
	alen = ATTRLEN(subattrs);
	sublen -= alen;
	if (findvendorsubattr(removevendorattrs, vendor, ATTRTYPE(subattrs))) {
1137
1138
	    memmove(subattrs, subattrs + alen, sublen);
	    attr->l -= alen;
venaas's avatar
venaas committed
1139
1140
1141
	} else
	    subattrs += alen;
    }
1142
    return 0;
venaas's avatar
venaas committed
1143
1144
}

1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
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);
1161
1162
1163
    }
}

1164
int dorewriteadd(struct radmsg *msg, struct list *addattrs) {
venaas's avatar
venaas committed
1165
    struct list_node *n;
1166
    struct tlv *a;
venaas's avatar
venaas committed
1167
1168
    
    for (n = list_first(addattrs); n; n = list_next(n)) {
1169
1170
1171
1172
1173
1174
1175
1176
	a = copytlv((struct tlv *)n->data);
	if (!a)
	    return 0;
	if (!radmsg_add(msg, a)) {
	    freetlv(a);
	    return 0;
	}
    }