GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/radiusctl/radiusctl.c Lines: 0 220 0.0 %
Date: 2017-11-13 Branches: 0 91 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: radiusctl.c,v 1.6 2015/12/31 16:22:27 millert Exp $	*/
2
/*
3
 * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
#include <sys/types.h>
18
#include <sys/socket.h>
19
#include <netinet/in.h>
20
21
#include <arpa/inet.h>
22
#include <err.h>
23
#include <md5.h>
24
#include <netdb.h>
25
#include <stdbool.h>
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <string.h>
29
#include <unistd.h>
30
31
#include <radius.h>
32
33
#include "parser.h"
34
#include "chap_ms.h"
35
36
37
static void		 radius_test (struct parse_result *);
38
static void		 radius_dump (FILE *, RADIUS_PACKET *, bool,
39
			    const char *);
40
static const char	*radius_code_str (int code);
41
static const char	*hexstr(const u_char *, int, char *, int);
42
43
static void
44
usage(void)
45
{
46
	extern char *__progname;
47
48
	fprintf(stderr, "usage: %s command [argument ...]\n", __progname);
49
}
50
51
int
52
main(int argc, char *argv[])
53
{
54
	int			 ch;
55
	struct parse_result	*result;
56
57
	while ((ch = getopt(argc, argv, "")) != -1)
58
		switch (ch) {
59
		default:
60
			usage();
61
			return (EXIT_FAILURE);
62
		}
63
	argc -= optind;
64
	argv += optind;
65
66
	if ((result = parse(argc, argv)) == NULL)
67
		return (EXIT_FAILURE);
68
69
	switch (result->action) {
70
	case NONE:
71
		break;
72
	case TEST:
73
		if (pledge("stdio dns inet flock rpath cpath wpath", NULL) == -1)
74
			err(EXIT_FAILURE, "pledge");
75
		radius_test(result);
76
		break;
77
	}
78
79
	return (EXIT_SUCCESS);
80
}
81
82
static void
83
radius_test(struct parse_result *res)
84
{
85
	struct addrinfo		 hints, *ai;
86
	int			 sock, retval;
87
	struct sockaddr_storage	 sockaddr;
88
	socklen_t		 sockaddrlen;
89
	RADIUS_PACKET		*reqpkt, *respkt;
90
	struct sockaddr_in	*sin4;
91
	struct sockaddr_in6	*sin6;
92
	uint32_t		 u32val;
93
	uint8_t			 id;
94
95
	reqpkt = radius_new_request_packet(RADIUS_CODE_ACCESS_REQUEST);
96
	if (reqpkt == NULL)
97
		err(1, "radius_new_request_packet");
98
	id = arc4random();
99
	radius_set_id(reqpkt, id);
100
101
	memset(&hints, 0, sizeof(hints));
102
	hints.ai_family = PF_UNSPEC;
103
	hints.ai_socktype = SOCK_DGRAM;
104
105
	retval = getaddrinfo(res->hostname, "radius", &hints, &ai);
106
	if (retval)
107
		errx(1, "%s %s", res->hostname, gai_strerror(retval));
108
109
	if (res->port != 0)
110
		((struct sockaddr_in *)ai->ai_addr)->sin_port =
111
		    htons(res->port);
112
113
	sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
114
	if (sock == -1)
115
		err(1, "socket");
116
117
	/* Prepare NAS-IP{,V6}-ADDRESS attribute */
118
	if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1)
119
		err(1, "connect");
120
	sockaddrlen = sizeof(sockaddr);
121
	if (getsockname(sock, (struct sockaddr *)&sockaddr, &sockaddrlen) == -1)
122
		err(1, "getsockname");
123
	sin4 = (struct sockaddr_in *)&sockaddr;
124
	sin6 = (struct sockaddr_in6 *)&sockaddr;
125
	switch (sockaddr.ss_family) {
126
	case AF_INET:
127
		radius_put_ipv4_attr(reqpkt, RADIUS_TYPE_NAS_IP_ADDRESS,
128
		    sin4->sin_addr);
129
		break;
130
	case AF_INET6:
131
		radius_put_raw_attr(reqpkt, RADIUS_TYPE_NAS_IPV6_ADDRESS,
132
		    sin6->sin6_addr.s6_addr, sizeof(sin6->sin6_addr.s6_addr));
133
		break;
134
	}
