radsecproxy.c 90.4 KB
Newer Older
venaas's avatar
 
venaas committed
1
/*
2
 * Copyright (C) 2006-2009 Stig Venaas <venaas@uninett.no>
Linus Nordberg's avatar
Linus Nordberg committed
3
 * Copyright (C) 2010, 2011 NORDUnet A/S
venaas's avatar
 
venaas committed
4 5 6 7 8 9
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 */

10 11 12
/* Code contributions from:
 *
 * Arne Schwabe <schwabe at uni-paderborn.de>
kolla's avatar
kolla committed
13 14
 * Maja Wolniewicz <mgw@umk.pl>
 * Simon Leinen <simon.leinen@switch.ch>
kolla's avatar
Credits  
kolla committed
15
 * Stefan Winter <stefan.winter@restena.lu>
16 17
 */

venaas's avatar
 
venaas committed
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
37
 * Example: With 3 UDP peers and 30 TLS peers, there will be a max of
venaas's avatar
 
venaas committed
38
 *          1 + (2 + 2 * 3) + (2 * 30) + (2 * 30) = 129 threads
39
 */
venaas's avatar
 
venaas committed
40

venaas's avatar
venaas committed
41
/* Bugs:
venaas's avatar
venaas committed
42
 * May segfault when dtls connections go down? More testing needed
43
 * Remove expired stuff from clients request list?
venaas's avatar
venaas committed
44 45
 * 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
46 47
 */

venaas's avatar
venaas committed
48
#include <signal.h>
venaas's avatar
venaas committed
49 50
#include <sys/socket.h>
#include <netinet/in.h>
venaas's avatar
 
venaas committed
51
#include <netdb.h>
venaas's avatar
venaas committed
52
#include <string.h>
venaas's avatar
 
venaas committed
53
#include <unistd.h>
venaas's avatar
venaas committed
54
#include <limits.h>
venaas's avatar
venaas committed
55 56 57
#ifdef SYS_SOLARIS9
#include <fcntl.h>
#endif
venaas's avatar
venaas committed
58
#include <sys/time.h>
59
#include <sys/types.h>
60
#include <ctype.h>
61
#include <sys/wait.h>
62
#include <arpa/inet.h>
venaas's avatar
venaas committed
63
#include <regex.h>
venaas's avatar
venaas committed
64
#include <libgen.h>
venaas's avatar
 
venaas committed
65
#include <pthread.h>
66
#include <errno.h>
venaas's avatar
 
venaas committed
67 68 69 70
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/md5.h>
venaas's avatar
venaas committed
71
#include "debug.h"
72
#include "hash.h"
73
#include "util.h"
74
#include "hostport.h"
venaas's avatar
venaas committed
75
#include "radsecproxy.h"
venaas's avatar
venaas committed
76
#include "udp.h"
venaas's avatar
venaas committed
77 78
#include "tcp.h"
#include "tls.h"
venaas's avatar
venaas committed
79
#include "dtls.h"
80
#if defined(WANT_FTICKS)
Linus Nordberg's avatar
Linus Nordberg committed
81
#include "fticks.h"
82
#endif
venaas's avatar
 
venaas committed
83

84
static struct options options;
venaas's avatar
venaas committed
85
static struct list *clconfs, *srvconfs;
86 87
static struct list *realms;
static struct hash *rewriteconfs;
88

venaas's avatar
venaas committed
89
static pthread_mutex_t *ssl_locks = NULL;
venaas's avatar
 
venaas committed
90 91 92
static long *ssl_lock_count;
extern int optind;
extern char *optarg;
venaas's avatar
venaas committed
93
static const struct protodefs *protodefs[RAD_PROTOCOUNT];
venaas's avatar
 
venaas committed
94

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

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

venaas's avatar
venaas committed
107
uint8_t protoname2int(const char *name) {
venaas's avatar
venaas committed
108
    uint8_t i;
venaas's avatar
venaas committed
109

venaas's avatar
venaas committed
110 111 112 113
    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
114
}
115

venaas's avatar
 
venaas committed
116 117
/* callbacks for making OpenSSL thread safe */
unsigned long ssl_thread_id() {
118
    return (unsigned long)pthread_self();
venaas's avatar
venaas committed
119
}
venaas's avatar
 
venaas committed
120 121 122 123 124 125 126 127 128

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

