GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/radiusd/radiusd_radius/../radiusd_module.c Lines: 0 223 0.0 %
Date: 2017-11-07 Branches: 0 117 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: radiusd_module.c,v 1.10 2016/04/05 21:24:02 krw Exp $	*/
2
3
/*
4
 * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
/* radiusd_module.c -- helper functions for radiusd modules */
20
21
#include <sys/types.h>
22
#include <sys/queue.h>
23
#include <sys/uio.h>
24
25
#include <err.h>
26
#include <errno.h>
27
#include <event.h>
28
#include <fcntl.h>
29
#include <imsg.h>
30
#include <stdio.h>
31
#include <stdlib.h>
32
#include <string.h>
33
#include <syslog.h>
34
#include <unistd.h>
35
#include <pwd.h>
36
37
#include "radiusd.h"
38
#include "radiusd_module.h"
39
#include "imsg_subr.h"
40
41
static void	(*module_config_set) (void *, const char *, int,
42
		    char * const *) = NULL;
43
static void	(*module_start_module) (void *) = NULL;
44
static void	(*module_stop_module) (void *) = NULL;
45
static void	(*module_userpass) (void *, u_int, const char *, const char *)
46
		    = NULL;
47
static void	(*module_access_request) (void *, u_int, const u_char *,
48
		    size_t) = NULL;
49
50
struct module_base {
51
	void			*ctx;
52
	struct imsgbuf		 ibuf;
53
	bool			 priv_dropped;
54
55
	/* Buffer for receiving the RADIUS packet */
56
	u_char			*radpkt;
57
	int			 radpktsiz;
58
	int			 radpktoff;
59
60
#ifdef USE_LIBEVENT
61
	struct module_imsgbuf	*module_imsgbuf;
62
	bool			 writeready;
63
	bool			 stopped;
64
	bool			 ev_onhandler;
65
	struct event		 ev;
66
#endif
67
};
68
69
static int	 module_common_radpkt(struct module_base *, uint32_t, u_int,
70
		    int, const u_char *, size_t);
71
static int	 module_recv_imsg(struct module_base *);
72
static int	 module_imsg_handler(struct module_base *, struct imsg *);
73
#ifdef USE_LIBEVENT
74
static void	 module_on_event(int, short, void *);
75
#endif
76
static void	 module_stop(struct module_base *);
77
static void	 module_reset_event(struct module_base *);
78
79
struct module_base *
80
module_create(int sock, void *ctx, struct module_handlers *handler)
81
{
82
	struct module_base	*base;
83
84
	if ((base = calloc(1, sizeof(struct module_base))) == NULL)
85
		return (NULL);
86
87
	imsg_init(&base->ibuf, sock);
88
	base->ctx = ctx;
89
90
	module_userpass = handler->userpass;
91
	module_access_request = handler->access_request;
92
	module_config_set = handler->config_set;
93
	module_start_module = handler->start;
94
	module_stop_module = handler->stop;
95
96
	return (base);
97
}
98
99
void
100
module_start(struct module_base *base)
101
{
102
#ifdef USE_LIBEVENT
103
	int	 ival;
104
105
	if ((ival = fcntl(base->ibuf.fd, F_GETFL)) < 0)
106
		err(1, "Failed to F_GETFL");
107
	if (fcntl(base->ibuf.fd, F_SETFL, ival | O_NONBLOCK) < 0)
108
		err(1, "Failed to setup NONBLOCK");
109
	event_set(&base->ev, base->ibuf.fd, EV_READ, module_on_event, base);
110
	event_add(&base->ev, NULL);
111
#endif
112
}
113
114
int
115
module_run(struct module_base *base)
116
{
117
	int	 ret;
118
119
	ret = module_recv_imsg(base);
120
	if (ret == 0)
121
		imsg_flush(&base->ibuf);
122
123
	return (ret);
124
}
125
126
void
127
module_destroy(struct module_base *base)
128
{
129
	imsg_clear(&base->ibuf);
130
	free(base);
131
}
132
133
void
134
module_load(struct module_base *base)
135
{
136
	struct radiusd_module_load_arg	 load;
137
138
	memset(&load, 0, sizeof(load));
139
	if (module_userpass != NULL)
140
		load.cap |= RADIUSD_MODULE_CAP_USERPASS;
141
	if (module_access_request != NULL)
142
		load.cap |= RADIUSD_MODULE_CAP_ACCSREQ;
143
	imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_LOAD, 0, 0, -1, &load,
144
	    sizeof(load));