135
136
	/* User-Name and User-Password */
137
	radius_put_string_attr(reqpkt, RADIUS_TYPE_USER_NAME,
138
	    res->username);
139
140
	switch (res->auth_method) {
141
	case PAP:
142
		if (res->password != NULL)
143
			radius_put_user_password_attr(reqpkt, res->password,
144
			    res->secret);
145
		break;
146
	case CHAP:
147
	    {
148
		u_char	 chal[16];
149
		u_char	 resp[1 + MD5_DIGEST_LENGTH]; /* "1 + " for CHAP Id */
150
		MD5_CTX	 md5ctx;
151
152
		arc4random_buf(resp, 1);	/* CHAP Id is random */
153
		MD5Init(&md5ctx);
154
		MD5Update(&md5ctx, resp, 1);
155
		if (res->password != NULL)
156
			MD5Update(&md5ctx, res->password,
157
			    strlen(res->password));
158
		MD5Update(&md5ctx, chal, sizeof(chal));
159
		MD5Final(resp + 1, &md5ctx);
160
		radius_put_raw_attr(reqpkt, RADIUS_TYPE_CHAP_CHALLENGE,
161
		    chal, sizeof(chal));
162
		radius_put_raw_attr(reqpkt, RADIUS_TYPE_CHAP_PASSWORD,
163
		    resp, sizeof(resp));
164
	    }
165
		break;
166
	case MSCHAPV2:
167
	    {
168
		u_char	pass[256], chal[16];
169
		u_int	i, lpass;
170
		struct _resp {
171
			u_int8_t ident;
172
			u_int8_t flags;
173
			char peer_challenge[16];
174
			char reserved[8];
175
			char response[24];
176
		} __packed resp;
177
178
		if (res->password == NULL) {
179
			lpass = 0;
180
		} else {
181
			lpass = strlen(res->password);
182
			if (lpass * 2 >= sizeof(pass))
183
				err(1, "password too long");
184
			for (i = 0; i < lpass; i++) {
185
				pass[i * 2] = res->password[i];
186
				pass[i * 2 + 1] = 0;
187
			}
188
		}
189
190
		memset(&resp, 0, sizeof(resp));
191
		resp.ident = arc4random();
192
		arc4random_buf(chal, sizeof(chal));
193
		arc4random_buf(resp.peer_challenge,
194
		    sizeof(resp.peer_challenge));
195
196
		mschap_nt_response(chal, resp.peer_challenge,
197
		    (char *)res->username, strlen(res->username), pass,
198
		    lpass * 2, resp.response);
199
200
		radius_put_vs_raw_attr(reqpkt, RADIUS_VENDOR_MICROSOFT,
201
		    RADIUS_VTYPE_MS_CHAP_CHALLENGE, chal, sizeof(chal));
202
		radius_put_vs_raw_attr(reqpkt, RADIUS_VENDOR_MICROSOFT,
203
		    RADIUS_VTYPE_MS_CHAP2_RESPONSE, &resp, sizeof(resp));
204
		explicit_bzero(pass, sizeof(pass));
205
	    }
206
		break;
207
208
	}
209
	u32val = htonl(res->nas_port);
210
	radius_put_raw_attr(reqpkt, RADIUS_TYPE_NAS_PORT, &u32val, 4);
211
212
	radius_put_message_authenticator(reqpkt, res->secret);
213
214
	/* Send! */
215
	fprintf(stderr, "Sending:\n");
216
	radius_dump(stdout, reqpkt, false, res->secret);
217
	if (send(sock, radius_get_data(reqpkt), radius_get_length(reqpkt), 0)
218
	    == -1)
219
		warn("send");
220
	if ((respkt = radius_recv(sock, 0)) == NULL)
221
		warn("recv");
222
	else {
223
		radius_set_request_packet(respkt, reqpkt);
224
		fprintf(stderr, "\nReceived:\n");
225
		radius_dump(stdout, respkt, true, res->secret);
226
	}
227
228
	/* Release the resources */
229
	radius_delete_packet(reqpkt);
230
	if (respkt)
231
		radius_delete_packet(respkt);
232
	close(sock);
233
	freeaddrinfo(ai);
234
235
	explicit_bzero((char *)res->secret, strlen(res->secret));
236
	if (res->password)
