radsecproxy.c 105 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
73
#include "util.h"
#include "gconfig.h"
venaas's avatar
venaas committed
74
#include "radsecproxy.h"
venaas's avatar
venaas committed
75
#include "udp.h"
venaas's avatar
venaas committed
76
77
#include "tcp.h"
#include "tls.h"
venaas's avatar
venaas committed
78
#include "dtls.h"
venaas's avatar
   
venaas committed
79

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

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

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

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

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

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

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
125
126
127
128
129
130
131
132
static int pem_passwd_cb(char *buf, int size, int rwflag, void *userdata) {
    int pwdlen = strlen(userdata);
    if (rwflag != 0 || pwdlen > size) /* not for decryption or too large */
	return 0;
    memcpy(buf, userdata, pwdlen);
    return pwdlen;
}

133
static int verify_cb(int ok, X509_STORE_CTX *ctx) {
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
    char *buf = NULL;
    X509 *err_cert;
    int err, depth;

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

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

    if (!ok) {
	if (err_cert)
	    buf = X509_NAME_oneline(X509_get_subject_name(err_cert), NULL, 0);
	debug(DBG_WARN, "verify error: num=%d:%s:depth=%d:%s", err, X509_verify_cert_error_string(err), depth, buf ? buf : "");
	free(buf);
	buf = NULL;
	
	switch (err) {
	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
	    if (err_cert) {
		buf = X509_NAME_oneline(X509_get_issuer_name(err_cert), NULL, 0);
		if (buf) {
		    debug(DBG_WARN, "\tIssuer=%s", buf);
		    free(buf);
		    buf = NULL;
		}
	    }
	    break;
	case X509_V_ERR_CERT_NOT_YET_VALID:
	case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
	    debug(DBG_WARN, "\tCertificate not yet valid");
	    break;
	case X509_V_ERR_CERT_HAS_EXPIRED:
	    debug(DBG_WARN, "Certificate has expired");
	    break;
	case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
	    debug(DBG_WARN, "Certificate no longer valid (after notAfter)");
	    break;
	case X509_V_ERR_NO_EXPLICIT_POLICY:
	    debug(DBG_WARN, "No Explicit Certificate Policy");
	    break;
	}
    }
181
#ifdef DEBUG  
182
    printf("certificate verify returns %d\n", ok);
183
#endif  
184
    return ok;
185
186
}

venaas's avatar
venaas committed
187
int resolvepeer(struct clsrvconf *conf, int ai_flags) {
188
    struct addrinfo hints, *addrinfo, *res;
189
    char *slash, *s;
venaas's avatar
venaas committed
190
    int plen = 0;
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210

    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
211
    memset(&hints, 0, sizeof(hints));
venaas's avatar
venaas committed
212
    hints.ai_socktype = conf->pdef->socktype;
venaas's avatar
   
venaas committed
213
    hints.ai_family = AF_UNSPEC;
venaas's avatar
venaas committed
214
    hints.ai_flags = ai_flags;
215
216
    if (!conf->host && !conf->port) {
	/* getaddrinfo() doesn't like host and port to be NULL */
venaas's avatar
venaas committed
217
	if (getaddrinfo(conf->host, conf->pdef->portdefault, &hints, &addrinfo)) {
218
219
220
	    debug(DBG_WARN, "resolvepeer: can't resolve (null) port (null)");
	    return 0;
	}
venaas's avatar
venaas committed
221
222
	for (res = addrinfo; res; res = res->ai_next)
	    port_set(res->ai_addr, 0);
223
224
225
226
    } else {
	if (slash)
	    hints.ai_flags |= AI_NUMERICHOST;
	if (getaddrinfo(conf->host, conf->port, &hints, &addrinfo)) {
227
	    debug(DBG_WARN, "resolvepeer: can't resolve %s port %s", conf->host ? conf->host : "(null)", conf->port ? conf->port : "(null)");
228
229
	    return 0;
	}
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
	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
247
	    conf->prefixlen = (uint8_t)plen;
248
249
250
	} else
	    conf->prefixlen = 255;
    }
