1 /* $OpenBSD: radiusd_bsdauth.c,v 1.19 2024/11/21 13:43:10 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 <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
main(int argc,char * argv[])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 if (imsgbuf_init(&ibuf, pairsock[0]) == 1)
117 err(EXIT_FAILURE, "imsgbuf_init");
118
119 if (pledge("stdio getpw rpath proc exec", NULL) == -1)
120 err(EXIT_FAILURE, "pledge");
121
122 for (;;) {
123 if (imsgbuf_read(&ibuf) != 1)
124 break;
125 for (;;) {
126 if ((n = imsg_get(&ibuf, &imsg)) == -1)
127 break;
128 if (n == 0)
129 break;
130 datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
131 switch (imsg.hdr.type) {
132 case IMSG_BSDAUTH_USERCHECK:
133 {
134 char *user, *pass;
135 bool authok = false;
136 struct auth_usercheck_args
137 *args;
138
139 if (datalen < sizeof(
140 struct auth_usercheck_args)) {
141 syslog(LOG_ERR, "Short message");
142 break;
143 }
144 args = (struct auth_usercheck_args *)imsg.data;
145
146 if (datalen < sizeof(struct auth_usercheck_args)
147 + args->userlen + args->passlen) {
148 syslog(LOG_ERR, "Short message");
149 break;
150 }
151 user = (char *)(args + 1);
152 user[args->userlen - 1] = '\0';
153 pass = user + args->userlen;
154 pass[args->passlen - 1] = '\0';
155
156 if (auth_userokay(user, NULL, NULL, pass))
157 authok = true;
158 explicit_bzero(pass, args->passlen);
159
160 imsg_compose(&ibuf, (authok)
161 ? IMSG_BSDAUTH_OK : IMSG_BSDAUTH_NG,
162 0, 0, -1, NULL, 0);
163 break;
164 }
165 case IMSG_BSDAUTH_GROUPCHECK:
166 {
167 int i;
168 char *user, *group;
169 struct passwd *pw;
170 struct group gr0, *gr;
171 char g_buf[4096];
172 bool group_ok = false;
173 struct auth_groupcheck_args
174 *args;
175
176 if (datalen < sizeof(
177 struct auth_groupcheck_args)) {
178 syslog(LOG_ERR, "Short message");
179 break;
180 }
181 args = (struct auth_groupcheck_args *)imsg.data;
182 if (datalen <
183 sizeof(struct auth_groupcheck_args) +
184 args->userlen + args->grouplen) {
185 syslog(LOG_ERR, "Short message");
186 break;
187 }
188 user = (char *)(args + 1);
189 user[args->userlen - 1] = '\0';
190 group = user + args->userlen;
191 group[args->grouplen - 1] = '\0';
192
193 user[strcspn(user, ":")] = '\0';
194 pw = getpwnam(user);
195 if (pw == NULL)
196 goto invalid;
197 if (getgrnam_r(group, &gr0, g_buf,
198 sizeof(g_buf), &gr) == -1 || gr == NULL)
199 goto invalid;
200
201 if (gr->gr_gid == pw->pw_gid) {
202 group_ok = true;
203 goto invalid;
204 }
205 for (i = 0; gr->gr_mem[i] != NULL; i++) {
206 if (strcmp(gr->gr_mem[i], pw->pw_name)
207 == 0) {
208 group_ok = true;
209 goto invalid;
210 }
211 }
212 invalid:
213 endgrent();
214
215 imsg_compose(&ibuf, (group_ok)
216 ? IMSG_BSDAUTH_OK : IMSG_BSDAUTH_NG,
217 0, 0, -1, NULL, 0);
218 break;
219 }
220 }
221 imsg_free(&imsg);
222 imsgbuf_flush(&ibuf);
223 }
224 imsgbuf_flush(&ibuf);
225 }
226 imsgbuf_clear(&ibuf);
227
228 while (waitpid(pid, &status, 0) == -1) {
229 if (errno != EINTR)
230 break;
231 }
232 exit(WEXITSTATUS(status));
233 }
234
235 static void
module_bsdauth_main(void)236 module_bsdauth_main(void)
237 {
238 int i;
239 struct module_bsdauth module_bsdauth;
240
241 /*
242 * main process
243 */
244 setproctitle("[main]");
245 openlog(NULL, LOG_PID, LOG_DAEMON);
246 memset(&module_bsdauth, 0, sizeof(module_bsdauth));
247 if ((module_bsdauth.base = module_create(STDIN_FILENO, &module_bsdauth,
248 &module_bsdauth_handlers)) == NULL)
249 err(1, "Could not create a module instance");
250
251 module_drop_privilege(module_bsdauth.base, 0);
252
253 module_load(module_bsdauth.base);
254 if (imsgbuf_init(&module_bsdauth.ibuf, 3) == -1)
255 err(EXIT_FAILURE, "imsgbuf_init");
256
257 if (pledge("stdio proc", NULL) == -1)
258 err(EXIT_FAILURE, "pledge");
259
260 while (module_run(module_bsdauth.base) == 0)
261 ;
262
263 module_destroy(module_bsdauth.base);
264 imsgbuf_clear(&module_bsdauth.ibuf);
265
266 if (module_bsdauth.okgroups) {
267 for (i = 0; module_bsdauth.okgroups[i] != NULL; i++)
268 free(module_bsdauth.okgroups[i]);
269 }
270 free(module_bsdauth.okgroups);
271
272 exit(EXIT_SUCCESS);
273 }
274
275 static void
module_bsdauth_config_set(void * ctx,const char * name,int argc,char * const * argv)276 module_bsdauth_config_set(void *ctx, const char *name, int argc,
277 char * const * argv)
278 {
279 struct module_bsdauth *module = ctx;
280 int i;
281 char **groups = NULL;
282
283 if (strcmp(name, "restrict-group") == 0) {
284 if (module->okgroups != NULL) {
285 module_send_message(module->base, IMSG_NG,
286 "`restrict-group' is already defined");
287 goto on_error;
288 }
289 if ((groups = calloc(sizeof(char *), argc + 1)) == NULL) {
290 module_send_message(module->base, IMSG_NG,
291 "Out of memory");
292 goto on_error;
293 }
294 for (i = 0; i < argc; i++) {
295 if ((groups[i] = strdup(argv[i])) == NULL) {
296 module_send_message(module->base,
297 IMSG_NG, "Out of memory");
298 goto on_error;
299 }
300 }
301 groups[i] = NULL;
302 module->okgroups = groups;
303 module_send_message(module->base, IMSG_OK, NULL);
304 } else if (strncmp(name, "_", 1) == 0)
305 /* ignore all internal messages */
306 module_send_message(module->base, IMSG_OK, NULL);
307 else
308 module_send_message(module->base, IMSG_NG,
309 "Unknown config parameter `%s'", name);
310 return;
311 on_error:
312 if (groups != NULL) {
313 for (i = 0; groups[i] != NULL; i++)
314 free(groups[i]);
315 free(groups);
316 }
317 return;
318 }
319
320
321 static void
module_bsdauth_userpass(void * ctx,u_int q_id,const char * user,const char * pass)322 module_bsdauth_userpass(void *ctx, u_int q_id, const char *user,
323 const char *pass)
324 {
325 struct module_bsdauth *module = ctx;
326 struct auth_usercheck_args
327 usercheck;
328 struct auth_groupcheck_args
329 groupcheck;
330 struct iovec iov[4];
331 const char *group;
332 u_int i;
333 const char *reason;
334 struct imsg imsg;
335 ssize_t n;
336
337 memset(&imsg, 0, sizeof(imsg));
338 if (pass == NULL)
339 pass = "";
340
341 usercheck.userlen = strlen(user) + 1;
342 usercheck.passlen = strlen(pass) + 1;
343 iov[0].iov_base = &usercheck;
344 iov[0].iov_len = sizeof(usercheck);
345 iov[1].iov_base = (char *)user;
346 iov[1].iov_len = usercheck.userlen;
347 iov[2].iov_base = (char *)pass;
348 iov[2].iov_len = usercheck.passlen;
349
350 imsg_composev(&module->ibuf, IMSG_BSDAUTH_USERCHECK, 0, 0, -1, iov, 3);
351 imsgbuf_flush(&module->ibuf);
352 if (imsgbuf_read(&module->ibuf) != 1)
353 fatal("imsgbuf_read() failed in module_bsdauth_userpass()");
354 if ((n = imsg_get(&module->ibuf, &imsg)) <= 0)
355 fatal("imsg_get() failed in module_bsdauth_userpass()");
356
357 if (imsg.hdr.type != IMSG_BSDAUTH_OK) {
358 reason = "Authentication failed";
359 goto auth_ng;
360 }
361 if (module->okgroups != NULL) {
362 reason = "Group restriction is not allowed";
363 for (i = 0; module->okgroups[i] != NULL; i++) {
364 group = module->okgroups[i];
365
366 groupcheck.userlen = strlen(user) + 1;
367 groupcheck.grouplen = strlen(group) + 1;
368 iov[0].iov_base = &groupcheck;
369 iov[0].iov_len = sizeof(groupcheck);
370 iov[1].iov_base = (char *)user;
371 iov[1].iov_len = groupcheck.userlen;
372 iov[2].iov_base = (char *)group;
373 iov[2].iov_len = groupcheck.grouplen;
374 imsg_composev(&module->ibuf, IMSG_BSDAUTH_GROUPCHECK,
375 0, 0, -1, iov, 3);
376 imsgbuf_flush(&module->ibuf);
377 if (imsgbuf_read(&module->ibuf) != 1)
378 fatal("imsgbuf_read() failed in "
379 "module_bsdauth_userpass()");
380 if ((n = imsg_get(&module->ibuf, &imsg)) <= 0)
381 fatal("imsg_get() failed in "
382 "module_bsdauth_userpass()");
383 if (imsg.hdr.type == IMSG_BSDAUTH_OK)
384 goto group_ok;
385 }
386 goto auth_ng;
387 }
388 group_ok:
389 module_userpass_ok(module->base, q_id, "Authentication succeeded");
390 imsg_free(&imsg);
391 return;
392 auth_ng:
393 module_userpass_fail(module->base, q_id, reason);
394 imsg_free(&imsg);
395 return;
396 }
397
398 pid_t
start_child(char * argv0,int fd)399 start_child(char *argv0, int fd)
400 {
401 char *argv[5];
402 int argc = 0;
403 pid_t pid;
404
405 switch (pid = fork()) {
406 case -1:
407 fatal("cannot fork");
408 case 0:
409 break;
410 default:
411 close(fd);
412 return (pid);
413 }
414
415 if (fd != 3) {
416 if (dup2(fd, 3) == -1)
417 fatal("cannot setup imsg fd");
418 } else if (fcntl(fd, F_SETFD, 0) == -1)
419 fatal("cannot setup imsg fd");
420
421 argv[argc++] = argv0;
422 argv[argc++] = "-M"; /* main proc */
423 argv[argc++] = NULL;
424 execvp(argv0, argv);
425 fatal("execvp");
426 }
427
428 static void
fatal(const char * msg)429 fatal(const char *msg)
430 {
431 syslog(LOG_ERR, "%s: %m", msg);
432 abort();
433 }
434