GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: libexec/login_radius/raddauth.c Lines: 0 255 0.0 %
Date: 2017-11-07 Branches: 0 127 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: raddauth.c,v 1.29 2016/09/03 11:04:23 gsoares Exp $	*/
2
3
/*-
4
 * Copyright (c) 1996, 1997 Berkeley Software Design, Inc. All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions
8
 * are met:
9
 * 1. Redistributions of source code must retain the above copyright
10
 *    notice, this list of conditions and the following disclaimer.
11
 * 2. Redistributions in binary form must reproduce the above copyright
12
 *    notice, this list of conditions and the following disclaimer in the
13
 *    documentation and/or other materials provided with the distribution.
14
 * 3. All advertising materials mentioning features or use of this software
15
 *    must display the following acknowledgement:
16
 *      This product includes software developed by Berkeley Software Design,
17
 *      Inc.
18
 * 4. The name of Berkeley Software Design, Inc.  may not be used to endorse
19
 *    or promote products derived from this software without specific prior
20
 *    written permission.
21
 *
22
 * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND
23
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
 * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE
26
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32
 * SUCH DAMAGE.
33
 *
34
 *	BSDI $From: raddauth.c,v 1.6 1998/04/14 00:39:04 prb Exp $
35
 */
36
/*
37
 * Copyright(c) 1996 by tfm associates.
38
 * All rights reserved.
39
 *
40
 * tfm associates
41
 * P.O. Box 2086
42
 * Eugene OR 97402-0031
43
 *
44
 * Redistribution and use in source and binary forms, with or without
45
 * modification, are permitted provided that the following conditions
46
 * are met:
47
 * 1. Redistributions of source code must retain the above copyright
48
 *    notice, this list of conditions and the following disclaimer.
49
 * 2. Redistributions in binary form must reproduce the above copyright
50
 *    notice, this list of conditions and the following disclaimer in the
51
 *    documentation and/or other materials provided with the distribution.
52
 * 3. The name of tfm associates may not be used to endorse or promote
53
 *    products derived from this software without specific prior written
54
 *    permission.
55
 *
56
 * THIS SOFTWARE IS PROVIDED BY TFM ASSOC``AS IS'' AND ANY EXPRESS OR
57
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
58
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
59
 * IN NO EVENT SHALL TFM ASSOCIATES BE LIABLE FOR ANY DIRECT, INDIRECT,
60
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
61
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
62
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
63
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
64
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
65
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
66
 */
67
68
#include <sys/types.h>
69
#include <sys/socket.h>
70
#include <netinet/in.h>
71
#include <arpa/inet.h>
72
73
#include <ctype.h>
74
#include <err.h>
75
#include <errno.h>
76
#include <stdint.h>
77
#include <limits.h>
78
#include <login_cap.h>
79
#include <netdb.h>
80
#include <signal.h>
81
#include <stdio.h>
82
#include <stdlib.h>
83
#include <string.h>
84
#include <syslog.h>
85
#include <time.h>
86
#include <unistd.h>
87
#include <md5.h>
88
#include <readpassphrase.h>
89
#include "login_radius.h"
90
91
92
#define	MAXPWNETNAM		64	/* longest username */
93
#define MAXSECRETLEN		128	/* maximum length of secret */
94
95
#define AUTH_VECTOR_LEN			16
96
#define AUTH_HDR_LEN			20
97
#define	AUTH_PASS_LEN			(256 - 16)
98
#define	PW_AUTHENTICATION_REQUEST	1
99
#define	PW_AUTHENTICATION_ACK		2
100
#define	PW_AUTHENTICATION_REJECT	3
101
#define PW_ACCESS_CHALLENGE		11
102
#define	PW_USER_NAME			1
103
#define	PW_PASSWORD			2
104
#define	PW_CLIENT_ID			4
105
#define	PW_CLIENT_PORT_ID		5
106
#define PW_PORT_MESSAGE			18
107
#define PW_STATE			24
108
109
#ifndef	RADIUS_DIR
110
#define RADIUS_DIR		"/etc/raddb"
111
#endif
112
#define RADIUS_SERVERS		"servers"
113
114
char *radius_dir = RADIUS_DIR;
115
char auth_secret[MAXSECRETLEN+1];
116
volatile sig_atomic_t timedout;
117
int alt_retries;
118
int retries;
119
int sockfd;
120
int timeout;
121
in_addr_t alt_server;
122
in_addr_t auth_server;
123
in_port_t radius_port;
124
125
typedef struct {
126
	u_char	code;
127
	u_char	id;
128
	u_short	length;
129
	u_char	vector[AUTH_VECTOR_LEN];
130
	u_char	data[4096 - AUTH_HDR_LEN];
131
} auth_hdr_t;
132
133
void servtimeout(int);
134
in_addr_t get_ipaddr(char *);
135
in_addr_t gethost(void);
136
int rad_recv(char *, char *, u_char *);
137
void parse_challenge(auth_hdr_t *, char *, char *);
138
void rad_request(u_char, char *, char *, int, char *, char *);
139
void getsecret(void);
140
141
/*
142
 * challenge -- NULL for interactive service
143
 * password -- NULL for interactive service and when requesting a challenge
144
 */
