xref: /openbsd-src/usr.sbin/radiusd/radiusd_bsdauth.c (revision 882428cdbdd2944d8f59bc8621c131fd814fb6ee)
1*882428cdSclaudio /*	$OpenBSD: radiusd_bsdauth.c,v 1.19 2024/11/21 13:43:10 claudio Exp $	*/
2a7ca44b8Syasuoka 
3a7ca44b8Syasuoka /*
4a7ca44b8Syasuoka  * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
5a7ca44b8Syasuoka  *
6a7ca44b8Syasuoka  * Permission to use, copy, modify, and distribute this software for any
7a7ca44b8Syasuoka  * purpose with or without fee is hereby granted, provided that the above
8a7ca44b8Syasuoka  * copyright notice and this permission notice appear in all copies.
9a7ca44b8Syasuoka  *
10a7ca44b8Syasuoka  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11a7ca44b8Syasuoka  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12a7ca44b8Syasuoka  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13a7ca44b8Syasuoka  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14a7ca44b8Syasuoka  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15a7ca44b8Syasuoka  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16a7ca44b8Syasuoka  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17a7ca44b8Syasuoka  */
18a7ca44b8Syasuoka 
19a7ca44b8Syasuoka #include <sys/types.h>
20a7ca44b8Syasuoka #include <sys/queue.h>
213d3cf35cSyasuoka #include <sys/socket.h>
223d3cf35cSyasuoka #include <sys/uio.h>
233d3cf35cSyasuoka #include <sys/wait.h>
24a7ca44b8Syasuoka 
25a7ca44b8Syasuoka #include <bsd_auth.h>
26a7ca44b8Syasuoka #include <err.h>
275ae85b70Sclaudio #include <errno.h>
28f1312718Syasuoka #include <fcntl.h>
29a7ca44b8Syasuoka #include <grp.h>
303d3cf35cSyasuoka #include <imsg.h>
31a7ca44b8Syasuoka #include <login_cap.h>
32a7ca44b8Syasuoka #include <pwd.h>
33a7ca44b8Syasuoka #include <stdbool.h>
343d3cf35cSyasuoka #include <stdio.h>
35a7ca44b8Syasuoka #include <stdlib.h>
36a7ca44b8Syasuoka #include <string.h>
37a7ca44b8Syasuoka #include <syslog.h>
38a7ca44b8Syasuoka #include <unistd.h>
39a7ca44b8Syasuoka 
40a7ca44b8Syasuoka #include "radiusd.h"
41a7ca44b8Syasuoka #include "radiusd_module.h"
42a7ca44b8Syasuoka 
43a7ca44b8Syasuoka struct module_bsdauth {
44a7ca44b8Syasuoka 	struct module_base	 *base;
453d3cf35cSyasuoka 	struct imsgbuf		  ibuf;
46a7ca44b8Syasuoka 	char			**okgroups;
47a7ca44b8Syasuoka };
48a7ca44b8Syasuoka 
493d3cf35cSyasuoka /* IPC between priv and main */
503d3cf35cSyasuoka enum {
513d3cf35cSyasuoka 	IMSG_BSDAUTH_OK = 1000,
523d3cf35cSyasuoka 	IMSG_BSDAUTH_NG,
533d3cf35cSyasuoka 	IMSG_BSDAUTH_USERCHECK,
543d3cf35cSyasuoka 	IMSG_BSDAUTH_GROUPCHECK
553d3cf35cSyasuoka };
563d3cf35cSyasuoka struct auth_usercheck_args {
573d3cf35cSyasuoka 	size_t	userlen;
583d3cf35cSyasuoka 	size_t	passlen;
593d3cf35cSyasuoka };
603d3cf35cSyasuoka struct auth_groupcheck_args {
613d3cf35cSyasuoka 	size_t	userlen;
623d3cf35cSyasuoka 	size_t	grouplen;
633d3cf35cSyasuoka };
643d3cf35cSyasuoka 
65f1312718Syasuoka __dead static void
66f1312718Syasuoka 		 module_bsdauth_main(void);
67a7ca44b8Syasuoka static void	 module_bsdauth_config_set(void *, const char *, int,
68a7ca44b8Syasuoka 		    char * const *);
693d3cf35cSyasuoka static void	 module_bsdauth_userpass(void *, u_int, const char *,
703d3cf35cSyasuoka 		    const char *);
71f1312718Syasuoka static pid_t	 start_child(char *, int);
723d3cf35cSyasuoka __dead static void
733d3cf35cSyasuoka 		 fatal(const char *);
74a7ca44b8Syasuoka 
75a7ca44b8Syasuoka static struct module_handlers module_bsdauth_handlers = {
76a7ca44b8Syasuoka 	.userpass = module_bsdauth_userpass,
77a7ca44b8Syasuoka 	.config_set = module_bsdauth_config_set
78a7ca44b8Syasuoka };
79a7ca44b8Syasuoka 
80a7ca44b8Syasuoka int
81a7ca44b8Syasuoka main(int argc, char *argv[])
82a7ca44b8Syasuoka {
83f1312718Syasuoka 	int		 ch, pairsock[2], status;
843d3cf35cSyasuoka 	struct imsgbuf	 ibuf;
853d3cf35cSyasuoka 	struct imsg	 imsg;
863d3cf35cSyasuoka 	ssize_t		 n;
873d3cf35cSyasuoka 	size_t		 datalen;
882aeb6b04Sderaadt 	pid_t		 pid;
89f1312718Syasuoka 	char		*saved_argv0;
903d3cf35cSyasuoka 
91f1312718Syasuoka 	while ((ch = getopt(argc, argv, "M")) != -1)
92f1312718Syasuoka 		switch (ch) {
93f1312718Syasuoka 		case 'M':
94f1312718Syasuoka 			module_bsdauth_main();
957a016c8eSyasuoka 			/* never return, not rearched here */
96f1312718Syasuoka 			break;
97f1312718Syasuoka 		default:
98f1312718Syasuoka 			break;
99f1312718Syasuoka 		}
100f1312718Syasuoka 	saved_argv0 = argv[0];
101f1312718Syasuoka 	argc -= optind;
102f1312718Syasuoka 	argv += optind;
103f1312718Syasuoka 
1047a016c8eSyasuoka 	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, PF_UNSPEC,
1057a016c8eSyasuoka 	    pairsock) == -1)
1063d3cf35cSyasuoka 		err(EXIT_FAILURE, "socketpair");
1073d3cf35cSyasuoka 
108f1312718Syasuoka 	openlog(NULL, LOG_PID, LOG_DAEMON);
109f1312718Syasuoka 
110f1312718Syasuoka 	pid = start_child(saved_argv0, pairsock[1]);
1113d3cf35cSyasuoka 
1123d3cf35cSyasuoka 	/*
113be657a2dSyasuoka 	 * Privileged process
1143d3cf35cSyasuoka 	 */
1153d3cf35cSyasuoka 	setproctitle("[priv]");
116*882428cdSclaudio 	if (imsgbuf_init(&ibuf, pairsock[0]) == 1)
117*882428cdSclaudio 		err(EXIT_FAILURE, "imsgbuf_init");
11804581dc7Syasuoka 
11904581dc7Syasuoka 	if (pledge("stdio getpw rpath proc exec", NULL) == -1)
12004581dc7Syasuoka 		err(EXIT_FAILURE, "pledge");
12104581dc7Syasuoka 
1223d3cf35cSyasuoka 	for (;;) {
1234f3fb1ffSclaudio 		if (imsgbuf_read(&ibuf) != 1)
1243d3cf35cSyasuoka 			break;
1253d3cf35cSyasuoka 		for (;;) {
1263d3cf35cSyasuoka 			if ((n = imsg_get(&ibuf, &imsg)) == -1)
1273d3cf35cSyasuoka 				break;
1283d3cf35cSyasuoka 			if (n == 0)
1293d3cf35cSyasuoka 				break;
1303d3cf35cSyasuoka 			datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
1313d3cf35cSyasuoka 			switch (imsg.hdr.type) {
1323d3cf35cSyasuoka 			case IMSG_BSDAUTH_USERCHECK:
1333d3cf35cSyasuoka 			    {
1343d3cf35cSyasuoka 				char		*user, *pass;
1353d3cf35cSyasuoka 				bool		 authok = false;
1363d3cf35cSyasuoka 				struct auth_usercheck_args
1373d3cf35cSyasuoka 						*args;
1383d3cf35cSyasuoka 
1393d3cf35cSyasuoka 				if (datalen < sizeof(
1403d3cf35cSyasuoka 				    struct auth_usercheck_args)) {
1413d3cf35cSyasuoka 					syslog(LOG_ERR, "Short message");
1423d3cf35cSyasuoka 					break;
1433d3cf35cSyasuoka 				}
1443d3cf35cSyasuoka 				args = (struct auth_usercheck_args *)imsg.data;
1453d3cf35cSyasuoka 
1463d3cf35cSyasuoka 				if (datalen < sizeof(struct auth_usercheck_args)
1473d3cf35cSyasuoka 				    + args->userlen + args->passlen) {
1483d3cf35cSyasuoka 					syslog(LOG_ERR, "Short message");
1493d3cf35cSyasuoka 					break;
1503d3cf35cSyasuoka 				}
1513d3cf35cSyasuoka 				user = (char *)(args + 1);
1523d3cf35cSyasuoka 				user[args->userlen - 1] = '\0';
1533d3cf35cSyasuoka 				pass = user + args->userlen;
1543d3cf35cSyasuoka 				pass[args->passlen - 1] = '\0';
1553d3cf35cSyasuoka 
1563d3cf35cSyasuoka 				if (auth_userokay(user, NULL, NULL, pass))
1573d3cf35cSyasuoka 					authok = true;
1583d3cf35cSyasuoka 				explicit_bzero(pass, args->passlen);
1593d3cf35cSyasuoka 
1603d3cf35cSyasuoka 				imsg_compose(&ibuf, (authok)
1613d3cf35cSyasuoka 				    ? IMSG_BSDAUTH_OK : IMSG_BSDAUTH_NG,
1623d3cf35cSyasuoka 				    0, 0, -1, NULL, 0);
1633d3cf35cSyasuoka 				break;
1643d3cf35cSyasuoka 			    }
1653d3cf35cSyasuoka 			case IMSG_BSDAUTH_GROUPCHECK:
1663d3cf35cSyasuoka 			    {
1673d3cf35cSyasuoka 				int		 i;
1683d3cf35cSyasuoka 				char		*user, *group;
1693d3cf35cSyasuoka 				struct passwd   *pw;
1703d3cf35cSyasuoka 				struct group	 gr0, *gr;
1713d3cf35cSyasuoka 				char		 g_buf[4096];
1723d3cf35cSyasuoka 				bool		 group_ok = false;
1733d3cf35cSyasuoka 				struct auth_groupcheck_args
1743d3cf35cSyasuoka 						*args;
1753d3cf35cSyasuoka 
1763d3cf35cSyasuoka 				if (datalen < sizeof(
1773d3cf35cSyasuoka 				    struct auth_groupcheck_args)) {
1783d3cf35cSyasuoka 					syslog(LOG_ERR, "Short message");
1793d3cf35cSyasuoka 					break;
1803d3cf35cSyasuoka 				}
1813d3cf35cSyasuoka 				args = (struct auth_groupcheck_args *)imsg.data;
1827a016c8eSyasuoka 				if (datalen <
1837a016c8eSyasuoka 				    sizeof(struct auth_groupcheck_args) +
1843d3cf35cSyasuoka 				    args->userlen + args->grouplen) {
1853d3cf35cSyasuoka 					syslog(LOG_ERR, "Short message");
1863d3cf35cSyasuoka 					break;
1873d3cf35cSyasuoka 				}
1883d3cf35cSyasuoka 				user = (char *)(args + 1);
1893d3cf35cSyasuoka 				user[args->userlen - 1] = '\0';
1903d3cf35cSyasuoka 				group = user + args->userlen;
1913d3cf35cSyasuoka 				group[args->grouplen - 1] = '\0';
1923d3cf35cSyasuoka 
1930914537fSmillert 				user[strcspn(user, ":")] = '\0';
1943d3cf35cSyasuoka 				pw = getpwnam(user);
195502b49aaSderaadt 				if (pw == NULL)
196502b49aaSderaadt 					goto invalid;
1973d3cf35cSyasuoka 				if (getgrnam_r(group, &gr0, g_buf,
198f6b02885Syasuoka 				    sizeof(g_buf), &gr) == -1 || gr == NULL)
199502b49aaSderaadt 					goto invalid;
2003d3cf35cSyasuoka 
2013d3cf35cSyasuoka 				if (gr->gr_gid == pw->pw_gid) {
2023d3cf35cSyasuoka 					group_ok = true;
203502b49aaSderaadt 					goto invalid;
2043d3cf35cSyasuoka 				}
2053d3cf35cSyasuoka 				for (i = 0; gr->gr_mem[i] != NULL; i++) {
2063d3cf35cSyasuoka 					if (strcmp(gr->gr_mem[i], pw->pw_name)
2073d3cf35cSyasuoka 					    == 0) {
2083d3cf35cSyasuoka 						group_ok = true;
209502b49aaSderaadt 						goto invalid;
2103d3cf35cSyasuoka 					}
2113d3cf35cSyasuoka 				}
212502b49aaSderaadt invalid:
2133d3cf35cSyasuoka 				endgrent();
2143d3cf35cSyasuoka 
2153d3cf35cSyasuoka 				imsg_compose(&ibuf, (group_ok)
2163d3cf35cSyasuoka 				    ? IMSG_BSDAUTH_OK : IMSG_BSDAUTH_NG,
2173d3cf35cSyasuoka 				    0, 0, -1, NULL, 0);
2183d3cf35cSyasuoka 				break;
2193d3cf35cSyasuoka 			    }
2203d3cf35cSyasuoka 			}
2216da13141Syasuoka 			imsg_free(&imsg);
222dd7efffeSclaudio 			imsgbuf_flush(&ibuf);
2233d3cf35cSyasuoka 		}
224dd7efffeSclaudio 		imsgbuf_flush(&ibuf);
2253d3cf35cSyasuoka 	}
226dd7efffeSclaudio 	imsgbuf_clear(&ibuf);
2273d3cf35cSyasuoka 
2282aeb6b04Sderaadt 	while (waitpid(pid, &status, 0) == -1) {
2292aeb6b04Sderaadt 		if (errno != EINTR)
2302aeb6b04Sderaadt 			break;
2312aeb6b04Sderaadt 	}
2323d3cf35cSyasuoka 	exit(WEXITSTATUS(status));
2333d3cf35cSyasuoka }
2343d3cf35cSyasuoka 
235f1312718Syasuoka static void
236f1312718Syasuoka module_bsdauth_main(void)
2373d3cf35cSyasuoka {
2383d3cf35cSyasuoka 	int			 i;
239a7ca44b8Syasuoka 	struct module_bsdauth	 module_bsdauth;
240a7ca44b8Syasuoka 
241f1312718Syasuoka 	/*
242f1312718Syasuoka 	 * main process
243f1312718Syasuoka 	 */
2443d3cf35cSyasuoka 	setproctitle("[main]");
245a7ca44b8Syasuoka 	openlog(NULL, LOG_PID, LOG_DAEMON);
2463d3cf35cSyasuoka 	memset(&module_bsdauth, 0, sizeof(module_bsdauth));
247a7ca44b8Syasuoka 	if ((module_bsdauth.base = module_create(STDIN_FILENO, &module_bsdauth,
248a7ca44b8Syasuoka 	    &module_bsdauth_handlers)) == NULL)
249a7ca44b8Syasuoka 		err(1, "Could not create a module instance");
250a7ca44b8Syasuoka 
251edd79a0eSyasuoka 	module_drop_privilege(module_bsdauth.base, 0);
2523d3cf35cSyasuoka 
253a7ca44b8Syasuoka 	module_load(module_bsdauth.base);
254*882428cdSclaudio 	if (imsgbuf_init(&module_bsdauth.ibuf, 3) == -1)
255*882428cdSclaudio 		err(EXIT_FAILURE, "imsgbuf_init");
25604581dc7Syasuoka 
25704581dc7Syasuoka 	if (pledge("stdio proc", NULL) == -1)
25804581dc7Syasuoka 		err(EXIT_FAILURE, "pledge");
25904581dc7Syasuoka 
260a7ca44b8Syasuoka 	while (module_run(module_bsdauth.base) == 0)
261a7ca44b8Syasuoka 		;
262a7ca44b8Syasuoka 
263a7ca44b8Syasuoka 	module_destroy(module_bsdauth.base);
264dd7efffeSclaudio 	imsgbuf_clear(&module_bsdauth.ibuf);
265a7ca44b8Syasuoka 
2663d3cf35cSyasuoka 	if (module_bsdauth.okgroups) {
2673d3cf35cSyasuoka 		for (i = 0; module_bsdauth.okgroups[i] != NULL; i++)
2683d3cf35cSyasuoka 			free(module_bsdauth.okgroups[i]);
2693d3cf35cSyasuoka 	}
2703d3cf35cSyasuoka 	free(module_bsdauth.okgroups);
2713d3cf35cSyasuoka 
272f1312718Syasuoka 	exit(EXIT_SUCCESS);
273a7ca44b8Syasuoka }
274a7ca44b8Syasuoka 
275a7ca44b8Syasuoka static void
276a7ca44b8Syasuoka module_bsdauth_config_set(void *ctx, const char *name, int argc,
277a7ca44b8Syasuoka     char * const * argv)
278a7ca44b8Syasuoka {
27958e9bc95Syasuoka 	struct module_bsdauth	 *module = ctx;
280a7ca44b8Syasuoka 	int			  i;
281a7ca44b8Syasuoka 	char			**groups = NULL;
282a7ca44b8Syasuoka 
283a7ca44b8Syasuoka 	if (strcmp(name, "restrict-group") == 0) {
28458e9bc95Syasuoka 		if (module->okgroups != NULL) {
28558e9bc95Syasuoka 			module_send_message(module->base, IMSG_NG,
286a7ca44b8Syasuoka 			    "`restrict-group' is already defined");
287a7ca44b8Syasuoka 			goto on_error;
288a7ca44b8Syasuoka 		}
289a7ca44b8Syasuoka 		if ((groups = calloc(sizeof(char *), argc + 1)) == NULL) {
29058e9bc95Syasuoka 			module_send_message(module->base, IMSG_NG,
291a7ca44b8Syasuoka 			    "Out of memory");
292a7ca44b8Syasuoka 			goto on_error;
293a7ca44b8Syasuoka 		}
294a7ca44b8Syasuoka 		for (i = 0; i < argc; i++) {
295a7ca44b8Syasuoka 			if ((groups[i] = strdup(argv[i])) == NULL) {
29658e9bc95Syasuoka 				module_send_message(module->base,
297a7ca44b8Syasuoka 				    IMSG_NG, "Out of memory");
298a7ca44b8Syasuoka 				goto on_error;
299a7ca44b8Syasuoka 			}
300a7ca44b8Syasuoka 		}
301a7ca44b8Syasuoka 		groups[i] = NULL;
30258e9bc95Syasuoka 		module->okgroups = groups;
30358e9bc95Syasuoka 		module_send_message(module->base, IMSG_OK, NULL);
30416971584Syasuoka 	} else if (strncmp(name, "_", 1) == 0)
30516971584Syasuoka 		/* ignore all internal messages */
30616971584Syasuoka 		module_send_message(module->base, IMSG_OK, NULL);
30716971584Syasuoka 	else
30858e9bc95Syasuoka 		module_send_message(module->base, IMSG_NG,
309a7ca44b8Syasuoka 		    "Unknown config parameter `%s'", name);
310a7ca44b8Syasuoka 	return;
311a7ca44b8Syasuoka on_error:
312a7ca44b8Syasuoka 	if (groups != NULL) {
313a7ca44b8Syasuoka 		for (i = 0; groups[i] != NULL; i++)
314a7ca44b8Syasuoka 			free(groups[i]);
315a7ca44b8Syasuoka 		free(groups);
316a7ca44b8Syasuoka 	}
317a7ca44b8Syasuoka 	return;
318a7ca44b8Syasuoka }
319a7ca44b8Syasuoka 
320a7ca44b8Syasuoka 
321a7ca44b8Syasuoka static void
322a7ca44b8Syasuoka module_bsdauth_userpass(void *ctx, u_int q_id, const char *user,
323a7ca44b8Syasuoka     const char *pass)
324a7ca44b8Syasuoka {
32558e9bc95Syasuoka 	struct module_bsdauth	*module = ctx;
3263d3cf35cSyasuoka 	struct auth_usercheck_args
3273d3cf35cSyasuoka 				 usercheck;
3283d3cf35cSyasuoka 	struct auth_groupcheck_args
3293d3cf35cSyasuoka 				 groupcheck;
3303d3cf35cSyasuoka 	struct iovec		iov[4];
3313d3cf35cSyasuoka 	const char		*group;
3323d3cf35cSyasuoka 	u_int			 i;
333a7ca44b8Syasuoka 	const char		*reason;
3343d3cf35cSyasuoka 	struct imsg		 imsg;
3353d3cf35cSyasuoka 	ssize_t			 n;
336a7ca44b8Syasuoka 
3373d3cf35cSyasuoka 	memset(&imsg, 0, sizeof(imsg));
338a7ca44b8Syasuoka 	if (pass == NULL)
339a7ca44b8Syasuoka 		pass = "";
340a7ca44b8Syasuoka 
3413d3cf35cSyasuoka 	usercheck.userlen = strlen(user) + 1;
3423d3cf35cSyasuoka 	usercheck.passlen = strlen(pass) + 1;
3433d3cf35cSyasuoka 	iov[0].iov_base = &usercheck;
3443d3cf35cSyasuoka 	iov[0].iov_len = sizeof(usercheck);
3453d3cf35cSyasuoka 	iov[1].iov_base = (char *)user;
3463d3cf35cSyasuoka 	iov[1].iov_len = usercheck.userlen;
3473d3cf35cSyasuoka 	iov[2].iov_base = (char *)pass;
3483d3cf35cSyasuoka 	iov[2].iov_len = usercheck.passlen;
3493d3cf35cSyasuoka 
35058e9bc95Syasuoka 	imsg_composev(&module->ibuf, IMSG_BSDAUTH_USERCHECK, 0, 0, -1, iov, 3);
351dd7efffeSclaudio 	imsgbuf_flush(&module->ibuf);
3524f3fb1ffSclaudio 	if (imsgbuf_read(&module->ibuf) != 1)
353dd7efffeSclaudio 		fatal("imsgbuf_read() failed in module_bsdauth_userpass()");
35458e9bc95Syasuoka 	if ((n = imsg_get(&module->ibuf, &imsg)) <= 0)
3553d3cf35cSyasuoka 		fatal("imsg_get() failed in module_bsdauth_userpass()");
3563d3cf35cSyasuoka 
3573d3cf35cSyasuoka 	if (imsg.hdr.type != IMSG_BSDAUTH_OK) {
358a7ca44b8Syasuoka 		reason = "Authentication failed";
359a7ca44b8Syasuoka 		goto auth_ng;
360a7ca44b8Syasuoka 	}
36158e9bc95Syasuoka 	if (module->okgroups != NULL) {
362a7ca44b8Syasuoka 		reason = "Group restriction is not allowed";
36358e9bc95Syasuoka 		for (i = 0; module->okgroups[i] != NULL; i++) {
36458e9bc95Syasuoka 			group = module->okgroups[i];
3653d3cf35cSyasuoka 
3663d3cf35cSyasuoka 			groupcheck.userlen = strlen(user) + 1;
3673d3cf35cSyasuoka 			groupcheck.grouplen = strlen(group) + 1;
3683d3cf35cSyasuoka 			iov[0].iov_base = &groupcheck;
3693d3cf35cSyasuoka 			iov[0].iov_len = sizeof(groupcheck);
3703d3cf35cSyasuoka 			iov[1].iov_base = (char *)user;
3713d3cf35cSyasuoka 			iov[1].iov_len = groupcheck.userlen;
3723d3cf35cSyasuoka 			iov[2].iov_base = (char *)group;
3733d3cf35cSyasuoka 			iov[2].iov_len = groupcheck.grouplen;
37458e9bc95Syasuoka 			imsg_composev(&module->ibuf, IMSG_BSDAUTH_GROUPCHECK,
3753d3cf35cSyasuoka 			    0, 0, -1, iov, 3);
376dd7efffeSclaudio 			imsgbuf_flush(&module->ibuf);
3774f3fb1ffSclaudio 			if (imsgbuf_read(&module->ibuf) != 1)
378dd7efffeSclaudio 				fatal("imsgbuf_read() failed in "
3793d3cf35cSyasuoka 				    "module_bsdauth_userpass()");
38058e9bc95Syasuoka 			if ((n = imsg_get(&module->ibuf, &imsg)) <= 0)
3813d3cf35cSyasuoka 				fatal("imsg_get() failed in "
3823d3cf35cSyasuoka 				    "module_bsdauth_userpass()");
3833d3cf35cSyasuoka 			if (imsg.hdr.type == IMSG_BSDAUTH_OK)
384a7ca44b8Syasuoka 				goto group_ok;
385a7ca44b8Syasuoka 		}
386a7ca44b8Syasuoka 		goto auth_ng;
387a7ca44b8Syasuoka 	}
3883d3cf35cSyasuoka group_ok:
38958e9bc95Syasuoka 	module_userpass_ok(module->base, q_id, "Authentication succeeded");
3903d3cf35cSyasuoka 	imsg_free(&imsg);
391a7ca44b8Syasuoka 	return;
392a7ca44b8Syasuoka auth_ng:
39358e9bc95Syasuoka 	module_userpass_fail(module->base, q_id, reason);
3943d3cf35cSyasuoka 	imsg_free(&imsg);
395a7ca44b8Syasuoka 	return;
396a7ca44b8Syasuoka }
3973d3cf35cSyasuoka 
398f1312718Syasuoka pid_t
399f1312718Syasuoka start_child(char *argv0, int fd)
400f1312718Syasuoka {
401f1312718Syasuoka 	char *argv[5];
402f1312718Syasuoka 	int argc = 0;
403f1312718Syasuoka 	pid_t pid;
404f1312718Syasuoka 
405f1312718Syasuoka 	switch (pid = fork()) {
406f1312718Syasuoka 	case -1:
407f1312718Syasuoka 		fatal("cannot fork");
408f1312718Syasuoka 	case 0:
409f1312718Syasuoka 		break;
410f1312718Syasuoka 	default:
411f1312718Syasuoka 		close(fd);
412f1312718Syasuoka 		return (pid);
413f1312718Syasuoka 	}
414f1312718Syasuoka 
415f1312718Syasuoka 	if (fd != 3) {
416f1312718Syasuoka 		if (dup2(fd, 3) == -1)
417f1312718Syasuoka 			fatal("cannot setup imsg fd");
418f1312718Syasuoka 	} else if (fcntl(fd, F_SETFD, 0) == -1)
419f1312718Syasuoka 		fatal("cannot setup imsg fd");
420f1312718Syasuoka 
421f1312718Syasuoka 	argv[argc++] = argv0;
422f1312718Syasuoka 	argv[argc++] = "-M";	/* main proc */
42375c5cf40Syasuoka 	argv[argc++] = NULL;
424f1312718Syasuoka 	execvp(argv0, argv);
425f1312718Syasuoka 	fatal("execvp");
426f1312718Syasuoka }
427f1312718Syasuoka 
4283d3cf35cSyasuoka static void
4293d3cf35cSyasuoka fatal(const char *msg)
4303d3cf35cSyasuoka {
4313d3cf35cSyasuoka 	syslog(LOG_ERR, "%s: %m", msg);
4323d3cf35cSyasuoka 	abort();
4333d3cf35cSyasuoka }
434