xref: /openbsd-src/usr.sbin/radiusd/radiusd_bsdauth.c (revision 7350f337b9e3eb4461d99580e625c7ef148d107c)
1 /*	$OpenBSD: radiusd_bsdauth.c,v 1.12 2019/04/01 11:05:41 yasuoka 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 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/socket.h>
22 #include <sys/uio.h>
23 #include <sys/wait.h>
24 
25 #include <bsd_auth.h>
26 #include <err.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <grp.h>
30 #include <imsg.h>
31 #include <login_cap.h>
32 #include <pwd.h>
33 #include <stdbool.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <syslog.h>
38 #include <unistd.h>
39 
40 #include "radiusd.h"
41 #include "radiusd_module.h"
42 
43 struct module_bsdauth {
44 	struct module_base	 *base;
45 	struct imsgbuf		  ibuf;
46 	char			**okgroups;
47 };
48 
49 /* IPC between priv and main */
50 enum {
51 	IMSG_BSDAUTH_OK = 1000,
52 	IMSG_BSDAUTH_NG,
53 	IMSG_BSDAUTH_USERCHECK,
54 	IMSG_BSDAUTH_GROUPCHECK
55 };
56 struct auth_usercheck_args {
57 	size_t	userlen;
58 	size_t	passlen;
59 };
60 struct auth_groupcheck_args {
61 	size_t	userlen;
62 	size_t	grouplen;
63 };
64 
65 __dead static void
66 		 module_bsdauth_main(void);
67 static void	 module_bsdauth_config_set(void *, const char *, int,
68 		    char * const *);
69 static void	 module_bsdauth_userpass(void *, u_int, const char *,
70 		    const char *);
71 static pid_t	 start_child(char *, int);
72 __dead static void
73 		 fatal(const char *);
74 
75 static struct module_handlers module_bsdauth_handlers = {
76 	.userpass = module_bsdauth_userpass,
77 	.config_set = module_bsdauth_config_set
78 };
79 
80 int
81 main(int argc, char *argv[])
82 {
83 	int		 ch, pairsock[2], status;
84 	struct imsgbuf	 ibuf;
85 	struct imsg	 imsg;
86 	ssize_t		 n;
87 	size_t		 datalen;
88 	pid_t		 pid;
89 	char		*saved_argv0;
90 
91 	while ((ch = getopt(argc, argv, "M")) != -1)
92 		switch (ch) {
93 		case 'M':
94 			module_bsdauth_main();
95 			/* never return, not rearched here */
96 			break;
97 		default:
98 			break;
99 		}
100 	saved_argv0 = argv[0];
101 	argc -= optind;
102 	argv += optind;
103 
104 	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, PF_UNSPEC,
105 	    pairsock) == -1)
106 		err(EXIT_FAILURE, "socketpair");
107 
108 	openlog(NULL, LOG_PID, LOG_DAEMON);
109 
110 	pid = start_child(saved_argv0, pairsock[1]);
111 
112 	/*
113 	 * Privileged process
114 	 */
115 	setproctitle("[priv]");
116 	imsg_init(&ibuf, pairsock[0]);
117 
118 	if (pledge("stdio getpw rpath proc exec", NULL) == -1)
119 		err(EXIT_FAILURE, "pledge");
120 
121 	for (;;) {
122 		if ((n = imsg_read(&ibuf)) <= 0 && errno != EAGAIN)
123 			break;
124 		for (;;) {
125 			if ((n = imsg_get(&ibuf, &imsg)) == -1)
126 				break;
127 			if (n == 0)
128 				break;
129 			datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
130 			switch (imsg.hdr.type) {
131 			case IMSG_BSDAUTH_USERCHECK:
132 			    {
133 				char		*user, *pass;
134 				bool		 authok = false;
135 				struct auth_usercheck_args
136 						*args;
137 
138 				if (datalen < sizeof(
139 				    struct auth_usercheck_args)) {
140 					syslog(LOG_ERR, "Short message");
141 					break;
142 				}
143 				args = (struct auth_usercheck_args *)imsg.data;
144 
145 				if (datalen < sizeof(struct auth_usercheck_args)
146 				    + args->userlen + args->passlen) {
147 					syslog(LOG_ERR, "Short message");
148 					break;
149 				}
150 				user = (char *)(args + 1);
151 				user[args->userlen - 1] = '\0';
152 				pass = user + args->userlen;
153 				pass[args->passlen - 1] = '\0';
154 
155 				if (auth_userokay(user, NULL, NULL, pass))
156 					authok = true;
157 				explicit_bzero(pass, args->passlen);
158 
159 				imsg_compose(&ibuf, (authok)
160 				    ? IMSG_BSDAUTH_OK : IMSG_BSDAUTH_NG,
161 				    0, 0, -1, NULL, 0);
162 				break;
163 			    }
164 			case IMSG_BSDAUTH_GROUPCHECK:
165 			    {
166 				int		 i;
167 				char		*user, *group;
168 				struct passwd   *pw;
169 				struct group	 gr0, *gr;
170 				char		 g_buf[4096];
171 				bool		 group_ok = false;
172 				struct auth_groupcheck_args
173 						*args;
174 
175 				if (datalen < sizeof(
176 				    struct auth_groupcheck_args)) {
177 					syslog(LOG_ERR, "Short message");
178 					break;
179 				}
180 				args = (struct auth_groupcheck_args *)imsg.data;
181 				if (datalen <
182 				    sizeof(struct auth_groupcheck_args) +
183 				    args->userlen + args->grouplen) {
184 					syslog(LOG_ERR, "Short message");
185 					break;
186 				}
187 				user = (char *)(args + 1);
188 				user[args->userlen - 1] = '\0';
189 				group = user + args->userlen;
190 				group[args->grouplen - 1] = '\0';
191 
192 				pw = getpwnam(user);
193 				if (getgrnam_r(group, &gr0, g_buf,
194 				    sizeof(g_buf), &gr) == -1 || gr == NULL)
195 					goto group_done;
196 
197 				if (gr->gr_gid == pw->pw_gid) {
198 					group_ok = true;
199 					goto group_done;
200 				}
201 				for (i = 0; gr->gr_mem[i] != NULL; i++) {
202 					if (strcmp(gr->gr_mem[i], pw->pw_name)
203 					    == 0) {
204 						group_ok = true;
205 						goto group_done;
206 					}
207 				}
208 group_done:
209 				endgrent();
210 
211 				imsg_compose(&ibuf, (group_ok)
212 				    ? IMSG_BSDAUTH_OK : IMSG_BSDAUTH_NG,
213 				    0, 0, -1, NULL, 0);
214 				break;
215 			    }
216 			}
217 			imsg_free(&imsg);
218 			imsg_flush(&ibuf);
219 		}
220 		imsg_flush(&ibuf);
221 	}
222 	imsg_clear(&ibuf);
223 
224 	while (waitpid(pid, &status, 0) == -1) {
225 		if (errno != EINTR)
226 			break;
227 	}
228 	exit(WEXITSTATUS(status));
229 }
230 
231 static void
232 module_bsdauth_main(void)
233 {
234 	int			 i;
235 	struct module_bsdauth	 module_bsdauth;
236 
237 	/*
238 	 * main process
239 	 */
240 	setproctitle("[main]");
241 	openlog(NULL, LOG_PID, LOG_DAEMON);
242 	memset(&module_bsdauth, 0, sizeof(module_bsdauth));
243 	if ((module_bsdauth.base = module_create(STDIN_FILENO, &module_bsdauth,
244 	    &module_bsdauth_handlers)) == NULL)
245 		err(1, "Could not create a module instance");
246 
247 	module_drop_privilege(module_bsdauth.base);
248 
249 	module_load(module_bsdauth.base);
250 	imsg_init(&module_bsdauth.ibuf, 3);
251 
252 	if (pledge("stdio proc", NULL) == -1)
253 		err(EXIT_FAILURE, "pledge");
254 
255 	while (module_run(module_bsdauth.base) == 0)
256 		;
257 
258 	module_destroy(module_bsdauth.base);
259 	imsg_clear(&module_bsdauth.ibuf);
260 
261 	if (module_bsdauth.okgroups) {
262 		for (i = 0; module_bsdauth.okgroups[i] != NULL; i++)
263 			free(module_bsdauth.okgroups[i]);
264 	}
265 	free(module_bsdauth.okgroups);
266 
267 	exit(EXIT_SUCCESS);
268 }
269 
270 static void
271 module_bsdauth_config_set(void *ctx, const char *name, int argc,
272     char * const * argv)
273 {
274 	struct module_bsdauth	 *module = ctx;
275 	int			  i;
276 	char			**groups = NULL;
277 
278 	if (strcmp(name, "restrict-group") == 0) {
279 		if (module->okgroups != NULL) {
280 			module_send_message(module->base, IMSG_NG,
281 			    "`restrict-group' is already defined");
282 			goto on_error;
283 		}
284 		if ((groups = calloc(sizeof(char *), argc + 1)) == NULL) {
285 			module_send_message(module->base, IMSG_NG,
286 			    "Out of memory");
287 			goto on_error;
288 		}
289 		for (i = 0; i < argc; i++) {
290 			if ((groups[i] = strdup(argv[i])) == NULL) {
291 				module_send_message(module->base,
292 				    IMSG_NG, "Out of memory");
293 				goto on_error;
294 			}
295 		}
296 		groups[i] = NULL;
297 		module->okgroups = groups;
298 		module_send_message(module->base, IMSG_OK, NULL);
299 	} else if (strncmp(name, "_", 1) == 0)
300 		/* ignore all internal messages */
301 		module_send_message(module->base, IMSG_OK, NULL);
302 	else
303 		module_send_message(module->base, IMSG_NG,
304 		    "Unknown config parameter `%s'", name);
305 	return;
306 on_error:
307 	if (groups != NULL) {
308 		for (i = 0; groups[i] != NULL; i++)
309 			free(groups[i]);
310 		free(groups);
311 	}
312 	return;
313 }
314 
315 
316 static void
317 module_bsdauth_userpass(void *ctx, u_int q_id, const char *user,
318     const char *pass)
319 {
320 	struct module_bsdauth	*module = ctx;
321 	struct auth_usercheck_args
322 				 usercheck;
323 	struct auth_groupcheck_args
324 				 groupcheck;
325 	struct iovec		iov[4];
326 	const char		*group;
327 	u_int			 i;
328 	const char		*reason;
329 	struct imsg		 imsg;
330 	ssize_t			 n;
331 
332 	memset(&imsg, 0, sizeof(imsg));
333 	if (pass == NULL)
334 		pass = "";
335 
336 	usercheck.userlen = strlen(user) + 1;
337 	usercheck.passlen = strlen(pass) + 1;
338 	iov[0].iov_base = &usercheck;
339 	iov[0].iov_len = sizeof(usercheck);
340 	iov[1].iov_base = (char *)user;
341 	iov[1].iov_len = usercheck.userlen;
342 	iov[2].iov_base = (char *)pass;
343 	iov[2].iov_len = usercheck.passlen;
344 
345 	imsg_composev(&module->ibuf, IMSG_BSDAUTH_USERCHECK, 0, 0, -1, iov, 3);
346 	imsg_flush(&module->ibuf);
347 	if ((n = imsg_read(&module->ibuf)) == -1 || n == 0)
348 		fatal("imsg_read() failed in module_bsdauth_userpass()");
349 	if ((n = imsg_get(&module->ibuf, &imsg)) <= 0)
350 		fatal("imsg_get() failed in module_bsdauth_userpass()");
351 
352 	if (imsg.hdr.type != IMSG_BSDAUTH_OK) {
353 		reason = "Authentication failed";
354 		goto auth_ng;
355 	}
356 	if (module->okgroups != NULL) {
357 		reason = "Group restriction is not allowed";
358 		for (i = 0; module->okgroups[i] != NULL; i++) {
359 			group = module->okgroups[i];
360 
361 			groupcheck.userlen = strlen(user) + 1;
362 			groupcheck.grouplen = strlen(group) + 1;
363 			iov[0].iov_base = &groupcheck;
364 			iov[0].iov_len = sizeof(groupcheck);
365 			iov[1].iov_base = (char *)user;
366 			iov[1].iov_len = groupcheck.userlen;
367 			iov[2].iov_base = (char *)group;
368 			iov[2].iov_len = groupcheck.grouplen;
369 			imsg_composev(&module->ibuf, IMSG_BSDAUTH_GROUPCHECK,
370 			    0, 0, -1, iov, 3);
371 			imsg_flush(&module->ibuf);
372 			if ((n = imsg_read(&module->ibuf)) == -1 || n == 0)
373 				fatal("imsg_read() failed in "
374 				    "module_bsdauth_userpass()");
375 			if ((n = imsg_get(&module->ibuf, &imsg)) <= 0)
376 				fatal("imsg_get() failed in "
377 				    "module_bsdauth_userpass()");
378 			if (imsg.hdr.type == IMSG_BSDAUTH_OK)
379 				goto group_ok;
380 		}
381 		goto auth_ng;
382 	}
383 group_ok:
384 	module_userpass_ok(module->base, q_id, "Authentication succeeded");
385 	imsg_free(&imsg);
386 	return;
387 auth_ng:
388 	module_userpass_fail(module->base, q_id, reason);
389 	imsg_free(&imsg);
390 	return;
391 }
392 
393 pid_t
394 start_child(char *argv0, int fd)
395 {
396 	char *argv[5];
397 	int argc = 0;
398 	pid_t pid;
399 
400 	switch (pid = fork()) {
401 	case -1:
402 		fatal("cannot fork");
403 	case 0:
404 		break;
405 	default:
406 		close(fd);
407 		return (pid);
408 	}
409 
410 	if (fd != 3) {
411 		if (dup2(fd, 3) == -1)
412 			fatal("cannot setup imsg fd");
413 	} else if (fcntl(fd, F_SETFD, 0) == -1)
414 		fatal("cannot setup imsg fd");
415 
416 	argv[argc++] = argv0;
417 	argv[argc++] = "-M";	/* main proc */
418 	execvp(argv0, argv);
419 	fatal("execvp");
420 }
421 
422 static void
423 fatal(const char *msg)
424 {
425 	syslog(LOG_ERR, "%s: %m", msg);
426 	abort();
427 }
428