145
int
146
raddauth(char *username, char *class, char *style, char *challenge,
147
    char *password, char **emsg)
148
{
149
	static char _pwstate[1024];
150
	u_char req_id;
151
	char *userstyle, *passwd, *pwstate, *rad_service;
152
	char pbuf[AUTH_PASS_LEN+1];
153
	int auth_port;
154
	char vector[AUTH_VECTOR_LEN+1], *p, *v;
155
	int i;
156
	login_cap_t *lc;
157
	u_int32_t r;
158
	struct servent *svp;
159
	struct sockaddr_in sin;
160
	struct sigaction sa;
161
	const char *errstr;
162
163
	memset(_pwstate, 0, sizeof(_pwstate));
164
	pwstate = password ? challenge : _pwstate;
165
166
	if ((lc = login_getclass(class)) == NULL) {
167
		snprintf(_pwstate, sizeof(_pwstate),
168
		    "%s: no such class", class);
169
		*emsg = _pwstate;
170
		return (1);
171
	}
172
173
	rad_service = login_getcapstr(lc, "radius-port", "radius", "radius");
174
	timeout = login_getcapnum(lc, "radius-timeout", 2, 2);
175
	retries = login_getcapnum(lc, "radius-retries", 6, 6);
176
177
	if (timeout < 1)
178
		timeout = 1;
179
	if (retries < 2)
180
		retries = 2;
181
182
	if (challenge == NULL) {
183
		passwd = NULL;
184
		v = login_getcapstr(lc, "radius-challenge-styles",
185
		    NULL, NULL);
186
		i = strlen(style);
187
		while (v && (p = strstr(v, style)) != NULL) {
188
			if ((p == v || p[-1] == ',') &&
189
			    (p[i] == ',' || p[i] == '\0')) {
190
				passwd = "";
191
				break;
192
			}
193
			v = p+1;
194
		}
195
		if (passwd == NULL)
196
			passwd = readpassphrase("Password:", pbuf, sizeof(pbuf),
197
			    RPP_ECHO_OFF);
198
	} else
199
		passwd = password;
200
	if (passwd == NULL)
201
		passwd = "";
202
203
	if ((v = login_getcapstr(lc, "radius-server", NULL, NULL)) == NULL){
204
		*emsg = "radius-server not configured";
205
		return (1);
206
	}
207
208
	auth_server = get_ipaddr(v);
209
210
	if ((v = login_getcapstr(lc, "radius-server-alt", NULL, NULL)) == NULL)
211
		alt_server = 0;
212
	else {
213
		alt_server = get_ipaddr(v);
214
		alt_retries = retries/2;
215
		retries >>= 1;
216
	}
217
218
	/* get port number */
219
	radius_port = strtonum(rad_service, 1, UINT16_MAX, &errstr);
220
	if (errstr) {
221
		svp = getservbyname(rad_service, "udp");
222
		if (svp == NULL) {
223
			snprintf(_pwstate, sizeof(_pwstate),
224
			    "No such service: %s/udp", rad_service);
225
			*emsg = _pwstate;
226
			return (1);
227
		}
228
		radius_port = svp->s_port;
229
	} else
230
		radius_port = htons(radius_port);
231
232
	/* get the secret from the servers file */
233
	getsecret();
234
235
	/* set up socket */
236
	if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
237
		snprintf(_pwstate, sizeof(_pwstate), "%s", strerror(errno));
238
		*emsg = _pwstate;
239
		return (1);
240
	}
241
242
	/* set up client structure */
243
	memset(&sin, 0, sizeof(sin));
