1 /*	$OpenBSD: radiusd_bsdauth.c,v 1.14 2019/12/14 15:56:20 millert 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 				user[strcspn(user, ":")] = '\0';
193 				pw = getpwnam(user);
194 				if (pw == NULL)
195 					goto invalid;
196 				if (getgrnam_r(group, &gr0, g_buf,
197 				    sizeof(g_buf), &gr) == -1 || gr == NULL)
198 					goto invalid;
199 
200 				if (gr->gr_gid == pw->pw_gid) {
201 					group_ok = true;
202 					goto invalid;
203 				}
204 				for (i = 0; gr->gr_mem[i] != NULL; i++) {
205 					if (strcmp(gr->gr_mem[i], pw->pw_name)
206 					    == 0) {
207 						group_ok = true;
208 						goto invalid;
209 					}
210 				}
211 invalid:
212 				endgrent();
213 
214 				imsg_compose(&ibuf, (group_ok)
215 				    ? IMSG_BSDAUTH_OK : IMSG_BSDAUTH_NG,
216 				    0, 0, -1, NULL, 0);
217 				break;
218 			    }
219 			}
220 			imsg_free(&imsg);
221 			imsg_flush(&ibuf);
222 		}
223 		imsg_flush(&ibuf);
224 	}
225 	imsg_clear(&ibuf);
226 
227 	while (waitpid(pid, &status, 0) == -1) {
228 		if (errno != EINTR)
229 			break;
230 	}
231 	exit(WEXITSTATUS(status));
232 }
233 
234 static void
235 module_bsdauth_main(void)
236 {
237 	int			 i;
238 	struct module_bsdauth	 module_bsdauth;
239 
240 	/*
241 	 * main process
242 	 */
243 	setproctitle("[main]");
244 	openlog(NULL, LOG_PID, LOG_DAEMON);
245 	memset(&module_bsdauth, 0, sizeof(module_bsdauth));
246 	if ((module_bsdauth.base = module_create(STDIN_FILENO, &module_bsdauth,
247 	    &module_bsdauth_handlers)) == NULL)
248 		err(1, "Could not create a module instance");
249 
250 	module_drop_privilege(module_bsdauth.base);
251 
252 	module_load(module_bsdauth.base);
253 	imsg_init(&module_bsdauth.ibuf, 3);
254 
255 	if (pledge("stdio proc", NULL) == -1)
256 		err(EXIT_FAILURE, "pledge");
257 
258 	while (module_run(module_bsdauth.base) == 0)
259 		;
260 
261 	module_destroy(module_bsdauth.base);
262 	imsg_clear(&module_bsdauth.ibuf);
263 
264 	if (module_bsdauth.okgroups) {
265 		for (i = 0; module_bsdauth.okgroups[i] != NULL; i++)
266 			free(module_bsdauth.okgroups[i]);
267 	}
268 	free(module_bsdauth.okgroups);
269 
270 	exit(EXIT_SUCCESS);
271 }
272 
273 static void
274 module_bsdauth_config_set(void *ctx, const char *name, int argc,
275     char * const * argv)
276 {
277 	struct module_bsdauth	 *module = ctx;
278 	int			  i;
279 	char			**groups = NULL;
280 
281 	if (strcmp(name, "restrict-group") == 0) {
282 		if (module->okgroups != NULL) {
283 			module_send_message(module->base, IMSG_NG,
284 			    "`restrict-group' is already defined");
285 			goto on_error;
286 		}
287 		if ((groups = calloc(sizeof(char *), argc + 1)) == NULL) {
288 			module_send_message(module->base, IMSG_NG,
289 			    "Out of memory");
290 			goto on_error;
291 		}
292 		for (i = 0; i < argc; i++) {
293 			if ((groups[i] = strdup(argv[i])) == NULL) {
294 				module_send_message(module->base,
295 				    IMSG_NG, "Out of memory");
296 				goto on_error;
297 			}
298 		}
299 		groups[i] = NULL;
300 		module->okgroups = groups;
301 		module_send_message(module->base, IMSG_OK, NULL);
302 	} else if (strncmp(name, "_", 1) == 0)
303 		/* ignore all internal messages */
304 		module_send_message(module->base, IMSG_OK, NULL);
305 	else
306 		module_send_message(module->base, IMSG_NG,
307 		    "Unknown config parameter `%s'", name);
308 	return;
309 on_error:
310 	if (groups != NULL) {
311 		for (i = 0; groups[i] != NULL; i++)
312 			free(groups[i]);
313 		free(groups);
314 	}
315 	return;
316 }
317 
318 
319 static void
320 module_bsdauth_userpass(void *ctx, u_int q_id, const char *user,
321     const char *pass)
322 {
323 	struct module_bsdauth	*module = ctx;
324 	struct auth_usercheck_args
325 				 usercheck;
326 	struct auth_groupcheck_args
327 				 groupcheck;
328 	struct iovec		iov[4];
329 	const char		*group;
330 	u_int			 i;
331 	const char		*reason;
332 	struct imsg		 imsg;
333 	ssize_t			 n;
334 
335 	memset(&imsg, 0, sizeof(imsg));
336 	if (pass == NULL)
337 		pass = "";
338 
339 	usercheck.userlen = strlen(user) + 1;
340 	usercheck.passlen = strlen(pass) + 1;
341 	iov[0].iov_base = &usercheck;
342 	iov[0].iov_len = sizeof(usercheck);
343 	iov[1].iov_base = (char *)user;
344 	iov[1].iov_len = usercheck.userlen;
345 	iov[2].iov_base = (char *)pass;
346 	iov[2].iov_len = usercheck.passlen;
347 
348 	imsg_composev(&module->ibuf, IMSG_BSDAUTH_USERCHECK, 0, 0, -1, iov, 3);
349 	imsg_flush(&module->ibuf);
350 	if ((n = imsg_read(&module->ibuf)) == -1 || n == 0)
351 		fatal("imsg_read() failed in module_bsdauth_userpass()");
352 	if ((n = imsg_get(&module->ibuf, &imsg)) <= 0)
353 		fatal("imsg_get() failed in module_bsdauth_userpass()");
354 
355 	if (imsg.hdr.type != IMSG_BSDAUTH_OK) {
356 		reason = "Authentication failed";
357 		goto auth_ng;
358 	}
359 	if (module->okgroups != NULL) {
360 		reason = "Group restriction is not allowed";
361 		for (i = 0; module->okgroups[i] != NULL; i++) {
362 			group = module->okgroups[i];
363 
364 			groupcheck.userlen = strlen(user) + 1;
365 			groupcheck.grouplen = strlen(group) + 1;
366 			iov[0].iov_base = &groupcheck;
367 			iov[0].iov_len = sizeof(groupcheck);
368 			iov[1].iov_base = (char *)user;
369 			iov[1].iov_len = groupcheck.userlen;
370 			iov[2].iov_base = (char *)group;
371 			iov[2].iov_len = groupcheck.grouplen;
372 			imsg_composev(&module->ibuf, IMSG_BSDAUTH_GROUPCHECK,
373 			    0, 0, -1, iov, 3);
374 			imsg_flush(&module->ibuf);
375 			if ((n = imsg_read(&module->ibuf)) == -1 || n == 0)
376 				fatal("imsg_read() failed in "
377 				    "module_bsdauth_userpass()");
378 			if ((n = imsg_get(&module->ibuf, &imsg)) <= 0)
379 				fatal("imsg_get() failed in "
380 				    "module_bsdauth_userpass()");
381 			if (imsg.hdr.type == IMSG_BSDAUTH_OK)
382 				goto group_ok;
383 		}
384 		goto auth_ng;
385 	}
386 group_ok:
387 	module_userpass_ok(module->base, q_id, "Authentication succeeded");
388 	imsg_free(&imsg);
389 	return;
390 auth_ng:
391 	module_userpass_fail(module->base, q_id, reason);
392 	imsg_free(&imsg);
393 	return;
394 }
395 
396 pid_t
397 start_child(char *argv0, int fd)
398 {
399 	char *argv[5];
400 	int argc = 0;
401 	pid_t pid;
402 
403 	switch (pid = fork()) {
404 	case -1:
405 		fatal("cannot fork");
406 	case 0:
407 		break;
408 	default:
409 		close(fd);
410 		return (pid);
411 	}
412 
413 	if (fd != 3) {
414 		if (dup2(fd, 3) == -1)
415 			fatal("cannot setup imsg fd");
416 	} else if (fcntl(fd, F_SETFD, 0) == -1)
417 		fatal("cannot setup imsg fd");
418 
419 	argv[argc++] = argv0;
420 	argv[argc++] = "-M";	/* main proc */
421 	execvp(argv0, argv);
422 	fatal("execvp");
423 }
424 
425 static void
426 fatal(const char *msg)
427 {
428 	syslog(LOG_ERR, "%s: %m", msg);
429 	abort();
430 }
431