237
		explicit_bzero((char *)res->password, strlen(res->password));
238
239
	return;
240
}
241
242
static void
243
radius_dump(FILE *out, RADIUS_PACKET *pkt, bool resp, const char *secret)
244
{
245
	size_t		 len;
246
	char		 buf[256], buf1[256];
247
	uint32_t	 u32val;
248
	struct in_addr	 ipv4;
249
250
	fprintf(out,
251
	    "    Id                        = %d\n"
252
	    "    Code                      = %s(%d)\n",
253
	    (int)radius_get_id(pkt), radius_code_str((int)radius_get_code(pkt)),
254
	    (int)radius_get_code(pkt));
255
	if (resp && secret)
256
		fprintf(out, "    Message-Authenticator     = %s\n",
257
		    (radius_check_response_authenticator(pkt, secret) == 0)
258
		    ? "Verified" : "NG");
259
260
	if (radius_get_string_attr(pkt, RADIUS_TYPE_USER_NAME, buf,
261
	    sizeof(buf)) == 0)
262
		fprintf(out, "    User-Name                 = \"%s\"\n", buf);
263
264
	if (secret &&
265
	    radius_get_user_password_attr(pkt, buf, sizeof(buf), secret) == 0)
266
		fprintf(out, "    User-Password             = \"%s\"\n", buf);
267
268
	memset(buf, 0, sizeof(buf));
269
	len = sizeof(buf);
270
	if (radius_get_raw_attr(pkt, RADIUS_TYPE_CHAP_PASSWORD, buf, &len)
271
	    == 0)
272
		fprintf(out, "    CHAP-Password             = %s\n",
273
		    (hexstr(buf, len, buf1, sizeof(buf1)))
274
			    ? buf1 : "(too long)");
275
276
	memset(buf, 0, sizeof(buf));
277
	len = sizeof(buf);
278
	if (radius_get_raw_attr(pkt, RADIUS_TYPE_CHAP_CHALLENGE, buf, &len)
279
	    == 0)
280
		fprintf(out, "    CHAP-Challenge            = %s\n",
281
		    (hexstr(buf, len, buf1, sizeof(buf1)))
282
			? buf1 : "(too long)");
283
284
	memset(buf, 0, sizeof(buf));
285
	len = sizeof(buf);
286
	if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT,
287
	    RADIUS_VTYPE_MS_CHAP_CHALLENGE, buf, &len) == 0)
288
		fprintf(out, "    MS-CHAP-Challenge         = %s\n",
289
		    (hexstr(buf, len, buf1, sizeof(buf1)))
290
			? buf1 : "(too long)");
291
292
	memset(buf, 0, sizeof(buf));
293
	len = sizeof(buf);
294
	if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT,
295
	    RADIUS_VTYPE_MS_CHAP2_RESPONSE, buf, &len) == 0)
296
		fprintf(out, "    MS-CHAP2-Response         = %s\n",
297
		    (hexstr(buf, len, buf1, sizeof(buf1)))
298
		    ? buf1 : "(too long)");
299
300
	memset(buf, 0, sizeof(buf));
301
	len = sizeof(buf) - 1;
302
	if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT,
303
	    RADIUS_VTYPE_MS_CHAP2_SUCCESS, buf, &len) == 0) {
304
		fprintf(out, "    MS-CHAP-Success           = Id=%u \"%s\"\n",
305
		    (u_int)(u_char)buf[0], buf + 1);
306
	}
307
308
	memset(buf, 0, sizeof(buf));
309
	len = sizeof(buf) - 1;
310
	if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT,
311
	    RADIUS_VTYPE_MS_CHAP_ERROR, buf, &len) == 0) {
312
		fprintf(out, "    MS-CHAP-Error             = Id=%u \"%s\"\n",
313
		    (u_int)(u_char)buf[0], buf + 1);
314
	}
315
316
	memset(buf, 0, sizeof(buf));
317
	len = sizeof(buf);
318
	if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT,
319
	    RADIUS_VTYPE_MPPE_SEND_KEY, buf, &len) == 0)
320
		fprintf(out, "    MS-MPPE-Send-Key          = %s\n",
321
		    (hexstr(buf, len, buf1, sizeof(buf1)))
322
		    ? buf1 : "(too long)");
323
324
	memset(buf, 0, sizeof(buf));
325
	len = sizeof(buf);