244
	sin.sin_family = AF_INET;
245
	sin.sin_addr.s_addr = INADDR_ANY;
246
	sin.sin_port = radius_port;
247
248
	req_id = (u_char) arc4random();
249
	auth_port = ttyslot();
250
	if (auth_port == 0)
251
		auth_port = (int)getppid();
252
	if (strcmp(style, "radius") != 0) {
253
		if (asprintf(&userstyle, "%s:%s", username, style) == -1)
254
			err(1, NULL);
255
	} else
256
		userstyle = username;
257
258
	/* generate random vector */
259
	for (i = 0; i < AUTH_VECTOR_LEN;) {
260
		r = arc4random();
261
		memcpy(&vector[i], &r, sizeof(r));
262
		i += sizeof(r);
263
	}
264
	vector[AUTH_VECTOR_LEN] = '\0';
265
266
	sigemptyset(&sa.sa_mask);
267
	sa.sa_handler = servtimeout;
268
	sa.sa_flags = 0;		/* don't restart system calls */
269
	(void)sigaction(SIGALRM, &sa, NULL);
270
retry:
271
	if (timedout) {
272
		timedout = 0;
273
		if (--retries <= 0) {
274
			/*
275
			 * If we ran out of tries but there is an alternate
276
			 * server, switch to it and try again.
277
			 */
278
			if (alt_retries) {
279
				auth_server = alt_server;
280
				retries = alt_retries;
281
				alt_retries = 0;
282
				getsecret();
283
			} else
284
				warnx("no response from authentication server");
285
		}
286
	}
287
288
	if (retries > 0) {
289
		rad_request(req_id, userstyle, passwd, auth_port, vector,
290
		    pwstate);
291
292
		switch (i = rad_recv(_pwstate, challenge, vector)) {
293
		case PW_AUTHENTICATION_ACK:
294
			/*
295
			 * Make sure we don't think a challenge was issued.
296
			 */
297
			if (challenge)
298
				*challenge = '\0';
299
			return (0);
300
301
		case PW_AUTHENTICATION_REJECT:
302
			return (1);
303
304
		case PW_ACCESS_CHALLENGE:
305
			/*
306
			 * If this is a response then reject them if
307
			 * we got a challenge.
308
			 */
309
			if (password)
310
				return (1);
311
			/*
312
			 * If we wanted a challenge, just return
313
			 */
314
			if (challenge) {
315
				if (strcmp(challenge, _pwstate) != 0)
316
					syslog(LOG_WARNING,
317
				    "challenge for %s does not match state",
318
				    userstyle);
319
				return (0);
320
			}
321
			req_id++;
322
			if ((passwd = readpassphrase("", pbuf, sizeof(pbuf),
323
				    RPP_ECHO_OFF)) == NULL)
324
				passwd = "";
325
			break;
326
327
		default:
328
			if (timedout)
329
				goto retry;
330
			snprintf(_pwstate, sizeof(_pwstate),
331
			    "invalid response type %d\n", i);
332
			*emsg = _pwstate;
333
			return (1);
334
		}
335
	}
336
	return (1);
337
}
338
339
/*
340
 * Build a radius authentication digest and submit it to the radius server
341
 */
342
void
343
rad_request(u_char id, char *name, char *password, int port, char *vector,
344
    char *state)
