GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/npppd/npppd/radius_req.c Lines: 0 181 0.0 %
Date: 2017-11-13 Branches: 0 115 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: radius_req.c,v 1.11 2015/12/05 18:43:36 mmcc Exp $ */
2
3
/*-
4
 * Copyright (c) 2009 Internet Initiative Japan Inc.
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 */
28
/**@file
29
 * This file provides functions for RADIUS request using radius(3) and event(3).
30
 * @author	Yasuoka Masahiko
31
 * $Id: radius_req.c,v 1.11 2015/12/05 18:43:36 mmcc Exp $
32
 */
33
#include <sys/types.h>
34
#include <sys/time.h>
35
#include <sys/socket.h>
36
#include <netinet/in.h>
37
#include <unistd.h>
38
#include <stdio.h>
39
#include <syslog.h>
40
#include <stdlib.h>
41
#include <debugutil.h>
42
#include <time.h>
43
#include <event.h>
44
#include <string.h>
45
#include <errno.h>
46
47
#include "radius_req.h"
48
#include <radius.h>
49
50
#ifndef nitems
51
#define	nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
52
#endif
53
54
struct overlapped {
55
	struct event		 ev_sock;
56
	int			 socket;
57
	int			 ntry;
58
	int			 max_tries;
59
	int			 failovers;
60
	int			 acct_delay_time;
61
	int			 response_fn_calling;
62
	struct sockaddr_storage	 ss;
63
	struct timespec		 req_time;
64
	void			*context;
65
	radius_response		*response_fn;
66
	char			 secret[MAX_RADIUS_SECRET];
67
	RADIUS_PACKET		*pkt;
68
	radius_req_setting	*setting;
69
};
70
71
static int   radius_request0 (struct overlapped *, int);
72
static int   radius_prepare_socket(struct overlapped *);
73
static void  radius_request_io_event (int, short, void *);
74
static void  radius_on_response(RADIUS_REQUEST_CTX, RADIUS_PACKET *, int, int);
75
static int   select_srcaddr(struct sockaddr const *, struct sockaddr *, socklen_t *);
76
static void  radius_req_setting_ref(radius_req_setting *);
77
static void  radius_req_setting_unref(radius_req_setting *);
78
79
#ifdef	RADIUS_REQ_DEBUG
80
#define RADIUS_REQ_DBG(x)	log_printf x
81
#define	RADIUS_REQ_ASSERT(cond)					\
82
	if (!(cond)) {						\
83
	    fprintf(stderr,					\
84
		"\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\
85
		, __func__, __FILE__, __LINE__);		\
86
	    abort(); 						\
87
	}
88
#else
89
#define	RADIUS_REQ_ASSERT(cond)
90
#define RADIUS_REQ_DBG(x)
91
#endif
92
93
/**
94
 * Send RADIUS request message.  The pkt(RADIUS packet) will be released
95
 * by this implementation.
96
 */
97
void
98
radius_request(RADIUS_REQUEST_CTX ctx, RADIUS_PACKET *pkt)
99
{
100
	uint32_t ival;
101
	struct overlapped *lap;
102
103
	RADIUS_REQ_ASSERT(pkt != NULL);
104
	RADIUS_REQ_ASSERT(ctx != NULL);
105
	lap = ctx;
106
	lap->pkt = pkt;
107
	if (radius_get_uint32_attr(pkt, RADIUS_TYPE_ACCT_DELAY_TIME, &ival)
108
	    == 0)
109
		lap->acct_delay_time = 1;
110
	radius_request0(lap, 0);
111
}
112
113
/**
114
 * Prepare NAS-IP-Address or NAS-IPv6-Address.  If
115
 * setting->server[setting->curr_server].sock is not initialized, address
116
 * will be selected automatically.
117
 */
118
int
119
radius_prepare_nas_address(radius_req_setting *setting,
120
    RADIUS_PACKET *pkt)