326
	if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT,
327
	    RADIUS_VTYPE_MPPE_RECV_KEY, buf, &len) == 0)
328
		fprintf(out, "    MS-MPPE-Recv-Key          = %s\n",
329
		    (hexstr(buf, len, buf1, sizeof(buf1)))
330
		    ? buf1 : "(too long)");
331
332
	memset(buf, 0, sizeof(buf));
333
	len = sizeof(buf);
334
	if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT,
335
	    RADIUS_VTYPE_MPPE_ENCRYPTION_POLICY, buf, &len) == 0)
336
		fprintf(out, "    MS-MPPE-Encryption-Policy = 0x%08x\n",
337
		    ntohl(*(u_long *)buf));
338
339
340
	memset(buf, 0, sizeof(buf));
341
	len = sizeof(buf);
342
	if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT,
343
	    RADIUS_VTYPE_MPPE_ENCRYPTION_TYPES, buf, &len) == 0)
344
		fprintf(out, "    MS-MPPE-Encryption-Types  = 0x%08x\n",
345
		    ntohl(*(u_long *)buf));
346
347
	if (radius_get_string_attr(pkt, RADIUS_TYPE_REPLY_MESSAGE, buf,
348
	    sizeof(buf)) == 0)
349
		fprintf(out, "    Reply-Message             = \"%s\"\n", buf);
350
351
	memset(buf, 0, sizeof(buf));
352
	len = sizeof(buf);
353
	if (radius_get_uint32_attr(pkt, RADIUS_TYPE_NAS_PORT, &u32val) == 0)
354
		fprintf(out, "    NAS-Port                  = %lu\n",
355
		    (u_long)u32val);
356
357
	memset(buf, 0, sizeof(buf));
358
	len = sizeof(buf);
359
	if (radius_get_ipv4_attr(pkt, RADIUS_TYPE_NAS_IP_ADDRESS, &ipv4) == 0)
360
		fprintf(out, "    NAS-IP-Address            = %s\n",
361
		    inet_ntoa(ipv4));
362
363
	memset(buf, 0, sizeof(buf));
364
	len = sizeof(buf);
365
	if (radius_get_raw_attr(pkt, RADIUS_TYPE_NAS_IPV6_ADDRESS, buf, &len)
366
	    == 0)
367
		fprintf(out, "    NAS-IPv6-Address          = %s\n",
368
		    inet_ntop(AF_INET6, buf, buf1, len));
369
370
}
371
372
static const char *
373
radius_code_str(int code)
374
{
375
	int i;
376
	static struct _codestr {
377
		int		 code;
378
		const char	*str;
379
	} codestr[] = {
380
	    { RADIUS_CODE_ACCESS_REQUEST,	"Access-Request" },
381
	    { RADIUS_CODE_ACCESS_ACCEPT,	"Access-Accept" },
382
	    { RADIUS_CODE_ACCESS_REJECT,	"Access-Reject" },
383
	    { RADIUS_CODE_ACCOUNTING_REQUEST,	"Accounting-Request" },
384
	    { RADIUS_CODE_ACCOUNTING_RESPONSE,	"Accounting-Response" },
385
	    { RADIUS_CODE_ACCESS_CHALLENGE,	"Access-Challenge" },
386
	    { RADIUS_CODE_STATUS_SERVER,	"Status-Server" },
387
	    { RADIUS_CODE_STATUS_CLIENT,	"Status-Client" },
388
	    { -1, NULL }
389
	};
390
391
	for (i = 0; codestr[i].code != -1; i++) {
392
		if (codestr[i].code == code)
393
			return (codestr[i].str);
394
	}
395
396
	return ("Unknown");
397
}
398
399
static const char *
400
hexstr(const u_char *data, int len, char *str, int strsiz)
401
{
402
	int			 i, off = 0;
403
	static const char	 hex[] = "0123456789abcdef";
404
405
	for (i = 0; i < len; i++) {
406
		if (strsiz - off < 3)
407
			return (NULL);
408
		str[off++] = hex[(data[i] & 0xf0) >> 4];
409
		str[off++] = hex[(data[i] & 0x0f)];
410
		str[off++] = ' ';
411
	}
412
	if (strsiz - off < 1)
413
		return (NULL);
414
415
	str[off++] = '\0';
416
417
	return (str);
418
}