venaas's avatar
venaas committed
251
252
253
    if (conf->addrinfo)
	freeaddrinfo(conf->addrinfo);
    conf->addrinfo = addrinfo;
venaas's avatar
   
venaas committed
254
255
256
    return 1;
}	  

257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
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
295
struct clsrvconf *resolve_hostport(uint8_t type, char *lconf, char *default_port) {
296
297
298
299
300
301
302
    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
303
    conf->pdef = protodefs[conf->type];
304
305
306
307
308
309
310
311
312
    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))
313
	debugx(1, DBG_ERR, "failed to resolve host %s port %s, exiting", conf->host ? conf->host : "(null)", conf->port ? conf->port : "(null)");
314
315
316
    return conf;
}

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

venaas's avatar
venaas committed
325
326
327
328
329
330
331
332
333
334
335
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;
}
    
336
337
338
/* 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
339
    uint8_t r, l = len / 8;
340
341
342
343
344
345
346
347
    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]);
}

348
/* returns next config with matching address, or NULL */
venaas's avatar
venaas committed
349
struct clsrvconf *find_conf(uint8_t type, struct sockaddr *addr, struct list *confs, struct list_node **cur) {
350
351
352
353
354
355
356
357
358
359
360
361
362
363
    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
364

venaas's avatar
venaas committed
365
    for (entry = (cur && *cur ? list_next(*cur) : list_first(confs)); entry; entry = list_next(entry)) {
venaas's avatar
venaas committed
366
	conf = (struct clsrvconf *)entry->data;
367
368
369
370
371
372
	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 &&
373
374
375
			 !memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16))) {
			if (cur)
			    *cur = entry;
376
			return conf;
377
		    }
378
379
380
381
382
383
	    } 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 &&
384
385
386
		      prefixmatch(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, conf->prefixlen)))) {
		    if (cur)
			*cur = entry;
venaas's avatar
venaas committed
387
		    return conf;
388
		}
389
390
	    }
	}
venaas's avatar
venaas committed
391
    }    
venaas's avatar
venaas committed
392
393
394
    return NULL;
}

venaas's avatar
venaas committed
395
396
397
398
399
400
401
402
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
403
/* returns next config of given type, or NULL */
venaas's avatar
venaas committed
404
struct clsrvconf *find_clconf_type(uint8_t type, struct list_node **cur) {
venaas's avatar
venaas committed
405
406
407
    struct list_node *entry;
    struct clsrvconf *conf;
    
venaas's avatar
venaas committed
408
    for (entry = (cur && *cur ? list_next(*cur) : list_first(clconfs)); entry; entry = list_next(entry)) {
venaas's avatar
venaas committed
409
410
411
412
413
414
415
416
417
418
	conf = (struct clsrvconf *)entry->data;
	if (conf->type == type) {
	    if (cur)
		*cur = entry;
	    return conf;
	}
    }    
    return NULL;
}

419
420
struct queue *newqueue() {
    struct queue *q;
421
    
422
423
    q = malloc(sizeof(struct queue));
    if (!q)
424
	debugx(1, DBG_ERR, "malloc failed");
425
426
    q->entries = list_create();
    if (!q->entries)
427
	debugx(1, DBG_ERR, "malloc failed");
428
429
430
431
432
433
434
    pthread_mutex_init(&q->mutex, NULL);
    pthread_cond_init(&q->cond, NULL);
    return q;
}

void removequeue(struct queue *q) {
    struct list_node *entry;
435
436
437

    if (!q)
	return;
438
439
    pthread_mutex_lock(&q->mutex);
    for (entry = list_first(q->entries); entry; entry = list_next(entry))
venaas's avatar
venaas committed
440
	freerq((struct request *)entry);
441
442
443
444
    list_destroy(q->entries);
    pthread_cond_destroy(&q->cond);
    pthread_mutex_unlock(&q->mutex);
    pthread_mutex_destroy(&q->mutex);
445
    free(q);
446
447
448
449
450
451
452
453
454
455
}

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);
456
457
}

