1*882428cdSclaudio /* $OpenBSD: radiusd_file.c,v 1.8 2024/11/21 13:43:10 claudio Exp $ */ 20156c836Syasuoka 30156c836Syasuoka /* 40156c836Syasuoka * Copyright (c) 2024 YASUOKA Masahiko <yasuoka@yasuoka.net> 50156c836Syasuoka * 60156c836Syasuoka * Permission to use, copy, modify, and distribute this software for any 70156c836Syasuoka * purpose with or without fee is hereby granted, provided that the above 80156c836Syasuoka * copyright notice and this permission notice appear in all copies. 90156c836Syasuoka * 100156c836Syasuoka * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 110156c836Syasuoka * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 120156c836Syasuoka * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 130156c836Syasuoka * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 140156c836Syasuoka * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 150156c836Syasuoka * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 160156c836Syasuoka * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 170156c836Syasuoka */ 180156c836Syasuoka 190156c836Syasuoka #include <sys/types.h> 200156c836Syasuoka #include <sys/cdefs.h> 210156c836Syasuoka #include <sys/queue.h> 220156c836Syasuoka #include <sys/socket.h> 230156c836Syasuoka #include <sys/wait.h> 240156c836Syasuoka #include <netinet/in.h> 250156c836Syasuoka 260156c836Syasuoka #include <err.h> 270156c836Syasuoka #include <errno.h> 280156c836Syasuoka #include <fcntl.h> 290156c836Syasuoka #include <imsg.h> 300156c836Syasuoka #include <limits.h> 310156c836Syasuoka #include <md5.h> 320156c836Syasuoka #include <radius.h> 330156c836Syasuoka #include <stddef.h> 340156c836Syasuoka #include <stdint.h> 350156c836Syasuoka #include <stdlib.h> 360156c836Syasuoka #include <string.h> 370156c836Syasuoka #include <unistd.h> 380156c836Syasuoka 390156c836Syasuoka #include "chap_ms.h" 400156c836Syasuoka #include "imsg_subr.h" 410156c836Syasuoka #include "log.h" 420156c836Syasuoka #include "radiusd.h" 430156c836Syasuoka #include "radiusd_module.h" 440156c836Syasuoka 450156c836Syasuoka struct module_file_params { 460156c836Syasuoka int debug; 470156c836Syasuoka char path[PATH_MAX]; 480156c836Syasuoka }; 490156c836Syasuoka 500156c836Syasuoka struct module_file { 510156c836Syasuoka struct module_base *base; 520156c836Syasuoka struct imsgbuf ibuf; 530156c836Syasuoka struct module_file_params 540156c836Syasuoka params; 550156c836Syasuoka }; 560156c836Syasuoka 570156c836Syasuoka struct module_file_userinfo { 580156c836Syasuoka struct in_addr frame_ip_address; 590156c836Syasuoka char password[0]; 600156c836Syasuoka }; 610156c836Syasuoka 620156c836Syasuoka /* IPC between priv and main */ 630156c836Syasuoka enum { 640156c836Syasuoka IMSG_RADIUSD_FILE_OK = 1000, 650156c836Syasuoka IMSG_RADIUSD_FILE_NG, 660156c836Syasuoka IMSG_RADIUSD_FILE_PARAMS, 670156c836Syasuoka IMSG_RADIUSD_FILE_USERINFO 680156c836Syasuoka }; 690156c836Syasuoka 700156c836Syasuoka static void parent_dispatch_main(struct module_file_params *, 710156c836Syasuoka struct imsgbuf *, struct imsg *); 720156c836Syasuoka static void module_file_main(void) __dead; 730156c836Syasuoka static pid_t start_child(char *, int); 740156c836Syasuoka static void module_file_config_set(void *, const char *, int, 750156c836Syasuoka char * const *); 760156c836Syasuoka static void module_file_start(void *); 770156c836Syasuoka static void module_file_access_request(void *, u_int, const u_char *, 780156c836Syasuoka size_t); 790156c836Syasuoka static void auth_pap(struct module_file *, u_int, RADIUS_PACKET *, char *, 800156c836Syasuoka struct module_file_userinfo *); 810156c836Syasuoka static void auth_md5chap(struct module_file *, u_int, RADIUS_PACKET *, 820156c836Syasuoka char *, struct module_file_userinfo *); 830156c836Syasuoka static void auth_mschapv2(struct module_file *, u_int, RADIUS_PACKET *, 840156c836Syasuoka char *, struct module_file_userinfo *); 85e83d1c67Syasuoka static void auth_reject(struct module_file *, u_int, RADIUS_PACKET *, 86e83d1c67Syasuoka char *, struct module_file_userinfo *); 870156c836Syasuoka 880156c836Syasuoka static struct module_handlers module_file_handlers = { 890156c836Syasuoka .access_request = module_file_access_request, 900156c836Syasuoka .config_set = module_file_config_set, 910156c836Syasuoka .start = module_file_start 920156c836Syasuoka }; 930156c836Syasuoka 940156c836Syasuoka int 950156c836Syasuoka main(int argc, char *argv[]) 960156c836Syasuoka { 970156c836Syasuoka int ch, pairsock[2], status; 980156c836Syasuoka pid_t pid; 990156c836Syasuoka char *saved_argv0; 1000156c836Syasuoka struct imsgbuf ibuf; 1010156c836Syasuoka struct imsg imsg; 1020156c836Syasuoka ssize_t n; 1030156c836Syasuoka size_t datalen; 1040156c836Syasuoka struct module_file_params *paramsp, params; 105e1e88b63Syasuoka char pathdb[PATH_MAX]; 1060156c836Syasuoka 1070156c836Syasuoka while ((ch = getopt(argc, argv, "M")) != -1) 1080156c836Syasuoka switch (ch) { 1090156c836Syasuoka case 'M': 1100156c836Syasuoka module_file_main(); 1110156c836Syasuoka /* not reached */ 1120156c836Syasuoka break; 1130156c836Syasuoka } 1140156c836Syasuoka saved_argv0 = argv[0]; 1150156c836Syasuoka 1160156c836Syasuoka argc -= optind; 1170156c836Syasuoka argv += optind; 1180156c836Syasuoka 1190156c836Syasuoka if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, PF_UNSPEC, 1200156c836Syasuoka pairsock) == -1) 1210156c836Syasuoka err(EXIT_FAILURE, "socketpair"); 1220156c836Syasuoka 1230156c836Syasuoka log_init(0); 1240156c836Syasuoka 1250156c836Syasuoka pid = start_child(saved_argv0, pairsock[1]); 1260156c836Syasuoka 1270156c836Syasuoka /* Privileged process */ 128e1e88b63Syasuoka if (pledge("stdio rpath unveil", NULL) == -1) 129e1e88b63Syasuoka err(EXIT_FAILURE, "pledge"); 1300156c836Syasuoka setproctitle("[priv]"); 131*882428cdSclaudio if (imsgbuf_init(&ibuf, pairsock[0]) == -1) 132*882428cdSclaudio err(EXIT_FAILURE, "imsgbuf_init"); 1330156c836Syasuoka 134e1e88b63Syasuoka /* Receive parameters from the main process. */ 1350156c836Syasuoka if (imsg_sync_read(&ibuf, 2000) <= 0 || 1360156c836Syasuoka (n = imsg_get(&ibuf, &imsg)) <= 0) 1370156c836Syasuoka exit(EXIT_FAILURE); 1380156c836Syasuoka if (imsg.hdr.type != IMSG_RADIUSD_FILE_PARAMS) 1390156c836Syasuoka err(EXIT_FAILURE, "Receieved unknown message type %d", 1400156c836Syasuoka imsg.hdr.type); 1410156c836Syasuoka datalen = imsg.hdr.len - IMSG_HEADER_SIZE; 1420156c836Syasuoka if (datalen < sizeof(params)) 1430156c836Syasuoka err(EXIT_FAILURE, "Receieved IMSG_RADIUSD_FILE_PARAMS " 1440156c836Syasuoka "message is wrong size"); 1450156c836Syasuoka paramsp = imsg.data; 1460156c836Syasuoka if (paramsp->path[0] != '\0') { 147e1e88b63Syasuoka strlcpy(pathdb, paramsp->path, sizeof(pathdb)); 148e1e88b63Syasuoka strlcat(pathdb, ".db", sizeof(pathdb)); 149e1e88b63Syasuoka if (unveil(paramsp->path, "r") == -1 || 150e1e88b63Syasuoka unveil(pathdb, "r") == -1) 1510156c836Syasuoka err(EXIT_FAILURE, "unveil"); 1520156c836Syasuoka } 1530156c836Syasuoka if (paramsp->debug) 1540156c836Syasuoka log_init(1); 1550156c836Syasuoka 1560156c836Syasuoka if (unveil(NULL, NULL) == -1) 1570156c836Syasuoka err(EXIT_FAILURE, "unveil"); 1580156c836Syasuoka 1590156c836Syasuoka memcpy(¶ms, paramsp, sizeof(params)); 1600156c836Syasuoka 1610156c836Syasuoka for (;;) { 1624f3fb1ffSclaudio if (imsgbuf_read(&ibuf) != 1) 1630156c836Syasuoka break; 1640156c836Syasuoka for (;;) { 1650156c836Syasuoka if ((n = imsg_get(&ibuf, &imsg)) == -1) 1660156c836Syasuoka break; 1670156c836Syasuoka if (n == 0) 1680156c836Syasuoka break; 1690156c836Syasuoka parent_dispatch_main(¶ms, &ibuf, &imsg); 1700156c836Syasuoka imsg_free(&imsg); 171dd7efffeSclaudio imsgbuf_flush(&ibuf); 1720156c836Syasuoka } 173dd7efffeSclaudio imsgbuf_flush(&ibuf); 1740156c836Syasuoka } 175dd7efffeSclaudio imsgbuf_clear(&ibuf); 1760156c836Syasuoka 1770156c836Syasuoka while (waitpid(pid, &status, 0) == -1) { 1780156c836Syasuoka if (errno != EINTR) 1790156c836Syasuoka break; 1800156c836Syasuoka } 1810156c836Syasuoka exit(WEXITSTATUS(status)); 1820156c836Syasuoka } 1830156c836Syasuoka 1840156c836Syasuoka void 1850156c836Syasuoka parent_dispatch_main(struct module_file_params *params, struct imsgbuf *ibuf, 1860156c836Syasuoka struct imsg *imsg) 1870156c836Syasuoka { 1880156c836Syasuoka size_t datalen, entsz, passz; 1890156c836Syasuoka const char *username; 1900156c836Syasuoka char *buf, *db[2], *str; 1910156c836Syasuoka int ret; 1920156c836Syasuoka struct module_file_userinfo *ent; 1930156c836Syasuoka 1940156c836Syasuoka datalen = imsg->hdr.len - IMSG_HEADER_SIZE; 1950156c836Syasuoka switch (imsg->hdr.type) { 1960156c836Syasuoka case IMSG_RADIUSD_FILE_USERINFO: 1970156c836Syasuoka if (datalen == 0 || 1980156c836Syasuoka *((char *)imsg->data + datalen - 1) != '\0') { 1990156c836Syasuoka log_warn("%s: received IMSG_RADIUSD_FILE_USERINFO " 2000156c836Syasuoka "is wrong", __func__); 2010156c836Syasuoka goto on_error; 2020156c836Syasuoka } 2030156c836Syasuoka username = imsg->data; 2040156c836Syasuoka db[0] = params->path; 2050156c836Syasuoka db[1] = NULL; 2060156c836Syasuoka if ((ret = cgetent(&buf, db, username)) < 0) { 2070156c836Syasuoka log_info("user `%s' is not configured", username); 2080156c836Syasuoka goto on_error; 2090156c836Syasuoka } 2100156c836Syasuoka if ((ret = cgetstr(buf, "password", &str)) < 0) { 2110156c836Syasuoka log_info("password for `%s' is not configured", 2120156c836Syasuoka username); 2130156c836Syasuoka goto on_error; 2140156c836Syasuoka } 2150156c836Syasuoka passz = strlen(str) + 1; 2160156c836Syasuoka entsz = offsetof(struct module_file_userinfo, password[passz]); 2170156c836Syasuoka if ((ent = calloc(1, entsz)) == NULL) { 2180156c836Syasuoka log_warn("%s; calloc", __func__); 2190156c836Syasuoka goto on_error; 2200156c836Syasuoka } 2210156c836Syasuoka strlcpy(ent->password, str, passz); 2220156c836Syasuoka imsg_compose(ibuf, IMSG_RADIUSD_FILE_USERINFO, 0, -1, -1, 2230156c836Syasuoka ent, entsz); 2240156c836Syasuoka freezero(ent, entsz); 2250156c836Syasuoka break; 2260156c836Syasuoka } 2270156c836Syasuoka return; 2280156c836Syasuoka on_error: 2290156c836Syasuoka imsg_compose(ibuf, IMSG_RADIUSD_FILE_NG, 0, -1, -1, NULL, 0); 2300156c836Syasuoka } 2310156c836Syasuoka 2320156c836Syasuoka /* main process */ 2330156c836Syasuoka void 2340156c836Syasuoka module_file_main(void) 2350156c836Syasuoka { 2360156c836Syasuoka struct module_file module_file; 2370156c836Syasuoka 2380156c836Syasuoka setproctitle("[main]"); 2390156c836Syasuoka 2400156c836Syasuoka memset(&module_file, 0, sizeof(module_file)); 2410156c836Syasuoka if ((module_file.base = module_create(STDIN_FILENO, &module_file, 2420156c836Syasuoka &module_file_handlers)) == NULL) 2430156c836Syasuoka err(1, "Could not create a module instance"); 2440156c836Syasuoka 2450156c836Syasuoka module_drop_privilege(module_file.base, 0); 2460156c836Syasuoka 2470156c836Syasuoka module_load(module_file.base); 248*882428cdSclaudio if (imsgbuf_init(&module_file.ibuf, 3) == -1) 249*882428cdSclaudio err(EXIT_FAILURE, "imsgbuf_init"); 2500156c836Syasuoka 2510156c836Syasuoka if (pledge("stdio", NULL) == -1) 2520156c836Syasuoka err(EXIT_FAILURE, "pledge"); 2530156c836Syasuoka while (module_run(module_file.base) == 0) 2540156c836Syasuoka ; 2550156c836Syasuoka 2560156c836Syasuoka module_destroy(module_file.base); 2570156c836Syasuoka 2580156c836Syasuoka exit(0); 2590156c836Syasuoka } 2600156c836Syasuoka 2610156c836Syasuoka pid_t 2620156c836Syasuoka start_child(char *argv0, int fd) 2630156c836Syasuoka { 2640156c836Syasuoka char *argv[5]; 2650156c836Syasuoka int argc = 0; 2660156c836Syasuoka pid_t pid; 2670156c836Syasuoka 2680156c836Syasuoka switch (pid = fork()) { 2690156c836Syasuoka case -1: 2700156c836Syasuoka fatal("cannot fork"); 2710156c836Syasuoka case 0: 2720156c836Syasuoka break; 2730156c836Syasuoka default: 2740156c836Syasuoka close(fd); 2750156c836Syasuoka return (pid); 2760156c836Syasuoka } 2770156c836Syasuoka 2780156c836Syasuoka if (fd != 3) { 2790156c836Syasuoka if (dup2(fd, 3) == -1) 2800156c836Syasuoka fatal("cannot setup imsg fd"); 2810156c836Syasuoka } else if (fcntl(fd, F_SETFD, 0) == -1) 2820156c836Syasuoka fatal("cannot setup imsg fd"); 2830156c836Syasuoka 2840156c836Syasuoka argv[argc++] = argv0; 2850156c836Syasuoka argv[argc++] = "-M"; /* main proc */ 2860156c836Syasuoka argv[argc++] = NULL; 2870156c836Syasuoka execvp(argv0, argv); 2880156c836Syasuoka fatal("execvp"); 2890156c836Syasuoka } 2900156c836Syasuoka 2910156c836Syasuoka void 2920156c836Syasuoka module_file_config_set(void *ctx, const char *name, int valc, 2930156c836Syasuoka char * const * valv) 2940156c836Syasuoka { 2950156c836Syasuoka struct module_file *module = ctx; 2960156c836Syasuoka char *errmsg; 2970156c836Syasuoka 2980156c836Syasuoka if (strcmp(name, "path") == 0) { 2990156c836Syasuoka SYNTAX_ASSERT(valc == 1, "`path' must have a argument"); 3000156c836Syasuoka if (strlcpy(module->params.path, valv[0], sizeof( 3010156c836Syasuoka module->params.path)) >= sizeof(module->params.path)) { 3020156c836Syasuoka module_send_message(module->base, IMSG_NG, 3030156c836Syasuoka "`path' is too long"); 3040156c836Syasuoka return; 3050156c836Syasuoka } 3060156c836Syasuoka module_send_message(module->base, IMSG_OK, NULL); 3070156c836Syasuoka } else if (strcmp(name, "_debug") == 0) { 3080156c836Syasuoka log_init(1); 3090156c836Syasuoka module->params.debug = 1; 3100156c836Syasuoka module_send_message(module->base, IMSG_OK, NULL); 3110156c836Syasuoka } else if (strncmp(name, "_", 1) == 0) 3120156c836Syasuoka /* ignore all internal messages */ 3130156c836Syasuoka module_send_message(module->base, IMSG_OK, NULL); 3140156c836Syasuoka else 3150156c836Syasuoka module_send_message(module->base, IMSG_NG, 3160156c836Syasuoka "Unknown config parameter `%s'", name); 3170156c836Syasuoka return; 3180156c836Syasuoka syntax_error: 3190156c836Syasuoka module_send_message(module->base, IMSG_NG, "%s", errmsg); 3200156c836Syasuoka return; 3210156c836Syasuoka } 3220156c836Syasuoka 3230156c836Syasuoka void 3240156c836Syasuoka module_file_start(void *ctx) 3250156c836Syasuoka { 3260156c836Syasuoka struct module_file *module = ctx; 3270156c836Syasuoka 328e1e88b63Syasuoka /* Send parameters to parent */ 3290156c836Syasuoka if (module->params.path[0] == '\0') { 3300156c836Syasuoka module_send_message(module->base, IMSG_NG, 3310156c836Syasuoka "`path' is not configured"); 3320156c836Syasuoka return; 3330156c836Syasuoka } 3340156c836Syasuoka imsg_compose(&module->ibuf, IMSG_RADIUSD_FILE_PARAMS, 0, -1, -1, 3350156c836Syasuoka &module->params, sizeof(module->params)); 336dd7efffeSclaudio imsgbuf_flush(&module->ibuf); 3370156c836Syasuoka 3380156c836Syasuoka module_send_message(module->base, IMSG_OK, NULL); 3390156c836Syasuoka } 3400156c836Syasuoka 3410156c836Syasuoka void 3420156c836Syasuoka module_file_access_request(void *ctx, u_int query_id, const u_char *pkt, 3430156c836Syasuoka size_t pktlen) 3440156c836Syasuoka { 3450156c836Syasuoka size_t datalen; 3460156c836Syasuoka struct module_file *self = ctx; 3470156c836Syasuoka RADIUS_PACKET *radpkt = NULL; 3480156c836Syasuoka char username[256]; 3490156c836Syasuoka ssize_t n; 3500156c836Syasuoka struct imsg imsg; 3510156c836Syasuoka struct module_file_userinfo *ent; 3520156c836Syasuoka 3530156c836Syasuoka memset(&imsg, 0, sizeof(imsg)); 3540156c836Syasuoka 3550156c836Syasuoka if ((radpkt = radius_convert_packet(pkt, pktlen)) == NULL) { 3560156c836Syasuoka log_warn("%s: radius_convert_packet()", __func__); 357e83d1c67Syasuoka goto out; 3580156c836Syasuoka } 3590156c836Syasuoka radius_get_string_attr(radpkt, RADIUS_TYPE_USER_NAME, username, 3600156c836Syasuoka sizeof(username)); 3610156c836Syasuoka 3620156c836Syasuoka imsg_compose(&self->ibuf, IMSG_RADIUSD_FILE_USERINFO, 0, -1, -1, 3630156c836Syasuoka username, strlen(username) + 1); 364dd7efffeSclaudio imsgbuf_flush(&self->ibuf); 3654f3fb1ffSclaudio if (imsgbuf_read(&self->ibuf) != 1) { 366dd7efffeSclaudio log_warn("%s: imsgbuf_read()", __func__); 367e83d1c67Syasuoka goto out; 3680156c836Syasuoka } 3690156c836Syasuoka if ((n = imsg_get(&self->ibuf, &imsg)) <= 0) { 3700156c836Syasuoka log_warn("%s: imsg_get()", __func__); 371e83d1c67Syasuoka goto out; 3720156c836Syasuoka } 3730156c836Syasuoka 3740156c836Syasuoka datalen = imsg.hdr.len - IMSG_HEADER_SIZE; 3750156c836Syasuoka if (imsg.hdr.type == IMSG_RADIUSD_FILE_USERINFO) { 3760156c836Syasuoka if (datalen <= offsetof(struct module_file_userinfo, 3770156c836Syasuoka password[0])) { 3780156c836Syasuoka log_warn("%s: received IMSG_RADIUSD_FILE_USERINFO is " 3790156c836Syasuoka "invalid", __func__); 380e83d1c67Syasuoka goto out; 3810156c836Syasuoka } 3820156c836Syasuoka ent = imsg.data; 3830156c836Syasuoka if (radius_has_attr(radpkt, RADIUS_TYPE_USER_PASSWORD)) 3840156c836Syasuoka auth_pap(self, query_id, radpkt, username, ent); 3850156c836Syasuoka else if (radius_has_attr(radpkt, RADIUS_TYPE_CHAP_PASSWORD)) 3860156c836Syasuoka auth_md5chap(self, query_id, radpkt, username, ent); 3870156c836Syasuoka else if (radius_has_vs_attr(radpkt, RADIUS_VENDOR_MICROSOFT, 3880156c836Syasuoka RADIUS_VTYPE_MS_CHAP2_RESPONSE)) 3890156c836Syasuoka auth_mschapv2(self, query_id, radpkt, username, ent); 390e83d1c67Syasuoka else 391e83d1c67Syasuoka auth_reject(self, query_id, radpkt, username, ent); 392e83d1c67Syasuoka } else 393e83d1c67Syasuoka auth_reject(self, query_id, radpkt, username, NULL); 394e83d1c67Syasuoka out: 3950156c836Syasuoka if (radpkt != NULL) 3960156c836Syasuoka radius_delete_packet(radpkt); 3970156c836Syasuoka imsg_free(&imsg); 3980156c836Syasuoka return; 3990156c836Syasuoka } 4000156c836Syasuoka 4010156c836Syasuoka void 4020156c836Syasuoka auth_pap(struct module_file *self, u_int q_id, RADIUS_PACKET *radpkt, 4030156c836Syasuoka char *username, struct module_file_userinfo *ent) 4040156c836Syasuoka { 4050156c836Syasuoka RADIUS_PACKET *respkt = NULL; 4060156c836Syasuoka char pass[256]; 4070156c836Syasuoka int ret; 4080156c836Syasuoka 4090156c836Syasuoka if (radius_get_string_attr(radpkt, RADIUS_TYPE_USER_PASSWORD, pass, 4100156c836Syasuoka sizeof(pass)) != 0) { 4110156c836Syasuoka log_warnx("%s: radius_get_string_attr", __func__); 4120156c836Syasuoka return; 4130156c836Syasuoka } 4140156c836Syasuoka ret = strcmp(ent->password, pass); 4150156c836Syasuoka explicit_bzero(ent->password, strlen(ent->password)); 4160156c836Syasuoka log_info("q=%u User `%s' authentication %s (PAP)", q_id, username, 4170156c836Syasuoka (ret == 0)? "succeeded" : "failed"); 4180156c836Syasuoka if ((respkt = radius_new_response_packet((ret == 0)? 4190156c836Syasuoka RADIUS_CODE_ACCESS_ACCEPT : RADIUS_CODE_ACCESS_REJECT, radpkt)) 4200156c836Syasuoka == NULL) { 4210156c836Syasuoka log_warn("%s: radius_new_response_packet()", __func__); 4220156c836Syasuoka return; 4230156c836Syasuoka } 4240156c836Syasuoka module_accsreq_answer(self->base, q_id, 4250156c836Syasuoka radius_get_data(respkt), radius_get_length(respkt)); 4260156c836Syasuoka radius_delete_packet(respkt); 4270156c836Syasuoka } 4280156c836Syasuoka 4290156c836Syasuoka void 4300156c836Syasuoka auth_md5chap(struct module_file *self, u_int q_id, RADIUS_PACKET *radpkt, 4310156c836Syasuoka char *username, struct module_file_userinfo *ent) 4320156c836Syasuoka { 4330156c836Syasuoka RADIUS_PACKET *respkt = NULL; 4340156c836Syasuoka size_t attrlen, challlen; 4350156c836Syasuoka u_char chall[256], idpass[17], digest[16]; 4360156c836Syasuoka int ret; 4370156c836Syasuoka MD5_CTX md5; 4380156c836Syasuoka 4390156c836Syasuoka attrlen = sizeof(idpass); 4400156c836Syasuoka if (radius_get_raw_attr(radpkt, RADIUS_TYPE_CHAP_PASSWORD, idpass, 4410156c836Syasuoka &attrlen) != 0) { 4420156c836Syasuoka log_warnx("%s: radius_get_string_attr", __func__); 4430156c836Syasuoka return; 4440156c836Syasuoka } 4450156c836Syasuoka challlen = sizeof(chall); 4460156c836Syasuoka if (radius_get_raw_attr(radpkt, RADIUS_TYPE_CHAP_CHALLENGE, chall, 4470156c836Syasuoka &challlen) != 0) { 4480156c836Syasuoka log_warnx("%s: radius_get_string_attr", __func__); 4490156c836Syasuoka return; 4500156c836Syasuoka } 4510156c836Syasuoka MD5Init(&md5); 4520156c836Syasuoka MD5Update(&md5, idpass, 1); 4530156c836Syasuoka MD5Update(&md5, ent->password, strlen(ent->password)); 4540156c836Syasuoka MD5Update(&md5, chall, challlen); 4550156c836Syasuoka MD5Final(digest, &md5); 4560156c836Syasuoka 4570156c836Syasuoka ret = timingsafe_bcmp(idpass + 1, digest, sizeof(digest)); 4580156c836Syasuoka log_info("q=%u User `%s' authentication %s (CHAP)", q_id, username, 4590156c836Syasuoka (ret == 0)? "succeeded" : "failed"); 4600156c836Syasuoka if ((respkt = radius_new_response_packet((ret == 0)? 4610156c836Syasuoka RADIUS_CODE_ACCESS_ACCEPT : RADIUS_CODE_ACCESS_REJECT, radpkt)) 4620156c836Syasuoka == NULL) { 4630156c836Syasuoka log_warn("%s: radius_new_response_packet()", __func__); 4640156c836Syasuoka return; 4650156c836Syasuoka } 4660156c836Syasuoka module_accsreq_answer(self->base, q_id, 4670156c836Syasuoka radius_get_data(respkt), radius_get_length(respkt)); 4680156c836Syasuoka radius_delete_packet(respkt); 4690156c836Syasuoka } 4700156c836Syasuoka 4710156c836Syasuoka void 4720156c836Syasuoka auth_mschapv2(struct module_file *self, u_int q_id, RADIUS_PACKET *radpkt, 4730156c836Syasuoka char *username, struct module_file_userinfo *ent) 4740156c836Syasuoka { 4750156c836Syasuoka RADIUS_PACKET *respkt = NULL; 4760156c836Syasuoka size_t attrlen; 4770156c836Syasuoka int i, lpass; 4780156c836Syasuoka char *pass = NULL; 4790156c836Syasuoka uint8_t chall[MSCHAPV2_CHALLENGE_SZ]; 4800156c836Syasuoka uint8_t ntresponse[24], authenticator[16]; 4810156c836Syasuoka uint8_t pwhash[16], pwhash2[16], master[64]; 4820156c836Syasuoka struct { 4830156c836Syasuoka uint8_t salt[2]; 4840156c836Syasuoka uint8_t len; 4850156c836Syasuoka uint8_t key[16]; 4860156c836Syasuoka uint8_t pad[15]; 4870156c836Syasuoka } __packed rcvkey, sndkey; 4880156c836Syasuoka struct { 4890156c836Syasuoka uint8_t ident; 4900156c836Syasuoka uint8_t flags; 4910156c836Syasuoka uint8_t peerchall[16]; 4920156c836Syasuoka uint8_t reserved[8]; 4930156c836Syasuoka uint8_t ntresponse[24]; 4940156c836Syasuoka } __packed resp; 4950156c836Syasuoka struct authresp { 4960156c836Syasuoka uint8_t ident; 4970156c836Syasuoka uint8_t authresp[42]; 4980156c836Syasuoka } __packed authresp; 4990156c836Syasuoka 5000156c836Syasuoka 5010156c836Syasuoka attrlen = sizeof(chall); 5020156c836Syasuoka if (radius_get_vs_raw_attr(radpkt, RADIUS_VENDOR_MICROSOFT, 5030156c836Syasuoka RADIUS_VTYPE_MS_CHAP_CHALLENGE, chall, &attrlen) != 0) { 5040156c836Syasuoka log_info("q=%u failed to retribute MS-CHAP-Challenge", q_id); 5050156c836Syasuoka goto on_error; 5060156c836Syasuoka } 5070156c836Syasuoka attrlen = sizeof(resp); 5080156c836Syasuoka if (radius_get_vs_raw_attr(radpkt, RADIUS_VENDOR_MICROSOFT, 5090156c836Syasuoka RADIUS_VTYPE_MS_CHAP2_RESPONSE, &resp, &attrlen) != 0) { 5100156c836Syasuoka log_info("q=%u failed to retribute MS-CHAP2-Response", q_id); 5110156c836Syasuoka goto on_error; 5120156c836Syasuoka } 5130156c836Syasuoka 5140156c836Syasuoka /* convert the password to UTF16-LE */ 5150156c836Syasuoka lpass = strlen(ent->password); 5160156c836Syasuoka if ((pass = calloc(1, lpass * 2)) == NULL) { 5170156c836Syasuoka log_warn("%s: calloc()", __func__); 5180156c836Syasuoka goto on_error; 5190156c836Syasuoka } 5200156c836Syasuoka for (i = 0; i < lpass; i++) { 5210156c836Syasuoka pass[i * 2] = ent->password[i]; 5220156c836Syasuoka pass[i * 2 + 1] = '\0'; 5230156c836Syasuoka } 5240156c836Syasuoka 5250156c836Syasuoka /* calculate NT-Response by the password */ 5260156c836Syasuoka mschap_nt_response(chall, resp.peerchall, 5270156c836Syasuoka username, strlen(username), pass, lpass * 2, ntresponse); 5280156c836Syasuoka 5290156c836Syasuoka if (timingsafe_bcmp(ntresponse, resp.ntresponse, 24) != 0) { 5300156c836Syasuoka log_info("q=%u User `%s' authentication failed (MSCHAPv2)", 5310156c836Syasuoka q_id, username); 5320156c836Syasuoka if ((respkt = radius_new_response_packet( 5330156c836Syasuoka RADIUS_CODE_ACCESS_REJECT, radpkt)) == NULL) { 5340156c836Syasuoka log_warn("%s: radius_new_response_packet()", __func__); 5350156c836Syasuoka goto on_error; 5360156c836Syasuoka } 5370156c836Syasuoka authresp.ident = resp.ident; 5380156c836Syasuoka strlcpy(authresp.authresp, "E=691 R=0 V=3", 5390156c836Syasuoka sizeof(authresp.authresp)); 5400156c836Syasuoka radius_put_vs_raw_attr(respkt, RADIUS_VENDOR_MICROSOFT, 5410156c836Syasuoka RADIUS_VTYPE_MS_CHAP_ERROR, &authresp, 5420156c836Syasuoka offsetof(struct authresp, authresp[13])); 5430156c836Syasuoka } else { 5440156c836Syasuoka log_info("q=%u User `%s' authentication succeeded (MSCHAPv2)", 5450156c836Syasuoka q_id, username); 5460156c836Syasuoka if ((respkt = radius_new_response_packet( 5470156c836Syasuoka RADIUS_CODE_ACCESS_ACCEPT, radpkt)) == NULL) { 5480156c836Syasuoka log_warn("%s: radius_new_response_packet()", __func__); 5490156c836Syasuoka goto on_error; 5500156c836Syasuoka } 5510156c836Syasuoka mschap_auth_response(pass, lpass * 2, ntresponse, chall, 5520156c836Syasuoka resp.peerchall, username, strlen(username), 5530156c836Syasuoka authresp.authresp); 5540156c836Syasuoka authresp.ident = resp.ident; 5550156c836Syasuoka 5560156c836Syasuoka radius_put_vs_raw_attr(respkt, RADIUS_VENDOR_MICROSOFT, 5570156c836Syasuoka RADIUS_VTYPE_MS_CHAP2_SUCCESS, &authresp, 5580156c836Syasuoka offsetof(struct authresp, authresp[42])); 5590156c836Syasuoka 5600156c836Syasuoka mschap_ntpassword_hash(pass, lpass * 2, pwhash); 5610156c836Syasuoka mschap_ntpassword_hash(pwhash, sizeof(pwhash), pwhash2); 5620156c836Syasuoka mschap_masterkey(pwhash2, ntresponse, master); 5630156c836Syasuoka radius_get_authenticator(radpkt, authenticator); 5640156c836Syasuoka 5650156c836Syasuoka /* MS-MPPE-Recv-Key */ 5660156c836Syasuoka memset(&rcvkey, 0, sizeof(rcvkey)); 5670156c836Syasuoka arc4random_buf(rcvkey.salt, sizeof(rcvkey.salt)); 5680156c836Syasuoka rcvkey.salt[0] |= 0x80; 569e303f425Syasuoka rcvkey.len = 16; 5700156c836Syasuoka mschap_asymetric_startkey(master, rcvkey.key, 16, 0, 1); 5710156c836Syasuoka radius_put_vs_raw_attr(respkt, RADIUS_VENDOR_MICROSOFT, 5720156c836Syasuoka RADIUS_VTYPE_MPPE_RECV_KEY, &rcvkey, sizeof(rcvkey)); 5730156c836Syasuoka 5740156c836Syasuoka /* MS-MPPE-Send-Key */ 5750156c836Syasuoka memset(&sndkey, 0, sizeof(sndkey)); 5760156c836Syasuoka arc4random_buf(sndkey.salt, sizeof(sndkey.salt)); 5770156c836Syasuoka sndkey.salt[0] |= 0x80; 578e303f425Syasuoka sndkey.len = 16; 5790156c836Syasuoka mschap_asymetric_startkey(master, sndkey.key, 16, 1, 1); 5800156c836Syasuoka radius_put_vs_raw_attr(respkt, RADIUS_VENDOR_MICROSOFT, 5810156c836Syasuoka RADIUS_VTYPE_MPPE_SEND_KEY, &sndkey, sizeof(sndkey)); 5820156c836Syasuoka } 5830156c836Syasuoka 5840156c836Syasuoka module_accsreq_answer(self->base, q_id, 5850156c836Syasuoka radius_get_data(respkt), radius_get_length(respkt)); 5860156c836Syasuoka on_error: 5870156c836Syasuoka /* bzero password */ 5880156c836Syasuoka explicit_bzero(ent->password, strlen(ent->password)); 5890156c836Syasuoka if (pass != NULL) 5900156c836Syasuoka explicit_bzero(pass, lpass * 2); 5910156c836Syasuoka free(pass); 5920156c836Syasuoka if (respkt != NULL) 5930156c836Syasuoka radius_delete_packet(respkt); 5940156c836Syasuoka } 595e83d1c67Syasuoka 596e83d1c67Syasuoka void 597e83d1c67Syasuoka auth_reject(struct module_file *self, u_int q_id, RADIUS_PACKET *radpkt, 598e83d1c67Syasuoka char *username, struct module_file_userinfo *ent) 599e83d1c67Syasuoka { 600e83d1c67Syasuoka RADIUS_PACKET *respkt = NULL; 601e83d1c67Syasuoka 602e83d1c67Syasuoka if (ent != NULL) 603e83d1c67Syasuoka explicit_bzero(ent->password, strlen(ent->password)); 604e83d1c67Syasuoka 605e83d1c67Syasuoka log_info("q=%u User `%s' authentication failed", q_id, 606e83d1c67Syasuoka username); 607e83d1c67Syasuoka if ((respkt = radius_new_response_packet(RADIUS_CODE_ACCESS_REJECT, 608e83d1c67Syasuoka radpkt)) == NULL) { 609e83d1c67Syasuoka log_warn("%s: radius_new_response_packet()", __func__); 610e83d1c67Syasuoka return; 611e83d1c67Syasuoka } 612e83d1c67Syasuoka module_accsreq_answer(self->base, q_id, 613e83d1c67Syasuoka radius_get_data(respkt), radius_get_length(respkt)); 614e83d1c67Syasuoka radius_delete_packet(respkt); 615e83d1c67Syasuoka } 616