File: | src/usr.sbin/radiusd/radiusd_bsdauth/../radiusd_bsdauth.c |
Warning: | line 122, column 8 Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: radiusd_bsdauth.c,v 1.15 2023/08/18 06:12:27 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 <errno(*__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__attribute__((__noreturn__)) 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__attribute__((__noreturn__)) 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_UNIX1, SOCK_STREAM1 | SOCK_CLOEXEC0x8000, PF_UNSPEC0, |
105 | pairsock) == -1) |
106 | err(EXIT_FAILURE1, "socketpair"); |
107 | |
108 | openlog(NULL((void *)0), LOG_PID0x01, LOG_DAEMON(3<<3)); |
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((void *)0)) == -1) |
119 | err(EXIT_FAILURE1, "pledge"); |
120 | |
121 | for (;;) { |
122 | if ((n = imsg_read(&ibuf)) <= 0 && errno(*__errno()) != EAGAIN35) |
Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n' | |
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_SIZEsizeof(struct imsg_hdr); |
130 | switch (imsg.hdr.type) { |
131 | case IMSG_BSDAUTH_USERCHECK: |
132 | { |
133 | char *user, *pass; |
134 | bool_Bool authok = false0; |
135 | struct auth_usercheck_args |
136 | *args; |
137 | |
138 | if (datalen < sizeof( |
139 | struct auth_usercheck_args)) { |
140 | syslog(LOG_ERR3, "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_ERR3, "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((void *)0), NULL((void *)0), pass)) |
156 | authok = true1; |
157 | explicit_bzero(pass, args->passlen); |
158 | |
159 | imsg_compose(&ibuf, (authok) |
160 | ? IMSG_BSDAUTH_OK : IMSG_BSDAUTH_NG, |
161 | 0, 0, -1, NULL((void *)0), 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_Bool group_ok = false0; |
172 | struct auth_groupcheck_args |
173 | *args; |
174 | |
175 | if (datalen < sizeof( |
176 | struct auth_groupcheck_args)) { |
177 | syslog(LOG_ERR3, "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_ERR3, "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((void *)0)) |
195 | goto invalid; |
196 | if (getgrnam_r(group, &gr0, g_buf, |
197 | sizeof(g_buf), &gr) == -1 || gr == NULL((void *)0)) |
198 | goto invalid; |
199 | |
200 | if (gr->gr_gid == pw->pw_gid) { |
201 | group_ok = true1; |
202 | goto invalid; |
203 | } |
204 | for (i = 0; gr->gr_mem[i] != NULL((void *)0); i++) { |
205 | if (strcmp(gr->gr_mem[i], pw->pw_name) |
206 | == 0) { |
207 | group_ok = true1; |
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((void *)0), 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(*__errno()) != EINTR4) |
229 | break; |
230 | } |
231 | exit(WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff)); |
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((void *)0), LOG_PID0x01, LOG_DAEMON(3<<3)); |
245 | memset(&module_bsdauth, 0, sizeof(module_bsdauth)); |
246 | if ((module_bsdauth.base = module_create(STDIN_FILENO0, &module_bsdauth, |
247 | &module_bsdauth_handlers)) == NULL((void *)0)) |
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((void *)0)) == -1) |
256 | err(EXIT_FAILURE1, "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((void *)0); i++) |
266 | free(module_bsdauth.okgroups[i]); |
267 | } |
268 | free(module_bsdauth.okgroups); |
269 | |
270 | exit(EXIT_SUCCESS0); |
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((void *)0); |
280 | |
281 | if (strcmp(name, "restrict-group") == 0) { |
282 | if (module->okgroups != NULL((void *)0)) { |
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((void *)0)) { |
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((void *)0)) { |
294 | module_send_message(module->base, |
295 | IMSG_NG, "Out of memory"); |
296 | goto on_error; |
297 | } |
298 | } |
299 | groups[i] = NULL((void *)0); |
300 | module->okgroups = groups; |
301 | module_send_message(module->base, IMSG_OK, NULL((void *)0)); |
302 | } else if (strncmp(name, "_", 1) == 0) |
303 | /* ignore all internal messages */ |
304 | module_send_message(module->base, IMSG_OK, NULL((void *)0)); |
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((void *)0)) { |
311 | for (i = 0; groups[i] != NULL((void *)0); 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((void *)0)) |
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((void *)0)) { |
360 | reason = "Group restriction is not allowed"; |
361 | for (i = 0; module->okgroups[i] != NULL((void *)0); 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_SETFD2, 0) == -1) |
417 | fatal("cannot setup imsg fd"); |
418 | |
419 | argv[argc++] = argv0; |
420 | argv[argc++] = "-M"; /* main proc */ |
421 | argv[argc++] = NULL((void *)0); |
422 | execvp(argv0, argv); |
423 | fatal("execvp"); |
424 | } |
425 | |
426 | static void |
427 | fatal(const char *msg) |
428 | { |
429 | syslog(LOG_ERR3, "%s: %m", msg); |
430 | abort(); |
431 | } |