129 130 131
/* 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
132
    uint8_t r, l = len / 8;
133 134 135 136 137 138 139 140
    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]);
}

141
/* returns next config with matching address, or NULL */
142
struct clsrvconf *find_conf(uint8_t type, struct sockaddr *addr, struct list *confs, struct list_node **cur, uint8_t server_p) {
143 144
    struct list_node *entry;
    struct clsrvconf *conf;
145

venaas's avatar
venaas committed
146
    for (entry = (cur && *cur ? list_next(*cur) : list_first(confs)); entry; entry = list_next(entry)) {
venaas's avatar
venaas committed
147
	conf = (struct clsrvconf *)entry->data;
148
	if (conf->type == type && addressmatches(conf->hostports, addr, server_p)) {
149 150 151
	    if (cur)
		*cur = entry;
	    return conf;
152
	}
153
    }
venaas's avatar
venaas committed
154 155 156
    return NULL;
}

venaas's avatar
venaas committed
157
struct clsrvconf *find_clconf(uint8_t type, struct sockaddr *addr, struct list_node **cur) {
158
    return find_conf(type, addr, clconfs, cur, 0);
venaas's avatar
venaas committed
159 160 161
}

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

venaas's avatar
venaas committed
165
/* returns next config of given type, or NULL */
venaas's avatar
venaas committed
166
struct clsrvconf *find_clconf_type(uint8_t type, struct list_node **cur) {
venaas's avatar
venaas committed
167 168
    struct list_node *entry;
    struct clsrvconf *conf;
169

venaas's avatar
venaas committed
170
    for (entry = (cur && *cur ? list_next(*cur) : list_first(clconfs)); entry; entry = list_next(entry)) {
venaas's avatar
venaas committed
171 172 173 174 175 176
	conf = (struct clsrvconf *)entry->data;
	if (conf->type == type) {
	    if (cur)
		*cur = entry;
	    return conf;
	}
177
    }
venaas's avatar
venaas committed
178 179 180
    return NULL;
}

181 182
struct gqueue *newqueue() {
    struct gqueue *q;
183

184
    q = malloc(sizeof(struct gqueue));
185
    if (!q)
186
	debugx(1, DBG_ERR, "malloc failed");
187 188
    q->entries = list_create();
    if (!q->entries)
189
	debugx(1, DBG_ERR, "malloc failed");
190 191 192 193 194
    pthread_mutex_init(&q->mutex, NULL);
    pthread_cond_init(&q->cond, NULL);
    return q;
}

195
void removequeue(struct gqueue *q) {
196
    struct list_node *entry;
197 198 199

    if (!q)
	return;
200 201
    pthread_mutex_lock(&q->mutex);
    for (entry = list_first(q->entries); entry; entry = list_next(entry))
venaas's avatar
venaas committed
202
	freerq((struct request *)entry);
203 204 205 206
    list_destroy(q->entries);
    pthread_cond_destroy(&q->cond);
    pthread_mutex_unlock(&q->mutex);
    pthread_mutex_destroy(&q->mutex);
207
    free(q);
208 209
}

210
void freebios(struct gqueue *q) {
211
    BIO *bio;
212

213 214 215 216 217
    pthread_mutex_lock(&q->mutex);
    while ((bio = (BIO *)list_shift(q->entries)))
	BIO_free(bio);
    pthread_mutex_unlock(&q->mutex);
    removequeue(q);
218 219
}

220
struct client *addclient(struct clsrvconf *conf, uint8_t lock) {
221
    struct client *new = malloc(sizeof(struct client));
222

223 224 225
    if (!new) {
	debug(DBG_ERR, "malloc failed");
	return NULL;
226
    }
227 228 229

    if (lock)
	pthread_mutex_lock(conf->lock);
230
    if (!conf->clients) {
231 232
	conf->clients = list_create();
	if (!conf->clients) {
233 234
	    if (lock)
		pthread_mutex_unlock(conf->lock);
235 236 237
	    debug(DBG_ERR, "malloc failed");
	    return NULL;
	}
238
    }
239

240 241
    memset(new, 0, sizeof(struct client));
    new->conf = conf;
venaas's avatar
venaas committed
242 243 244 245
    if (conf->pdef->addclient)
	conf->pdef->addclient(new);
    else
	new->replyq = newqueue();
246
    list_push(conf->clients, new);
247 248
    if (lock)
	pthread_mutex_unlock(conf->lock);
249 250 251
    return new;
}

