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