458
struct client *addclient(struct clsrvconf *conf, uint8_t lock) {
459
460
461
462
463
    struct client *new = malloc(sizeof(struct client));
    
    if (!new) {
	debug(DBG_ERR, "malloc failed");
	return NULL;
464
    }
465
466
467

    if (lock)
	pthread_mutex_lock(conf->lock);
468
    if (!conf->clients) {
469
470
	conf->clients = list_create();
	if (!conf->clients) {
471
472
	    if (lock)
		pthread_mutex_unlock(conf->lock);
473
474
475
	    debug(DBG_ERR, "malloc failed");
	    return NULL;
	}
476
    }
477
478
479
    
    memset(new, 0, sizeof(struct client));
    new->conf = conf;
venaas's avatar
venaas committed
480
481
482
483
    if (conf->pdef->addclient)
	conf->pdef->addclient(new);
    else
	new->replyq = newqueue();
484
    list_push(conf->clients, new);
485
486
    if (lock)
	pthread_mutex_unlock(conf->lock);
487
488
489
    return new;
}

490
491
492
493
494
495
496
497
498
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);
}
    
499
500
501
502
503
void removeclientrqs(struct client *client) {
    struct request *rq;
    struct rqout *rqout;
    int i;

504
    removeclientrqs_sendrq_freeserver_lock(1);
505
506
507
508
    for (i = 0; i < MAX_REQUESTS; i++) {
	rq = client->rqs[i];
	if (!rq)
	    continue;
509
510
511
512
513
514
515
	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);
	}
516
517
	freerq(rq);
    }
518
    removeclientrqs_sendrq_freeserver_lock(0);
519
520
}

venaas's avatar
venaas committed
521
void removelockedclient(struct client *client) {
522
523
524
525
    struct clsrvconf *conf;
    
    conf = client->conf;
    if (conf->clients) {
526
	removeclientrqs(client);
527
	removequeue(client->replyq);
528
	list_removedata(conf->clients, client);
529
530
531
	free(client->addr);
	free(client);
    }
venaas's avatar
venaas committed
532
533
534
535
536
537
538
539
540
541
542
}

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

    conf = client->conf;
    pthread_mutex_lock(conf->lock);
    removelockedclient(client);
543
    pthread_mutex_unlock(conf->lock);
544
545
}

venaas's avatar
venaas committed
546
void freeserver(struct server *server, uint8_t destroymutex) {
547
    struct rqout *rqout, *end;
548

venaas's avatar
venaas committed
549
550
    if (!server)
	return;
551

552
    removeclientrqs_sendrq_freeserver_lock(1);
553
    if (server->requests) {
554
	rqout = server->requests;
555
	for (end = rqout + MAX_REQUESTS; rqout < end; rqout++) {
556
557
	    if (rqout->rq)
		rqout->rq->to = NULL;
558
	    freerqoutdata(rqout);
559
560
561
	    pthread_mutex_destroy(rqout->lock);
	    free(rqout->lock);
	}
562
563
	free(server->requests);
    }
564
565
    if (server->rbios)
	freebios(server->rbios);
566
    free(server->dynamiclookuparg);
567
568
    if (server->ssl)
	SSL_free(server->ssl);
venaas's avatar
venaas committed
569
570
571
572
573
    if (destroymutex) {
	pthread_mutex_destroy(&server->lock);
	pthread_cond_destroy(&server->newrq_cond);
	pthread_mutex_destroy(&server->newrq_mutex);
    }
574
    removeclientrqs_sendrq_freeserver_lock(0);
venaas's avatar
venaas committed
575
576
577
578
    free(server);
}