252 253 254 255 256 257 258 259
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);
}
260

261 262 263 264 265
void removeclientrqs(struct client *client) {
    struct request *rq;
    struct rqout *rqout;
    int i;

266
    removeclientrqs_sendrq_freeserver_lock(1);
267 268 269 270
    for (i = 0; i < MAX_REQUESTS; i++) {
	rq = client->rqs[i];
	if (!rq)
	    continue;
271 272 273 274 275 276 277
	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);
	}
278 279
	freerq(rq);
    }
280
    removeclientrqs_sendrq_freeserver_lock(0);
281 282
}

venaas's avatar
venaas committed
283
void removelockedclient(struct client *client) {
284
    struct clsrvconf *conf;
285

286 287
    conf = client->conf;
    if (conf->clients) {
288
	removeclientrqs(client);
289
	removequeue(client->replyq);
290
	list_removedata(conf->clients, client);
291 292 293
	free(client->addr);
	free(client);
    }
venaas's avatar
venaas committed
294 295 296 297
}

void removeclient(struct client *client) {
    struct clsrvconf *conf;
298

venaas's avatar
venaas committed
299 300 301 302 303 304
    if (!client)
	return;

    conf = client->conf;
    pthread_mutex_lock(conf->lock);
    removelockedclient(client);
305
    pthread_mutex_unlock(conf->lock);
306 307
}

venaas's avatar
venaas committed
308
void freeserver(struct server *server, uint8_t destroymutex) {
309
    struct rqout *rqout, *end;
310

venaas's avatar
venaas committed
311 312
    if (!server)
	return;
313

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

int addserver(struct clsrvconf *conf) {
341
    int i;
342

venaas's avatar
venaas committed
343 344 345 346
    if (conf->servers) {
	debug(DBG_ERR, "addserver: currently works with just one server per conf");
	return 0;
    }
347
    conf->servers = malloc(sizeof(struct server));
venaas's avatar
venaas committed
348 349 350 351
    if (!conf->servers) {
	debug(DBG_ERR, "malloc failed");
	return 0;
    }
352 353 354
    memset(conf->servers, 0, sizeof(struct server));
    conf->servers->conf = conf;

355
#ifdef RADPROT_DTLS
356
    if (conf->type == RAD_DTLS)
357
	conf->servers->rbios = newqueue();
358
#endif
venaas's avatar
venaas committed
359
    conf->pdef->setsrcres();
venaas's avatar
venaas committed
360

venaas's avatar
venaas committed
361 362 363
    conf->servers->sock = -1;
    if (conf->pdef->addserverextra)
	conf->pdef->addserverextra(conf);
364

365
    conf->servers->requests = calloc(MAX_REQUESTS, sizeof(struct rqout));
venaas's avatar
venaas committed
366 367 368 369
    if (!conf->servers->requests) {
	debug(DBG_ERR, "malloc failed");
	goto errexit;
    }
370 371 372 373 374 375 376
    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)) {
377
	    debugerrno(errno, DBG_ERR, "mutex init failed");
378 379 380 381 382
	    free(conf->servers->requests[i].lock);
	    conf->servers->requests[i].lock = NULL;
	    goto errexit;
	}
    }
venaas's avatar
venaas committed
383
    if (pthread_mutex_init(&conf->servers->lock, NULL)) {
384
	debugerrno(errno, DBG_ERR, "mutex init failed");
venaas's avatar
venaas committed
385 386
	goto errexit;
    }
387
    conf->servers->newrq = 0;
venaas's avatar
venaas committed
388
    if (pthread_mutex_init(&conf->servers->newrq_mutex, NULL)) {
389
	debugerrno(errno, DBG_ERR, "mutex init failed");
venaas's avatar
venaas committed
390 391 392 393
	pthread_mutex_destroy(&conf->servers->lock);
	goto errexit;
    }
    if (pthread_cond_init(&conf->servers->newrq_cond, NULL)) {
394
	debugerrno(errno, DBG_ERR, "mutex init failed");
venaas's avatar
venaas committed
395 396 397 398 399 400
	pthread_mutex_destroy(&conf->servers->newrq_mutex);
	pthread_mutex_destroy(&conf->servers->lock);
	goto errexit;
    }

    return 1;
401 402

errexit:
venaas's avatar
venaas committed
403 404 405
    freeserver(conf->servers, 0);
    conf->servers = NULL;
    return 0;
406 407
}

408
unsigned char *attrget(unsigned char *attrs, int length, uint8_t type) {
409
    while (length > 1) {
venaas's avatar
venaas committed
410
	if (ATTRTYPE(attrs) == type)
411
	    return attrs;
venaas's avatar
venaas committed
412 413
	length -= ATTRLEN(attrs);
	attrs += ATTRLEN(attrs);
414 415 416 417
    }
    return NULL;
}

venaas's avatar
venaas committed
418
struct request *newrqref(struct request *rq) {
419 420
    if (rq)
	rq->refcount++;
venaas's avatar
venaas committed
421 422 423
    return rq;
}

424 425 426 427 428 429
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
430 431 432 433
    if (rq->origusername)
	free(rq->origusername);
    if (rq->buf)
	free(rq->buf);
venaas's avatar
venaas committed
434 435 436
    if (rq->replybuf)
	free(rq->replybuf);
    if (rq->msg)
venaas's avatar
venaas committed
437
	radmsg_free(rq->msg);
438 439 440 441 442 443
    free(rq);
}

void freerqoutdata(struct rqout *rqout) {
    if (!rqout)
	return;
444
    if (rqout->rq) {
445 446 447 448
	if (rqout->rq->buf) {
	    free(rqout->rq->buf);
	    rqout->rq->buf = NULL;
	}
449
	freerq(rqout->rq);
450 451 452 453
	rqout->rq = NULL;
    }
    rqout->tries = 0;
    memset(&rqout->expiry, 0, sizeof(struct timeval));
454 455
}

456
void sendrq(struct request *rq) {
457
    int i, start;
458 459 460 461 462 463
    struct server *to;

    removeclientrqs_sendrq_freeserver_lock(1);
    to = rq->to;
    if (!to)
	goto errexit;
464

465
    start = to->conf->statusserver ? 1 : 0;
venaas's avatar
 
venaas committed
466
    pthread_mutex_lock(&to->newrq_mutex);
467 468 469 470
    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);
471
	    debug(DBG_INFO, "sendrq: status server already in queue, dropping request");
472
	    goto errexit;
473
	}
474 475 476 477 478 479
	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
480
	    if (!to->requests[i].rq) {
481
		pthread_mutex_lock(to->requests[i].lock);
venaas's avatar
venaas committed
482
		if (!to->requests[i].rq)
483 484 485 486
		    break;
		pthread_mutex_unlock(to->requests[i].lock);
	    }
	}
487 488 489 490 491 492 493 494 495 496
	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) {
497
		debug(DBG_INFO, "sendrq: no room in queue, dropping request");
498 499
		goto errexit;
	    }
venaas's avatar
venaas committed
500
	}
venaas's avatar
 
venaas committed
501
    }
502
    rq->newid = (uint8_t)i;
venaas's avatar
venaas committed
503 504 505
    rq->msg->id = (uint8_t)i;
    rq->buf = radmsg2buf(rq->msg, (uint8_t *)to->conf->secret);
    if (!rq->buf) {
506
	pthread_mutex_unlock(to->requests[i].lock);
venaas's avatar
venaas committed
507
	debug(DBG_ERR, "sendrq: radmsg2buf failed");
508
	goto errexit;
venaas's avatar
venaas committed
509
    }
510

511
    debug(DBG_DBG, "sendrq: inserting packet with id %d in queue for %s", i, to->conf->name);
venaas's avatar
venaas committed
512
    to->requests[i].rq = rq;
513
    pthread_mutex_unlock(to->requests[i].lock);
514 515
    if (i >= start) /* i is not reserved for statusserver */
	to->nextid = i + 1;
venaas's avatar
 
venaas committed
516 517 518

    if (!to->newrq) {
	to->newrq = 1;
venaas's avatar
venaas committed
519
	debug(DBG_DBG, "sendrq: signalling client writer");
venaas's avatar
 
venaas committed
520 521
	pthread_cond_signal(&to->newrq_cond);
    }
venaas's avatar
venaas committed
522