145
	imsg_flush(&base->ibuf);
146
}
147
148
void
149
module_drop_privilege(struct module_base *base)
150
{
151
	struct passwd	*pw;
152
153
	tzset();
154
155
	/* Drop the privilege */
156
	if ((pw = getpwnam(RADIUSD_USER)) == NULL)
157
		goto on_fail;
158
	if (chroot(pw->pw_dir) == -1)
159
		goto on_fail;
160
	if (chdir("/") == -1)
161
		goto on_fail;
162
	if (setgroups(1, &pw->pw_gid) ||
163
	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
164
	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
165
		goto on_fail;
166
	base->priv_dropped = true;
167
168
on_fail:
169
	return;
170
}
171
172
int
173
module_notify_secret(struct module_base *base, const char *secret)
174
{
175
	int		 ret;
176
177
	ret = imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_NOTIFY_SECRET,
178
	    0, 0, -1, secret, strlen(secret) + 1);
179
	module_reset_event(base);
180
181
	return (ret);
182
}
183
184
int
185
module_send_message(struct module_base *base, uint32_t cmd, const char *fmt,
186
    ...)
187
{
188
	char	*msg;
189
	va_list	 ap;
190
	int	 ret;
191
192
	if (fmt == NULL)
193
		ret = imsg_compose(&base->ibuf, cmd, 0, 0, -1, NULL, 0);
194
	else {
195
		va_start(ap, fmt);
196
		vasprintf(&msg, fmt, ap);
197
		va_end(ap);
198
		if (msg == NULL)
199
			return (-1);
200
		ret = imsg_compose(&base->ibuf, cmd, 0, 0, -1, msg,
201
		    strlen(msg) + 1);
202
		free(msg);
203
	}
204
	module_reset_event(base);
205
206
	return (ret);
207
}
208
209
int
210
module_userpass_ok(struct module_base *base, u_int q_id, const char *msg)
211
{
212
	int		 ret;
213
	struct iovec	 iov[2];
214
215
	iov[0].iov_base = &q_id;
216
	iov[0].iov_len = sizeof(q_id);
217
	iov[1].iov_base = (char *)msg;
218
	iov[1].iov_len = strlen(msg) + 1;
219
	ret = imsg_composev(&base->ibuf, IMSG_RADIUSD_MODULE_USERPASS_OK,
220
	    0, 0, -1, iov, 2);
221
	module_reset_event(base);
222
223
	return (ret);
224
}
225
226
int
227
module_userpass_fail(struct module_base *base, u_int q_id, const char *msg)
228
{
229
	int		 ret;
230
	struct iovec	 iov[2];
231
232
	iov[0].iov_base = &q_id;
233
	iov[0].iov_len = sizeof(q_id);
234
	iov[1].iov_base = (char *)msg;
235
	iov[1].iov_len = strlen(msg) + 1;
236
	ret = imsg_composev(&base->ibuf, IMSG_RADIUSD_MODULE_USERPASS_FAIL,
237
	    0, 0, -1, iov, 2);
238
	module_reset_event(base);
239
240
	return (ret);
241
}
242
243
int
244
module_accsreq_answer(struct module_base *base, u_int q_id, int modified,
245
    const u_char *pkt, size_t pktlen)
246
{
247
	return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER,
248
	    q_id, modified, pkt, pktlen));
249
}
250
251
int
252
module_accsreq_aborted(struct module_base *base, u_int q_id)
253
{
254
	int	 ret;
255
256
	ret = imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_ACCSREQ_ABORTED,
257
	    0, 0, -1, &q_id, sizeof(u_int));
258
	module_reset_event(base);
259
260
	return (ret);
261
}
262
263
static int
264
module_common_radpkt(struct module_base *base, uint32_t imsg_type, u_int q_id,
265
    int modified, const u_char *pkt, size_t pktlen)