int addserver(struct clsrvconf *conf) {
venaas's avatar
venaas committed
579
    uint8_t type;
580
    int i;
581
    
venaas's avatar
venaas committed
582
583
584
585
    if (conf->servers) {
	debug(DBG_ERR, "addserver: currently works with just one server per conf");
	return 0;
    }
586
    conf->servers = malloc(sizeof(struct server));
venaas's avatar
venaas committed
587
588
589
590
    if (!conf->servers) {
	debug(DBG_ERR, "malloc failed");
	return 0;
    }
591
592
593
    memset(conf->servers, 0, sizeof(struct server));
    conf->servers->conf = conf;

venaas's avatar
venaas committed
594
    type = conf->type;
595
    if (type == RAD_DTLS)
596
	conf->servers->rbios = newqueue();
venaas's avatar
venaas committed
597

venaas's avatar
venaas committed
598
    conf->pdef->setsrcres();
venaas's avatar
venaas committed
599

venaas's avatar
venaas committed
600
601
602
    conf->servers->sock = -1;
    if (conf->pdef->addserverextra)
	conf->pdef->addserverextra(conf);
603
    
604
    conf->servers->requests = calloc(MAX_REQUESTS, sizeof(struct rqout));
venaas's avatar
venaas committed
605
606
607
608
    if (!conf->servers->requests) {
	debug(DBG_ERR, "malloc failed");
	goto errexit;
    }
609
610
611
612
613
614
615
616
617
618
619
620
621
    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
622
623
624
625
    if (pthread_mutex_init(&conf->servers->lock, NULL)) {
	debug(DBG_ERR, "mutex init failed");
	goto errexit;
    }
626
    conf->servers->newrq = 0;
venaas's avatar
venaas committed
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
    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;
645
646
}

647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
int subjectaltnameaddr(X509 *cert, int family, struct in6_addr *addr) {
    int loc, i, l, n, r = 0;
    char *v;
    X509_EXTENSION *ex;
    STACK_OF(GENERAL_NAME) *alt;
    GENERAL_NAME *gn;
    
    debug(DBG_DBG, "subjectaltnameaddr");
    
    loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1);
    if (loc < 0)
	return r;
    
    ex = X509_get_ext(cert, loc);
    alt = X509V3_EXT_d2i(ex);
    if (!alt)
	return r;
    
    n = sk_GENERAL_NAME_num(alt);
    for (i = 0; i < n; i++) {
	gn = sk_GENERAL_NAME_value(alt, i);
	if (gn->type != GEN_IPADD)
	    continue;
	r = -1;
	v = (char *)ASN1_STRING_data(gn->d.ia5);
	l = ASN1_STRING_length(gn->d.ia5);
	if (((family == AF_INET && l == sizeof(struct in_addr)) || (family == AF_INET6 && l == sizeof(struct in6_addr)))
	    && !memcmp(v, &addr, l)) {
	    r = 1;
	    break;
	}
    }
    GENERAL_NAMES_free(alt);
    return r;
}

venaas's avatar
venaas committed
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
int cnregexp(X509 *cert, char *exact, regex_t *regex) {
    int loc, l;
    char *v, *s;
    X509_NAME *nm;
    X509_NAME_ENTRY *e;
    ASN1_STRING *t;

    nm = X509_get_subject_name(cert);
    loc = -1;
    for (;;) {
	loc = X509_NAME_get_index_by_NID(nm, NID_commonName, loc);
	if (loc == -1)
	    break;
	e = X509_NAME_get_entry(nm, loc);
	t = X509_NAME_ENTRY_get_data(e);
	v = (char *) ASN1_STRING_data(t);
	l = ASN1_STRING_length(t);
	if (l < 0)
	    continue;
	if (exact) {
	    if (l == strlen(exact) && !strncasecmp(exact, v, l))
		return 1;
	} else {
	    s = stringcopy((char *)v, l);
	    if (!s) {
		debug(DBG_ERR, "malloc failed");
		continue;
	    }
	    if (regexec(regex, s, 0, NULL, 0)) {
		free(s);
		continue;
	    }
	    free(s);
	    return 1;
	}
    }
    return 0;
}

