📄 x99_rlm.c
字号:
/* * x99_rlm.c * $Id: x99_rlm.c,v 1.39.2.1 2004/11/03 17:28:25 aland Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright 2000,2001,2002 The FreeRADIUS server project * Copyright 2001,2002 Google, Inc. *//* * STRONG WARNING SECTION: * * ANSI X9.9 has been withdrawn as a standard, due to the weakness of DES. * An attacker can learn the token's secret by observing two * challenge/response pairs. See ANSI document X9 TG-24-1999 * <URL:http://www.x9.org/TG24_1999.pdf>. * * Please read the accompanying docs. *//* * TODO: support setting multiple auth-types in authorize() * TODO: support soft PIN? ??? * TODO: support other than ILP32 (for State) */#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <fcntl.h>#include <time.h>#include <netinet/in.h> /* htonl() */#ifdef FREERADIUS#include "radiusd.h"#include "modules.h"#endif#include "x99.h"static const char rcsid[] = "$Id: x99_rlm.c,v 1.39.2.1 2004/11/03 17:28:25 aland Exp $";/* Global data */static int rnd_fd; /* fd for random device */static unsigned char hmac_key[16]; /* to protect State attribute *//* A mapping of configuration file names to internal variables. */static CONF_PARSER module_config[] = { { "pwdfile", PW_TYPE_STRING_PTR, offsetof(x99_token_t, pwdfile), NULL, PWDFILE }, { "syncdir", PW_TYPE_STRING_PTR, offsetof(x99_token_t, syncdir), NULL, SYNCDIR }, { "challenge_prompt", PW_TYPE_STRING_PTR, offsetof(x99_token_t,chal_prompt), NULL, CHALLENGE_PROMPT }, { "challenge_length", PW_TYPE_INTEGER, offsetof(x99_token_t, chal_len), NULL, "6" }, { "challenge_delay", PW_TYPE_INTEGER, offsetof(x99_token_t, chal_delay), NULL, "30" }, { "softfail", PW_TYPE_INTEGER, offsetof(x99_token_t, softfail), NULL, "5" }, { "hardfail", PW_TYPE_INTEGER, offsetof(x99_token_t, hardfail), NULL, "0" }, { "allow_sync", PW_TYPE_BOOLEAN, offsetof(x99_token_t, allow_sync), NULL, "yes" }, { "fast_sync", PW_TYPE_BOOLEAN, offsetof(x99_token_t, fast_sync), NULL, "yes" }, { "allow_async", PW_TYPE_BOOLEAN, offsetof(x99_token_t, allow_async), NULL, "no" }, { "challenge_req", PW_TYPE_STRING_PTR, offsetof(x99_token_t, chal_req), NULL, CHALLENGE_REQ }, { "resync_req", PW_TYPE_STRING_PTR, offsetof(x99_token_t, resync_req), NULL, RESYNC_REQ }, { "ewindow_size", PW_TYPE_INTEGER, offsetof(x99_token_t, ewindow_size), NULL, "0" }, { "ewindow2_size", PW_TYPE_INTEGER, offsetof(x99_token_t, ewindow2_size), NULL, "0" }, { "ewindow2_delay", PW_TYPE_INTEGER, offsetof(x99_token_t, ewindow2_delay), NULL, "60" }, { "mschapv2_mppe", PW_TYPE_INTEGER, offsetof(x99_token_t, mschapv2_mppe_policy), NULL, "2" }, { "mschapv2_mppe_bits", PW_TYPE_INTEGER, offsetof(x99_token_t, mschapv2_mppe_types), NULL, "2" }, { "mschap_mppe", PW_TYPE_INTEGER, offsetof(x99_token_t, mschap_mppe_policy), NULL, "2" }, { "mschap_mppe_bits", PW_TYPE_INTEGER, offsetof(x99_token_t, mschap_mppe_types), NULL, "2" },#if 0 { "twindow_min", PW_TYPE_INTEGER, offsetof(x99_token_t, twindow_min), NULL, "0" }, { "twindow_max", PW_TYPE_INTEGER, offsetof(x99_token_t, twindow_max), NULL, "0" },#endif { NULL, -1, 0, NULL, NULL } /* end the list */};/* per-module initialization */static intx99_token_init(void){ if ((rnd_fd = open(DEVURANDOM, O_RDONLY)) == -1) { x99_log(X99_LOG_ERR, "init: error opening %s: %s", DEVURANDOM, strerror(errno)); return -1; } /* Generate a random key, used to protect the State attribute. */ if (x99_get_random(rnd_fd, hmac_key, sizeof(hmac_key)) == -1) { x99_log(X99_LOG_ERR, "init: failed to obtain random data for hmac_key"); return -1; } /* Initialize the passcode encoding/checking functions. */ x99_pwe_init(); return 0;}/* per-instance initialization */static intx99_token_instantiate(CONF_SECTION *conf, void **instance){ x99_token_t *data; char *p; struct stat st; /* Set up a storage area for instance data. */ data = rad_malloc(sizeof(*data)); if (!data) return -1; memset(data, 0, sizeof(*data)); /* If the configuration parameters can't be parsed, then fail. */ if (cf_section_parse(conf, data, module_config) < 0) { free(data); return -1; } /* Verify ranges for those vars that are limited. */ if ((data->chal_len < 5) || (data->chal_len > MAX_CHALLENGE_LEN)) { data->chal_len = 6; x99_log(X99_LOG_ERR, "invalid challenge_length, range 5-%d, using default of 6", MAX_CHALLENGE_LEN); } /* Enforce a single "%" sequence, which must be "%s" */ p = strchr(data->chal_prompt, '%'); if ((p == NULL) || (p != strrchr(data->chal_prompt, '%')) || strncmp(p,"%s",2)){ free(data->chal_prompt); data->chal_prompt = strdup(CHALLENGE_PROMPT); x99_log(X99_LOG_ERR, "invalid challenge_prompt, using default of \"%s\"", CHALLENGE_PROMPT); } if (data->softfail < 0) { data->softfail = 5; x99_log(X99_LOG_ERR, "softfail must be at least 1 " "(or 0 == infinite), using default of 5"); } if (data->hardfail < 0) { data->hardfail = 0; x99_log(X99_LOG_ERR, "hardfail must be at least 1 " "(or 0 == infinite), using default of 0"); } if (data->fast_sync && !data->allow_sync) { data->fast_sync = 0; x99_log(X99_LOG_INFO, "fast_sync is yes, but allow_sync is no; disabling fast_sync"); } if (!data->allow_sync && !data->allow_async) { x99_log(X99_LOG_ERR, "at least one of {allow_async, allow_sync} must be set"); free(data); return -1; } if ((data->ewindow_size > MAX_EWINDOW_SIZE) || (data->ewindow_size < 0)) { data->ewindow_size = 0; x99_log(X99_LOG_ERR, "max ewindow_size is %d, using default of 0", MAX_EWINDOW_SIZE); } if (data->ewindow2_size && (data->ewindow2_size < data->ewindow_size)) { data->ewindow2_size = 0; x99_log(X99_LOG_ERR, "ewindow2_size must be at least as large as " "ewindow_size, using default of 0"); } if (data->ewindow2_size && !data->ewindow2_delay) { data->ewindow2_size = 0; x99_log(X99_LOG_ERR, "ewindow2_size is non-zero, " "but ewindow2_delay is zero; disabling ewindow2"); } if ((data->mschapv2_mppe_policy > 2) || (data->mschapv2_mppe_policy < 0)) { data->mschapv2_mppe_policy = 2; x99_log(X99_LOG_ERR, "invalid value for mschapv2_mppe, using default of 2"); } if ((data->mschapv2_mppe_types > 2) || (data->mschapv2_mppe_types < 0)) { data->mschapv2_mppe_types = 2; x99_log(X99_LOG_ERR, "invalid value for mschapv2_mppe_bits, using default of 2"); } if ((data->mschap_mppe_policy > 2) || (data->mschap_mppe_policy < 0)) { data->mschap_mppe_policy = 2; x99_log(X99_LOG_ERR, "invalid value for mschap_mppe, using default of 2"); } if (data->mschap_mppe_types != 2) { data->mschap_mppe_types = 2; x99_log(X99_LOG_ERR, "invalid value for mschap_mppe_bits, using default of 2"); }#if 0 if (data->twindow_max - data->twindow_min > MAX_TWINDOW_SIZE) { data->twindow_min = data->twindow_max = 0; x99_log(X99_LOG_ERR, "max time window size is %d, using default of 0", MAX_TWINDOW_SIZE); } if ((data->twindow_min > 0) || (data->twindow_max < 0) || (data->twindow_max < data->twindow_min)) { data->twindow_min = data->twindow_max = 0; x99_log(X99_LOG_ERR, "invalid values for time window, using default of 0"); }#endif if (stat(data->syncdir, &st) != 0) { x99_log(X99_LOG_ERR, "syncdir %s error: %s", data->syncdir, strerror(errno)); free(data); return -1; } if (st.st_mode != (S_IFDIR|S_IRWXU)) { x99_log(X99_LOG_ERR, "syncdir %s has loose permissions", data->syncdir); free(data); return -1; } /* Set the instance name (for use with authorize()) */ data->name = cf_section_name2(conf); if (!data->name) data->name = cf_section_name1(conf); if (!data->name) { x99_log(X99_LOG_ERR, "no instance name (this can't happen)"); free(data); return -1; } *instance = data; return 0;}/* Generate a challenge to be presented to the user. */static intx99_token_authorize(void *instance, REQUEST *request){ x99_token_t *inst = (x99_token_t *) instance; char challenge[MAX_CHALLENGE_LEN + 1]; /* +1 for '\0' terminator */ char *state; int rc; x99_user_info_t user_info; int user_found, auth_type_found; int pwattr; int32_t sflags = 0; /* flags for state */ VALUE_PAIR *vp; /* Early exit if Auth-Type != inst->name */ auth_type_found = 0; if ((vp = pairfind(request->config_items, PW_AUTHTYPE)) != NULL) { auth_type_found = 1; if (strcmp(vp->strvalue, inst->name)) { return RLM_MODULE_NOOP; } } /* The State attribute will be present if this is a response. */ if (pairfind(request->packet->vps, PW_STATE) != NULL) { DEBUG("rlm_x99_token: autz: Found response to access challenge"); return RLM_MODULE_OK; } /* User-Name attribute required. */ if (!request->username) { x99_log(X99_LOG_AUTH, "autz: Attribute \"User-Name\" required for authentication."); return RLM_MODULE_INVALID; } if ((pwattr = x99_pw_present(request)) == 0) { x99_log(X99_LOG_AUTH, "autz: Attribute \"User-Password\" " "or equivalent required for authentication."); return RLM_MODULE_INVALID; } /* Look up the user's info. */ user_found = 1; if ((rc = x99_get_user_info(inst->pwdfile, request->username->strvalue, &user_info)) == -2) {#if 0 /* x99_get_user_info() logs a more useful message, this is noisy. */ x99_log(X99_LOG_ERR, "autz: error reading user [%s] info", request->username->strvalue);#endif return RLM_MODULE_FAIL; } if (rc == -1) { x99_log(X99_LOG_AUTH, "autz: user [%s] not found in %s", request->username->strvalue, inst->pwdfile); memset(&user_info, 0, sizeof(user_info)); /* X99_CF_NONE */ user_found = 0; } /* fast_sync mode (challenge only if requested) */ if (inst->fast_sync && ((user_info.card_id & X99_CF_SM) || !user_found)) { if ((x99_pw_valid(request, inst, pwattr, inst->resync_req, NULL) && /* Set a bit indicating resync */ (sflags |= htonl(1))) || x99_pw_valid(request, inst, pwattr, inst->chal_req, NULL)) { /* * Generate a challenge if requested. We don't test for card * support [for async] because it's tricky for unknown users. * Some configurations would have a problem where known users * cannot request a challenge, but unknown users can. This * reveals information. The easiest fix seems to be to always * hand out a challenge on request. * We also don't test if the server allows async mode, this * would also reveal information. */ DEBUG("rlm_x99_token: autz: fast_sync challenge requested"); goto gen_challenge; } else { /* * Otherwise, this is the token sync response. Signal * the authenticate code to ignore State. We don't need * to set a value, /existence/ of the vp is the signal. */ if ((vp = paircreate(PW_X99_FAST, PW_TYPE_INTEGER)) == NULL) { x99_log(X99_LOG_CRIT, "autz: no memory"); return RLM_MODULE_FAIL; } pairadd(&request->config_items, vp); DEBUG("rlm_x99_token: autz: using fast_sync"); if (!auth_type_found) pairadd(&request->config_items, pairmake("Auth-Type", "x99_token", T_OP_EQ)); return RLM_MODULE_OK; } } /* if (fast_sync && card supports sync mode) */gen_challenge: /* Set the resync bit by default if the user can't request it. */ if (!inst->fast_sync) sflags |= htonl(1); /* Generate a random challenge. */ if (x99_get_challenge(rnd_fd, challenge, inst->chal_len) == -1) { x99_log(X99_LOG_ERR, "autz: failed to obtain random challenge"); return RLM_MODULE_FAIL; } /* * Create the State attribute, which will be returned to us along with * the response. We will need this to verify the response. Create * a strong state if the user will be able use this with their token. * Otherwise, we discard it anyway, so don't "waste" time with hmac. * We also don't do the hmac if the user wasn't found (mask won't match). * We always create at least a trivial state, so x99_token_authorize() * can easily pass on to x99_token_authenticate(). */ if (user_info.card_id & X99_CF_AM) { time_t now = time(NULL); if (sizeof(now) != 4 || sizeof(long) != 4) { x99_log(X99_LOG_ERR, "autz: only ILP32 arch is supported"); return RLM_MODULE_FAIL; } now = htonl(now); if (x99_gen_state(&state, NULL, challenge, sflags, now, hmac_key) != 0){ x99_log(X99_LOG_ERR, "autz: failed to generate state"); return RLM_MODULE_FAIL; } } else { /* x2 b/c pairmake() string->octet needs even num of digits */ state = rad_malloc(3 + inst->chal_len * 2); (void) sprintf(state, "0x%s%s", challenge, challenge); } pairadd(&request->reply->vps, pairmake("State", state, T_OP_EQ)); free(state); /* Add the challenge to the reply. */ { char *u_challenge; /* challenge with addt'l presentation text */ u_challenge = rad_malloc(strlen(inst->chal_prompt)+MAX_CHALLENGE_LEN+1); (void) sprintf(u_challenge, inst->chal_prompt, challenge); pairadd(&request->reply->vps, pairmake("Reply-Message", u_challenge, T_OP_EQ)); free(u_challenge);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -