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