722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
int subjectaltnameregexp(X509 *cert, int type, char *exact,  regex_t *regex) {
    int loc, i, l, n, r = 0;
    char *s, *v;
    X509_EXTENSION *ex;
    STACK_OF(GENERAL_NAME) *alt;
    GENERAL_NAME *gn;
    
    debug(DBG_DBG, "subjectaltnameregexp");
    
    loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1);
    if (loc < 0)
	return r;
    
    ex = X509_get_ext(cert, loc);
    alt = X509V3_EXT_d2i(ex);
    if (!alt)
	return r;
    
    n = sk_GENERAL_NAME_num(alt);
    for (i = 0; i < n; i++) {
	gn = sk_GENERAL_NAME_value(alt, i);
	if (gn->type != type)
	    continue;
	r = -1;
	v = (char *)ASN1_STRING_data(gn->d.ia5);
	l = ASN1_STRING_length(gn->d.ia5);
	if (l <= 0)
	    continue;
#ifdef DEBUG
	printfchars(NULL, gn->type == GEN_DNS ? "dns" : "uri", NULL, v, l);
#endif	
	if (exact) {
	    if (memcmp(v, exact, l))
		continue;
	} else {
	    s = stringcopy((char *)v, l);
	    if (!s) {
		debug(DBG_ERR, "malloc failed");
		continue;
	    }
	    if (regexec(regex, s, 0, NULL, 0)) {
		free(s);
		continue;
	    }
	    free(s);
	}
	r = 1;
	break;
    }
    GENERAL_NAMES_free(alt);
    return r;
}

venaas's avatar
venaas committed
775
X509 *verifytlscert(SSL *ssl) {
776
777
    X509 *cert;
    unsigned long error;
778
    
venaas's avatar
venaas committed
779
    if (SSL_get_verify_result(ssl) != X509_V_OK) {
venaas's avatar
venaas committed
780
	debug(DBG_ERR, "verifytlscert: basic validation failed");
781
	while ((error = ERR_get_error()))
venaas's avatar
venaas committed
782
783
	    debug(DBG_ERR, "verifytlscert: TLS: %s", ERR_error_string(error, NULL));
	return NULL;
784
    }
785

venaas's avatar
venaas committed
786
    cert = SSL_get_peer_certificate(ssl);
venaas's avatar
venaas committed
787
788
789
790
791
792
793
794
795
796
    if (!cert)
	debug(DBG_ERR, "verifytlscert: failed to obtain certificate");
    return cert;
}
    