345
{
346
	auth_hdr_t auth;
347
	int i, len, secretlen, total_length, p;
348
	struct sockaddr_in sin;
349
	u_char md5buf[MAXSECRETLEN+AUTH_VECTOR_LEN], digest[AUTH_VECTOR_LEN],
350
	    pass_buf[AUTH_PASS_LEN], *pw, *ptr;
351
	u_int length;
352
	in_addr_t ipaddr;
353
	MD5_CTX context;
354
355
	memset(&auth, 0, sizeof(auth));
356
	auth.code = PW_AUTHENTICATION_REQUEST;
357
	auth.id = id;
358
	memcpy(auth.vector, vector, AUTH_VECTOR_LEN);
359
	total_length = AUTH_HDR_LEN;
360
	ptr = auth.data;
361
362
	/* User name */
363
	*ptr++ = PW_USER_NAME;
364
	length = strlen(name);
365
	if (length > MAXPWNETNAM)
366
		length = MAXPWNETNAM;
367
	*ptr++ = length + 2;
368
	memcpy(ptr, name, length);
369
	ptr += length;
370
	total_length += length + 2;
371
372
	/* Password */
373
	length = strlen(password);
374
	if (length > AUTH_PASS_LEN)
375
		length = AUTH_PASS_LEN;
376
377
	p = (length + AUTH_VECTOR_LEN - 1) / AUTH_VECTOR_LEN;
378
	*ptr++ = PW_PASSWORD;
379
	*ptr++ = p * AUTH_VECTOR_LEN + 2;
380
381
	memset(pass_buf, 0, sizeof(pass_buf));		/* must zero fill */
382
	strlcpy((char *)pass_buf, password, sizeof(pass_buf));
383
384
	/* Calculate the md5 digest */
385
	secretlen = strlen(auth_secret);
386
	memcpy(md5buf, auth_secret, secretlen);
387
	memcpy(md5buf + secretlen, auth.vector, AUTH_VECTOR_LEN);
388
389
	total_length += 2;
390
391
	/* XOR the password into the md5 digest */
392
	pw = pass_buf;
393
	while (p-- > 0) {
394
		MD5Init(&context);
395
		MD5Update(&context, md5buf, secretlen + AUTH_VECTOR_LEN);
396
		MD5Final(digest, &context);
397
		for (i = 0; i < AUTH_VECTOR_LEN; ++i) {
398
			*ptr = digest[i] ^ *pw;
399
			md5buf[secretlen+i] = *ptr++;
400
			*pw++ = '\0';
401
		}
402
		total_length += AUTH_VECTOR_LEN;
403
	}
404
	explicit_bzero(pass_buf, strlen(pass_buf));
405
406
	/* Client id */
407
	*ptr++ = PW_CLIENT_ID;
408
	*ptr++ = sizeof(in_addr_t) + 2;
409
	ipaddr = gethost();
410
	memcpy(ptr, &ipaddr, sizeof(in_addr_t));
411
	ptr += sizeof(in_addr_t);
412
	total_length += sizeof(in_addr_t) + 2;
413
414
	/* client port */
415
	*ptr++ = PW_CLIENT_PORT_ID;
416
	*ptr++ = sizeof(in_addr_t) + 2;
417
	port = htonl(port);
418
	memcpy(ptr, &port, sizeof(int));
419
	ptr += sizeof(int);
420
	total_length += sizeof(int) + 2;
421
422
	/* Append the state info */
423
	if ((state != NULL) && (strlen(state) > 0)) {
424
		len = strlen(state);
425
		*ptr++ = PW_STATE;
426
		*ptr++ = len + 2;
427
		memcpy(ptr, state, len);
428
		ptr += len;
429
		total_length += len + 2;
430
	}
431
432
	auth.length = htons(total_length);
433
434
	memset(&sin, 0, sizeof (sin));
435
	sin.sin_family = AF_INET;
436
	sin.sin_addr.s_addr = auth_server;
437
	sin.sin_port = radius_port;
438
	if (sendto(sockfd, &auth, total_length, 0, (struct sockaddr *)&sin,
439
	    sizeof(sin)) == -1)
440
		err(1, NULL);
441
}
442
443
/*
444
 * Receive UDP responses from the radius server
445
 */
446
int
447
rad_recv(char *state, char *challenge, u_char *req_vector)
448
{
449
	auth_hdr_t auth;
450
	socklen_t salen;
451
	struct sockaddr_in sin;
452
	u_char recv_vector[AUTH_VECTOR_LEN], test_vector[AUTH_VECTOR_LEN];
453
	MD5_CTX context;
454
455
	salen = sizeof(sin);
456
457
	alarm(timeout);
458
	if ((recvfrom(sockfd, &auth, sizeof(auth), 0,
459
	    (struct sockaddr *)&sin, &salen)) < AUTH_HDR_LEN) {
460
		if (timedout)
461
			return(-1);
462
		errx(1, "bogus auth packet from server");
463
	}
464
	alarm(0);
465
466
	if (sin.sin_addr.s_addr != auth_server)
467
		errx(1, "bogus authentication server");
468
469
	/* verify server's shared secret */
470
	memcpy(recv_vector, auth.vector, AUTH_VECTOR_LEN);
471
	memcpy(auth.vector, req_vector, AUTH_VECTOR_LEN);
472
	MD5Init(&context);
473
	MD5Update(&context, (u_char *)&auth, ntohs(auth.length));
474
	MD5Update(&context, auth_secret, strlen(auth_secret));
475
	MD5Final(test_vector, &context);
476
	if (memcmp(recv_vector, test_vector, AUTH_VECTOR_LEN) != 0)
477
		errx(1, "shared secret incorrect");
478
479
	if (auth.code == PW_ACCESS_CHALLENGE)
480
		parse_challenge(&auth, state, challenge);
481
482
	return (auth.code);
483
}
484
485
/*
486
 * Get IP address of local hostname
487
 */