266
{
267
	int		 ret = 0, off = 0, len, siz;
268
	struct iovec	 iov[2];
269
	struct radiusd_module_radpkt_arg	 ans;
270
271
	len = pktlen;
272
	ans.q_id = q_id;
273
	ans.modified = modified;
274
	while (off < len) {
275
		siz = MAX_IMSGSIZE - sizeof(ans);
276
		if (len - off > siz) {
277
			ans.final = true;
278
			ans.datalen = siz;
279
		} else {
280
			ans.final = true;
281
			ans.datalen = len - off;
282
		}
283
		iov[0].iov_base = &ans;
284
		iov[0].iov_len = sizeof(ans);
285
		iov[1].iov_base = (u_char *)pkt + off;
286
		iov[1].iov_len = ans.datalen;
287
		ret = imsg_composev(&base->ibuf, imsg_type, 0, 0, -1, iov, 2);
288
		if (ret == -1)
289
			break;
290
		off += ans.datalen;
291
	}
292
	module_reset_event(base);
293
294
	return (ret);
295
}
296
297
static int
298
module_recv_imsg(struct module_base *base)
299
{
300
	ssize_t		 n;
301
	struct imsg	 imsg;
302
303
	if (((n = imsg_read(&base->ibuf)) == -1 && errno != EAGAIN) || n == 0) {
304
		if (n != 0)
305
			syslog(LOG_ERR, "%s: imsg_read(): %m", __func__);
306
		module_stop(base);
307
		return (-1);
308
	}
309
310
	for (;;) {
311
		if ((n = imsg_get(&base->ibuf, &imsg)) == -1) {
312
			syslog(LOG_ERR, "%s: imsg_get(): %m", __func__);
313
			module_stop(base);
314
			return (-1);
315
		}
316
		if (n == 0)
317
			break;
318
		module_imsg_handler(base, &imsg);
319
		imsg_free(&imsg);
320
	}
321
	module_reset_event(base);
322
323
	return (0);
324
}
325
326
static int
327
module_imsg_handler(struct module_base *base, struct imsg *imsg)
328
{
329
	ssize_t	 datalen;
330
331
	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
332
	switch (imsg->hdr.type) {
333
	case IMSG_RADIUSD_MODULE_SET_CONFIG:
334
	    {
335
		struct radiusd_module_set_arg	 *arg;
336
		struct radiusd_module_object	 *val;
337
		u_int				  i;
338
		size_t				  off;
339
		char				**argv;
340
341
		arg = (struct radiusd_module_set_arg *)imsg->data;
342
		off = sizeof(struct radiusd_module_set_arg);
343
344
		if ((argv = calloc(sizeof(const char *), arg->nparamval))
345
		    == NULL) {
346
			module_send_message(base, IMSG_NG,
347
			    "Out of memory: %s", strerror(errno));
348
			break;
349
		}
350
		for (i = 0; i < arg->nparamval; i++) {
351
			if (datalen - off <
352
			    sizeof(struct radiusd_module_object))
353
				break;
354
			val = (struct radiusd_module_object *)
355
			    ((caddr_t)imsg->data + off);
356
			if (datalen - off < val->size)
357
				break;
358
			argv[i] = (char *)(val + 1);
359
			off += val->size;
360
		}
361
		if (i >= arg->nparamval)
362
			module_config_set(base->ctx, arg->paramname,
363
			    arg->nparamval, argv);
364
		else
365
			module_send_message(base, IMSG_NG,
366
			    "Internal protocol error");
367
		free(argv);
368
369
		break;
370
	    }
371
	case IMSG_RADIUSD_MODULE_START:
372
		if (module_start_module != NULL) {
373
			module_start_module(base->ctx);
374
			if (!base->priv_dropped) {
375
				syslog(LOG_ERR, "Module tried to start with "
376
				    "root privileges");
377
				abort();
378
			}
379
		} else {
380
			if (!base->priv_dropped) {
381
				syslog(LOG_ERR, "Module tried to start with "
382
				    "root privileges");
383
				abort();
384
			}
385
			module_send_message(base, IMSG_OK, NULL);
386
		}
387
		break;
388
	case IMSG_RADIUSD_MODULE_STOP:
389
		module_stop(base);
390
		break;
391
	case IMSG_RADIUSD_MODULE_USERPASS:
392
	    {
393
		struct radiusd_module_userpass_arg *userpass;
394
395
		if (module_userpass == NULL) {
396
			syslog(LOG_ERR, "Received USERPASS message, but "
397
			    "module doesn't support");
398
			break;
399
		}
400
		if (datalen <
401
		    (ssize_t)sizeof(struct radiusd_module_userpass_arg)) {
402
			syslog(LOG_ERR, "Received USERPASS message, but "
403
			    "length is wrong");
404
			break;
405
		}
406
		userpass = (struct radiusd_module_userpass_arg *)imsg->data;
407
		module_userpass(base->ctx, userpass->q_id, userpass->user,
408
		    (userpass->has_pass)? userpass->pass : NULL);
409
		explicit_bzero(userpass,
410
		    sizeof(struct radiusd_module_userpass_arg));
411
		break;
412
	    }
413
	case IMSG_RADIUSD_MODULE_ACCSREQ:
414
	    {
415
		struct radiusd_module_radpkt_arg	*accessreq;
416
		int					 chunklen;
417
418
		if (module_access_request == NULL) {
419
			syslog(LOG_ERR, "Received ACCSREQ message, but "
420
			    "module doesn't support");
421
			break;
422
		}
423
		if (datalen <
424
		    (ssize_t)sizeof(struct radiusd_module_radpkt_arg)) {
425
			syslog(LOG_ERR, "Received ACCSREQ message, but "
426
			    "length is wrong");
427
			break;
428
		}
429
		accessreq = (struct radiusd_module_radpkt_arg *)imsg->data;
430
		if (base->radpktsiz < accessreq->datalen) {
431
			u_char *nradpkt;
432
			if ((nradpkt = realloc(base->radpkt,
433
			    accessreq->datalen)) == NULL) {
434
				syslog(LOG_ERR, "Could not handle received "
435
				    "ACCSREQ message: %m");
436
				base->radpktoff = 0;
437
				goto accsreq_out;
438
			}
439
			base->radpkt = nradpkt;
440
			base->radpktsiz = accessreq->datalen;
441
		}
442
		chunklen = datalen - sizeof(struct radiusd_module_radpkt_arg);
443
		if (chunklen > base->radpktsiz - base->radpktoff){
444
			syslog(LOG_ERR,
445
			    "Could not handle received ACCSREQ message: "
446
			    "received length is too big");
447
			base->radpktoff = 0;
448
			goto accsreq_out;
449
		}
450
		memcpy(base->radpkt + base->radpktoff,
451
		    (caddr_t)(accessreq + 1), chunklen);
452
		base->radpktoff += chunklen;
453
		if (!accessreq->final)
454
			goto accsreq_out;
455
		if (base->radpktoff != accessreq->datalen) {
456
			syslog(LOG_ERR,
457
			    "Could not handle received ACCSREQ "
458
			    "message: length is mismatch");
459
			base->radpktoff = 0;
460
			goto accsreq_out;
461
		}
462
		module_access_request(base->ctx, accessreq->q_id,
463
		    base->radpkt, base->radpktoff);
464
		base->radpktoff = 0;
465
accsreq_out:
466
		break;
467
	    }
468
	}
469
470
	return (0);
471
}
472
473
static void
474
module_stop(struct module_base *base)
475
{
476
	if (module_stop_module != NULL)
477
		module_stop_module(base->ctx);
478
#ifdef USE_LIBEVENT
479
	event_del(&base->ev);
480
	base->stopped = true;
481
#endif
482
	close(base->ibuf.fd);
483
}
484
485
#ifdef USE_LIBEVENT
486
static void
487
module_on_event(int fd, short evmask, void *ctx)
488
{
489
	struct module_base	*base = ctx;
490
	int			 ret;
491
492
	base->ev_onhandler = true;
493
	if (evmask & EV_WRITE)
494
		base->writeready = true;
495
	if (evmask & EV_READ) {
496
		ret = module_recv_imsg(base);
497
		if (ret < 0)
498
			return;
499
	}
500
	while (base->writeready && base->ibuf.w.queued) {
501
		ret = msgbuf_write(&base->ibuf.w);
502
		if (ret > 0)
503
			continue;
504
		base->writeready = false;
505
		if (ret == 0 && errno == EAGAIN)
506
			break;
507
		syslog(LOG_ERR, "%s: msgbuf_write: %m", __func__);
508
		module_stop(base);
509
		return;
510
	}
511
	base->ev_onhandler = false;
512
	module_reset_event(base);
513
	return;
514
}
515
#endif
516
517
static void
518
module_reset_event(struct module_base *base)
519
{
520
#ifdef USE_LIBEVENT
521
	short		 evmask = 0;
522
	struct timeval	*tvp = NULL, tv = { 0, 0 };
523
524
	if (base->ev_onhandler)
525
		return;
526
	if (base->stopped)
527
		return;
528
	event_del(&base->ev);
529
530
	evmask |= EV_READ;
531
	if (base->ibuf.w.queued) {
532
		if (!base->writeready)
533
			evmask |= EV_WRITE;
534
		else
535
			tvp = &tv;	/* fire immediately */
536
	}
537
	event_set(&base->ev, base->ibuf.fd, evmask, module_on_event, base);
538
	if (event_add(&base->ev, tvp) == -1)
539
		syslog(LOG_ERR, "event_add() failed in %s()", __func__);
540
#endif
541
}