int verifyconfcert(X509 *cert, struct clsrvconf *conf) {
    int r;
    uint8_t type = 0; /* 0 for DNS, AF_INET for IPv4, AF_INET6 for IPv6 */
    struct in6_addr addr;
    
797
    if (conf->certnamecheck && conf->prefixlen == 255) {
798
799
800
801
802
803
804
805
	if (inet_pton(AF_INET, conf->host, &addr))
	    type = AF_INET;
	else if (inet_pton(AF_INET6, conf->host, &addr))
	    type = AF_INET6;

	r = type ? subjectaltnameaddr(cert, type, &addr) : subjectaltnameregexp(cert, GEN_DNS, conf->host, NULL);
	if (r) {
	    if (r < 0) {
venaas's avatar
venaas committed
806
		debug(DBG_WARN, "verifyconfcert: No subjectaltname matching %s %s", type ? "address" : "host", conf->host);
venaas's avatar
venaas committed
807
		return 0;
808
	    }
venaas's avatar
venaas committed
809
	    debug(DBG_DBG, "verifyconfcert: Found subjectaltname matching %s %s", type ? "address" : "host", conf->host);
810
	} else {
venaas's avatar
venaas committed
811
	    if (!cnregexp(cert, conf->host, NULL)) {
venaas's avatar
venaas committed
812
		debug(DBG_WARN, "verifyconfcert: cn not matching host %s", conf->host);
venaas's avatar
venaas committed
813
		return 0;
venaas's avatar
venaas committed
814
	    }		
venaas's avatar
venaas committed
815
	    debug(DBG_DBG, "verifyconfcert: Found cn matching host %s", conf->host);
venaas's avatar
venaas committed
816
817
818
819
	}
    }
    if (conf->certcnregex) {
	if (cnregexp(cert, NULL, conf->certcnregex) < 1) {
venaas's avatar
venaas committed
820
	    debug(DBG_WARN, "verifyconfcert: CN not matching regex");
venaas's avatar
venaas committed
821
	    return 0;
822
	}
venaas's avatar
venaas committed
823
	debug(DBG_DBG, "verifyconfcert: CN matching regex");
824
825
    }
    if (conf->certuriregex) {
venaas's avatar
venaas committed
826
	if (subjectaltnameregexp(cert, GEN_URI, NULL, conf->certuriregex) < 1) {
venaas's avatar
venaas committed
827
	    debug(DBG_WARN, "verifyconfcert: subjectaltname URI not matching regex");
venaas's avatar
venaas committed
828
	    return 0;
829
	}
venaas's avatar
venaas committed
830
	debug(DBG_DBG, "verifyconfcert: subjectaltname URI matching regex");
831
    }
832
    return 1;
833
834
}

835
unsigned char *attrget(unsigned char *attrs, int length, uint8_t type) {
836
    while (length > 1) {
venaas's avatar
venaas committed
837
	if (ATTRTYPE(attrs) == type)
838
	    return attrs;
venaas's avatar
venaas committed
839
840
	length -= ATTRLEN(attrs);
	attrs += ATTRLEN(attrs);
841
842
843
844
    }
    return NULL;
}

venaas's avatar
venaas committed
845
struct request *newrqref(struct request *rq) {
846
847
    if (rq)
	rq->refcount++;
venaas's avatar
venaas committed
848
849
850
    return rq;
}

851
852
853
854
855
856
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
857
858
859
860
    if (rq->origusername)
	free(rq->origusername);
    if (rq->buf)
	free(rq->buf);
venaas's avatar
venaas committed
861
862
863
    if (rq->replybuf)
	free(rq->replybuf);
    if (rq->msg)
venaas's avatar
venaas committed
864
	radmsg_free(rq->msg);
865
866
867
868
869
870
    free(rq);
}

void freerqoutdata(struct rqout *rqout) {
    if (!rqout)
	return;
871
    if (rqout->rq) {
872
873
874
875
	if (rqout->rq->buf) {
	    free(rqout->rq->buf);
	    rqout->rq->buf = NULL;
	}
876
	freerq(rqout->rq);
877
878
879
880
	rqout->rq = NULL;
    }
    rqout->tries = 0;
    memset(&rqout->expiry, 0, sizeof(struct timeval));
881
882
}

883
void sendrq(struct request *rq) {
884
    int i, start;
885
886
887
888
889
890
    struct server *to;

    removeclientrqs_sendrq_freeserver_lock(1);
    to = rq->to;
    if (!to)
	goto errexit;
891
    
892
    start = to->conf->statusserver ? 1 : 0;
venaas's avatar
   
venaas committed
893
    pthread_mutex_lock(&to->newrq_mutex);
894
895
896
897
898
899
    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;
900
	}
901
902
903
904
905
906
	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
907
	    if (!to->requests[i].rq) {
908
		pthread_mutex_lock(to->requests[i].lock);
venaas's avatar
venaas committed
909
		if (!to->requests[i].rq)
910
911
912
913
		    break;
		pthread_mutex_unlock(to->requests[i].lock);
	    }
	}
914
915
916
917
918
919
920
921
922
923
924
925
926
	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
927
	}