488
in_addr_t
489
gethost(void)
490
{
491
	char hostname[HOST_NAME_MAX+1];
492
493
	if (gethostname(hostname, sizeof(hostname)))
494
		err(1, "gethost");
495
	return (get_ipaddr(hostname));
496
}
497
498
/*
499
 * Get an IP address in host in_addr_t notation from a hostname or dotted quad.
500
 */
501
in_addr_t
502
get_ipaddr(char *host)
503
{
504
	struct hostent *hp;
505
506
	if ((hp = gethostbyname(host)) == NULL)
507
		return (0);
508
509
	return (((struct in_addr *)hp->h_addr)->s_addr);
510
}
511
512
/*
513
 * Get the secret from the servers file
514
 */
515
void
516
getsecret(void)
517
{
518
	FILE *servfd;
519
	char *host, *secret, buffer[PATH_MAX];
520
	size_t len;
521
522
	snprintf(buffer, sizeof(buffer), "%s/%s",
523
	    radius_dir, RADIUS_SERVERS);
524
525
	if ((servfd = fopen(buffer, "r")) == NULL) {
526
		syslog(LOG_ERR, "%s: %m", buffer);
527
		return;
528
	}
529
530
	secret = NULL;			/* Keeps gcc happy */
531
	while ((host = fgetln(servfd, &len)) != NULL) {
532
		if (*host == '#') {
533
			memset(host, 0, len);
534
			continue;
535
		}
536
		if (host[len-1] == '\n')
537
			--len;
538
		else {
539
			/* No trailing newline, must allocate len+1 for NUL */
540
			if ((secret = malloc(len + 1)) == NULL) {
541
				memset(host, 0, len);
542
				continue;
543
			}
544
			memcpy(secret, host, len);
545
			memset(host, 0, len);
546
			host = secret;
547
		}
548
		while (len > 0 && isspace((unsigned char)host[--len]))
549
			;
550
		host[++len] = '\0';
551
		while (isspace((unsigned char)*host)) {
552
			++host;
553
			--len;
554
		}
555
		if (*host == '\0')
556
			continue;
557
		secret = host;
558
		while (*secret && !isspace((unsigned char)*secret))
559
			++secret;
560
		if (*secret)
561
			*secret++ = '\0';
562
		if (get_ipaddr(host) != auth_server) {
563
			memset(host, 0, len);
564
			continue;
565
		}
566
		while (isspace((unsigned char)*secret))
567
			++secret;
568
		if (*secret)
569
			break;
570
	}
571
	if (host) {
572
		strlcpy(auth_secret, secret, sizeof(auth_secret));
573
		memset(host, 0, len);
574
	}
575
	fclose(servfd);
576
}
577
578
void
579
servtimeout(int signo)
580
{
581
582
	timedout = 1;
583
}
584
585
/*
586
 * Parse a challenge received from the server
587
 */
588
void
589
parse_challenge(auth_hdr_t *authhdr, char *state, char *challenge)
590
{
591
	int length;
592
	int attribute, attribute_len;
593
	u_char *ptr;
594
595
	ptr = authhdr->data;
596
	length = ntohs(authhdr->length) - AUTH_HDR_LEN;
597
598
	*state = 0;
599
600
	while (length > 0) {
601
		attribute = *ptr++;
602
		attribute_len = *ptr++;
603
		length -= attribute_len;
604
		attribute_len -= 2;
605
606
		switch (attribute) {
607
		case PW_PORT_MESSAGE:
608
			if (challenge) {
609
				memcpy(challenge, ptr, attribute_len);
610
				challenge[attribute_len] = '\0';
611
			} else
612
				printf("%.*s", attribute_len, ptr);
613
			break;
614
		case PW_STATE:
615
			memcpy(state, ptr, attribute_len);
616
			state[attribute_len] = '\0';
617
			break;
618
		}
619
		ptr += attribute_len;
620
	}
621
}