121
{
122
	int af;
123
	struct sockaddr_in *sin4;
124
	struct sockaddr_in6 *sin6;
125
	socklen_t socklen;
126
127
	/* See RFC 2765, 3162 */
128
	RADIUS_REQ_ASSERT(setting != NULL);
129
130
	af = setting->server[setting->curr_server].peer.sin6.sin6_family;
131
	RADIUS_REQ_ASSERT(af == AF_INET6 || af == AF_INET);
132
133
	sin4 = &setting->server[setting->curr_server].sock.sin4;
134
	sin6 = &setting->server[setting->curr_server].sock.sin6;
135
136
	switch (af) {
137
	case AF_INET:
138
		socklen = sizeof(*sin4);
139
		if (sin4->sin_addr.s_addr == INADDR_ANY) {
140
			if (select_srcaddr((struct sockaddr const *)
141
			    &setting->server[setting->curr_server].peer,
142
			    (struct sockaddr *)sin4, &socklen) != 0) {
143
				RADIUS_REQ_ASSERT("NOTREACHED" == NULL);
144
				goto fail;
145
			}
146
		}
147
		if (radius_put_ipv4_attr(pkt, RADIUS_TYPE_NAS_IP_ADDRESS,
148
		    sin4->sin_addr) != 0)
149
			goto fail;
150
		break;
151
	case AF_INET6:
152
		socklen = sizeof(*sin6);
153
		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
154
			if (select_srcaddr((struct sockaddr const *)
155
			    &setting->server[setting->curr_server].peer,
156
			    (struct sockaddr *)sin4, &socklen) != 0) {
157
				RADIUS_REQ_ASSERT("NOTREACHED" == NULL);
158
				goto fail;
159
			}
160
		}
161
		if (radius_put_raw_attr(pkt, RADIUS_TYPE_NAS_IPV6_ADDRESS,
162
		    sin6->sin6_addr.s6_addr, sizeof(sin6->sin6_addr.s6_addr))
163
		    != 0)
164
			goto fail;
165
		break;
166
	}
167
168
	return 0;
169
fail:
170
	return 1;