523
    pthread_mutex_unlock(&to->newrq_mutex);
524
    removeclientrqs_sendrq_freeserver_lock(0);
525 526
    return;

527
errexit:
528 529
    if (rq->from)
	rmclientrq(rq, rq->msg->id);
530
    freerq(rq);
venaas's avatar
 
venaas committed
531
    pthread_mutex_unlock(&to->newrq_mutex);
532
    removeclientrqs_sendrq_freeserver_lock(0);
venaas's avatar
 
venaas committed
533 534
}

venaas's avatar
venaas committed
535
void sendreply(struct request *rq) {
536
    uint8_t first;
venaas's avatar
venaas committed
537
    struct client *to = rq->from;
538

venaas's avatar
venaas committed
539 540 541 542 543 544
    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
545
	debug(DBG_ERR, "sendreply: radmsg2buf failed");
venaas's avatar
venaas committed
546 547
	return;
    }
548 549

    pthread_mutex_lock(&to->replyq->mutex);
550
    first = list_first(to->replyq->entries) == NULL;
551

venaas's avatar
venaas committed
552
    if (!list_push(to->replyq->entries, rq)) {
553
	pthread_mutex_unlock(&to->replyq->mutex);
venaas's avatar
venaas committed
554
	freerq(rq);
555 556 557
	debug(DBG_ERR, "sendreply: malloc failed");
	return;
    }
558

559
    if (first) {
560
	debug(DBG_DBG, "signalling server writer");
561
	pthread_cond_signal(&to->replyq->cond);
venaas's avatar
 
venaas committed
562
    }
563
    pthread_mutex_unlock(&to->replyq->mutex);
venaas's avatar
 
venaas committed
564 565
}

566
int pwdcrypt(char encrypt_flag, uint8_t *in, uint8_t len, char *shared, uint8_t sharedlen, uint8_t *auth) {
venaas's avatar
venaas committed
567 568 569 570 571
    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
572
    uint8_t i, offset = 0, out[128];
573

venaas's avatar
venaas committed
574 575 576 577 578 579 580 581 582
    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
583
	    !EVP_DigestUpdate(&mdctx, (uint8_t *)shared, sharedlen) ||
venaas's avatar
venaas committed
584 585 586 587 588 589 590 591
	    !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];
592 593 594 595
	if (encrypt_flag)
	    input = out + offset;
	else
	    input = in + offset;
venaas's avatar
 
venaas committed
596
	offset += 16;
venaas's avatar
venaas committed
597
	if (offset == len)
venaas's avatar
 
venaas committed
598 599
	    break;
    }
venaas's avatar
venaas committed
600
    memcpy(in, out, len);
venaas's avatar
 
venaas committed
601 602 603 604
    pthread_mutex_unlock(&lock);
    return 1;
}

venaas's avatar
venaas committed
605 606 607 608 609 610 611
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;
612

venaas's avatar
venaas committed
613 614 615 616 617 618
    pthread_mutex_lock(&lock);
    if (first) {
	EVP_MD_CTX_init(&mdctx);
	first = 0;
    }

619 620 621 622
#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
623
#endif
624

venaas's avatar
venaas committed
625 626 627 628 629 630 631 632 633
    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;
    }

634
#if 0
635
    printfchars(NULL, "msppencrypt hash", "%02x ", hash, 16);
venaas's avatar
venaas committed
636
#endif
637

venaas's avatar
venaas committed
638 639
    for (i = 0; i < 16; i++)
	text[i] ^= hash[i];
640

venaas's avatar
venaas committed
641
    for (offset = 16; offset < len; offset += 16) {
642
#if 0
venaas's avatar
venaas committed
643
	printf("text + offset - 16 c(%d): ", offset / 16);
644
	printfchars(NULL, NULL, "%02x ", text + offset - 16, 16);
venaas's avatar
venaas committed
645
#endif
venaas's avatar
venaas committed
646 647 648 649 650 651 652 653
	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;
	}
654 655
#if 0
	printfchars(NULL, "msppencrypt hash", "%02x ", hash, 16);
656 657
#endif

venaas's avatar
venaas committed
658 659 660
	for (i = 0; i < 16; i++)
	    text[offset + i] ^= hash[i];
    }
661

