xref: /openbsd-src/usr.sbin/radiusd/radiusd_file.c (revision 882428cdbdd2944d8f59bc8621c131fd814fb6ee)
1*882428cdSclaudio /*	$OpenBSD: radiusd_file.c,v 1.8 2024/11/21 13:43:10 claudio Exp $	*/
20156c836Syasuoka 
30156c836Syasuoka /*
40156c836Syasuoka  * Copyright (c) 2024 YASUOKA Masahiko <yasuoka@yasuoka.net>
50156c836Syasuoka  *
60156c836Syasuoka  * Permission to use, copy, modify, and distribute this software for any
70156c836Syasuoka  * purpose with or without fee is hereby granted, provided that the above
80156c836Syasuoka  * copyright notice and this permission notice appear in all copies.
90156c836Syasuoka  *
100156c836Syasuoka  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
110156c836Syasuoka  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
120156c836Syasuoka  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
130156c836Syasuoka  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
140156c836Syasuoka  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
150156c836Syasuoka  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
160156c836Syasuoka  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
170156c836Syasuoka  */
180156c836Syasuoka 
190156c836Syasuoka #include <sys/types.h>
200156c836Syasuoka #include <sys/cdefs.h>
210156c836Syasuoka #include <sys/queue.h>
220156c836Syasuoka #include <sys/socket.h>
230156c836Syasuoka #include <sys/wait.h>
240156c836Syasuoka #include <netinet/in.h>
250156c836Syasuoka 
260156c836Syasuoka #include <err.h>
270156c836Syasuoka #include <errno.h>
280156c836Syasuoka #include <fcntl.h>
290156c836Syasuoka #include <imsg.h>
300156c836Syasuoka #include <limits.h>
310156c836Syasuoka #include <md5.h>
320156c836Syasuoka #include <radius.h>
330156c836Syasuoka #include <stddef.h>
340156c836Syasuoka #include <stdint.h>
350156c836Syasuoka #include <stdlib.h>
360156c836Syasuoka #include <string.h>
370156c836Syasuoka #include <unistd.h>
380156c836Syasuoka 
390156c836Syasuoka #include "chap_ms.h"
400156c836Syasuoka #include "imsg_subr.h"
410156c836Syasuoka #include "log.h"
420156c836Syasuoka #include "radiusd.h"
430156c836Syasuoka #include "radiusd_module.h"
440156c836Syasuoka 
450156c836Syasuoka struct module_file_params {
460156c836Syasuoka 	int			 debug;
470156c836Syasuoka 	char			 path[PATH_MAX];
480156c836Syasuoka };
490156c836Syasuoka 
500156c836Syasuoka struct module_file {
510156c836Syasuoka 	struct module_base	*base;
520156c836Syasuoka 	struct imsgbuf		 ibuf;
530156c836Syasuoka 	struct module_file_params
540156c836Syasuoka 				 params;
550156c836Syasuoka };
560156c836Syasuoka 
570156c836Syasuoka struct module_file_userinfo {
580156c836Syasuoka 	struct in_addr		frame_ip_address;
590156c836Syasuoka 	char			password[0];
600156c836Syasuoka };
610156c836Syasuoka 
620156c836Syasuoka /* IPC between priv and main */
630156c836Syasuoka enum {
640156c836Syasuoka 	IMSG_RADIUSD_FILE_OK = 1000,
650156c836Syasuoka 	IMSG_RADIUSD_FILE_NG,
660156c836Syasuoka 	IMSG_RADIUSD_FILE_PARAMS,
670156c836Syasuoka 	IMSG_RADIUSD_FILE_USERINFO
680156c836Syasuoka };
690156c836Syasuoka 
700156c836Syasuoka static void	 parent_dispatch_main(struct module_file_params *,
710156c836Syasuoka 		    struct imsgbuf *, struct imsg *);
720156c836Syasuoka static void	 module_file_main(void) __dead;
730156c836Syasuoka static pid_t	 start_child(char *, int);
740156c836Syasuoka static void	 module_file_config_set(void *, const char *, int,
750156c836Syasuoka 		    char * const *);
760156c836Syasuoka static void	 module_file_start(void *);
770156c836Syasuoka static void	 module_file_access_request(void *, u_int, const u_char *,
780156c836Syasuoka 		    size_t);
790156c836Syasuoka static void	 auth_pap(struct module_file *, u_int, RADIUS_PACKET *, char *,
800156c836Syasuoka 		    struct module_file_userinfo *);
810156c836Syasuoka static void	 auth_md5chap(struct module_file *, u_int, RADIUS_PACKET *,
820156c836Syasuoka 		    char *, struct module_file_userinfo *);
830156c836Syasuoka static void	 auth_mschapv2(struct module_file *, u_int, RADIUS_PACKET *,
840156c836Syasuoka 		    char *, struct module_file_userinfo *);
85e83d1c67Syasuoka static void	 auth_reject(struct module_file *, u_int, RADIUS_PACKET *,
86e83d1c67Syasuoka 		    char *, struct module_file_userinfo *);
870156c836Syasuoka 
880156c836Syasuoka static struct module_handlers module_file_handlers = {
890156c836Syasuoka 	.access_request		= module_file_access_request,
900156c836Syasuoka 	.config_set		= module_file_config_set,
910156c836Syasuoka 	.start			= module_file_start
920156c836Syasuoka };
930156c836Syasuoka 
940156c836Syasuoka int
950156c836Syasuoka main(int argc, char *argv[])
960156c836Syasuoka {
970156c836Syasuoka 	int				 ch, pairsock[2], status;
980156c836Syasuoka 	pid_t				 pid;
990156c836Syasuoka 	char				*saved_argv0;
1000156c836Syasuoka 	struct imsgbuf			 ibuf;
1010156c836Syasuoka 	struct imsg			 imsg;
1020156c836Syasuoka 	ssize_t				 n;
1030156c836Syasuoka 	size_t				 datalen;
1040156c836Syasuoka 	struct module_file_params	*paramsp, params;
105e1e88b63Syasuoka 	char				 pathdb[PATH_MAX];
1060156c836Syasuoka 
1070156c836Syasuoka 	while ((ch = getopt(argc, argv, "M")) != -1)
1080156c836Syasuoka 		switch (ch) {
1090156c836Syasuoka 		case 'M':
1100156c836Syasuoka 			module_file_main();
1110156c836Syasuoka 			/* not reached */
1120156c836Syasuoka 			break;
1130156c836Syasuoka 		}
1140156c836Syasuoka 	saved_argv0 = argv[0];
1150156c836Syasuoka 
1160156c836Syasuoka 	argc -= optind;
1170156c836Syasuoka 	argv += optind;
1180156c836Syasuoka 
1190156c836Syasuoka 	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, PF_UNSPEC,
1200156c836Syasuoka 	    pairsock) == -1)
1210156c836Syasuoka 		err(EXIT_FAILURE, "socketpair");
1220156c836Syasuoka 
1230156c836Syasuoka 	log_init(0);
1240156c836Syasuoka 
1250156c836Syasuoka 	pid = start_child(saved_argv0, pairsock[1]);
1260156c836Syasuoka 
1270156c836Syasuoka 	/* Privileged process */
128e1e88b63Syasuoka 	if (pledge("stdio rpath unveil", NULL) == -1)
129e1e88b63Syasuoka 		err(EXIT_FAILURE, "pledge");
1300156c836Syasuoka 	setproctitle("[priv]");
131*882428cdSclaudio 	if (imsgbuf_init(&ibuf, pairsock[0]) == -1)
132*882428cdSclaudio 		err(EXIT_FAILURE, "imsgbuf_init");
1330156c836Syasuoka 
134e1e88b63Syasuoka 	/* Receive parameters from the main process. */
1350156c836Syasuoka 	if (imsg_sync_read(&ibuf, 2000) <= 0 ||
1360156c836Syasuoka 	    (n = imsg_get(&ibuf, &imsg)) <= 0)
1370156c836Syasuoka 		exit(EXIT_FAILURE);
1380156c836Syasuoka 	if (imsg.hdr.type != IMSG_RADIUSD_FILE_PARAMS)
1390156c836Syasuoka 		err(EXIT_FAILURE, "Receieved unknown message type %d",
1400156c836Syasuoka 		    imsg.hdr.type);
1410156c836Syasuoka 	datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
1420156c836Syasuoka 	if (datalen < sizeof(params))
1430156c836Syasuoka 		err(EXIT_FAILURE, "Receieved IMSG_RADIUSD_FILE_PARAMS "
1440156c836Syasuoka 		    "message is wrong size");
1450156c836Syasuoka 	paramsp = imsg.data;
1460156c836Syasuoka 	if (paramsp->path[0] != '\0') {
147e1e88b63Syasuoka 		strlcpy(pathdb, paramsp->path, sizeof(pathdb));
148e1e88b63Syasuoka 		strlcat(pathdb, ".db", sizeof(pathdb));
149e1e88b63Syasuoka 		if (unveil(paramsp->path, "r") == -1 ||
150e1e88b63Syasuoka 		    unveil(pathdb, "r") == -1)
1510156c836Syasuoka 			err(EXIT_FAILURE, "unveil");
1520156c836Syasuoka 	}
1530156c836Syasuoka 	if (paramsp->debug)
1540156c836Syasuoka 		log_init(1);
1550156c836Syasuoka 
1560156c836Syasuoka 	if (unveil(NULL, NULL) == -1)
1570156c836Syasuoka 		err(EXIT_FAILURE, "unveil");
1580156c836Syasuoka 
1590156c836Syasuoka 	memcpy(&params, paramsp, sizeof(params));
1600156c836Syasuoka 
1610156c836Syasuoka 	for (;;) {
1624f3fb1ffSclaudio 		if (imsgbuf_read(&ibuf) != 1)
1630156c836Syasuoka 			break;
1640156c836Syasuoka 		for (;;) {
1650156c836Syasuoka 			if ((n = imsg_get(&ibuf, &imsg)) == -1)
1660156c836Syasuoka 				break;
1670156c836Syasuoka 			if (n == 0)
1680156c836Syasuoka 				break;
1690156c836Syasuoka 			parent_dispatch_main(&params, &ibuf, &imsg);
1700156c836Syasuoka 			imsg_free(&imsg);
171dd7efffeSclaudio 			imsgbuf_flush(&ibuf);
1720156c836Syasuoka 		}
173dd7efffeSclaudio 		imsgbuf_flush(&ibuf);
1740156c836Syasuoka 	}
175dd7efffeSclaudio 	imsgbuf_clear(&ibuf);
1760156c836Syasuoka 
1770156c836Syasuoka 	while (waitpid(pid, &status, 0) == -1) {
1780156c836Syasuoka 		if (errno != EINTR)
1790156c836Syasuoka 			break;
1800156c836Syasuoka 	}
1810156c836Syasuoka 	exit(WEXITSTATUS(status));
1820156c836Syasuoka }
1830156c836Syasuoka 
1840156c836Syasuoka void
1850156c836Syasuoka parent_dispatch_main(struct module_file_params *params, struct imsgbuf *ibuf,
1860156c836Syasuoka     struct imsg *imsg)
1870156c836Syasuoka {
1880156c836Syasuoka 	size_t				 datalen, entsz, passz;
1890156c836Syasuoka 	const char			*username;
1900156c836Syasuoka 	char				*buf, *db[2], *str;
1910156c836Syasuoka 	int				 ret;
1920156c836Syasuoka 	struct module_file_userinfo	*ent;
1930156c836Syasuoka 
1940156c836Syasuoka 	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
1950156c836Syasuoka 	switch (imsg->hdr.type) {
1960156c836Syasuoka 	case IMSG_RADIUSD_FILE_USERINFO:
1970156c836Syasuoka 		if (datalen == 0 ||
1980156c836Syasuoka 		    *((char *)imsg->data + datalen - 1) != '\0') {
1990156c836Syasuoka 			log_warn("%s: received IMSG_RADIUSD_FILE_USERINFO "
2000156c836Syasuoka 			    "is wrong", __func__);
2010156c836Syasuoka 			goto on_error;
2020156c836Syasuoka 		}
2030156c836Syasuoka 		username = imsg->data;
2040156c836Syasuoka 		db[0] = params->path;
2050156c836Syasuoka 		db[1] = NULL;
2060156c836Syasuoka 		if ((ret = cgetent(&buf, db, username)) < 0) {
2070156c836Syasuoka 			log_info("user `%s' is not configured", username);
2080156c836Syasuoka 			goto on_error;
2090156c836Syasuoka 		}
2100156c836Syasuoka 		if ((ret = cgetstr(buf, "password", &str)) < 0) {
2110156c836Syasuoka 			log_info("password for `%s' is not configured",
2120156c836Syasuoka 			    username);
2130156c836Syasuoka 			goto on_error;
2140156c836Syasuoka 		}
2150156c836Syasuoka 		passz = strlen(str) + 1;
2160156c836Syasuoka 		entsz = offsetof(struct module_file_userinfo, password[passz]);
2170156c836Syasuoka 		if ((ent = calloc(1, entsz)) == NULL) {
2180156c836Syasuoka 			log_warn("%s; calloc", __func__);
2190156c836Syasuoka 			goto on_error;
2200156c836Syasuoka 		}
2210156c836Syasuoka 		strlcpy(ent->password, str, passz);
2220156c836Syasuoka 		imsg_compose(ibuf, IMSG_RADIUSD_FILE_USERINFO, 0, -1, -1,
2230156c836Syasuoka 		    ent, entsz);
2240156c836Syasuoka 		freezero(ent, entsz);
2250156c836Syasuoka 		break;
2260156c836Syasuoka 	}
2270156c836Syasuoka 	return;
2280156c836Syasuoka  on_error:
2290156c836Syasuoka 	imsg_compose(ibuf, IMSG_RADIUSD_FILE_NG, 0, -1, -1, NULL, 0);
2300156c836Syasuoka }
2310156c836Syasuoka 
2320156c836Syasuoka /* main process */
2330156c836Syasuoka void
2340156c836Syasuoka module_file_main(void)
2350156c836Syasuoka {
2360156c836Syasuoka 	struct module_file	 module_file;
2370156c836Syasuoka 
2380156c836Syasuoka 	setproctitle("[main]");
2390156c836Syasuoka 
2400156c836Syasuoka 	memset(&module_file, 0, sizeof(module_file));
2410156c836Syasuoka 	if ((module_file.base = module_create(STDIN_FILENO, &module_file,
2420156c836Syasuoka 	    &module_file_handlers)) == NULL)
2430156c836Syasuoka 		err(1, "Could not create a module instance");
2440156c836Syasuoka 
2450156c836Syasuoka 	module_drop_privilege(module_file.base, 0);
2460156c836Syasuoka 
2470156c836Syasuoka 	module_load(module_file.base);
248*882428cdSclaudio 	if (imsgbuf_init(&module_file.ibuf, 3) == -1)
249*882428cdSclaudio 		err(EXIT_FAILURE, "imsgbuf_init");
2500156c836Syasuoka 
2510156c836Syasuoka 	if (pledge("stdio", NULL) == -1)
2520156c836Syasuoka 		err(EXIT_FAILURE, "pledge");
2530156c836Syasuoka 	while (module_run(module_file.base) == 0)
2540156c836Syasuoka 		;
2550156c836Syasuoka 
2560156c836Syasuoka 	module_destroy(module_file.base);
2570156c836Syasuoka 
2580156c836Syasuoka 	exit(0);
2590156c836Syasuoka }
2600156c836Syasuoka 
2610156c836Syasuoka pid_t
2620156c836Syasuoka start_child(char *argv0, int fd)
2630156c836Syasuoka {
2640156c836Syasuoka 	char *argv[5];
2650156c836Syasuoka 	int argc = 0;
2660156c836Syasuoka 	pid_t pid;
2670156c836Syasuoka 
2680156c836Syasuoka 	switch (pid = fork()) {
2690156c836Syasuoka 	case -1:
2700156c836Syasuoka 		fatal("cannot fork");
2710156c836Syasuoka 	case 0:
2720156c836Syasuoka 		break;
2730156c836Syasuoka 	default:
2740156c836Syasuoka 		close(fd);
2750156c836Syasuoka 		return (pid);
2760156c836Syasuoka 	}
2770156c836Syasuoka 
2780156c836Syasuoka 	if (fd != 3) {
2790156c836Syasuoka 		if (dup2(fd, 3) == -1)
2800156c836Syasuoka 			fatal("cannot setup imsg fd");
2810156c836Syasuoka 	} else if (fcntl(fd, F_SETFD, 0) == -1)
2820156c836Syasuoka 		fatal("cannot setup imsg fd");
2830156c836Syasuoka 
2840156c836Syasuoka 	argv[argc++] = argv0;
2850156c836Syasuoka 	argv[argc++] = "-M";	/* main proc */
2860156c836Syasuoka 	argv[argc++] = NULL;
2870156c836Syasuoka 	execvp(argv0, argv);
2880156c836Syasuoka 	fatal("execvp");
2890156c836Syasuoka }
2900156c836Syasuoka 
2910156c836Syasuoka void
2920156c836Syasuoka module_file_config_set(void *ctx, const char *name, int valc,
2930156c836Syasuoka     char * const * valv)
2940156c836Syasuoka {
2950156c836Syasuoka 	struct module_file	*module = ctx;
2960156c836Syasuoka 	char			*errmsg;
2970156c836Syasuoka 
2980156c836Syasuoka 	if (strcmp(name, "path") == 0) {
2990156c836Syasuoka 		SYNTAX_ASSERT(valc == 1, "`path' must have a argument");
3000156c836Syasuoka 		if (strlcpy(module->params.path, valv[0], sizeof(
3010156c836Syasuoka 		    module->params.path)) >= sizeof(module->params.path)) {
3020156c836Syasuoka 			module_send_message(module->base, IMSG_NG,
3030156c836Syasuoka 			    "`path' is too long");
3040156c836Syasuoka 			return;
3050156c836Syasuoka 		}
3060156c836Syasuoka 		module_send_message(module->base, IMSG_OK, NULL);
3070156c836Syasuoka 	} else if (strcmp(name, "_debug") == 0) {
3080156c836Syasuoka 		log_init(1);
3090156c836Syasuoka 		module->params.debug = 1;
3100156c836Syasuoka 		module_send_message(module->base, IMSG_OK, NULL);
3110156c836Syasuoka 	} else if (strncmp(name, "_", 1) == 0)
3120156c836Syasuoka 		/* ignore all internal messages */
3130156c836Syasuoka 		module_send_message(module->base, IMSG_OK, NULL);
3140156c836Syasuoka 	else
3150156c836Syasuoka 		module_send_message(module->base, IMSG_NG,
3160156c836Syasuoka 		    "Unknown config parameter `%s'", name);
3170156c836Syasuoka 	return;
3180156c836Syasuoka  syntax_error:
3190156c836Syasuoka 	module_send_message(module->base, IMSG_NG, "%s", errmsg);
3200156c836Syasuoka 	return;
3210156c836Syasuoka }
3220156c836Syasuoka 
3230156c836Syasuoka void
3240156c836Syasuoka module_file_start(void *ctx)
3250156c836Syasuoka {
3260156c836Syasuoka 	struct module_file	*module = ctx;
3270156c836Syasuoka 
328e1e88b63Syasuoka 	/* Send parameters to parent */
3290156c836Syasuoka 	if (module->params.path[0] == '\0') {
3300156c836Syasuoka 		module_send_message(module->base, IMSG_NG,
3310156c836Syasuoka 		    "`path' is not configured");
3320156c836Syasuoka 		return;
3330156c836Syasuoka 	}
3340156c836Syasuoka 	imsg_compose(&module->ibuf, IMSG_RADIUSD_FILE_PARAMS, 0, -1, -1,
3350156c836Syasuoka 	    &module->params, sizeof(module->params));
336dd7efffeSclaudio 	imsgbuf_flush(&module->ibuf);
3370156c836Syasuoka 
3380156c836Syasuoka 	module_send_message(module->base, IMSG_OK, NULL);
3390156c836Syasuoka }
3400156c836Syasuoka 
3410156c836Syasuoka void
3420156c836Syasuoka module_file_access_request(void *ctx, u_int query_id, const u_char *pkt,
3430156c836Syasuoka     size_t pktlen)
3440156c836Syasuoka {
3450156c836Syasuoka 	size_t				 datalen;
3460156c836Syasuoka 	struct module_file		*self = ctx;
3470156c836Syasuoka 	RADIUS_PACKET			*radpkt = NULL;
3480156c836Syasuoka 	char				 username[256];
3490156c836Syasuoka 	ssize_t				 n;
3500156c836Syasuoka 	struct imsg			 imsg;
3510156c836Syasuoka 	struct module_file_userinfo	*ent;
3520156c836Syasuoka 
3530156c836Syasuoka 	memset(&imsg, 0, sizeof(imsg));
3540156c836Syasuoka 
3550156c836Syasuoka 	if ((radpkt = radius_convert_packet(pkt, pktlen)) == NULL) {
3560156c836Syasuoka 		log_warn("%s: radius_convert_packet()", __func__);
357e83d1c67Syasuoka 		goto out;
3580156c836Syasuoka 	}
3590156c836Syasuoka 	radius_get_string_attr(radpkt, RADIUS_TYPE_USER_NAME, username,
3600156c836Syasuoka 	    sizeof(username));
3610156c836Syasuoka 
3620156c836Syasuoka 	imsg_compose(&self->ibuf, IMSG_RADIUSD_FILE_USERINFO, 0, -1, -1,
3630156c836Syasuoka 	    username, strlen(username) + 1);
364dd7efffeSclaudio 	imsgbuf_flush(&self->ibuf);
3654f3fb1ffSclaudio 	if (imsgbuf_read(&self->ibuf) != 1) {
366dd7efffeSclaudio 		log_warn("%s: imsgbuf_read()", __func__);
367e83d1c67Syasuoka 		goto out;
3680156c836Syasuoka 	}
3690156c836Syasuoka 	if ((n = imsg_get(&self->ibuf, &imsg)) <= 0) {
3700156c836Syasuoka 		log_warn("%s: imsg_get()", __func__);
371e83d1c67Syasuoka 		goto out;
3720156c836Syasuoka 	}
3730156c836Syasuoka 
3740156c836Syasuoka 	datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
3750156c836Syasuoka 	if (imsg.hdr.type == IMSG_RADIUSD_FILE_USERINFO) {
3760156c836Syasuoka 		if (datalen <= offsetof(struct module_file_userinfo,
3770156c836Syasuoka 		    password[0])) {
3780156c836Syasuoka 			log_warn("%s: received IMSG_RADIUSD_FILE_USERINFO is "
3790156c836Syasuoka 			    "invalid", __func__);
380e83d1c67Syasuoka 			goto out;
3810156c836Syasuoka 		}
3820156c836Syasuoka 		ent = imsg.data;
3830156c836Syasuoka 		if (radius_has_attr(radpkt, RADIUS_TYPE_USER_PASSWORD))
3840156c836Syasuoka 			auth_pap(self, query_id, radpkt, username, ent);
3850156c836Syasuoka 		else if (radius_has_attr(radpkt, RADIUS_TYPE_CHAP_PASSWORD))
3860156c836Syasuoka 			auth_md5chap(self, query_id, radpkt, username, ent);
3870156c836Syasuoka 		else if (radius_has_vs_attr(radpkt, RADIUS_VENDOR_MICROSOFT,
3880156c836Syasuoka 		    RADIUS_VTYPE_MS_CHAP2_RESPONSE))
3890156c836Syasuoka 			auth_mschapv2(self, query_id, radpkt, username, ent);
390e83d1c67Syasuoka 		else
391e83d1c67Syasuoka 			auth_reject(self, query_id, radpkt, username, ent);
392e83d1c67Syasuoka 	} else
393e83d1c67Syasuoka 		auth_reject(self, query_id, radpkt, username, NULL);
394e83d1c67Syasuoka  out:
3950156c836Syasuoka 	if (radpkt != NULL)
3960156c836Syasuoka 		radius_delete_packet(radpkt);
3970156c836Syasuoka 	imsg_free(&imsg);
3980156c836Syasuoka 	return;
3990156c836Syasuoka }
4000156c836Syasuoka 
4010156c836Syasuoka void
4020156c836Syasuoka auth_pap(struct module_file *self, u_int q_id, RADIUS_PACKET *radpkt,
4030156c836Syasuoka     char *username, struct module_file_userinfo *ent)
4040156c836Syasuoka {
4050156c836Syasuoka 	RADIUS_PACKET	*respkt = NULL;
4060156c836Syasuoka 	char		 pass[256];
4070156c836Syasuoka 	int		 ret;
4080156c836Syasuoka 
4090156c836Syasuoka 	if (radius_get_string_attr(radpkt, RADIUS_TYPE_USER_PASSWORD, pass,
4100156c836Syasuoka 	    sizeof(pass)) != 0) {
4110156c836Syasuoka 		log_warnx("%s: radius_get_string_attr", __func__);
4120156c836Syasuoka 		return;
4130156c836Syasuoka 	}
4140156c836Syasuoka 	ret = strcmp(ent->password, pass);
4150156c836Syasuoka 	explicit_bzero(ent->password, strlen(ent->password));
4160156c836Syasuoka 	log_info("q=%u User `%s' authentication %s (PAP)", q_id, username,
4170156c836Syasuoka 	    (ret == 0)? "succeeded" : "failed");
4180156c836Syasuoka 	if ((respkt = radius_new_response_packet((ret == 0)?
4190156c836Syasuoka 	    RADIUS_CODE_ACCESS_ACCEPT : RADIUS_CODE_ACCESS_REJECT, radpkt))
4200156c836Syasuoka 	    == NULL) {
4210156c836Syasuoka 		log_warn("%s: radius_new_response_packet()", __func__);
4220156c836Syasuoka 		return;
4230156c836Syasuoka 	}
4240156c836Syasuoka 	module_accsreq_answer(self->base, q_id,
4250156c836Syasuoka 	    radius_get_data(respkt), radius_get_length(respkt));
4260156c836Syasuoka 	radius_delete_packet(respkt);
4270156c836Syasuoka }
4280156c836Syasuoka 
4290156c836Syasuoka void
4300156c836Syasuoka auth_md5chap(struct module_file *self, u_int q_id, RADIUS_PACKET *radpkt,
4310156c836Syasuoka     char *username, struct module_file_userinfo *ent)
4320156c836Syasuoka {
4330156c836Syasuoka 	RADIUS_PACKET	*respkt = NULL;
4340156c836Syasuoka 	size_t		 attrlen, challlen;
4350156c836Syasuoka 	u_char		 chall[256], idpass[17], digest[16];
4360156c836Syasuoka 	int		 ret;
4370156c836Syasuoka 	MD5_CTX		 md5;
4380156c836Syasuoka 
4390156c836Syasuoka 	attrlen = sizeof(idpass);
4400156c836Syasuoka 	if (radius_get_raw_attr(radpkt, RADIUS_TYPE_CHAP_PASSWORD, idpass,
4410156c836Syasuoka 	    &attrlen) != 0) {
4420156c836Syasuoka 		log_warnx("%s: radius_get_string_attr", __func__);
4430156c836Syasuoka 		return;
4440156c836Syasuoka 	}
4450156c836Syasuoka 	challlen = sizeof(chall);
4460156c836Syasuoka 	if (radius_get_raw_attr(radpkt, RADIUS_TYPE_CHAP_CHALLENGE, chall,
4470156c836Syasuoka 	    &challlen) != 0) {
4480156c836Syasuoka 		log_warnx("%s: radius_get_string_attr", __func__);
4490156c836Syasuoka 		return;
4500156c836Syasuoka 	}
4510156c836Syasuoka 	MD5Init(&md5);
4520156c836Syasuoka 	MD5Update(&md5, idpass, 1);
4530156c836Syasuoka 	MD5Update(&md5, ent->password, strlen(ent->password));
4540156c836Syasuoka 	MD5Update(&md5, chall, challlen);
4550156c836Syasuoka 	MD5Final(digest, &md5);
4560156c836Syasuoka 
4570156c836Syasuoka 	ret = timingsafe_bcmp(idpass + 1, digest, sizeof(digest));
4580156c836Syasuoka 	log_info("q=%u User `%s' authentication %s (CHAP)", q_id, username,
4590156c836Syasuoka 	    (ret == 0)? "succeeded" : "failed");
4600156c836Syasuoka 	if ((respkt = radius_new_response_packet((ret == 0)?
4610156c836Syasuoka 	    RADIUS_CODE_ACCESS_ACCEPT : RADIUS_CODE_ACCESS_REJECT, radpkt))
4620156c836Syasuoka 	    == NULL) {
4630156c836Syasuoka 		log_warn("%s: radius_new_response_packet()", __func__);
4640156c836Syasuoka 		return;
4650156c836Syasuoka 	}
4660156c836Syasuoka 	module_accsreq_answer(self->base, q_id,
4670156c836Syasuoka 	    radius_get_data(respkt), radius_get_length(respkt));
4680156c836Syasuoka 	radius_delete_packet(respkt);
4690156c836Syasuoka }
4700156c836Syasuoka 
4710156c836Syasuoka void
4720156c836Syasuoka auth_mschapv2(struct module_file *self, u_int q_id, RADIUS_PACKET *radpkt,
4730156c836Syasuoka     char *username, struct module_file_userinfo *ent)
4740156c836Syasuoka {
4750156c836Syasuoka 	RADIUS_PACKET		*respkt = NULL;
4760156c836Syasuoka 	size_t			 attrlen;
4770156c836Syasuoka 	int			 i, lpass;
4780156c836Syasuoka 	char			*pass = NULL;
4790156c836Syasuoka 	uint8_t			 chall[MSCHAPV2_CHALLENGE_SZ];
4800156c836Syasuoka 	uint8_t			 ntresponse[24], authenticator[16];
4810156c836Syasuoka 	uint8_t			 pwhash[16], pwhash2[16], master[64];
4820156c836Syasuoka 	struct {
4830156c836Syasuoka 		uint8_t		 salt[2];
4840156c836Syasuoka 		uint8_t		 len;
4850156c836Syasuoka 		uint8_t		 key[16];
4860156c836Syasuoka 		uint8_t		 pad[15];
4870156c836Syasuoka 	} __packed		 rcvkey, sndkey;
4880156c836Syasuoka 	struct {
4890156c836Syasuoka 		uint8_t		 ident;
4900156c836Syasuoka 		uint8_t		 flags;
4910156c836Syasuoka 		uint8_t		 peerchall[16];
4920156c836Syasuoka 		uint8_t		 reserved[8];
4930156c836Syasuoka 		uint8_t		 ntresponse[24];
4940156c836Syasuoka 	} __packed		 resp;
4950156c836Syasuoka 	struct authresp {
4960156c836Syasuoka 		uint8_t		 ident;
4970156c836Syasuoka 		uint8_t		 authresp[42];
4980156c836Syasuoka 	} __packed		 authresp;
4990156c836Syasuoka 
5000156c836Syasuoka 
5010156c836Syasuoka 	attrlen = sizeof(chall);
5020156c836Syasuoka 	if (radius_get_vs_raw_attr(radpkt, RADIUS_VENDOR_MICROSOFT,
5030156c836Syasuoka 	    RADIUS_VTYPE_MS_CHAP_CHALLENGE, chall, &attrlen) != 0) {
5040156c836Syasuoka 		log_info("q=%u failed to retribute MS-CHAP-Challenge", q_id);
5050156c836Syasuoka 		goto on_error;
5060156c836Syasuoka 	}
5070156c836Syasuoka 	attrlen = sizeof(resp);
5080156c836Syasuoka 	if (radius_get_vs_raw_attr(radpkt, RADIUS_VENDOR_MICROSOFT,
5090156c836Syasuoka 	    RADIUS_VTYPE_MS_CHAP2_RESPONSE, &resp, &attrlen) != 0) {
5100156c836Syasuoka 		log_info("q=%u failed to retribute MS-CHAP2-Response", q_id);
5110156c836Syasuoka 		goto on_error;
5120156c836Syasuoka 	}
5130156c836Syasuoka 
5140156c836Syasuoka 	/* convert the password to UTF16-LE */
5150156c836Syasuoka 	lpass = strlen(ent->password);
5160156c836Syasuoka 	if ((pass = calloc(1, lpass * 2)) == NULL) {
5170156c836Syasuoka 		log_warn("%s: calloc()", __func__);
5180156c836Syasuoka 		goto on_error;
5190156c836Syasuoka 	}
5200156c836Syasuoka 	for (i = 0; i < lpass; i++) {
5210156c836Syasuoka 		pass[i * 2] = ent->password[i];
5220156c836Syasuoka 		pass[i * 2 + 1] = '\0';
5230156c836Syasuoka 	}
5240156c836Syasuoka 
5250156c836Syasuoka 	/* calculate NT-Response by the password */
5260156c836Syasuoka 	mschap_nt_response(chall, resp.peerchall,
5270156c836Syasuoka 	    username, strlen(username), pass, lpass * 2, ntresponse);
5280156c836Syasuoka 
5290156c836Syasuoka 	if (timingsafe_bcmp(ntresponse, resp.ntresponse, 24) != 0) {
5300156c836Syasuoka 		log_info("q=%u User `%s' authentication failed (MSCHAPv2)",
5310156c836Syasuoka 		    q_id, username);
5320156c836Syasuoka 		if ((respkt = radius_new_response_packet(
5330156c836Syasuoka 		    RADIUS_CODE_ACCESS_REJECT, radpkt)) == NULL) {
5340156c836Syasuoka 			log_warn("%s: radius_new_response_packet()", __func__);
5350156c836Syasuoka 			goto on_error;
5360156c836Syasuoka 		}
5370156c836Syasuoka 		authresp.ident = resp.ident;
5380156c836Syasuoka 		strlcpy(authresp.authresp, "E=691 R=0 V=3",
5390156c836Syasuoka 		    sizeof(authresp.authresp));
5400156c836Syasuoka 		radius_put_vs_raw_attr(respkt, RADIUS_VENDOR_MICROSOFT,
5410156c836Syasuoka 		    RADIUS_VTYPE_MS_CHAP_ERROR, &authresp,
5420156c836Syasuoka 		    offsetof(struct authresp, authresp[13]));
5430156c836Syasuoka 	} else {
5440156c836Syasuoka 		log_info("q=%u User `%s' authentication succeeded (MSCHAPv2)",
5450156c836Syasuoka 		    q_id, username);
5460156c836Syasuoka 		if ((respkt = radius_new_response_packet(
5470156c836Syasuoka 		    RADIUS_CODE_ACCESS_ACCEPT, radpkt)) == NULL) {
5480156c836Syasuoka 			log_warn("%s: radius_new_response_packet()", __func__);
5490156c836Syasuoka 			goto on_error;
5500156c836Syasuoka 		}
5510156c836Syasuoka 		mschap_auth_response(pass, lpass * 2, ntresponse, chall,
5520156c836Syasuoka 		    resp.peerchall, username, strlen(username),
5530156c836Syasuoka 		    authresp.authresp);
5540156c836Syasuoka 		authresp.ident = resp.ident;
5550156c836Syasuoka 
5560156c836Syasuoka 		radius_put_vs_raw_attr(respkt, RADIUS_VENDOR_MICROSOFT,
5570156c836Syasuoka 		    RADIUS_VTYPE_MS_CHAP2_SUCCESS, &authresp,
5580156c836Syasuoka 		    offsetof(struct authresp, authresp[42]));
5590156c836Syasuoka 
5600156c836Syasuoka 		mschap_ntpassword_hash(pass, lpass * 2, pwhash);
5610156c836Syasuoka 		mschap_ntpassword_hash(pwhash, sizeof(pwhash), pwhash2);
5620156c836Syasuoka 		mschap_masterkey(pwhash2, ntresponse, master);
5630156c836Syasuoka 		radius_get_authenticator(radpkt, authenticator);
5640156c836Syasuoka 
5650156c836Syasuoka 		/* MS-MPPE-Recv-Key  */
5660156c836Syasuoka 		memset(&rcvkey, 0, sizeof(rcvkey));
5670156c836Syasuoka 		arc4random_buf(rcvkey.salt, sizeof(rcvkey.salt));
5680156c836Syasuoka 		rcvkey.salt[0] |= 0x80;
569e303f425Syasuoka 		rcvkey.len = 16;
5700156c836Syasuoka 		mschap_asymetric_startkey(master, rcvkey.key, 16, 0, 1);
5710156c836Syasuoka 		radius_put_vs_raw_attr(respkt, RADIUS_VENDOR_MICROSOFT,
5720156c836Syasuoka 		    RADIUS_VTYPE_MPPE_RECV_KEY, &rcvkey, sizeof(rcvkey));
5730156c836Syasuoka 
5740156c836Syasuoka 		/* MS-MPPE-Send-Key  */
5750156c836Syasuoka 		memset(&sndkey, 0, sizeof(sndkey));
5760156c836Syasuoka 		arc4random_buf(sndkey.salt, sizeof(sndkey.salt));
5770156c836Syasuoka 		sndkey.salt[0] |= 0x80;
578e303f425Syasuoka 		sndkey.len = 16;
5790156c836Syasuoka 		mschap_asymetric_startkey(master, sndkey.key, 16, 1, 1);
5800156c836Syasuoka 		radius_put_vs_raw_attr(respkt, RADIUS_VENDOR_MICROSOFT,
5810156c836Syasuoka 		    RADIUS_VTYPE_MPPE_SEND_KEY, &sndkey, sizeof(sndkey));
5820156c836Syasuoka 	}
5830156c836Syasuoka 
5840156c836Syasuoka 	module_accsreq_answer(self->base, q_id,
5850156c836Syasuoka 	    radius_get_data(respkt), radius_get_length(respkt));
5860156c836Syasuoka  on_error:
5870156c836Syasuoka 	/* bzero password */
5880156c836Syasuoka 	explicit_bzero(ent->password, strlen(ent->password));
5890156c836Syasuoka 	if (pass != NULL)
5900156c836Syasuoka 		explicit_bzero(pass, lpass * 2);
5910156c836Syasuoka 	free(pass);
5920156c836Syasuoka 	if (respkt != NULL)
5930156c836Syasuoka 		radius_delete_packet(respkt);
5940156c836Syasuoka }
595e83d1c67Syasuoka 
596e83d1c67Syasuoka void
597e83d1c67Syasuoka auth_reject(struct module_file *self, u_int q_id, RADIUS_PACKET *radpkt,
598e83d1c67Syasuoka     char *username, struct module_file_userinfo *ent)
599e83d1c67Syasuoka {
600e83d1c67Syasuoka 	RADIUS_PACKET	*respkt = NULL;
601e83d1c67Syasuoka 
602e83d1c67Syasuoka 	if (ent != NULL)
603e83d1c67Syasuoka 		explicit_bzero(ent->password, strlen(ent->password));
604e83d1c67Syasuoka 
605e83d1c67Syasuoka 	log_info("q=%u User `%s' authentication failed", q_id,
606e83d1c67Syasuoka 	    username);
607e83d1c67Syasuoka 	if ((respkt = radius_new_response_packet(RADIUS_CODE_ACCESS_REJECT,
608e83d1c67Syasuoka 	    radpkt)) == NULL) {
609e83d1c67Syasuoka 		log_warn("%s: radius_new_response_packet()", __func__);
610e83d1c67Syasuoka 		return;
611e83d1c67Syasuoka 	}
612e83d1c67Syasuoka 	module_accsreq_answer(self->base, q_id,
613e83d1c67Syasuoka 	    radius_get_data(respkt), radius_get_length(respkt));
614e83d1c67Syasuoka 	radius_delete_packet(respkt);
615e83d1c67Syasuoka }
616