⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 x99_rlm.c

📁 RADIUS认证协议
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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 + -