radsecproxy.c 104 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 + 1];
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
uint8_t protoname2int(const char *name) {
venaas's avatar
venaas committed
102
    uint8_t i;
venaas's avatar
venaas committed
103

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

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
121
122
123
124
125
126
127
128
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;
}

129
static int verify_cb(int ok, X509_STORE_CTX *ctx) {
130
131
132
133
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
    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;
	}
    }
177
#ifdef DEBUG  
178
    printf("certificate verify returns %d\n", ok);
179
#endif  
180
    return ok;
181
182
}

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

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

253
254
255
256
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
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
291
struct clsrvconf *resolve_hostport(uint8_t type, char *lconf, char *default_port) {
292
293
294
295
296
297
298
    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
299
    conf->pdef = protodefs[conf->type];
300
301
302
303
304
305
306
307
308
    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))
309
	debugx(1, DBG_ERR, "failed to resolve host %s port %s, exiting", conf->host ? conf->host : "(null)", conf->port ? conf->port : "(null)");
310
311
312
    return conf;
}

313
314
315
316
317
318
319
320
void freeclsrvres(struct clsrvconf *res) {
    free(res->host);
    free(res->port);
    if (res->addrinfo)
	freeaddrinfo(res->addrinfo);
    free(res);
}

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

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

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

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

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

void removequeue(struct queue *q) {
    struct list_node *entry;
431
432
433

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

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);
452
453
}

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

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

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

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

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

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

    conf = client->conf;
    pthread_mutex_lock(conf->lock);
    removelockedclient(client);
539
    pthread_mutex_unlock(conf->lock);
540
541
}

venaas's avatar
venaas committed
542
void freeserver(struct server *server, uint8_t destroymutex) {
543
    struct rqout *rqout, *end;
544

venaas's avatar
venaas committed
545
546
    if (!server)
	return;
547

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

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

venaas's avatar
venaas committed
590
    type = conf->type;
591
    if (type == RAD_DTLS)
592
	conf->servers->rbios = newqueue();
venaas's avatar
venaas committed
593
594

    conf->pdef->setsrcres(options.sourcearg[type]);
venaas's avatar
venaas committed
595

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

643
644
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
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
679
680
681
682
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
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;
}

718
719
720
721
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
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
771
X509 *verifytlscert(SSL *ssl) {
772
773
    X509 *cert;
    unsigned long error;
774
    
venaas's avatar
venaas committed
775
    if (SSL_get_verify_result(ssl) != X509_V_OK) {
venaas's avatar
venaas committed
776
	debug(DBG_ERR, "verifytlscert: basic validation failed");
777
	while ((error = ERR_get_error()))
venaas's avatar
venaas committed
778
779
	    debug(DBG_ERR, "verifytlscert: TLS: %s", ERR_error_string(error, NULL));
	return NULL;
780
    }
781

venaas's avatar
venaas committed
782
    cert = SSL_get_peer_certificate(ssl);
venaas's avatar
venaas committed
783
784
785
786
787
788
789
790
791
792
    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;
    
793
    if (conf->certnamecheck && conf->prefixlen == 255) {
794
795
796
797
798
799
800
801
	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
802
		debug(DBG_WARN, "verifyconfcert: No subjectaltname matching %s %s", type ? "address" : "host", conf->host);
venaas's avatar
venaas committed
803
		return 0;
804
	    }
venaas's avatar
venaas committed
805
	    debug(DBG_DBG, "verifyconfcert: Found subjectaltname matching %s %s", type ? "address" : "host", conf->host);
806
	} else {
venaas's avatar
venaas committed
807
	    if (!cnregexp(cert, conf->host, NULL)) {
venaas's avatar
venaas committed
808
		debug(DBG_WARN, "verifyconfcert: cn not matching host %s", conf->host);
venaas's avatar
venaas committed
809
		return 0;
venaas's avatar
venaas committed
810
	    }		
venaas's avatar
venaas committed
811
	    debug(DBG_DBG, "verifyconfcert: Found cn matching host %s", conf->host);
venaas's avatar
venaas committed
812
813
814
815
	}
    }
    if (conf->certcnregex) {
	if (cnregexp(cert, NULL, conf->certcnregex) < 1) {
venaas's avatar
venaas committed
816
	    debug(DBG_WARN, "verifyconfcert: CN not matching regex");
venaas's avatar
venaas committed
817
	    return 0;
818
	}
venaas's avatar
venaas committed
819
	debug(DBG_DBG, "verifyconfcert: CN matching regex");
820
821
    }
    if (conf->certuriregex) {
venaas's avatar
venaas committed
822
	if (subjectaltnameregexp(cert, GEN_URI, NULL, conf->certuriregex) < 1) {
venaas's avatar
venaas committed
823
	    debug(DBG_WARN, "verifyconfcert: subjectaltname URI not matching regex");
venaas's avatar
venaas committed
824
	    return 0;
825
	}
venaas's avatar
venaas committed
826
	debug(DBG_DBG, "verifyconfcert: subjectaltname URI matching regex");
827
    }
828
    return 1;
829
830
}

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

venaas's avatar
venaas committed
841
struct request *newrqref(struct request *rq) {
842
843
    if (rq)
	rq->refcount++;
venaas's avatar
venaas committed
844
845
846
    return rq;
}

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

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

879
void sendrq(struct request *rq) {
880
    int i, start;
881
882
883
884
885
886
    struct server *to;

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

    if (!to->newrq) {
	to->newrq = 1;
venaas's avatar
venaas committed
942
	debug(DBG_DBG, "sendrq: signalling client writer");
venaas's avatar
   
venaas committed
943
944
	pthread_cond_signal(&to->newrq_cond);
    }
venaas's avatar
venaas committed
945

946
    pthread_mutex_unlock(&to->newrq_mutex);
947
    removeclientrqs_sendrq_freeserver_lock(0);
948
949
950
    return;

 errexit:
951
952
    if (rq->from)
	rmclientrq(rq, rq->msg->id);
953
    freerq(rq);
venaas's avatar
   
venaas committed
954
    pthread_mutex_unlock(&to->newrq_mutex);
955
    removeclientrqs_sendrq_freeserver_lock(0);
venaas's avatar
   
venaas committed
956
957
}

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

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

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

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

venaas's avatar
venaas committed
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
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;
    }

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

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

1180
1181
	for (i = 0; i < 16; i++)
	    plain[offset + i] = text[offset + i] ^ hash[i];
venaas's avatar
venaas committed
1182
1183
1184
1185
    }

    memcpy(text, plain, len);
#if 0
1186
    printfchars(NULL, "msppdecrypt out", "%02x ", text, len);
venaas's avatar
venaas committed
1187
1188
1189
1190
1191
1192
#endif

    pthread_mutex_unlock(&lock);
    return 1;
}

1193
1194
1195
1196
1197
1198
1199
struct realm *newrealmref(struct realm *r) {
    if (r)
	r->refcount++;
    return r;
}

/* returns with lock on realm */
venaas's avatar
venaas committed
1200
struct realm *id2realm(struct list *realmlist, char *id) {
venaas's avatar
venaas committed
1201
    struct list_node *entry;
1202
    struct realm *realm, *subrealm;
venaas's avatar
venaas committed
1203
1204
1205

    /* 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
1206
1207
	realm = (struct realm *)entry->data;
	if (!regexec(&realm->regex, id, 0, NULL, 0)) {
1208