171
}
172
173
174
/** Checks whether the request can fail over to another server */
175
int
176
radius_request_can_failover(RADIUS_REQUEST_CTX ctx)
177
{
178
	struct overlapped *lap;
179
	radius_req_setting *setting;
180
181
	lap = ctx;
182
	setting = lap->setting;
183
184
	if (lap->failovers >= setting->max_failovers)
185
		return 0;
186
	if (memcmp(&lap->ss, &setting->server[setting->curr_server].peer,
187
	    setting->server[setting->curr_server].peer.sin6.sin6_len) == 0)
188
		/* flagged server doesn't differ from the last server. */
189
		return 0;
190
191
	return 1;
192
}
193
194
/** Send RADIUS request failing over to another server. */
195
int
196
radius_request_failover(RADIUS_REQUEST_CTX ctx)
197
{
198
	struct overlapped *lap;
199
200
	lap = ctx;
201
	RADIUS_REQ_ASSERT(lap != NULL);
202
	RADIUS_REQ_ASSERT(lap->socket >= 0)
203
204
	if (!radius_request_can_failover(lap))
205
		return -1;
206
207
	if (radius_prepare_socket(lap) != 0)
208
		return -1;
209
210
	if (radius_request0(lap, 1) != 0)
211
		return -1;
212
213
	lap->failovers++;
214
215
	return 0;
216
}
217
218
static int
219
radius_prepare_socket(struct overlapped *lap)
220
{
221
	int sock;
222
	radius_req_setting *setting;
223
	struct sockaddr *sa;
224
225
	setting = lap->setting;
226
	if (lap->socket >= 0)
227
		close(lap->socket);
228
	lap->socket = -1;
229
230
	sa = (struct sockaddr *)&setting->server[setting->curr_server].peer;
231
232
	if ((sock = socket(sa->sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
233
		log_printf(LOG_ERR, "socket() failed in %s: %m", __func__);
234
		return -1;
235
	}
236
	if (connect(sock, sa, sa->sa_len) != 0) {
237
		log_printf(LOG_ERR, "connect() failed in %s: %m", __func__);
238
		close(sock);
239
		return -1;
240
	}
241
	memcpy(&lap->ss, sa, sa->sa_len);
242
	lap->socket = sock;
243
	memcpy(lap->secret, setting->server[setting->curr_server].secret,
244
	    sizeof(lap->secret));
245
	lap->ntry = lap->max_tries;
246
247
	return 0;
248
}
249
250
/**
251
 * Prepare sending RADIUS request.  This implementation will call back to
252
 * notice that it receives the response or it fails for timeouts to the
253
 * The context that is set as 'pctx' and response packet that is given
254
 * by the callback function will be released by this implementation internally.
255
 * @param setting	Setting for RADIUS server or request.
256
 * @param context	Context for the caller.
257
 * @param pctx		Pointer to the space for context of RADIUS request
258
 *			(RADIUS_REQUEST_CTX).  This will be used for canceling.
259
 *			NULL can be specified when you don't need.
260
 * @param response_fn	Specify callback function as a pointer. The function
261
 *			will be called when it receives a response or when
262
 *			request fails for timeouts.
263
 * @param timeout	response timeout in second.
264
 */
265
int
266
radius_prepare(radius_req_setting *setting, void *context,
267
    RADIUS_REQUEST_CTX *pctx, radius_response response_fn)
268
{
269
	struct overlapped *lap;
270
271
	RADIUS_REQ_ASSERT(setting != NULL);
272
	lap = NULL;
273
274
	if (setting->server[setting->curr_server].enabled == 0)
275
		return 1;
276
	if ((lap = calloc(1, sizeof(struct overlapped))) == NULL) {
277
		log_printf(LOG_ERR, "calloc() failed in %s: %m", __func__);
278
		goto fail;
279
	}
280
	lap->context = context;
281
	lap->response_fn = response_fn;
282
	lap->socket = -1;
283
	lap->setting = setting;
284
285
	lap->max_tries = setting->max_tries;
286
	if (lap->max_tries <= 0)
287
		lap->max_tries = 3;	/* default max tries */
288
289
	if (radius_prepare_socket(lap) != 0)
290
		goto fail;
291
292
	if (pctx != NULL)
293
		*pctx = lap;
294
295
	radius_req_setting_ref(setting);
296
297
	return 0;
298
fail:
299
	free(lap);
300
301
	return 1;
302
}
303
304
/**
305
 * Cancel the RADIUS request.
306
 * @param	The context received by {@link radius_request()}
307
 */
308
void
309
radius_cancel_request(RADIUS_REQUEST_CTX ctx)
310
{
311
	struct overlapped	*lap = ctx;
312
313
	/*
314
	 * Don't call this function from the callback function.
315
	 * The context will be freed after the callback function is called.
316
	 */
317
	RADIUS_REQ_ASSERT(lap->response_fn_calling == 0);
318
	if (lap->response_fn_calling != 0)
319
		return;
320
321
	if (lap->socket >= 0) {
322
		event_del(&lap->ev_sock);
323
		close(lap->socket);
324
		lap->socket = -1;
325
	}
326
	if (lap->pkt != NULL) {
327
		radius_delete_packet(lap->pkt);
328
		lap->pkt = NULL;
329
	}
330
	radius_req_setting_unref(lap->setting);
331
332
	memset(lap->secret, 0x41, sizeof(lap->secret));
333
334
	free(lap);
335
}
336
337
/** Return the shared secret for RADIUS server that is used by this context.  */
338
const char *
339
radius_get_server_secret(RADIUS_REQUEST_CTX ctx)
340
{
341
	struct overlapped *lap;
342
343
	lap = ctx;
344
	RADIUS_REQ_ASSERT(lap != NULL);
345
346
	return lap->secret;
347
}
348
349
/** Return the address of RADIUS server that is used by this context.  */
350
struct sockaddr *
351
radius_get_server_address(RADIUS_REQUEST_CTX ctx)
352
{
353
	struct overlapped *lap;
354
355
	lap = ctx;
356
	RADIUS_REQ_ASSERT(lap != NULL);
357
358
	return (struct sockaddr *)&lap->ss;
359
}
360
361
static int
362
radius_request0(struct overlapped *lap, int new_message)
363
{
364
	struct timeval tv0;
365
366
	RADIUS_REQ_ASSERT(lap->ntry > 0);
367
368
	if (lap->acct_delay_time != 0) {
369
		struct timespec curr, delta;
370
371
		if (clock_gettime(CLOCK_MONOTONIC, &curr) != 0) {
372
			log_printf(LOG_CRIT,
373
			    "clock_gettime(CLOCK_MONOTONIC,) failed: %m");
374
			RADIUS_REQ_ASSERT(0);
375
		}
376
		if (!timespecisset(&lap->req_time))
377
			lap->req_time = curr;
378
		else {
379
			timespecsub(&curr, &lap->req_time, &delta);
380
			if (radius_set_uint32_attr(lap->pkt,
381
			    RADIUS_TYPE_ACCT_DELAY_TIME, delta.tv_sec) == 0) {
382
				radius_update_id(lap->pkt);
383
				new_message = 1;
384
			}
385
		}
386
	}
387
	if (new_message) {
388
		radius_set_accounting_request_authenticator(lap->pkt,
389
		    radius_get_server_secret(lap));
390
	}
391
392
	lap->ntry--;
393
	if (radius_send(lap->socket, lap->pkt, 0) != 0) {
394
		log_printf(LOG_ERR, "sendto() failed in %s: %m",
395
		    __func__);
396
		radius_on_response(lap, NULL, RADIUS_REQUEST_ERROR, 1);
397
		return 1;
398
	}
399
	tv0.tv_usec = 0;
400
	tv0.tv_sec = lap->setting->timeout;
401
402
	event_set(&lap->ev_sock, lap->socket, EV_READ | EV_PERSIST,
403
	    radius_request_io_event, lap);
404
	event_add(&lap->ev_sock, &tv0);
405
406
	return 0;
407
}
408
409
static void
410
radius_request_io_event(int fd, short evmask, void *context)
411
{
412
	struct overlapped *lap;
413
	struct sockaddr_storage ss;
414
	int flags;
415
	socklen_t len;
416
	RADIUS_PACKET *respkt;
417
418
	RADIUS_REQ_ASSERT(context != NULL);
419
420
	lap = context;
421
	respkt = NULL;
422
	flags = 0;
423
	if ((evmask & EV_READ) != 0) {
424
		RADIUS_REQ_ASSERT(lap->socket >= 0);
425
		if (lap->socket < 0)
426
			return;
427
		RADIUS_REQ_ASSERT(lap->pkt != NULL);
428
		memset(&ss, 0, sizeof(ss));
429
		len = sizeof(ss);
430
		if ((respkt = radius_recv(lap->socket, 0)) == NULL) {
431
			RADIUS_REQ_DBG((LOG_DEBUG,
432
			    "radius_recv() on %s(): %m", __func__));
433
			/*
434
			 * Ignore error by icmp.  Wait a response from the
435
			 * server anyway, it may eventually become ready.
436
			 */
437
			switch (errno) {
438
			case EHOSTDOWN: case EHOSTUNREACH: case ECONNREFUSED:
439
				return;	/* sleep the rest of timeout time */
440
			}
441
			flags |= RADIUS_REQUEST_ERROR;
442
		} else if (lap->secret[0] == '\0') {
443
			flags |= RADIUS_REQUEST_CHECK_AUTHENTICATOR_NO_CHECK;
444
		} else {
445
			radius_set_request_packet(respkt, lap->pkt);
446
			if (!radius_check_response_authenticator(respkt,
447
			    lap->secret))
448
				flags |= RADIUS_REQUEST_CHECK_AUTHENTICATOR_OK;
449
		}
450
		radius_on_response(lap, respkt, flags, 0);
451
		radius_delete_packet(respkt);
452
	} else if ((evmask & EV_TIMEOUT) != 0) {
453
		if (lap->ntry > 0) {
454
			RADIUS_REQ_DBG((LOG_DEBUG,
455
			    "%s() timed out retry", __func__));
456
			radius_request0(lap, 0);
457
			return;
458
		}
459
		RADIUS_REQ_DBG((LOG_DEBUG, "%s() timed out", __func__));
460
		flags |= RADIUS_REQUEST_TIMEOUT;
461
		radius_on_response(lap, NULL, flags, 1);
462
	}
463
}
464
465
static void
466
radius_on_response(RADIUS_REQUEST_CTX ctx, RADIUS_PACKET *pkt, int flags,
467
    int server_failure)
468
{
469
	struct overlapped *lap;
470
	int failovers;
471
472
	lap = ctx;
473
	if (server_failure) {
474
		int i, n;
475
		struct sockaddr *sa_curr;
476
477
		sa_curr = (struct sockaddr *)&lap->setting->server[
478
		    lap->setting->curr_server].peer;
479
		if (sa_curr->sa_len == lap->ss.ss_len &&
480
		    memcmp(sa_curr, &lap->ss, sa_curr->sa_len) == 0) {
481
			/*
482
			 * The server on failure is flagged as the current.
483
			 * change the current
484
			 */
485
			for (i = 1; i < nitems(lap->setting->server); i++) {
486
				n = (lap->setting->curr_server + i) %
487
				    nitems(lap->setting->server);
488
				if (lap->setting->server[n].enabled) {
489
					lap->setting->curr_server = n;
490
					break;
491
				}
492
			}
493
		}
494
	}
495
496
	failovers = lap->failovers;
497
	if (lap->response_fn != NULL) {
498
		lap->response_fn_calling++;
499
		lap->response_fn(lap->context, pkt, flags, ctx);
500
		lap->response_fn_calling--;
501
	}
502
	if (failovers == lap->failovers)
503
		radius_cancel_request(lap);
504
}
505
506
static int
507
select_srcaddr(struct sockaddr const *dst, struct sockaddr *src,
508
    socklen_t *srclen)
509
{
510
	int sock;
511
512
	sock = -1;
513
	if ((sock = socket(dst->sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0)
514
		goto fail;
515
	if (connect(sock, dst, dst->sa_len) != 0)
516
		goto fail;
517
	if (getsockname(sock, src, srclen) != 0)
518
		goto fail;
519
520
	close(sock);
521
522
	return 0;
523
fail:
524
	if (sock >= 0)
525
		close(sock);
526
527
	return 1;
528
}
529
530
radius_req_setting *
531
radius_req_setting_create(void)
532
{
533
	return calloc(1, sizeof(radius_req_setting));
534
}
535
536
int
537
radius_req_setting_has_server(radius_req_setting *setting)
538
{
539
	return setting->server[setting->curr_server].enabled;
540
}
541
542
void
543
radius_req_setting_destroy(radius_req_setting *setting)
544
{
545
	setting->destroyed = 1;
546
547
	if (setting->refcnt == 0)
548
		free(setting);
549
}
550
551
static void
552
radius_req_setting_ref(radius_req_setting *setting)
553
{
554
	setting->refcnt++;
555
}
556
557
static void
558
radius_req_setting_unref(radius_req_setting *setting)
559
{
560
	setting->refcnt--;
561
	if (setting->destroyed)
562
		radius_req_setting_destroy(setting);
563
}