venaas's avatar
venaas committed
662
#if 0
663
    printfchars(NULL, "msppencrypt out", "%02x ", text, len);
venaas's avatar
venaas committed
664 665 666 667 668 669 670 671 672 673 674 675 676 677
#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];
678

venaas's avatar
venaas committed
679 680 681 682 683 684
    pthread_mutex_lock(&lock);
    if (first) {
	EVP_MD_CTX_init(&mdctx);
	first = 0;
    }

685 686 687 688
#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
689
#endif
690

venaas's avatar
venaas committed
691 692 693 694 695 696 697 698 699
    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;
    }

700
#if 0
701
    printfchars(NULL, "msppdecrypt hash", "%02x ", hash, 16);
venaas's avatar
venaas committed
702
#endif
703

venaas's avatar
venaas committed
704 705
    for (i = 0; i < 16; i++)
	plain[i] = text[i] ^ hash[i];
706

venaas's avatar
venaas committed
707
    for (offset = 16; offset < len; offset += 16) {
708
#if 0
venaas's avatar
venaas committed
709
	printf("text + offset - 16 c(%d): ", offset / 16);
710
	printfchars(NULL, NULL, "%02x ", text + offset - 16, 16);
venaas's avatar
venaas committed
711 712 713 714 715 716 717 718 719
#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;
	}
720 721
#if 0
	printfchars(NULL, "msppdecrypt hash", "%02x ", hash, 16);
722
#endif
venaas's avatar
venaas committed
723

724 725
	for (i = 0; i < 16; i++)
	    plain[offset + i] = text[offset + i] ^ hash[i];
venaas's avatar
venaas committed
726 727 728 729
    }

    memcpy(text, plain, len);
#if 0
730
    printfchars(NULL, "msppdecrypt out", "%02x ", text, len);
venaas's avatar
venaas committed
731 732 733 734 735 736
#endif

    pthread_mutex_unlock(&lock);
    return 1;
}

737 738 739 740 741 742 743
struct realm *newrealmref(struct realm *r) {
    if (r)
	r->refcount++;
    return r;
}

/* returns with lock on realm */
venaas's avatar
venaas committed
744
struct realm *id2realm(struct list *realmlist, char *id) {
venaas's avatar
venaas committed
745
    struct list_node *entry;
746
    struct realm *realm, *subrealm;
venaas's avatar
venaas committed
747 748 749

