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