venaas's avatar
   
venaas committed
928
    }
929
    rq->newid = (uint8_t)i;
venaas's avatar
venaas committed
930
931
932
    rq->msg->id = (uint8_t)i;
    rq->buf = radmsg2buf(rq->msg, (uint8_t *)to->conf->secret);
    if (!rq->buf) {
933
	pthread_mutex_unlock(to->requests[i].lock);
venaas's avatar
venaas committed
934
	debug(DBG_ERR, "sendrq: radmsg2buf failed");
935
	goto errexit;
venaas's avatar
venaas committed
936
937
    }
    
venaas's avatar
venaas committed
938
    debug(DBG_DBG, "sendrq: inserting packet with id %d in queue for %s", i, to->conf->host);
venaas's avatar
venaas committed
939
    to->requests[i].rq = rq;
940
    pthread_mutex_unlock(to->requests[i].lock);
941
942
    if (i >= start) /* i is not reserved for statusserver */
	to->nextid = i + 1;
venaas's avatar
   
venaas committed
943
944
945

    if (!to->newrq) {
	to->newrq = 1;
venaas's avatar
venaas committed
946
	debug(DBG_DBG, "sendrq: signalling client writer");
venaas's avatar
   
venaas committed
947
948
	pthread_cond_signal(&to->newrq_cond);
    }
venaas's avatar
venaas committed
949

950
    pthread_mutex_unlock(&to->newrq_mutex);
951
    removeclientrqs_sendrq_freeserver_lock(0);
952
953
954
    return;

 errexit:
955
956
    if (rq->from)
	rmclientrq(rq, rq->msg->id);
957
    freerq(rq);
venaas's avatar
   
venaas committed
958
    pthread_mutex_unlock(&to->newrq_mutex);
959
    removeclientrqs_sendrq_freeserver_lock(0);
venaas's avatar
   
venaas committed
960
961
}

venaas's avatar
venaas committed
962
void sendreply(struct request *rq) {
963
    uint8_t first;
venaas's avatar
venaas committed
964
965
966
967
968
969
970
971
    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
972
	debug(DBG_ERR, "sendreply: radmsg2buf failed");
venaas's avatar
venaas committed
973
974
	return;
    }
975
976

    pthread_mutex_lock(&to->replyq->mutex);
977
    first = list_first(to->replyq->entries) == NULL;
978
    
venaas's avatar
venaas committed
979
    if (!list_push(to->replyq->entries, rq)) {
980
	pthread_mutex_unlock(&to->replyq->mutex);
venaas's avatar
venaas committed
981
	freerq(rq);
982
983
984
985
986
	debug(DBG_ERR, "sendreply: malloc failed");
	return;
    }
    
    if (first) {
987
	debug(DBG_DBG, "signalling server writer");
988
	pthread_cond_signal(&to->replyq->cond);
venaas's avatar
   
venaas committed
989
    }
990
    pthread_mutex_unlock(&to->replyq->mutex);
venaas's avatar
   
venaas committed
991
992
}

venaas's avatar
venaas committed
993
int pwdencrypt(uint8_t *in, uint8_t len, char *shared, uint8_t sharedlen, uint8_t *auth) {
venaas's avatar
venaas committed
994
995
996
997
998
    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
999
    uint8_t i, offset = 0, out[128];
venaas's avatar
venaas committed
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
    
    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
1010
	    !EVP_DigestUpdate(&mdctx, (uint8_t *)shared, sharedlen) ||
venaas's avatar
venaas committed
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
	    !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
1024
    memcpy(in, out, len);
venaas's avatar
venaas committed
1025
1026
1027
1028
    pthread_mutex_unlock(&lock);
    return 1;
}