    /* 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
750 751
	realm = (struct realm *)entry->data;
	if (!regexec(&realm->regex, id, 0, NULL, 0)) {
752 753
	    pthread_mutex_lock(&realm->mutex);
	    if (realm->subrealms) {
venaas's avatar
venaas committed
754
		subrealm = id2realm(realm->subrealms, id);
755 756 757 758 759 760
		if (subrealm) {
		    pthread_mutex_unlock(&realm->mutex);
		    return subrealm;
		}
	    }
	    return newrealmref(realm);
venaas's avatar
venaas committed
761
	}
venaas's avatar
venaas committed
762
    }
venaas's avatar
venaas committed
763
    return NULL;
venaas's avatar
venaas committed
764 765
}

766 767 768 769 770 771 772 773 774
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;
}

775 776
/* helper function, only used by removeserversubrealms() */
void _internal_removeserversubrealms(struct list *realmlist, struct clsrvconf *srv) {
777
    struct list_node *entry, *entry2;
778
    struct realm *realm;
779

780
    for (entry = list_first(realmlist); entry;) {
781 782
	realm = newrealmref((struct realm *)entry->data);
	pthread_mutex_lock(&realm->mutex);
783
	entry = list_next(entry);
784

785
	if (realm->srvconfs) {
786 787 788
	    for (entry2 = list_first(realm->srvconfs); entry2; entry2 = list_next(entry2))
		if (entry2->data == srv)
		    freerealm(realm);
789 790 791
	    list_removedata(realm->srvconfs, srv);
	}
	if (realm->accsrvconfs) {
792 793 794
	    for (entry2 = list_first(realm->accsrvconfs); entry2; entry2 = list_next(entry2))
		if (entry2->data == srv)
		    freerealm(realm);
795 796 797
	    list_removedata(realm->accsrvconfs, srv);
	}

798 799 800 801 802 803 804 805 806 807
	/* 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;
808
	    list_removedata(realmlist, realm);
809
	}
810 811
	pthread_mutex_unlock(&realm->mutex);
	freerealm(realm);
812 813 814 815 816 817
    }
}

void removeserversubrealms(struct list *realmlist, struct clsrvconf *srv) {
    struct list_node *entry;
    struct realm *realm;
818

819 820
    for (entry = list_first(realmlist); entry; entry = list_next(entry)) {
	realm = (struct realm *)entry->data;
821
	pthread_mutex_lock(&realm->mutex);
822 823 824 825 826 827 828
	if (realm->subrealms) {
	    _internal_removeserversubrealms(realm->subrealms, srv);
	    if (!list_first(realm->subrealms)) {
		list_destroy(realm->subrealms);
		realm->subrealms = NULL;
	    }
	}
829
	pthread_mutex_unlock(&realm->mutex);
830 831
    }
}
832

833 834
int attrvalidate(unsigned char *attrs, int length) {
    while (length > 1) {
venaas's avatar
venaas committed
835
	if (ATTRLEN(attrs) < 2) {
836
	    debug(DBG_INFO, "attrvalidate: invalid attribute length %d", ATTRLEN(attrs));
837 838
	    return 0;
	}
venaas's avatar
venaas committed
839
	length -= ATTRLEN(attrs);
840
	if (length < 0) {
841
	    debug(DBG_INFO, "attrvalidate: attribute length %d exceeds packet length", ATTRLEN(attrs));
842 843
	    return 0;
	}
venaas's avatar
venaas committed
844
	attrs += ATTRLEN(attrs);
845 846
    }
    if (length)
847
	debug(DBG_INFO, "attrvalidate: malformed packet? remaining byte after last attribute");
848 849 850
    return 1;
}

venaas's avatar
venaas committed
851 852 853 854 855
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;
    }
856

857
    if (!pwdcrypt(0, pwd, len, oldsecret, strlen(oldsecret), oldauth)) {
venaas's avatar
venaas committed
858 859 860 861
	debug(DBG_WARN, "pwdrecrypt: cannot decrypt password");
	return 0;
    }
#ifdef DEBUG
862
    printfchars(NULL, "pwdrecrypt: password", "%02x ", pwd, len);
863
#endif
864
    if (!pwdcrypt(1, pwd, len, newsecret, strlen(newsecret), newauth)) {
venaas's avatar
venaas committed
865 866 867 868 869 870
	debug(DBG_WARN, "pwdrecrypt: cannot encrypt password");
	return 0;
    }
    return 1;
}

871
int msmpprecrypt(uint8_t *msmpp, uint8_t len, char *oldsecret, char *newsecret, uint8_t *oldauth, uint8_t *newauth) {
venaas's avatar
venaas committed
872 873
    if (len < 18)
	return 0;
874
    if (!msmppdecrypt(msmpp + 2, len - 2, (uint8_t *)oldsecret, strlen(oldsecret), oldauth, msmpp)) {
venaas's avatar
venaas committed
875 876 877
	debug(DBG_WARN, "msmpprecrypt: failed to decrypt msppe key");
	return 0;
    }
878
    if (!msmppencrypt(msmpp + 2, len - 2, (uint8_t *)newsecret, strlen(newsecret), newauth, msmpp)) {
venaas's avatar
venaas committed
879 880 881 882 883 884
	debug(DBG_WARN, "msmpprecrypt: failed to encrypt msppe key");
	return 0;
    }
    return 1;
}

venaas's avatar
venaas committed
885
int msmppe(unsigned char *attrs, int length, uint8_t type, char *attrtxt, struct request *rq,
venaas's avatar
venaas committed
886 887
	   char *oldsecret, char *newsecret) {
    unsigned char *attr;
888

venaas's avatar
venaas committed
889 890
    for (attr = attrs; (attr = attrget(attr, length - (attr - attrs), type)); attr += ATTRLEN(attr)) {
	debug(DBG_DBG, "msmppe: Got %s", attrtxt);
891
	if (!msmpprecrypt(ATTRVAL(attr), ATTRVALLEN(attr), oldsecret, newsecret, rq->buf + 4, rq->rqauth))
venaas's avatar
venaas committed
892 893 894 895 896
	    return 0;
    }
    return 1;
}

897
int findvendorsubattr(uint32_t *attrs, uint32_t vendor, uint32_t su