xref: /openbsd-src/usr.sbin/radiusd/radiusd_file.c (revision 882428cdbdd2944d8f59bc8621c131fd814fb6ee)
1 /*	$OpenBSD: radiusd_file.c,v 1.8 2024/11/21 13:43:10 claudio Exp $	*/
2 
3 /*
4  * Copyright (c) 2024 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/cdefs.h>
21 #include <sys/queue.h>
22 #include <sys/socket.h>
23 #include <sys/wait.h>
24 #include <netinet/in.h>
25 
26 #include <err.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <imsg.h>
30 #include <limits.h>
31 #include <md5.h>
32 #include <radius.h>
33 #include <stddef.h>
34 #include <stdint.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 
39 #include "chap_ms.h"
40 #include "imsg_subr.h"
41 #include "log.h"
42 #include "radiusd.h"
43 #include "radiusd_module.h"
44 
45 struct module_file_params {
46 	int			 debug;
47 	char			 path[PATH_MAX];
48 };
49 
50 struct module_file {
51 	struct module_base	*base;
52 	struct imsgbuf		 ibuf;
53 	struct module_file_params
54 				 params;
55 };
56 
57 struct module_file_userinfo {
58 	struct in_addr		frame_ip_address;
59 	char			password[0];
60 };
61 
62 /* IPC between priv and main */
63 enum {
64 	IMSG_RADIUSD_FILE_OK = 1000,
65 	IMSG_RADIUSD_FILE_NG,
66 	IMSG_RADIUSD_FILE_PARAMS,
67 	IMSG_RADIUSD_FILE_USERINFO
68 };
69 
70 static void	 parent_dispatch_main(struct module_file_params *,
71 		    struct imsgbuf *, struct imsg *);
72 static void	 module_file_main(void) __dead;
73 static pid_t	 start_child(char *, int);
74 static void	 module_file_config_set(void *, const char *, int,
75 		    char * const *);
76 static void	 module_file_start(void *);
77 static void	 module_file_access_request(void *, u_int, const u_char *,
78 		    size_t);
79 static void	 auth_pap(struct module_file *, u_int, RADIUS_PACKET *, char *,
80 		    struct module_file_userinfo *);
81 static void	 auth_md5chap(struct module_file *, u_int, RADIUS_PACKET *,
82 		    char *, struct module_file_userinfo *);
83 static void	 auth_mschapv2(struct module_file *, u_int, RADIUS_PACKET *,
84 		    char *, struct module_file_userinfo *);
85 static void	 auth_reject(struct module_file *, u_int, RADIUS_PACKET *,
86 		    char *, struct module_file_userinfo *);
87 
88 static struct module_handlers module_file_handlers = {
89 	.access_request		= module_file_access_request,
90 	.config_set		= module_file_config_set,
91 	.start			= module_file_start
92 };
93 
94 int
95 main(int argc, char *argv[])
96 {
97 	int				 ch, pairsock[2], status;
98 	pid_t				 pid;
99 	char				*saved_argv0;
100 	struct imsgbuf			 ibuf;
101 	struct imsg			 imsg;
102 	ssize_t				 n;
103 	size_t				 datalen;
104 	struct module_file_params	*paramsp, params;
105 	char				 pathdb[PATH_MAX];
106 
107 	while ((ch = getopt(argc, argv, "M")) != -1)
108 		switch (ch) {
109 		case 'M':
110 			module_file_main();
111 			/* not reached */
112 			break;
113 		}
114 	saved_argv0 = argv[0];
115 
116 	argc -= optind;
117 	argv += optind;
118 
119 	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, PF_UNSPEC,
120 	    pairsock) == -1)
121 		err(EXIT_FAILURE, "socketpair");
122 
123 	log_init(0);
124 
125 	pid = start_child(saved_argv0, pairsock[1]);
126 
127 	/* Privileged process */
128 	if (pledge("stdio rpath unveil", NULL) == -1)
129 		err(EXIT_FAILURE, "pledge");
130 	setproctitle("[priv]");
131 	if (imsgbuf_init(&ibuf, pairsock[0]) == -1)
132 		err(EXIT_FAILURE, "imsgbuf_init");
133 
134 	/* Receive parameters from the main process. */
135 	if (imsg_sync_read(&ibuf, 2000) <= 0 ||
136 	    (n = imsg_get(&ibuf, &imsg)) <= 0)
137 		exit(EXIT_FAILURE);
138 	if (imsg.hdr.type != IMSG_RADIUSD_FILE_PARAMS)
139 		err(EXIT_FAILURE, "Receieved unknown message type %d",
140 		    imsg.hdr.type);
141 	datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
142 	if (datalen < sizeof(params))
143 		err(EXIT_FAILURE, "Receieved IMSG_RADIUSD_FILE_PARAMS "
144 		    "message is wrong size");
145 	paramsp = imsg.data;
146 	if (paramsp->path[0] != '\0') {
147 		strlcpy(pathdb, paramsp->path, sizeof(pathdb));
148 		strlcat(pathdb, ".db", sizeof(pathdb));
149 		if (unveil(paramsp->path, "r") == -1 ||
150 		    unveil(pathdb, "r") == -1)
151 			err(EXIT_FAILURE, "unveil");
152 	}
153 	if (paramsp->debug)
154 		log_init(1);
155 
156 	if (unveil(NULL, NULL) == -1)
157 		err(EXIT_FAILURE, "unveil");
158 
159 	memcpy(&params, paramsp, sizeof(params));
160 
161 	for (;;) {
162 		if (imsgbuf_read(&ibuf) != 1)
163 			break;
164 		for (;;) {
165 			if ((n = imsg_get(&ibuf, &imsg)) == -1)
166 				break;
167 			if (n == 0)
168 				break;
169 			parent_dispatch_main(&params, &ibuf, &imsg);
170 			imsg_free(&imsg);
171 			imsgbuf_flush(&ibuf);
172 		}
173 		imsgbuf_flush(&ibuf);
174 	}
175 	imsgbuf_clear(&ibuf);
176 
177 	while (waitpid(pid, &status, 0) == -1) {
178 		if (errno != EINTR)
179 			break;
180 	}
181 	exit(WEXITSTATUS(status));
182 }
183 
184 void
185 parent_dispatch_main(struct module_file_params *params, struct imsgbuf *ibuf,
186     struct imsg *imsg)
187 {
188 	size_t				 datalen, entsz, passz;
189 	const char			*username;
190 	char				*buf, *db[2], *str;
191 	int				 ret;
192 	struct module_file_userinfo	*ent;
193 
194 	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
195 	switch (imsg->hdr.type) {
196 	case IMSG_RADIUSD_FILE_USERINFO:
197 		if (datalen == 0 ||
198 		    *((char *)imsg->data + datalen - 1) != '\0') {
199 			log_warn("%s: received IMSG_RADIUSD_FILE_USERINFO "
200 			    "is wrong", __func__);
201 			goto on_error;
202 		}
203 		username = imsg->data;
204 		db[0] = params->path;
205 		db[1] = NULL;
206 		if ((ret = cgetent(&buf, db, username)) < 0) {
207 			log_info("user `%s' is not configured", username);
208 			goto on_error;
209 		}
210 		if ((ret = cgetstr(buf, "password", &str)) < 0) {
211 			log_info("password for `%s' is not configured",
212 			    username);
213 			goto on_error;
214 		}
215 		passz = strlen(str) + 1;
216 		entsz = offsetof(struct module_file_userinfo, password[passz]);
217 		if ((ent = calloc(1, entsz)) == NULL) {
218 			log_warn("%s; calloc", __func__);
219 			goto on_error;
220 		}
221 		strlcpy(ent->password, str, passz);
222 		imsg_compose(ibuf, IMSG_RADIUSD_FILE_USERINFO, 0, -1, -1,
223 		    ent, entsz);
224 		freezero(ent, entsz);
225 		break;
226 	}
227 	return;
228  on_error:
229 	imsg_compose(ibuf, IMSG_RADIUSD_FILE_NG, 0, -1, -1, NULL, 0);
230 }
231 
232 /* main process */
233 void
234 module_file_main(void)
235 {
236 	struct module_file	 module_file;
237 
238 	setproctitle("[main]");
239 
240 	memset(&module_file, 0, sizeof(module_file));
241 	if ((module_file.base = module_create(STDIN_FILENO, &module_file,
242 	    &module_file_handlers)) == NULL)
243 		err(1, "Could not create a module instance");
244 
245 	module_drop_privilege(module_file.base, 0);
246 
247 	module_load(module_file.base);
248 	if (imsgbuf_init(&module_file.ibuf, 3) == -1)
249 		err(EXIT_FAILURE, "imsgbuf_init");
250 
251 	if (pledge("stdio", NULL) == -1)
252 		err(EXIT_FAILURE, "pledge");
253 	while (module_run(module_file.base) == 0)
254 		;
255 
256 	module_destroy(module_file.base);
257 
258 	exit(0);
259 }
260 
261 pid_t
262 start_child(char *argv0, int fd)
263 {
264 	char *argv[5];
265 	int argc = 0;
266 	pid_t pid;
267 
268 	switch (pid = fork()) {
269 	case -1:
270 		fatal("cannot fork");
271 	case 0:
272 		break;
273 	default:
274 		close(fd);
275 		return (pid);
276 	}
277 
278 	if (fd != 3) {
279 		if (dup2(fd, 3) == -1)
280 			fatal("cannot setup imsg fd");
281 	} else if (fcntl(fd, F_SETFD, 0) == -1)
282 		fatal("cannot setup imsg fd");
283 
284 	argv[argc++] = argv0;
285 	argv[argc++] = "-M";	/* main proc */
286 	argv[argc++] = NULL;
287 	execvp(argv0, argv);
288 	fatal("execvp");
289 }
290 
291 void
292 module_file_config_set(void *ctx, const char *name, int valc,
293     char * const * valv)
294 {
295 	struct module_file	*module = ctx;
296 	char			*errmsg;
297 
298 	if (strcmp(name, "path") == 0) {
299 		SYNTAX_ASSERT(valc == 1, "`path' must have a argument");
300 		if (strlcpy(module->params.path, valv[0], sizeof(
301 		    module->params.path)) >= sizeof(module->params.path)) {
302 			module_send_message(module->base, IMSG_NG,
303 			    "`path' is too long");
304 			return;
305 		}
306 		module_send_message(module->base, IMSG_OK, NULL);
307 	} else if (strcmp(name, "_debug") == 0) {
308 		log_init(1);
309 		module->params.debug = 1;
310 		module_send_message(module->base, IMSG_OK, NULL);
311 	} else if (strncmp(name, "_", 1) == 0)
312 		/* ignore all internal messages */
313 		module_send_message(module->base, IMSG_OK, NULL);
314 	else
315 		module_send_message(module->base, IMSG_NG,
316 		    "Unknown config parameter `%s'", name);
317 	return;
318  syntax_error:
319 	module_send_message(module->base, IMSG_NG, "%s", errmsg);
320 	return;
321 }
322 
323 void
324 module_file_start(void *ctx)
325 {
326 	struct module_file	*module = ctx;
327 
328 	/* Send parameters to parent */
329 	if (module->params.path[0] == '\0') {
330 		module_send_message(module->base, IMSG_NG,
331 		    "`path' is not configured");
332 		return;
333 	}
334 	imsg_compose(&module->ibuf, IMSG_RADIUSD_FILE_PARAMS, 0, -1, -1,
335 	    &module->params, sizeof(module->params));
336 	imsgbuf_flush(&module->ibuf);
337 
338 	module_send_message(module->base, IMSG_OK, NULL);
339 }
340 
341 void
342 module_file_access_request(void *ctx, u_int query_id, const u_char *pkt,
343     size_t pktlen)
344 {
345 	size_t				 datalen;
346 	struct module_file		*self = ctx;
347 	RADIUS_PACKET			*radpkt = NULL;
348 	char				 username[256];
349 	ssize_t				 n;
350 	struct imsg			 imsg;
351 	struct module_file_userinfo	*ent;
352 
353 	memset(&imsg, 0, sizeof(imsg));
354 
355 	if ((radpkt = radius_convert_packet(pkt, pktlen)) == NULL) {
356 		log_warn("%s: radius_convert_packet()", __func__);
357 		goto out;
358 	}
359 	radius_get_string_attr(radpkt, RADIUS_TYPE_USER_NAME, username,
360 	    sizeof(username));
361 
362 	imsg_compose(&self->ibuf, IMSG_RADIUSD_FILE_USERINFO, 0, -1, -1,
363 	    username, strlen(username) + 1);
364 	imsgbuf_flush(&self->ibuf);
365 	if (imsgbuf_read(&self->ibuf) != 1) {
366 		log_warn("%s: imsgbuf_read()", __func__);
367 		goto out;
368 	}
369 	if ((n = imsg_get(&self->ibuf, &imsg)) <= 0) {
370 		log_warn("%s: imsg_get()", __func__);
371 		goto out;
372 	}
373 
374 	datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
375 	if (imsg.hdr.type == IMSG_RADIUSD_FILE_USERINFO) {
376 		if (datalen <= offsetof(struct module_file_userinfo,
377 		    password[0])) {
378 			log_warn("%s: received IMSG_RADIUSD_FILE_USERINFO is "
379 			    "invalid", __func__);
380 			goto out;
381 		}
382 		ent = imsg.data;
383 		if (radius_has_attr(radpkt, RADIUS_TYPE_USER_PASSWORD))
384 			auth_pap(self, query_id, radpkt, username, ent);
385 		else if (radius_has_attr(radpkt, RADIUS_TYPE_CHAP_PASSWORD))
386 			auth_md5chap(self, query_id, radpkt, username, ent);
387 		else if (radius_has_vs_attr(radpkt, RADIUS_VENDOR_MICROSOFT,
388 		    RADIUS_VTYPE_MS_CHAP2_RESPONSE))
389 			auth_mschapv2(self, query_id, radpkt, username, ent);
390 		else
391 			auth_reject(self, query_id, radpkt, username, ent);
392 	} else
393 		auth_reject(self, query_id, radpkt, username, NULL);
394  out:
395 	if (radpkt != NULL)
396 		radius_delete_packet(radpkt);
397 	imsg_free(&imsg);
398 	return;
399 }
400 
401 void
402 auth_pap(struct module_file *self, u_int q_id, RADIUS_PACKET *radpkt,
403     char *username, struct module_file_userinfo *ent)
404 {
405 	RADIUS_PACKET	*respkt = NULL;
406 	char		 pass[256];
407 	int		 ret;
408 
409 	if (radius_get_string_attr(radpkt, RADIUS_TYPE_USER_PASSWORD, pass,
410 	    sizeof(pass)) != 0) {
411 		log_warnx("%s: radius_get_string_attr", __func__);
412 		return;
413 	}
414 	ret = strcmp(ent->password, pass);
415 	explicit_bzero(ent->password, strlen(ent->password));
416 	log_info("q=%u User `%s' authentication %s (PAP)", q_id, username,
417 	    (ret == 0)? "succeeded" : "failed");
418 	if ((respkt = radius_new_response_packet((ret == 0)?
419 	    RADIUS_CODE_ACCESS_ACCEPT : RADIUS_CODE_ACCESS_REJECT, radpkt))
420 	    == NULL) {
421 		log_warn("%s: radius_new_response_packet()", __func__);
422 		return;
423 	}
424 	module_accsreq_answer(self->base, q_id,
425 	    radius_get_data(respkt), radius_get_length(respkt));
426 	radius_delete_packet(respkt);
427 }
428 
429 void
430 auth_md5chap(struct module_file *self, u_int q_id, RADIUS_PACKET *radpkt,
431     char *username, struct module_file_userinfo *ent)
432 {
433 	RADIUS_PACKET	*respkt = NULL;
434 	size_t		 attrlen, challlen;
435 	u_char		 chall[256], idpass[17], digest[16];
436 	int		 ret;
437 	MD5_CTX		 md5;
438 
439 	attrlen = sizeof(idpass);
440 	if (radius_get_raw_attr(radpkt, RADIUS_TYPE_CHAP_PASSWORD, idpass,
441 	    &attrlen) != 0) {
442 		log_warnx("%s: radius_get_string_attr", __func__);
443 		return;
444 	}
445 	challlen = sizeof(chall);
446 	if (radius_get_raw_attr(radpkt, RADIUS_TYPE_CHAP_CHALLENGE, chall,
447 	    &challlen) != 0) {
448 		log_warnx("%s: radius_get_string_attr", __func__);
449 		return;
450 	}
451 	MD5Init(&md5);
452 	MD5Update(&md5, idpass, 1);
453 	MD5Update(&md5, ent->password, strlen(ent->password));
454 	MD5Update(&md5, chall, challlen);
455 	MD5Final(digest, &md5);
456 
457 	ret = timingsafe_bcmp(idpass + 1, digest, sizeof(digest));
458 	log_info("q=%u User `%s' authentication %s (CHAP)", q_id, username,
459 	    (ret == 0)? "succeeded" : "failed");
460 	if ((respkt = radius_new_response_packet((ret == 0)?
461 	    RADIUS_CODE_ACCESS_ACCEPT : RADIUS_CODE_ACCESS_REJECT, radpkt))
462 	    == NULL) {
463 		log_warn("%s: radius_new_response_packet()", __func__);
464 		return;
465 	}
466 	module_accsreq_answer(self->base, q_id,
467 	    radius_get_data(respkt), radius_get_length(respkt));
468 	radius_delete_packet(respkt);
469 }
470 
471 void
472 auth_mschapv2(struct module_file *self, u_int q_id, RADIUS_PACKET *radpkt,
473     char *username, struct module_file_userinfo *ent)
474 {
475 	RADIUS_PACKET		*respkt = NULL;
476 	size_t			 attrlen;
477 	int			 i, lpass;
478 	char			*pass = NULL;
479 	uint8_t			 chall[MSCHAPV2_CHALLENGE_SZ];
480 	uint8_t			 ntresponse[24], authenticator[16];
481 	uint8_t			 pwhash[16], pwhash2[16], master[64];
482 	struct {
483 		uint8_t		 salt[2];
484 		uint8_t		 len;
485 		uint8_t		 key[16];
486 		uint8_t		 pad[15];
487 	} __packed		 rcvkey, sndkey;
488 	struct {
489 		uint8_t		 ident;
490 		uint8_t		 flags;
491 		uint8_t		 peerchall[16];
492 		uint8_t		 reserved[8];
493 		uint8_t		 ntresponse[24];
494 	} __packed		 resp;
495 	struct authresp {
496 		uint8_t		 ident;
497 		uint8_t		 authresp[42];
498 	} __packed		 authresp;
499 
500 
501 	attrlen = sizeof(chall);
502 	if (radius_get_vs_raw_attr(radpkt, RADIUS_VENDOR_MICROSOFT,
503 	    RADIUS_VTYPE_MS_CHAP_CHALLENGE, chall, &attrlen) != 0) {
504 		log_info("q=%u failed to retribute MS-CHAP-Challenge", q_id);
505 		goto on_error;
506 	}
507 	attrlen = sizeof(resp);
508 	if (radius_get_vs_raw_attr(radpkt, RADIUS_VENDOR_MICROSOFT,
509 	    RADIUS_VTYPE_MS_CHAP2_RESPONSE, &resp, &attrlen) != 0) {
510 		log_info("q=%u failed to retribute MS-CHAP2-Response", q_id);
511 		goto on_error;
512 	}
513 
514 	/* convert the password to UTF16-LE */
515 	lpass = strlen(ent->password);
516 	if ((pass = calloc(1, lpass * 2)) == NULL) {
517 		log_warn("%s: calloc()", __func__);
518 		goto on_error;
519 	}
520 	for (i = 0; i < lpass; i++) {
521 		pass[i * 2] = ent->password[i];
522 		pass[i * 2 + 1] = '\0';
523 	}
524 
525 	/* calculate NT-Response by the password */
526 	mschap_nt_response(chall, resp.peerchall,
527 	    username, strlen(username), pass, lpass * 2, ntresponse);
528 
529 	if (timingsafe_bcmp(ntresponse, resp.ntresponse, 24) != 0) {
530 		log_info("q=%u User `%s' authentication failed (MSCHAPv2)",
531 		    q_id, username);
532 		if ((respkt = radius_new_response_packet(
533 		    RADIUS_CODE_ACCESS_REJECT, radpkt)) == NULL) {
534 			log_warn("%s: radius_new_response_packet()", __func__);
535 			goto on_error;
536 		}
537 		authresp.ident = resp.ident;
538 		strlcpy(authresp.authresp, "E=691 R=0 V=3",
539 		    sizeof(authresp.authresp));
540 		radius_put_vs_raw_attr(respkt, RADIUS_VENDOR_MICROSOFT,
541 		    RADIUS_VTYPE_MS_CHAP_ERROR, &authresp,
542 		    offsetof(struct authresp, authresp[13]));
543 	} else {
544 		log_info("q=%u User `%s' authentication succeeded (MSCHAPv2)",
545 		    q_id, username);
546 		if ((respkt = radius_new_response_packet(
547 		    RADIUS_CODE_ACCESS_ACCEPT, radpkt)) == NULL) {
548 			log_warn("%s: radius_new_response_packet()", __func__);
549 			goto on_error;
550 		}
551 		mschap_auth_response(pass, lpass * 2, ntresponse, chall,
552 		    resp.peerchall, username, strlen(username),
553 		    authresp.authresp);
554 		authresp.ident = resp.ident;
555 
556 		radius_put_vs_raw_attr(respkt, RADIUS_VENDOR_MICROSOFT,
557 		    RADIUS_VTYPE_MS_CHAP2_SUCCESS, &authresp,
558 		    offsetof(struct authresp, authresp[42]));
559 
560 		mschap_ntpassword_hash(pass, lpass * 2, pwhash);
561 		mschap_ntpassword_hash(pwhash, sizeof(pwhash), pwhash2);
562 		mschap_masterkey(pwhash2, ntresponse, master);
563 		radius_get_authenticator(radpkt, authenticator);
564 
565 		/* MS-MPPE-Recv-Key  */
566 		memset(&rcvkey, 0, sizeof(rcvkey));
567 		arc4random_buf(rcvkey.salt, sizeof(rcvkey.salt));
568 		rcvkey.salt[0] |= 0x80;
569 		rcvkey.len = 16;
570 		mschap_asymetric_startkey(master, rcvkey.key, 16, 0, 1);
571 		radius_put_vs_raw_attr(respkt, RADIUS_VENDOR_MICROSOFT,
572 		    RADIUS_VTYPE_MPPE_RECV_KEY, &rcvkey, sizeof(rcvkey));
573 
574 		/* MS-MPPE-Send-Key  */
575 		memset(&sndkey, 0, sizeof(sndkey));
576 		arc4random_buf(sndkey.salt, sizeof(sndkey.salt));
577 		sndkey.salt[0] |= 0x80;
578 		sndkey.len = 16;
579 		mschap_asymetric_startkey(master, sndkey.key, 16, 1, 1);
580 		radius_put_vs_raw_attr(respkt, RADIUS_VENDOR_MICROSOFT,
581 		    RADIUS_VTYPE_MPPE_SEND_KEY, &sndkey, sizeof(sndkey));
582 	}
583 
584 	module_accsreq_answer(self->base, q_id,
585 	    radius_get_data(respkt), radius_get_length(respkt));
586  on_error:
587 	/* bzero password */
588 	explicit_bzero(ent->password, strlen(ent->password));
589 	if (pass != NULL)
590 		explicit_bzero(pass, lpass * 2);
591 	free(pass);
592 	if (respkt != NULL)
593 		radius_delete_packet(respkt);
594 }
595 
596 void
597 auth_reject(struct module_file *self, u_int q_id, RADIUS_PACKET *radpkt,
598     char *username, struct module_file_userinfo *ent)
599 {
600 	RADIUS_PACKET	*respkt = NULL;
601 
602 	if (ent != NULL)
603 		explicit_bzero(ent->password, strlen(ent->password));
604 
605 	log_info("q=%u User `%s' authentication failed", q_id,
606 	    username);
607 	if ((respkt = radius_new_response_packet(RADIUS_CODE_ACCESS_REJECT,
608 	    radpkt)) == NULL) {
609 		log_warn("%s: radius_new_response_packet()", __func__);
610 		return;
611 	}
612 	module_accsreq_answer(self->base, q_id,
613 	    radius_get_data(respkt), radius_get_length(respkt));
614 	radius_delete_packet(respkt);
615 }
616