venaas's avatar
venaas committed
1029
int pwddecrypt(uint8_t *in, uint8_t len, char *shared, uint8_t sharedlen, uint8_t *auth) {
venaas's avatar
   
venaas committed
1030
1031
1032
1033
1034
    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
1035
    uint8_t i, offset = 0, out[128];
venaas's avatar
   
venaas committed
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
    
    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
1046
	    !EVP_DigestUpdate(&mdctx, (uint8_t *)shared, sharedlen) ||
venaas's avatar
   
venaas committed
1047
1048
1049
1050
1051
1052
1053
	    !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
1054
1055
	    out[offset + i] = hash[i] ^ in[offset + i];
	input = in + offset;
venaas's avatar
   
venaas committed
1056
	offset += 16;
venaas's avatar
venaas committed
1057
	if (offset == len)
venaas's avatar
   
venaas committed
1058
1059
	    break;
    }
venaas's avatar
venaas committed
1060
    memcpy(in, out, len);
venaas's avatar
   
venaas committed
1061
1062
1063
1064
    pthread_mutex_unlock(&lock);
    return 1;
}

venaas's avatar
venaas committed
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
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;
    }

1079
1080
1081
1082
#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
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
#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    
1095
    printfchars(NULL, "msppencrypt hash", "%02x ", hash, 16);
venaas's avatar
venaas committed
1096
1097
1098
1099
1100
1101
#endif
    
    for (i = 0; i < 16; i++)
	text[i] ^= hash[i];
    
    for (offset = 16; offset < len; offset += 16) {
venaas's avatar
venaas committed
1102
#if 0	
venaas's avatar
venaas committed
1103
	printf("text + offset - 16 c(%d): ", offset / 16);
1104
	printfchars(NULL, NULL, "%02x ", text + offset - 16, 16);
venaas's avatar
venaas committed
1105
#endif
venaas's avatar
venaas committed
1106
1107
1108
1109
1110
1111
1112
1113
	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;
	}
1114
1115
#if 0
	printfchars(NULL, "msppencrypt hash", "%02x ", hash, 16);
venaas's avatar
venaas committed
1116
1117
1118
1119
1120
1121
1122
#endif    
	
	for (i = 0; i < 16; i++)
	    text[offset + i] ^= hash[i];
    }
    
#if 0
1123
    printfchars(NULL, "msppencrypt out", "%02x ", text, len);
venaas's avatar
venaas committed
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
#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;
    }

1145
1146
1147
1148
#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
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
#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    
1161
    printfchars(NULL, "msppdecrypt hash", "%02x ", hash, 16);
venaas's avatar
venaas committed
1162
1163
1164
1165
1166
1167
1168
1169
#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);
1170
	printfchars(NULL, NULL, "%02x ", text + offset - 16, 16);
venaas's avatar
venaas committed
1171
1172
1173
1174
1175
1176
1177
1178
1179
#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;
	}
1180
1181
#if 0
	printfchars(NULL, "msppdecrypt hash", "%02x ", hash, 16);
venaas's avatar
venaas committed
1182
1183
#endif    

1184
1185
	for (i = 0; i < 16; i++)
	    plain[offset + i] = text[offset + i] ^ hash[i];
venaas's avatar
venaas committed
1186
1187
1188
1189
    }

    memcpy(text, plain, len);
#if 0
1190
    printfchars(NULL, "msppdecrypt out", "%02x ", text, len);
venaas's avatar
venaas committed
1191
1192
1193
1194
1195
1196
#endif

    pthread_mutex_unlock(&lock);
    return 1;
}

1197
1198
1199
1200
1201
1202
1203
struct realm *newrealmref(struct realm *r) {
    if (r)
	r->refcount++;
    return r;
}

/* returns with lock on realm */
venaas's avatar
venaas committed
1204
struct realm *id2realm(struct list *realmlist, char *id) {
venaas's avatar
venaas committed
1205
    struct list_node *entry;
1206
    struct realm *realm, *subrealm;
venaas's avatar
venaas committed
1207
1208
1209

    /* need to do locking for subrealms and check subrealm timers */
    for (entry = list_first(realmlist); entry; entry = list_next