Beispiel #1
0
/** Converts data stored in a file name back to its original form
 *
 * @param out Where to write the unescaped string (may be the same as in).
 * @param outlen Length of the output buffer.
 * @param in Input filename.
 * @param inlen Length of input.
 * @return
 *	- Number of bytes written to output buffer
 *	- offset where parse error occurred on failure.
 */
ssize_t rad_filename_unescape(char *out, size_t outlen, char const *in, size_t inlen)
{
	char const *p, *end = in + inlen;
	size_t freespace = outlen;

	for (p = in; p < end; p++) {
		if (freespace <= 1) break;

		if (((*p >= 'A') && (*p <= 'Z')) ||
		    ((*p >= 'a') && (*p <= 'z')) ||
		    ((*p >= '0') && (*p <= '9')) ||
		    (*p == '_')) {
		 	*out++ = *p;
		 	freespace--;
		 	continue;
		}

		if (p[0] == '-') {
			/*
			 *	End of input, '-' needs at least one extra char after
			 *	it to be valid.
			 */
			if ((end - p) < 2) return in - p;
			if (p[1] == '-') {
				p++;
				*out++ = '-';
				freespace--;
				continue;
			}

			/*
			 *	End of input, '-' must be followed by <hex><hex>
			 *	but there aren't enough chars left
			 */
			if ((end - p) < 3) return in - p;

			/*
			 *	If hex2bin returns 0 the next two chars weren't hexits.
			 */
			if (fr_hex2bin((uint8_t *) out, 1, in, 1) == 0) return in - (p + 1);
			in += 2;
			out++;
			freespace--;
		}

		return in - p; /* offset we found the bad char at */
	}
	*out = '\0';

	return outlen - freespace;	/* how many bytes were written */
}
Beispiel #2
0
/** Determine the PSK to use for an outgoing connection
 *
 * @param[in] ssl		session.
 * @param[in] identity		The identity of the PSK to search for.
 * @param[out] psk		Where to write the PSK we found (if any).
 * @param[in] max_psk_len	The length of the buffer provided for PSK.
 * @return
 *	- 0 if no PSK matching identity was found.
 *	- >0 if a PSK matching identity was found (the length of bytes written to psk).
 */
unsigned int tls_session_psk_client_cb(SSL *ssl, UNUSED char const *hint,
				       char *identity, unsigned int max_identity_len,
				       unsigned char *psk, unsigned int max_psk_len)
{
	unsigned int psk_len;
	fr_tls_conf_t *conf;

	conf = (fr_tls_conf_t *)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_CONF);
	if (!conf) return 0;

	psk_len = strlen(conf->psk_password);
	if (psk_len > (2 * max_psk_len)) return 0;

	strlcpy(identity, conf->psk_identity, max_identity_len);

	return fr_hex2bin(psk, max_psk_len, conf->psk_password, psk_len);
}
Beispiel #3
0
static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
				     int brace, char const **error)
{
	char *p, *q;
	xlat_exp_t *node;

	if (!*fmt) return 0;

	XLAT_DEBUG("LITERAL: %s", fmt);

	node = talloc_zero(ctx, xlat_exp_t);
	node->fmt = fmt;
	node->len = 0;
	node->type = XLAT_LITERAL;

	p = fmt;
	q = fmt;

	while (*p) {
		/*
		 *	Convert \n to it's literal representation.
		 */
		if (p[0] == '\\') switch (p[1]) {
			case 't':
				*(q++) = '\t';
				p += 2;
				node->len++;
				continue;

			case 'n':
				*(q++) = '\n';
				p += 2;
				node->len++;
				continue;

			case 'x':
				p += 2;
				if (!p[0] || !p[1]) {
					talloc_free(node);
					*error = "Hex expansion requires two hex digits";
					return -(p - fmt);
				}

				if (!fr_hex2bin((uint8_t *) q, p, 2)) {
					talloc_free(node);
					*error = "Invalid hex characters";
					return -(p - fmt);
				}

				/*
				 *	Don't let people shoot themselves in the foot.
				 *	\x00 is forbidden.
				 */
				if (!*q) {
					talloc_free(node);
					*error = "Cannot add zero byte to printable string";
					return -(p - fmt);
				}

				p += 2;
				q++;
				node->len++;
				continue;

			default:
				*(q++) = *p;
				p += 2;
				node->len++;
				continue;
			}

		/*
		 *	Process the expansion.
		 */
		if ((p[0] == '%') && (p[1] == '{')) {
			ssize_t slen;

			XLAT_DEBUG("LITERAL: %s --> %s", node->fmt, p);
			slen = xlat_tokenize_expansion(node, p, &node->next, error);
			if (slen <= 0) {
				talloc_free(node);
				return slen - (p - fmt);
			}
			*p = '\0'; /* end the literal */
			p += slen;

			rad_assert(node->next != NULL);

			/*
			 *	Short-circuit the recursive call.
			 *	This saves another function call and
			 *	memory allocation.
			 */
			if (!*p) break;

			/*
			 *	"foo %{User-Name} bar"
			 *	LITERAL		"foo "
			 *	EXPANSION	User-Name
			 *	LITERAL		" bar"
			 */
			slen = xlat_tokenize_literal(node->next, p, &(node->next->next), brace, error);
			rad_assert(slen != 0);
			if (slen < 0) {
				talloc_free(node);
				return slen - (p - fmt);
			}

			p += slen;
			break;	/* stop processing the string */
		}

		/*
		 *	Check for valid single-character expansions.
		 */
		if (p[0] == '%') {
			ssize_t slen;
			xlat_exp_t *next;

			if (!p[1] || !strchr("%dlmtDGHISTY", p[1])) {
					talloc_free(node);
					*error = "Invalid variable expansion";
					p++;
					return - (p - fmt);
			}

			XLAT_DEBUG("PERCENT: %s --> %c", node->fmt, p[1]);
			next = talloc_zero(node, xlat_exp_t);
			next->fmt = p + 1;
			next->len = 1;
			next->type = XLAT_PERCENT;

			node->next = next;
			*p = '\0';
			p += 2;

			if (!*p) break;

			/*
			 *	And recurse.
			 */
			slen = xlat_tokenize_literal(node->next, p, &(node->next->next), brace, error);
			rad_assert(slen != 0);
			if (slen < 0) {
				talloc_free(node);
				return slen - (p - fmt);
			}

			p += slen;
			break;	/* stop processing the string */
		}

		/*
		 *	If required, eat the brace.
		 */
		if (brace && (*p == '}')) {
			*q = '\0';
			p++;
			break;
		}

		*(q++) = *(p++);
		node->len++;
	}

	/*
	 *	Squash zero-width literals
	 */
	if (node->len > 0) {
		*head = node;

	} else {
		(void) talloc_steal(ctx, node->next);
		*head = node->next;
		talloc_free(node);
	}

	return p - fmt;
}
/** Unpack data
 *
 *  Example: %{unpack:&Class 0 integer}
 *
 *  Expands Class, treating octet at offset 0 (bytes 0-3) as an "integer".
 */
static ssize_t unpack_xlat(UNUSED void *instance, REQUEST *request, char const *fmt,
			   char *out, size_t outlen)
{
	char *data_name, *data_size, *data_type;
	char *p;
	size_t len, input_len;
	int offset;
	PW_TYPE type;
	DICT_ATTR const *da;
	VALUE_PAIR *vp, *cast;
	uint8_t const *input;
	char buffer[256];
	uint8_t blob[256];

	/*
	 *	FIXME: copy only the fields here, as we parse them.
	 */
	strlcpy(buffer, fmt, sizeof(buffer));

	p = buffer;
	while (isspace((int) *p)) p++; /* skip leading spaces */

	data_name = p;

	while (*p && !isspace((int) *p)) p++;

	if (!*p) {
	error:
		REDEBUG("Format string should be '<data> <offset> <type>' e.g. '&Class 1 integer'");
	nothing:
		*out = '\0';
		return -1;
	}

	while (isspace((int) *p)) *(p++) = '\0';
	if (!*p) GOTO_ERROR;

	data_size = p;

	while (*p && !isspace((int) *p)) p++;
	if (!*p) GOTO_ERROR;

	while (isspace((int) *p)) *(p++) = '\0';
	if (!*p) GOTO_ERROR;

	data_type = p;

	while (*p && !isspace((int) *p)) p++;
	if (*p) GOTO_ERROR;	/* anything after the type is an error */

	/*
	 *	Attribute reference
	 */
	if (*data_name == '&') {
		if (radius_get_vp(&vp, request, data_name) < 0) goto nothing;

		if ((vp->da->type != PW_TYPE_OCTETS) &&
		    (vp->da->type != PW_TYPE_STRING)) {
			REDEBUG("unpack requires the input attribute to be 'string' or 'octets'");
			goto nothing;
		}
		input = vp->vp_octets;
		input_len = vp->vp_length;

	} else if ((data_name[0] == '0') && (data_name[1] == 'x')) {
		/*
		 *	Hex data.
		 */
		len = strlen(data_name + 2);
		if ((len & 0x01) != 0) {
			RDEBUG("Invalid hex string in '%s'", data_name);
			goto nothing;
		}
		input = blob;
		input_len = fr_hex2bin(blob, sizeof(blob), data_name + 2, len);

	} else {
		GOTO_ERROR;
	}

	offset = (int) strtoul(data_size, &p, 10);
	if (*p) {
		REDEBUG("unpack requires a decimal number, not '%s'", data_size);
		goto nothing;
	}

	type = fr_str2int(dict_attr_types, data_type, PW_TYPE_INVALID);
	if (type == PW_TYPE_INVALID) {
		REDEBUG("Invalid data type '%s'", data_type);
		goto nothing;
	}

	/*
	 *	Output must be a non-zero limited size.
	 */
	if ((dict_attr_sizes[type][0] ==  0) ||
	    (dict_attr_sizes[type][0] != dict_attr_sizes[type][1])) {
		REDEBUG("unpack requires fixed-size output type, not '%s'", data_type);
		goto nothing;
	}

	if (input_len < (offset + dict_attr_sizes[type][0])) {
		REDEBUG("Insufficient data to unpack '%s' from '%s'", data_type, data_name);
		goto nothing;
	}

	da = dict_attrbyvalue(PW_CAST_BASE + type, 0);
	if (!da) {
		REDEBUG("Cannot decode type '%s'", data_type);
		goto nothing;
	}

	cast = pairalloc(request, da);
	if (!cast) goto nothing;

	memcpy(&(cast->data), input + offset, dict_attr_sizes[type][0]);
	cast->vp_length = dict_attr_sizes[type][0];

	/*
	 *	Hacks
	 */
	switch (type) {
	case PW_TYPE_SIGNED:
	case PW_TYPE_INTEGER:
	case PW_TYPE_DATE:
		cast->vp_integer = ntohl(cast->vp_integer);
		break;

	case PW_TYPE_SHORT:
		cast->vp_short = ((input[offset] << 8) | input[offset + 1]);
		break;

	case PW_TYPE_INTEGER64:
		cast->vp_integer64 = ntohll(cast->vp_integer64);
		break;

	default:
		break;
	}

	len = vp_prints_value(out, outlen, cast, 0);
	talloc_free(cast);
	if (is_truncated(len, outlen)) {
		REDEBUG("Insufficient buffer space to unpack data");
		goto nothing;
	}

	return len;
}
Beispiel #5
0
/*
 *	Do the MS-CHAP stuff.
 *
 *	This function is here so that all of the MS-CHAP related
 *	authentication is in one place, and we can perhaps later replace
 *	it with code to call winbindd, or something similar.
 */
static int do_mschap(rlm_mschap_t *inst,
		     REQUEST *request, VALUE_PAIR *password,
		     uint8_t *challenge, uint8_t *response,
		     uint8_t *nthashhash, int do_ntlm_auth)
{
	uint8_t		calculated[24];

	/*
	 *	Do normal authentication.
	 */
	if (!do_ntlm_auth) {
		/*
		 *	No password: can't do authentication.
		 */
		if (!password) {
			RDEBUG2("FAILED: No NT/LM-Password.  Cannot perform authentication.");
			return -1;
		}

		smbdes_mschap(password->vp_strvalue, challenge, calculated);
		if (memcmp(response, calculated, 24) != 0) {
			return -1;
		}

		/*
		 *	If the password exists, and is an NT-Password,
		 *	then calculate the hash of the NT hash.  Doing this
		 *	here minimizes work for later.
		 */
		if (password && (password->attribute == PW_NT_PASSWORD)) {
			fr_md4_calc(nthashhash, password->vp_octets, 16);
		} else {
			memset(nthashhash, 0, 16);
		}
	} else {		/* run ntlm_auth */
		int	result;
		char	buffer[256];

		memset(nthashhash, 0, 16);

		/*
		 *	Run the program, and expect that we get 16
		 */
		result = radius_exec_program(inst->ntlm_auth, request,
					     TRUE, /* wait */
					     buffer, sizeof(buffer),
					     NULL, NULL, 1);
		if (result != 0) {
			RDEBUG2("External script failed.");
			return -1;
		}

		/*
		 *	Parse the answer as an nthashhash.
		 *
		 *	ntlm_auth currently returns:
		 *	NT_KEY: 000102030405060708090a0b0c0d0e0f
		 */
		if (memcmp(buffer, "NT_KEY: ", 8) != 0) {
			RDEBUG2("Invalid output from ntlm_auth: expecting NT_KEY");
			return -1;
		}

		/*
		 *	Check the length.  It should be at least 32,
		 *	with an LF at the end.
		 */
		if (strlen(buffer + 8) < 32) {
			RDEBUG2("Invalid output from ntlm_auth: NT_KEY has unexpected length");
			return -1;
		}

		/*
		 *	Update the NT hash hash, from the NT key.
		 */
		if (fr_hex2bin(buffer + 8, nthashhash, 16) != 16) {
			RDEBUG2("Invalid output from ntlm_auth: NT_KEY has non-hex values");
			return -1;
		}
	}

	return 0;
}
Beispiel #6
0
/*
 *	mschap_authenticate() - authenticate user based on given
 *	attributes and configuration.
 *	We will try to find out password in configuration
 *	or in configured passwd file.
 *	If one is found we will check paraneters given by NAS.
 *
 *	If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have
 *	one of:
 *		PAP:      PW_USER_PASSWORD or
 *		MS-CHAP:  PW_MSCHAP_CHALLENGE and PW_MSCHAP_RESPONSE or
 *		MS-CHAP2: PW_MSCHAP_CHALLENGE and PW_MSCHAP2_RESPONSE
 *	In case of password mismatch or locked account we MAY return
 *	PW_MSCHAP_ERROR for MS-CHAP or MS-CHAP v2
 *	If MS-CHAP2 succeeds we MUST return
 *	PW_MSCHAP2_SUCCESS
 */
static int mschap_authenticate(void * instance, REQUEST *request)
{
#define inst ((rlm_mschap_t *)instance)
	VALUE_PAIR *challenge = NULL;
	VALUE_PAIR *response = NULL;
	VALUE_PAIR *password = NULL;
	VALUE_PAIR *lm_password, *nt_password, *smb_ctrl;
	VALUE_PAIR *username;
	uint8_t nthashhash[16];
	char msch2resp[42];
	char *username_string;
	int chap = 0;
	int		do_ntlm_auth;

	/*
	 *	If we have ntlm_auth configured, use it unless told
	 *	otherwise
	 */
	do_ntlm_auth = (inst->ntlm_auth != NULL);

	/*
	 *	If we have an ntlm_auth configuration, then we may
	 *	want to suppress it.
	 */
	if (do_ntlm_auth) {
		VALUE_PAIR *vp = pairfind(request->config_items,
					  PW_MS_CHAP_USE_NTLM_AUTH);
		if (vp) do_ntlm_auth = vp->vp_integer;
	}

	/*
	 *	Find the SMB-Account-Ctrl attribute, or the
	 *	SMB-Account-Ctrl-Text attribute.
	 */
	smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL);
	if (!smb_ctrl) {
		password = pairfind(request->config_items,
				    PW_SMB_ACCOUNT_CTRL_TEXT);
		if (password) {
			smb_ctrl = radius_pairmake(request,
						   &request->config_items,
						   "SMB-Account-CTRL", "0",
						   T_OP_SET);
			if (smb_ctrl) {
				smb_ctrl->vp_integer = pdb_decode_acct_ctrl(password->vp_strvalue);
			}
		}
	}

	/*
	 *	We're configured to do MS-CHAP authentication.
	 *	and account control information exists.  Enforce it.
	 */
	if (smb_ctrl) {
		/*
		 *	Password is not required.
		 */
		if ((smb_ctrl->vp_integer & ACB_PWNOTREQ) != 0) {
			RDEBUG2("SMB-Account-Ctrl says no password is required.");
			return RLM_MODULE_OK;
		}
	}

	/*
	 *	Decide how to get the passwords.
	 */
	password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD);

	/*
	 *	We need an LM-Password.
	 */
	lm_password = pairfind(request->config_items, PW_LM_PASSWORD);
	if (lm_password) {
		/*
		 *	Allow raw octets.
		 */
		if ((lm_password->length == 16) ||
		    ((lm_password->length == 32) &&
		     (fr_hex2bin(lm_password->vp_strvalue,
				 lm_password->vp_octets, 16) == 16))) {
			RDEBUG2("Found LM-Password");
			lm_password->length = 16;

		} else {
			radlog_request(L_ERR, 0, request, "Invalid LM-Password");
			lm_password = NULL;
		}

	} else if (!password) {
		if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured.  Cannot create LM-Password.");

	} else {		/* there is a configured Cleartext-Password */
		lm_password = radius_pairmake(request, &request->config_items,
					      "LM-Password", "", T_OP_EQ);
		if (!lm_password) {
			radlog_request(L_ERR, 0, request, "No memory");
		} else {
			smbdes_lmpwdhash(password->vp_strvalue,
					 lm_password->vp_octets);
			lm_password->length = 16;
		}
	}

	/*
	 *	We need an NT-Password.
	 */
	nt_password = pairfind(request->config_items, PW_NT_PASSWORD);
	if (nt_password) {
		if ((nt_password->length == 16) ||
		    ((nt_password->length == 32) &&
		     (fr_hex2bin(nt_password->vp_strvalue,
				 nt_password->vp_octets, 16) == 16))) {
			RDEBUG2("Found NT-Password");
			nt_password->length = 16;

                } else {
			radlog_request(L_ERR, 0, request, "Invalid NT-Password");
			nt_password = NULL;
		}
	} else if (!password) {
		if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured.  Cannot create NT-Password.");

	} else {		/* there is a configured Cleartext-Password */
		nt_password = radius_pairmake(request, &request->config_items,
					      "NT-Password", "", T_OP_EQ);
		if (!nt_password) {
			radlog_request(L_ERR, 0, request, "No memory");
			return RLM_MODULE_FAIL;
		} else {
			ntpwdhash(nt_password->vp_octets,
				  password->vp_strvalue);
			nt_password->length = 16;
		}
	}

	challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
	if (!challenge) {
		RDEBUG2("No MS-CHAP-Challenge in the request");
		return RLM_MODULE_REJECT;
	}

	/*
	 *	We also require an MS-CHAP-Response.
	 */
	response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);

	/*
	 *	MS-CHAP-Response, means MS-CHAPv1
	 */
	if (response) {
		int offset;

		/*
		 *	MS-CHAPv1 challenges are 8 octets.
		 */
		if (challenge->length < 8) {
			radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format.");
			return RLM_MODULE_INVALID;
		}

		/*
		 *	Responses are 50 octets.
		 */
		if (response->length < 50) {
			radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format.");
			return RLM_MODULE_INVALID;
		}

		/*
		 *	We are doing MS-CHAP.  Calculate the MS-CHAP
		 *	response
		 */
		if (response->vp_octets[1] & 0x01) {
			RDEBUG2("Told to do MS-CHAPv1 with NT-Password");
			password = nt_password;
			offset = 26;
		} else {
			RDEBUG2("Told to do MS-CHAPv1 with LM-Password");
			password = lm_password;
			offset = 2;
		}

		/*
		 *	Do the MS-CHAP authentication.
		 */
		if (do_mschap(inst, request, password, challenge->vp_octets,
			      response->vp_octets + offset, nthashhash,
			      do_ntlm_auth) < 0) {
			RDEBUG2("MS-CHAP-Response is incorrect.");
			mschap_add_reply(request, &request->reply->vps,
					 *response->vp_octets,
					 "MS-CHAP-Error", "E=691 R=1", 9);
			return RLM_MODULE_REJECT;
		}

		chap = 1;

	} else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) != NULL) {
		uint8_t	mschapv1_challenge[16];

		/*
		 *	MS-CHAPv2 challenges are 16 octets.
		 */
		if (challenge->length < 16) {
			radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format.");
			return RLM_MODULE_INVALID;
		}

		/*
		 *	Responses are 50 octets.
		 */
		if (response->length < 50) {
			radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format.");
			return RLM_MODULE_INVALID;
		}

		/*
		 *	We also require a User-Name
		 */
		username = pairfind(request->packet->vps, PW_USER_NAME);
		if (!username) {
			radlog_request(L_AUTH, 0, request, "We require a User-Name for MS-CHAPv2");
			return RLM_MODULE_INVALID;
		}


		/*
		 *	with_ntdomain_hack moved here
		 */
		if ((username_string = strchr(username->vp_strvalue, '\\')) != NULL) {
		        if (inst->with_ntdomain_hack) {
			        username_string++;
			} else {
				RDEBUG2("  NT Domain delimeter found, should we have enabled with_ntdomain_hack?");
				username_string = username->vp_strvalue;
			}
		} else {
		        username_string = username->vp_strvalue;
		}

#ifdef __APPLE__
		/*
		 *  No "known good" NT-Password attribute.  Try to do
		 *  OpenDirectory authentication.
		 *
		 *  If OD determines the user is an AD user it will return noop, which
		 *  indicates the auth process should continue directly to AD.
		 *  Otherwise OD will determine auth success/fail.
		 */
		if (!nt_password && inst->open_directory) {
			RDEBUG2("No NT-Password configured. Trying OpenDirectory Authentication.");
			int odStatus = od_mschap_auth(request, challenge, username);
			if (odStatus != RLM_MODULE_NOOP) {
				return odStatus;
			}
		}
#endif
		/*
		 *	The old "mschapv2" function has been moved to
		 *	here.
		 *
		 *	MS-CHAPv2 takes some additional data to create an
		 *	MS-CHAPv1 challenge, and then does MS-CHAPv1.
		 */
		challenge_hash(response->vp_octets + 2, /* peer challenge */
			       challenge->vp_octets, /* our challenge */
			       username_string,	/* user name */
			       mschapv1_challenge); /* resulting challenge */

		RDEBUG2("Told to do MS-CHAPv2 for %s with NT-Password",
		       username_string);

		if (do_mschap(inst, request, nt_password, mschapv1_challenge,
			      response->vp_octets + 26, nthashhash,
			      do_ntlm_auth) < 0) {
			RDEBUG2("FAILED: MS-CHAP2-Response is incorrect");
			mschap_add_reply(request, &request->reply->vps,
					 *response->vp_octets,
					 "MS-CHAP-Error", "E=691 R=1", 9);
			return RLM_MODULE_REJECT;
		}

		/*
		 *	Get the NT-hash-hash, if necessary
		 */
		if (nt_password) {
		}

		auth_response(username_string, /* without the domain */
			      nthashhash, /* nt-hash-hash */
			      response->vp_octets + 26, /* peer response */
			      response->vp_octets + 2, /* peer challenge */
			      challenge->vp_octets, /* our challenge */
			      msch2resp); /* calculated MPPE key */
		mschap_add_reply(request, &request->reply->vps, *response->vp_octets,
				 "MS-CHAP2-Success", msch2resp, 42);
		chap = 2;

	} else {		/* Neither CHAPv1 or CHAPv2 response: die */
		radlog_request(L_AUTH, 0, request, "No MS-CHAP response found");
		return RLM_MODULE_INVALID;
	}

	/*
	 *	We have a CHAP response, but the account may be
	 *	disabled.  Reject the user with the same error code
	 *	we use when their password is invalid.
	 */
	if (smb_ctrl) {
		/*
		 *	Account is disabled.
		 *
		 *	They're found, but they don't exist, so we
		 *	return 'not found'.
		 */
		if (((smb_ctrl->vp_integer & ACB_DISABLED) != 0) ||
		    ((smb_ctrl->vp_integer & ACB_NORMAL) == 0)) {
			RDEBUG2("SMB-Account-Ctrl says that the account is disabled, or is not a normal account.");
			mschap_add_reply(request, &request->reply->vps,
					  *response->vp_octets,
					  "MS-CHAP-Error", "E=691 R=1", 9);
			return RLM_MODULE_NOTFOUND;
		}

		/*
		 *	User is locked out.
		 */
		if ((smb_ctrl->vp_integer & ACB_AUTOLOCK) != 0) {
			RDEBUG2("SMB-Account-Ctrl says that the account is locked out.");
			mschap_add_reply(request, &request->reply->vps,
					  *response->vp_octets,
					  "MS-CHAP-Error", "E=647 R=0", 9);
			return RLM_MODULE_USERLOCK;
		}
	}

	/* now create MPPE attributes */
	if (inst->use_mppe) {
		uint8_t mppe_sendkey[34];
		uint8_t mppe_recvkey[34];

		if (chap == 1){
			RDEBUG2("adding MS-CHAPv1 MPPE keys");
			memset(mppe_sendkey, 0, 32);
			if (lm_password) {
				memcpy(mppe_sendkey, lm_password->vp_octets, 8);
			}

			/*
			 *	According to RFC 2548 we
			 *	should send NT hash.  But in
			 *	practice it doesn't work.
			 *	Instead, we should send nthashhash
			 *
			 *	This is an error on RFC 2548.
			 */
			/*
			 *	do_mschap cares to zero nthashhash if NT hash
			 *	is not available.
			 */
			memcpy(mppe_sendkey + 8,
			       nthashhash, 16);
			mppe_add_reply(request,
				       "MS-CHAP-MPPE-Keys",
				       mppe_sendkey, 32);
		} else if (chap == 2) {
			RDEBUG2("adding MS-CHAPv2 MPPE keys");
			mppe_chap2_gen_keys128(nthashhash,
					       response->vp_octets + 26,
					       mppe_sendkey, mppe_recvkey);

			mppe_add_reply(request,
				       "MS-MPPE-Recv-Key",
				       mppe_recvkey, 16);
			mppe_add_reply(request,
				       "MS-MPPE-Send-Key",
				       mppe_sendkey, 16);

		}
		radius_pairmake(request, &request->reply->vps,
				"MS-MPPE-Encryption-Policy",
				(inst->require_encryption)? "0x00000002":"0x00000001",
				T_OP_EQ);
		radius_pairmake(request, &request->reply->vps,
				"MS-MPPE-Encryption-Types",
				(inst->require_strong)? "0x00000004":"0x00000006",
				T_OP_EQ);
	} /* else we weren't asked to use MPPE */

	return RLM_MODULE_OK;
#undef inst
}
/*
 *	mschap_authenticate() - authenticate user based on given
 *	attributes and configuration.
 *	We will try to find out password in configuration
 *	or in configured passwd file.
 *	If one is found we will check paraneters given by NAS.
 *
 *	If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have
 *	one of:
 *		PAP:      PW_USER_PASSWORD or
 *		MS-CHAP:  PW_MSCHAP_CHALLENGE and PW_MSCHAP_RESPONSE or
 *		MS-CHAP2: PW_MSCHAP_CHALLENGE and PW_MSCHAP2_RESPONSE
 *	In case of password mismatch or locked account we MAY return
 *	PW_MSCHAP_ERROR for MS-CHAP or MS-CHAP v2
 *	If MS-CHAP2 succeeds we MUST return
 *	PW_MSCHAP2_SUCCESS
 */
static int mschap_authenticate(void * instance, REQUEST *request)
{
#define inst ((rlm_mschap_t *)instance)
	VALUE_PAIR *challenge = NULL;
	VALUE_PAIR *response = NULL;
	VALUE_PAIR *password = NULL;
	VALUE_PAIR *lm_password, *nt_password, *smb_ctrl;
	VALUE_PAIR *username;
	uint8_t nthashhash[16];
	char msch2resp[42];
	char *username_string;
	int chap = 0;
	int		do_ntlm_auth;

	/*
	 *	If we have ntlm_auth configured, use it unless told
	 *	otherwise
	 */
	do_ntlm_auth = (inst->ntlm_auth != NULL);

	/*
	 *	If we have an ntlm_auth configuration, then we may
	 *	want to suppress it.
	 */
	if (do_ntlm_auth) {
		VALUE_PAIR *vp = pairfind(request->config_items,
					  PW_MS_CHAP_USE_NTLM_AUTH);
		if (vp) do_ntlm_auth = vp->vp_integer;
	}

	/*
	 *	Find the SMB-Account-Ctrl attribute, or the
	 *	SMB-Account-Ctrl-Text attribute.
	 */
	smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL);
	if (!smb_ctrl) {
		password = pairfind(request->config_items,
				    PW_SMB_ACCOUNT_CTRL_TEXT);
		if (password) {
			smb_ctrl = radius_pairmake(request,
						   &request->config_items,
						   "SMB-Account-CTRL", "0",
						   T_OP_SET);
			if (smb_ctrl) {
				smb_ctrl->vp_integer = pdb_decode_acct_ctrl(password->vp_strvalue);
			}
		}
	}

	/*
	 *	We're configured to do MS-CHAP authentication.
	 *	and account control information exists.  Enforce it.
	 */
	if (smb_ctrl) {
		/*
		 *	Password is not required.
		 */
		if ((smb_ctrl->vp_integer & ACB_PWNOTREQ) != 0) {
			RDEBUG2("SMB-Account-Ctrl says no password is required.");
			return RLM_MODULE_OK;
		}
	}

	/*
	 *	Decide how to get the passwords.
	 */
	password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD);

	/*
	 *	We need an LM-Password.
	 */
	lm_password = pairfind(request->config_items, PW_LM_PASSWORD);
	if (lm_password) {
		/*
		 *	Allow raw octets.
		 */
		if ((lm_password->length == 16) ||
		    ((lm_password->length == 32) &&
		     (fr_hex2bin(lm_password->vp_strvalue,
				 lm_password->vp_octets, 16) == 16))) {
			RDEBUG2("Found LM-Password");
			lm_password->length = 16;

		} else {
			radlog_request(L_ERR, 0, request, "Invalid LM-Password");
			lm_password = NULL;
		}

	} else if (!password) {
		if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured.  Cannot create LM-Password.");

	} else {		/* there is a configured Cleartext-Password */
		lm_password = radius_pairmake(request, &request->config_items,
					      "LM-Password", "", T_OP_EQ);
		if (!lm_password) {
			radlog_request(L_ERR, 0, request, "No memory");
		} else {
			smbdes_lmpwdhash(password->vp_strvalue,
					 lm_password->vp_octets);
			lm_password->length = 16;
		}
	}

	/*
	 *	We need an NT-Password.
	 */
	nt_password = pairfind(request->config_items, PW_NT_PASSWORD);
	if (nt_password) {
		if ((nt_password->length == 16) ||
		    ((nt_password->length == 32) &&
		     (fr_hex2bin(nt_password->vp_strvalue,
				 nt_password->vp_octets, 16) == 16))) {
			RDEBUG2("Found NT-Password");
			nt_password->length = 16;

                } else {
			radlog_request(L_ERR, 0, request, "Invalid NT-Password");
			nt_password = NULL;
		}
	} else if (!password) {
		if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured.  Cannot create NT-Password.");

	} else {		/* there is a configured Cleartext-Password */
		nt_password = radius_pairmake(request, &request->config_items,
					      "NT-Password", "", T_OP_EQ);
		if (!nt_password) {
			radlog_request(L_ERR, 0, request, "No memory");
			return RLM_MODULE_FAIL;
		} else {
			mschap_ntpwdhash(nt_password->vp_octets,
				  password->vp_strvalue);
			nt_password->length = 16;
		}
	}

	challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
	if (!challenge) {
		RDEBUG("ERROR: You set 'Auth-Type = MS-CHAP' for a request that does not contain any MS-CHAP attributes!");
		return RLM_MODULE_REJECT;
	}

	/*
	 *	We also require an MS-CHAP-Response.
	 */
	response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);

	/*
	 *	MS-CHAP-Response, means MS-CHAPv1
	 */
	if (response) {
		int offset;

		/*
		 *	MS-CHAPv1 challenges are 8 octets.
		 */
		if (challenge->length < 8) {
			radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format.");
			return RLM_MODULE_INVALID;
		}

		/*
		 *	Responses are 50 octets.
		 */
		if (response->length < 50) {
			radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format.");
			return RLM_MODULE_INVALID;
		}

		/*
		 *	We are doing MS-CHAP.  Calculate the MS-CHAP
		 *	response
		 */
		if (response->vp_octets[1] & 0x01) {
			RDEBUG2("Client is using MS-CHAPv1 with NT-Password");
			password = nt_password;
			offset = 26;
		} else {
			RDEBUG2("Client is using MS-CHAPv1 with LM-Password");
			password = lm_password;
			offset = 2;
		}

		/*
		 *	Do the MS-CHAP authentication.
		 */
		if (do_mschap(inst, request, password, challenge->vp_octets,
			      response->vp_octets + offset, nthashhash,
			      do_ntlm_auth) < 0) {
			RDEBUG2("MS-CHAP-Response is incorrect.");
			goto do_error;
		}

		chap = 1;

	} else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) != NULL) {
		uint8_t	mschapv1_challenge[16];
		VALUE_PAIR *name_attr, *response_name;

		/*
		 *	MS-CHAPv2 challenges are 16 octets.
		 */
		if (challenge->length < 16) {
			radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format.");
			return RLM_MODULE_INVALID;
		}

		/*
		 *	Responses are 50 octets.
		 */
		if (response->length < 50) {
			radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format.");
			return RLM_MODULE_INVALID;
		}

		/*
		 *	We also require a User-Name
		 */
		username = pairfind(request->packet->vps, PW_USER_NAME);
		if (!username) {
			radlog_request(L_AUTH, 0, request, "We require a User-Name for MS-CHAPv2");
			return RLM_MODULE_INVALID;
		}

		/*
		 *      Check for MS-CHAP-User-Name and if found, use it
		 *      to construct the MSCHAPv1 challenge.  This is
		 *      set by rlm_eap_mschap to the MS-CHAP Response
		 *      packet Name field.
		 *
		 *	We prefer this to the User-Name in the
		 *	packet.
		 */
		response_name = pairfind(request->packet->vps, PW_MS_CHAP_USER_NAME);
		if (response_name) {
			name_attr = response_name;
		} else {
			name_attr = username;
		}
		
		/*
		 *	with_ntdomain_hack moved here, too.
		 */
		if ((username_string = strchr(name_attr->vp_strvalue, '\\')) != NULL) {
			if (inst->with_ntdomain_hack) {
				username_string++;
			} else {
				RDEBUG2("NT Domain delimeter found, should we have enabled with_ntdomain_hack?");
				username_string = name_attr->vp_strvalue;
			}
		} else {
			username_string = name_attr->vp_strvalue;
		}
		
		if (response_name &&
		    ((username->length != response_name->length) ||
		     (strncasecmp(username->vp_strvalue, response_name->vp_strvalue, username->length) != 0))) {
			RDEBUG("ERROR: User-Name (%s) is not the same as MS-CHAP Name (%s) from EAP-MSCHAPv2", username->vp_strvalue, response_name->vp_strvalue);
			return RLM_MODULE_REJECT;
		}

		/*
		 *	The old "mschapv2" function has been moved to
		 *	here.
		 *
		 *	MS-CHAPv2 takes some additional data to create an
		 *	MS-CHAPv1 challenge, and then does MS-CHAPv1.
		 */
		RDEBUG2("Creating challenge hash with username: %s",
			username_string);
		mschap_challenge_hash(response->vp_octets + 2, /* peer challenge */
			       challenge->vp_octets, /* our challenge */
			       username_string,	/* user name */
			       mschapv1_challenge); /* resulting challenge */

		RDEBUG2("Client is using MS-CHAPv2 for %s, we need NT-Password",
		       username_string);

#ifdef __APPLE__
		if (inst->open_directory) {
			return do_od_mschap(request, response, challenge, username_string);
		}
#endif

		if (do_mschap(inst, request, nt_password, mschapv1_challenge,
			      response->vp_octets + 26, nthashhash,
			      do_ntlm_auth) < 0) {
			int i;
			char buffer[128];

			RDEBUG2("FAILED: MS-CHAP2-Response is incorrect");

		do_error:
			snprintf(buffer, sizeof(buffer), "E=691 R=%d",
				 inst->allow_retry);

			if (inst->retry_msg) {
				snprintf(buffer + 9, sizeof(buffer) - 9, " C=");
				for (i = 0; i < 16; i++) {
					snprintf(buffer + 12 + i*2,
						 sizeof(buffer) - 12 - i*2, "%02x",
						 fr_rand() & 0xff);
				}
				snprintf(buffer + 44, sizeof(buffer) - 44,
					 " V=3 M=%s", inst->retry_msg);
			}
			mschap_add_reply(request, &request->reply->vps,
					 *response->vp_octets, "MS-CHAP-Error",
					 buffer, strlen(buffer));
			return RLM_MODULE_REJECT;
		}

		mschap_auth_response(username_string, /* without the domain */
			      nthashhash, /* nt-hash-hash */
			      response->vp_octets + 26, /* peer response */
			      response->vp_octets + 2, /* peer challenge */
			      challenge->vp_octets, /* our challenge */
			      msch2resp); /* calculated MPPE key */
		mschap_add_reply(request, &request->reply->vps, *response->vp_octets,
				 "MS-CHAP2-Success", msch2resp, 42);
		chap = 2;

	} else {		/* Neither CHAPv1 or CHAPv2 response: die */
		RDEBUG("ERROR: You set 'Auth-Type = MS-CHAP' for a request that does not contain any MS-CHAP attributes!");
		return RLM_MODULE_INVALID;
	}

	/*
	 *	We have a CHAP response, but the account may be
	 *	disabled.  Reject the user with the same error code
	 *	we use when their password is invalid.
	 */
	if (smb_ctrl) {
		/*
		 *	Account is disabled.
		 *
		 *	They're found, but they don't exist, so we
		 *	return 'not found'.
		 */
		if (((smb_ctrl->vp_integer & ACB_DISABLED) != 0) ||
		    ((smb_ctrl->vp_integer & (ACB_NORMAL|ACB_WSTRUST)) == 0)) {
			RDEBUG2("SMB-Account-Ctrl says that the account is disabled, or is not a normal or workstatin trust account.");
			mschap_add_reply(request, &request->reply->vps,
					  *response->vp_octets,
					  "MS-CHAP-Error", "E=691 R=1", 9);
			return RLM_MODULE_NOTFOUND;
		}

		/*
		 *	User is locked out.
		 */
		if ((smb_ctrl->vp_integer & ACB_AUTOLOCK) != 0) {
			RDEBUG2("SMB-Account-Ctrl says that the account is locked out.");
			mschap_add_reply(request, &request->reply->vps,
					  *response->vp_octets,
					  "MS-CHAP-Error", "E=647 R=0", 9);
			return RLM_MODULE_USERLOCK;
		}
	}

	/* now create MPPE attributes */
	if (inst->use_mppe) {
		uint8_t mppe_sendkey[34];
		uint8_t mppe_recvkey[34];

		if (chap == 1){
			RDEBUG2("adding MS-CHAPv1 MPPE keys");
			memset(mppe_sendkey, 0, 32);
			if (lm_password) {
				memcpy(mppe_sendkey, lm_password->vp_octets, 8);
			}

			/*
			 *	According to RFC 2548 we
			 *	should send NT hash.  But in
			 *	practice it doesn't work.
			 *	Instead, we should send nthashhash
			 *
			 *	This is an error on RFC 2548.
			 */
			/*
			 *	do_mschap cares to zero nthashhash if NT hash
			 *	is not available.
			 */
			memcpy(mppe_sendkey + 8,
			       nthashhash, 16);
			mppe_add_reply(request,
				       "MS-CHAP-MPPE-Keys",
				       mppe_sendkey, 32);
		} else if (chap == 2) {
			RDEBUG2("adding MS-CHAPv2 MPPE keys");
			mppe_chap2_gen_keys128(nthashhash,
					       response->vp_octets + 26,
					       mppe_sendkey, mppe_recvkey);

			mppe_add_reply(request,
				       "MS-MPPE-Recv-Key",
				       mppe_recvkey, 16);
			mppe_add_reply(request,
				       "MS-MPPE-Send-Key",
				       mppe_sendkey, 16);

		}
		radius_pairmake(request, &request->reply->vps,
				"MS-MPPE-Encryption-Policy",
				(inst->require_encryption)? "0x00000002":"0x00000001",
				T_OP_EQ);
		radius_pairmake(request, &request->reply->vps,
				"MS-MPPE-Encryption-Types",
				(inst->require_strong)? "0x00000004":"0x00000006",
				T_OP_EQ);
	} /* else we weren't asked to use MPPE */

	return RLM_MODULE_OK;
#undef inst
}
/*
 *	Do the MS-CHAP stuff.
 *
 *	This function is here so that all of the MS-CHAP related
 *	authentication is in one place, and we can perhaps later replace
 *	it with code to call winbindd, or something similar.
 */
static int do_mschap(rlm_mschap_t *inst,
		     REQUEST *request, VALUE_PAIR *password,
		     uint8_t *challenge, uint8_t *response,
		     uint8_t *nthashhash, int do_ntlm_auth)
{
	uint8_t		calculated[24];

	/*
	 *	Do normal authentication.
	 */
	if (!do_ntlm_auth) {
		/*
		 *	No password: can't do authentication.
		 */
		if (!password) {
			RDEBUG2("FAILED: No NT/LM-Password.  Cannot perform authentication.");
			return -1;
		}

		smbdes_mschap(password->vp_strvalue, challenge, calculated);
		if (rad_digest_cmp(response, calculated, 24) != 0) {
			return -1;
		}

		/*
		 *	If the password exists, and is an NT-Password,
		 *	then calculate the hash of the NT hash.  Doing this
		 *	here minimizes work for later.
		 */
		if (password && (password->attribute == PW_NT_PASSWORD)) {
			fr_md4_calc(nthashhash, password->vp_octets, 16);
		} else {
			memset(nthashhash, 0, 16);
		}
	} else {		/* run ntlm_auth */
		int	result;
		char	buffer[256];

		memset(nthashhash, 0, 16);

		/*
		 *	Run the program, and expect that we get 16
		 */
		result = radius_exec_program(inst->ntlm_auth, request,
					     TRUE, /* wait */
					     buffer, sizeof(buffer),
					     NULL, NULL, 1);
		if (result != 0) {
			char *p;
			VALUE_PAIR *vp = NULL;

			RDEBUG2("External script failed.");

			vp = pairmake("Module-Failure-Message", "", T_OP_EQ);
			if (!vp) {
				radlog_request(L_ERR, 0, request, "No memory to allocate Module-Failure-Message");
				return RLM_MODULE_FAIL;
			}

			p = strchr(buffer, '\n');
			if (p) *p = '\0';
			snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue),
				"%s: External script says %s",
				 inst->xlat_name, buffer);
			vp->length = strlen(vp->vp_strvalue);
			pairadd(&request->packet->vps, vp);
			return -1;
		}

		/*
		 *	Parse the answer as an nthashhash.
		 *
		 *	ntlm_auth currently returns:
		 *	NT_KEY: 000102030405060708090a0b0c0d0e0f
		 */
		if (memcmp(buffer, "NT_KEY: ", 8) != 0) {
			RDEBUG2("Invalid output from ntlm_auth: expecting NT_KEY");
			return -1;
		}

		/*
		 *	Check the length.  It should be at least 32,
		 *	with an LF at the end.
		 */
		if (strlen(buffer + 8) < 32) {
			RDEBUG2("Invalid output from ntlm_auth: NT_KEY has unexpected length");
			return -1;
		}

		/*
		 *	Update the NT hash hash, from the NT key.
		 */
		if (fr_hex2bin(buffer + 8, nthashhash, 16) != 16) {
			RDEBUG2("Invalid output from ntlm_auth: NT_KEY has non-hex values");
			return -1;
		}
	}

	return 0;
}
/*
 *	ascend_parse_ipx_net
 *
 *	srcipxnet nnnn srcipxnode mmmmm [srcipxsoc cmd value ]
 */
static int ascend_parse_ipx_net(int argc, char **argv,
				ascend_ipx_net_t *net, uint8_t *comp)
{
	int		token;
	char const	*p;

	if (argc < 3) return -1;

	/*
	 *	Parse the net, which is a hex number.
	 */
	net->net = htonl(strtol(argv[0], NULL, 16));

	/*
	 *	Parse the node.
	 */
	token = fr_str2int(filterKeywords, argv[1], -1);
	switch (token) {
	case FILTER_IPX_SRC_IPXNODE:
	case FILTER_IPX_DST_IPXNODE:
		break;

	default:
		return -1;
	}

	/*
	 *	Can have a leading "0x" or "0X"
	 */
	p = argv[2];
	if ((memcmp(p, "0X", 2) == 0) ||
	    (memcmp(p, "0x", 2) == 0)) p += 2;

	/*
	 *	Node must be 6 octets long.
	 */
	token = fr_hex2bin(net->node, IPX_NODE_ADDR_LEN, p, strlen(p));
	if (token != IPX_NODE_ADDR_LEN) return -1;

	/*
	 *	Nothing more, die.
	 */
	if (argc == 3) return 3;

	/*
	 *	Can't be too little or too much.
	 */
	if (argc != 6) return -1;

	/*
	 *	Parse the socket.
	 */
	token = fr_str2int(filterKeywords, argv[3], -1);
	switch (token) {
	case FILTER_IPX_SRC_IPXSOCK:
	case FILTER_IPX_DST_IPXSOCK:
		break;

	default:
		return -1;
	}

	/*
	 *	Parse the command "<", ">", "=" or "!="
	 */
	token = fr_str2int(filterCompare, argv[4], -1);
	switch (token) {
	case RAD_COMPARE_LESS:
	case RAD_COMPARE_EQUAL:
	case RAD_COMPARE_GREATER:
	case RAD_COMPARE_NOT_EQUAL:
		*comp = token;
		break;

	default:
		return -1;
	}

	/*
	 *	Parse the value.
	 */
	token = strtoul(argv[5], NULL, 16);
	if (token > 65535) return -1;

	net->socket = token;
	net->socket = htons(net->socket);


	/*
	 *	Everything's OK, we parsed 6 entries.
	 */
	return 6;
}
/*
 *	ascend_parse_generic
 *
 *	This routine parses a Generic filter string from a RADIUS
 *	reply. The format of the string is:
 *
 *	generic dir action offset mask value [== or != ] [more]
 *
 *	Fields in [...] are optional.
 *
 *	offset:		A Number. Specifies an offset into a frame
 *			to start comparing.
 *
 *	mask:		A hexadecimal mask of bits to compare.
 *
 *	value:		A value to compare with the masked data.
 *
 *	compNeq:	Defines type of comparison. ( "==" or "!=")
 *			Default is "==".
 *
 *	more:		Optional keyword MORE, to represent the attachment
 *			to the next entry.
 */
static int ascend_parse_generic(int argc, char **argv,
				ascend_generic_filter_t *filter)
{
	int rcode;
	int token;
	int flags;

	/*
	 *	We may have nothing, in which case we simply return.
	 */
	if (argc == 0) return 0;

	/*
	 *	We need at least "offset mask value"
	 */
	if (argc < 3) return -1;

	/*
	 *	No more than optional comparison and "more"
	 */
	if (argc > 5) return -1;

	/*
	 *	Offset is a uint16_t number.
	 */
	if (strspn(argv[0], "0123456789") != strlen(argv[0])) return -1;

	rcode = atoi(argv[0]);
	if (rcode > 65535) return -1;

	filter->offset = rcode;
	filter->offset = htons(filter->offset);

	rcode = fr_hex2bin(filter->mask, sizeof(filter->mask), argv[1], strlen(argv[1]));
	if (rcode != sizeof(filter->mask)) return -1;

	token = fr_hex2bin(filter->value, sizeof(filter->value), argv[2], strlen(argv[2]));
	if (token != sizeof(filter->value)) return -1;

	filter->len = rcode;
	filter->len = htons(filter->len);

	/*
	 *	Nothing more.  Exit.
	 */
	if (argc == 3) return 0;

	argc -= 3;
	argv += 3;
	flags = 0;

	while (argc >= 1) {
		token = fr_str2int(filterKeywords, argv[0], -1);
		switch (token) {
		case FILTER_GENERIC_COMPNEQ:
			if (flags & 0x01) return -1;
			filter->compNeq = true;
			flags |= 0x01;
			break;
		case FILTER_GENERIC_COMPEQ:
			if (flags & 0x01) return -1;
			filter->compNeq = false;
			flags |= 0x01;
			break;

		case FILTER_MORE:
			if (flags & 0x02) return -1;
			filter->more = htons( 1 );
			flags |= 0x02;
			break;

		default:
			fr_strerror_printf("Invalid string \"%s\" in generic data filter",
				   argv[0]);
			return -1;
		}

		argc--;
		argv++;
	}

	return 0;
}
/*
 *	Authenticate a previously sent challenge.
 */
static int mschapv2_authenticate(void *arg, EAP_HANDLER *handler)
{
	int rcode, ccode;
	mschapv2_opaque_t *data;
	EAP_DS *eap_ds = handler->eap_ds;
	VALUE_PAIR *challenge, *response, *name;
	rlm_eap_mschapv2_t *inst = (rlm_eap_mschapv2_t *) arg;

	rad_assert(handler->request != NULL);
	rad_assert(handler->stage == AUTHENTICATE);

	data = (mschapv2_opaque_t *) handler->opaque;

	/*
	 *	Sanity check the response.
	 */
	if (eap_ds->response->length <= 5) {
		radlog(L_ERR, "rlm_eap_mschapv2: corrupted data");
		return 0;
	}

	ccode = eap_ds->response->type.data[0];

	switch (data->code) {
		case PW_EAP_MSCHAPV2_FAILURE:
			if (ccode == PW_EAP_MSCHAPV2_RESPONSE) {
				DEBUG2("  rlm_eap_mschapv2: authentication re-try from client after we sent a failure");
				break;
			}

			/*
			 * if we sent error 648 (password expired) to the client
			 * we might get an MSCHAP-CPW packet here; turn it into a
			 * regular MS-CHAP2-CPW packet and pass it to rlm_mschap
			 * (or proxy it, I guess)
			 */
			if (ccode == PW_EAP_MSCHAPV2_CHGPASSWD) {
				VALUE_PAIR *cpw;
				int mschap_id = eap_ds->response->type.data[1];
				int copied=0,seq=1;

				DEBUG2("  rlm_eap_mschapv2: password change packet received");

				challenge = pairmake("MS-CHAP-Challenge", "0x00", T_OP_EQ);
				if (!challenge) {
					radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
					return 0;
				}
				challenge->length = MSCHAPV2_CHALLENGE_LEN;
				memcpy(challenge->vp_strvalue, data->challenge, MSCHAPV2_CHALLENGE_LEN);
				pairadd(&handler->request->packet->vps, challenge);

				cpw = pairmake("MS-CHAP2-CPW", "", T_OP_EQ);
				cpw->vp_octets[0] = 7;
				cpw->vp_octets[1] = mschap_id;
				memcpy(cpw->vp_octets+2, eap_ds->response->type.data + 520, 66);
				cpw->length = 68;
				pairadd(&handler->request->packet->vps, cpw);

				/*
				 * break the encoded password into VPs (3 of them)
				 */
				while (copied < 516) {
					VALUE_PAIR *nt_enc;

					int to_copy = 516 - copied;
					if (to_copy > 243)
						to_copy = 243;

					nt_enc = pairmake("MS-CHAP-NT-Enc-PW", "", T_OP_ADD);
					nt_enc->vp_octets[0] = 6;
					nt_enc->vp_octets[1] = mschap_id;
					nt_enc->vp_octets[2] = 0;
					nt_enc->vp_octets[3] = seq++;

					memcpy(nt_enc->vp_octets + 4, eap_ds->response->type.data + 4 + copied, to_copy);
					copied += to_copy;
					nt_enc->length = 4 + to_copy;
					pairadd(&handler->request->packet->vps, nt_enc);
				}

				DEBUG2("  rlm_eap_mschapv2: built change password packet");
				debug_pair_list(handler->request->packet->vps);

				/*
				 * jump to "authentication"
				 */
				goto packet_ready;
			}

			/*
			 * we sent a failure and are expecting a failure back
			 */
			if (ccode != PW_EAP_MSCHAPV2_FAILURE) {
				radlog(L_ERR, "rlm_eap_mschapv2: Sent FAILURE expecting FAILURE but got %d", ccode);
				return 0;
			}

	failure:
			handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
			eap_ds->request->code = PW_EAP_FAILURE;
	                return 1;

		case PW_EAP_MSCHAPV2_SUCCESS:
			/*
			 * we sent a success to the client; some clients send a
			 * success back as-per the RFC, some send an ACK. Permit
			 * both, I guess...
			 */

			switch (ccode) {
				case PW_EAP_MSCHAPV2_SUCCESS:
					eap_ds->request->code = PW_EAP_SUCCESS;
					pairadd(&handler->request->reply->vps, data->mppe_keys);
					data->mppe_keys = NULL;
					/* fall through... */

				case PW_EAP_MSCHAPV2_ACK:
#ifdef WITH_PROXY
					/*
					 *	It's a success.  Don't proxy it.
					 */
					handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
#endif
					pairadd(&handler->request->reply->vps, data->reply);
					data->reply = NULL;
					return 1;
			}
			radlog(L_ERR, "rlm_eap_mschapv2: Sent SUCCESS expecting SUCCESS (or ACK) but got %d", ccode);
			return 0;

		case PW_EAP_MSCHAPV2_CHALLENGE:
			if (ccode == PW_EAP_MSCHAPV2_FAILURE) goto failure;

			/*
			 * we sent a challenge, expecting a response
			 */
			if (ccode != PW_EAP_MSCHAPV2_RESPONSE) {
				radlog(L_ERR, "rlm_eap_mschapv2: Sent CHALLENGE expecting RESPONSE but got %d", ccode);
				return 0;
			}
			/* authentication happens below */
			break;


		default:
			/* should never happen */
			radlog(L_ERR, "rlm_eap_mschapv2: unknown state %d", data->code);
			return 0;
	}


	/*
	 *	Ensure that we have at least enough data
	 *	to do the following checks.
	 *
	 *	EAP header (4), EAP type, MS-CHAP opcode,
	 *	MS-CHAP ident, MS-CHAP data length (2),
	 *	MS-CHAP value length.
	 */
	if (eap_ds->response->length < (4 + 1 + 1 + 1 + 2 + 1)) {
		radlog(L_ERR, "rlm_eap_mschapv2: Response is too short");
		return 0;
	}

	/*
	 *	The 'value_size' is the size of the response,
	 *	which is supposed to be the response (48
	 *	bytes) plus 1 byte of flags at the end.
	 */
	if (eap_ds->response->type.data[4] != 49) {
		radlog(L_ERR, "rlm_eap_mschapv2: Response is of incorrect length %d", eap_ds->response->type.data[4]);
		return 0;
	}

	/*
	 *	The MS-Length field is 5 + value_size + length
	 *	of name, which is put after the response.
	 */
	if (((eap_ds->response->type.data[2] << 8) |
	     eap_ds->response->type.data[3]) < (5 + 49)) {
		radlog(L_ERR, "rlm_eap_mschapv2: Response contains contradictory length %d %d",
		      (eap_ds->response->type.data[2] << 8) |
		       eap_ds->response->type.data[3], 5 + 49);
		return 0;
	}

	/*
	 *	We now know that the user has sent us a response
	 *	to the challenge.  Let's try to authenticate it.
	 *
	 *	We do this by taking the challenge from 'data',
	 *	the response from the EAP packet, and creating VALUE_PAIR's
	 *	to pass to the 'mschap' module.  This is a little wonky,
	 *	but it works.
	 */
	challenge = pairmake("MS-CHAP-Challenge", "0x00", T_OP_EQ);
	if (!challenge) {
		radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
		return 0;
	}
	challenge->length = MSCHAPV2_CHALLENGE_LEN;
	memcpy(challenge->vp_strvalue, data->challenge, MSCHAPV2_CHALLENGE_LEN);

	response = pairmake("MS-CHAP2-Response", "0x00", T_OP_EQ);
	if (!response) {
		pairfree(&challenge);
		radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
		return 0;
	}

	response->length = MSCHAPV2_RESPONSE_LEN;
	memcpy(response->vp_strvalue + 2, &eap_ds->response->type.data[5],
	       MSCHAPV2_RESPONSE_LEN - 2);
	response->vp_strvalue[0] = eap_ds->response->type.data[1];
	response->vp_strvalue[1] = eap_ds->response->type.data[5 + MSCHAPV2_RESPONSE_LEN];

	name = pairmake("NTLM-User-Name", "", T_OP_EQ);
	if (!name) {
		pairfree(&challenge);
		pairfree(&response);
		radlog(L_ERR, "rlm_eap_mschapv2: Failed creating NTLM-User-Name: %s", fr_strerror());
		return 0;
	}
	
	/*
	 *	MS-Length - MS-Value - 5.
	 */
	name->length = (((eap_ds->response->type.data[2] << 8) |
                         eap_ds->response->type.data[3]) -
			eap_ds->response->type.data[4] - 5);
	if (name->length >= sizeof(name->vp_strvalue)) {
		name->length = sizeof(name->vp_strvalue) - 1;
	}

	memcpy(name->vp_strvalue,
	       &eap_ds->response->type.data[4 + MSCHAPV2_RESPONSE_LEN],
	       name->length);
	name->vp_strvalue[name->length] = '\0';

	/*
	 *	Add the pairs to the request, and call the 'mschap'
	 *	module.
	 */
	pairadd(&handler->request->packet->vps, challenge);
	pairadd(&handler->request->packet->vps, response);
	pairadd(&handler->request->packet->vps, name);

packet_ready:

#ifdef WITH_PROXY
	/*
	 *	If this options is set, then we do NOT authenticate the
	 *	user here.  Instead, now that we've added the MS-CHAP
	 *	attributes to the request, we STOP, and let the outer
	 *	tunnel code handle it.
	 *
	 *	This means that the outer tunnel code will DELETE the
	 *	EAP attributes, and proxy the MS-CHAP attributes to a
	 *	home server.
	 */
	if (handler->request->options & RAD_REQUEST_OPTION_PROXY_EAP) {
		char *username = NULL;
		eap_tunnel_data_t *tunnel;

		DEBUG2("rlm_eap_mschapv2: cancelling authentication and letting it be proxied");

		/*
		 *	Set up the callbacks for the tunnel
		 */
		tunnel = rad_malloc(sizeof(*tunnel));
		memset(tunnel, 0, sizeof(*tunnel));

		tunnel->tls_session = arg;
		tunnel->callback = mschap_postproxy;

		/*
		 *	Associate the callback with the request.
		 */
		rcode = request_data_add(handler->request,
					 handler->request->proxy,
					 REQUEST_DATA_EAP_TUNNEL_CALLBACK,
					 tunnel, free);
		rad_assert(rcode == 0);

		/*
		 *	The State attribute is NOT supposed to
		 *	go into the proxied packet, it will confuse
		 *	other RADIUS servers, and they will discard
		 *	the request.
		 *
		 *	The PEAP module will take care of adding
		 *	the State attribute back, before passing
		 *	the handler & request back into the tunnel.
		 */
		pairdelete(&handler->request->packet->vps, PW_STATE, 0, TAG_ANY);

		/*
		 *	Fix the User-Name when proxying, to strip off
		 *	the NT Domain, if we're told to, and a User-Name
		 *	exists, and there's a \\, meaning an NT-Domain
		 *	in the user name, THEN discard the user name.
		 */
		if (inst->with_ntdomain_hack &&
		    ((challenge = pairfind(handler->request->packet->vps, PW_USER_NAME, 0, TAG_ANY)) != NULL) &&
		    ((username = strchr(challenge->vp_strvalue, '\\')) != NULL)) {
			/*
			 *	Wipe out the NT domain.
			 *
			 *	FIXME: Put it into MS-CHAP-Domain?
			 */
			username++; /* skip the \\ */
			memmove(challenge->vp_strvalue,
				username,
				strlen(username) + 1); /* include \0 */
			challenge->length = strlen(challenge->vp_strvalue);
		}

		/*
		 *	Remember that in the post-proxy stage, we've got
		 *	to do the work below, AFTER the call to MS-CHAP
		 *	authentication...
		 */
		return 1;
	}
#endif

	/*
	 *	This is a wild & crazy hack.
	 */
	rcode = module_authenticate(PW_AUTHTYPE_MS_CHAP, handler->request);

	/*
	 *	Delete MPPE keys & encryption policy.  We don't
	 *	want these here.
	 */
	fix_mppe_keys(handler, data);

	/*
	 *	Take the response from the mschap module, and
	 *	return success or failure, depending on the result.
	 */
	response = NULL;
	if (rcode == RLM_MODULE_OK) {
		pairmove2(&response, &handler->request->reply->vps,
			 PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY);
		data->code = PW_EAP_MSCHAPV2_SUCCESS;

	} else if (inst->send_error) {
		pairmove2(&response, &handler->request->reply->vps,
			  PW_MSCHAP_ERROR, VENDORPEC_MICROSOFT, TAG_ANY);
		if (response) {
			int n,err,retry;
			char buf[34];

			DEBUG2("  MSCHAP-Error: %s", response->vp_strvalue);

			/*
			 *	Pxarse the new challenge out of the
			 *	MS-CHAP-Error, so that if the client
			 *	issues a re-try, we will know which
			 *	challenge value that they used.
			 */
			n = sscanf(response->vp_strvalue, "%*cE=%d R=%d C=%32s", &err, &retry, &buf[0]);
			if (n == 3) {
				DEBUG2("  Found new challenge from MS-CHAP-Error: err=%d retry=%d challenge=%s", err, retry, buf);
				fr_hex2bin(buf, data->challenge, 16);
			} else {
				DEBUG2("  Could not parse new challenge from MS-CHAP-Error: %d", n);
			}
		}
		data->code = PW_EAP_MSCHAPV2_FAILURE;
	} else {
		eap_ds->request->code = PW_EAP_FAILURE;
		return 1;
	}

	/*
	 *	No response, die.
	 */
	if (!response) {
		radlog(L_ERR, "rlm_eap_mschapv2: No MS-CHAPv2-Success or MS-CHAP-Error was found.");
		return 0;
	}

	/*
	 *	Compose the response (whatever it is),
	 *	and return it to the over-lying EAP module.
	 */
	eapmschapv2_compose(handler, response);
	pairfree(&response);

	return 1;
}
Beispiel #12
0
int pairparsevalue(VALUE_PAIR *vp, char const *value)
{
	char		*p;
	char const	*cp, *cs;
	int		x;
	uint64_t	y;
	size_t		length;
	DICT_VALUE	*dval;

	if (!value) return false;
	VERIFY(vp);

	switch(vp->da->type) {
	case PW_TYPE_STRING:
		/*
		 *	Do escaping here
		 */
		p = talloc_strdup(vp, value);
		vp->vp_strvalue = p;
		cp = value;
		length = 0;

		while (*cp) {
			char c = *cp++;

			if (c == '\\') {
				switch (*cp) {
				case 'r':
					c = '\r';
					cp++;
					break;
				case 'n':
					c = '\n';
					cp++;
					break;
				case 't':
					c = '\t';
					cp++;
					break;
				case '"':
					c = '"';
					cp++;
					break;
				case '\'':
					c = '\'';
					cp++;
					break;
				case '\\':
					c = '\\';
					cp++;
					break;
				case '`':
					c = '`';
					cp++;
					break;
				case '\0':
					c = '\\'; /* no cp++ */
					break;
				default:
					if ((cp[0] >= '0') &&
					    (cp[0] <= '9') &&
					    (cp[1] >= '0') &&
					    (cp[1] <= '9') &&
					    (cp[2] >= '0') &&
					    (cp[2] <= '9') &&
					    (sscanf(cp, "%3o", &x) == 1)) {
						c = x;
						cp += 3;
					} /* else just do '\\' */
				}
			}
			*p++ = c;
			length++;
		}
		*p = '\0';
		vp->length = length;
		break;

	case PW_TYPE_IPADDR:
		/*
		 *	It's a comparison, not a real IP.
		 */
		if ((vp->op == T_OP_REG_EQ) ||
		    (vp->op == T_OP_REG_NE)) {
			break;
		}

		/*
		 *	FIXME: complain if hostname
		 *	cannot be resolved, or resolve later!
		 */
		p = NULL;
		cs = value;

		{
			fr_ipaddr_t ipaddr;

			if (ip_hton(cs, AF_INET, &ipaddr) < 0) {
				fr_strerror_printf("Failed to find IP address for %s", cs);
				return false;
			}

			vp->vp_ipaddr = ipaddr.ipaddr.ip4addr.s_addr;
		}
		vp->length = 4;
		break;

	case PW_TYPE_BYTE:
		vp->length = 1;

		/*
		 *	Note that ALL integers are unsigned!
		 */
		vp->vp_integer = getint(value, &p);
		if (!*p) {
			if (vp->vp_integer > 255) {
				fr_strerror_printf("Byte value \"%s\" is larger than 255", value);
				return false;
			}
			break;
		}
		if (check_for_whitespace(p)) break;
		goto check_for_value;

	case PW_TYPE_SHORT:
		/*
		 *	Note that ALL integers are unsigned!
		 */
		vp->vp_integer = getint(value, &p);
		vp->length = 2;
		if (!*p) {
			if (vp->vp_integer > 65535) {
				fr_strerror_printf("Byte value \"%s\" is larger than 65535", value);
				return false;
			}
			break;
		}
		if (check_for_whitespace(p)) break;
		goto check_for_value;

	case PW_TYPE_INTEGER:
		/*
		 *	Note that ALL integers are unsigned!
		 */
		vp->vp_integer = getint(value, &p);
		vp->length = 4;
		if (!*p) break;
		if (check_for_whitespace(p)) break;

	check_for_value:
		/*
		 *	Look for the named value for the given
		 *	attribute.
		 */
		if ((dval = dict_valbyname(vp->da->attr, vp->da->vendor, value)) == NULL) {
			fr_strerror_printf("Unknown value %s for attribute %s",
				   value, vp->da->name);
			return false;
		}
		vp->vp_integer = dval->value;
		break;

	case PW_TYPE_INTEGER64:
		/*
		 *	Note that ALL integers are unsigned!
		 */
		if (sscanf(vp->vp_strvalue, "%" PRIu64, &y) != 1) {
			fr_strerror_printf("Invalid value %s for attribute %s",
					   value, vp->da->name);
			return false;
		}
		vp->vp_integer64 = y;
		vp->length = 8;
		length = strspn(vp->vp_strvalue, "0123456789");
		if (check_for_whitespace(vp->vp_strvalue + length)) break;
		break;

	case PW_TYPE_DATE:
		{
			/*
			 *	time_t may be 64 bits, whule vp_date
			 *	MUST be 32-bits.  We need an
			 *	intermediary variable to handle
			 *	the conversions.
			 */
			time_t date;

			if (gettime(value, &date) < 0) {
				fr_strerror_printf("failed to parse time string "
					   "\"%s\"", value);
				return false;
			}

			vp->vp_date = date;
		}
		vp->length = 4;
		break;

	case PW_TYPE_ABINARY:
#ifdef WITH_ASCEND_BINARY
		if (strncasecmp(value, "0x", 2) == 0) {
			goto do_octets;
		}

		if (ascend_parse_filter(vp) < 0 ) {
			char buffer[256];

			snprintf(buffer, sizeof(buffer), "failed to parse Ascend binary attribute: %s", fr_strerror());
			fr_strerror_printf("%s", buffer);
			return false;
		}
		break;

		/*
		 *	If Ascend binary is NOT defined,
		 *	then fall through to raw octets, so that
		 *	the user can at least make them by hand...
		 */
#endif
	/* raw octets: 0x01020304... */
	case PW_TYPE_VSA:
		if (strcmp(value, "ANY") == 0) {
			vp->length = 0;
			break;
		} /* else it's hex */

	case PW_TYPE_OCTETS:
		if (strncasecmp(value, "0x", 2) == 0) {
			size_t size;
			uint8_t *us;

#ifdef WITH_ASCEND_BINARY
		do_octets:
#endif
			cp = value + 2;
			size = strlen(cp);
			vp->length = size >> 1;
			us = talloc_array(vp, uint8_t, vp->length);

			/*
			 *	Invalid.
			 */
			if ((size  & 0x01) != 0) {
				fr_strerror_printf("Hex string is not an even length string.");
				return false;
			}

			if (fr_hex2bin(cp, us,
				       vp->length) != vp->length) {
				fr_strerror_printf("Invalid hex data");
				return false;
			}
			vp->vp_octets = us;
		} else {
Beispiel #13
0
/** Determine the PSK to use for an incoming connection
 *
 * @param[in] ssl		session.
 * @param[in] identity		The identity of the PSK to search for.
 * @param[out] psk		Where to write the PSK we found (if any).
 * @param[in] max_psk_len	The length of the buffer provided for PSK.
 * @return
 *	- 0 if no PSK matching identity was found.
 *	- >0 if a PSK matching identity was found (the length of bytes written to psk).
 */
unsigned int tls_session_psk_server_cb(SSL *ssl, const char *identity,
				       unsigned char *psk, unsigned int max_psk_len)
{
	unsigned int	psk_len = 0;
	fr_tls_conf_t	*conf;
	REQUEST		*request;

	conf = (fr_tls_conf_t *)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_CONF);
	if (!conf) return 0;

	request = (REQUEST *)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST);
	if (request && conf->psk_query) {
		size_t hex_len;
		VALUE_PAIR *vp;
		char buffer[2 * PSK_MAX_PSK_LEN + 4]; /* allow for too-long keys */

		/*
		 *	The passed identity is weird.  Deny it.
		 */
		if (!session_psk_identity_is_safe(identity)) {
			RWDEBUG("Invalid characters in PSK identity %s", identity);
			return 0;
		}

		MEM(pair_update_request(&vp, attr_tls_psk_identity) >= 0);
		if (fr_pair_value_from_str(vp, identity, -1, '\0', true) < 0) {
			RPWDEBUG2("Failed parsing TLS PSK Identity");
			talloc_free(vp);
			return 0;
		}

		hex_len = xlat_eval(buffer, sizeof(buffer), request, conf->psk_query, NULL, NULL);
		if (!hex_len) {
			RWDEBUG("PSK expansion returned an empty string.");
			return 0;
		}

		/*
		 *	The returned key is truncated at MORE than
		 *	OpenSSL can handle.  That way we can detect
		 *	the truncation, and complain about it.
		 */
		if (hex_len > (2 * max_psk_len)) {
			RWDEBUG("Returned PSK is too long (%u > %u)", (unsigned int) hex_len, 2 * max_psk_len);
			return 0;
		}

		/*
		 *	Leave the TLS-PSK-Identity in the request, and
		 *	convert the expansion from printable string
		 *	back to hex.
		 */
		return fr_hex2bin(psk, max_psk_len, buffer, hex_len);
	}

	if (!conf->psk_identity) {
		DEBUG("No static PSK identity set.  Rejecting the user");
		return 0;
	}

	/*
	 *	No REQUEST, or no dynamic query.  Just look for a
	 *	static identity.
	 */
	if (strcmp(identity, conf->psk_identity) != 0) {
		ERROR("Supplied PSK identity %s does not match configuration.  Rejecting.",
		      identity);
		return 0;
	}

	psk_len = strlen(conf->psk_password);
	if (psk_len > (2 * max_psk_len)) return 0;

	return fr_hex2bin(psk, max_psk_len, conf->psk_password, psk_len);
}
Beispiel #14
0
/*
 *	Create a VALUE_PAIR from an ASCII attribute and value,
 *	where the attribute name is in the form:
 *
 *	Attr-%d
 *	Vendor-%d-Attr-%d
 *	VendorName-Attr-%d
 */
static VALUE_PAIR *pairmake_any(const char *attribute, const char *value,
				int operator)
{
	int		attr, vendor;
	size_t		size;
	const char	*p = attribute;
	char		*q;
	VALUE_PAIR	*vp;

	/*
	 *	Unknown attributes MUST be of type 'octets'
	 */
	if (value && (strncasecmp(value, "0x", 2) != 0)) {
		fr_strerror_printf("Unknown attribute \"%s\" requires a hex string, not \"%s\"", attribute, value);
		return NULL;
	}

	vendor = 0;

	/*
	 *	Pull off vendor prefix first.
	 */
	if (strncasecmp(p, "Attr-", 5) != 0) {
		if (strncasecmp(p, "Vendor-", 7) == 0) {
			vendor = (int) strtol(p + 7, &q, 10);
			if ((vendor == 0) || (vendor > 65535)) {
				fr_strerror_printf("Invalid vendor value in attribute name \"%s\"", attribute);
				return NULL;
			}

			p = q;

		} else {	/* must be vendor name */
			char buffer[256];

			q = strchr(p, '-');

			if (!q) {
				fr_strerror_printf("Invalid vendor name in attribute name \"%s\"", attribute);
				return NULL;
			}

			if ((size_t) (q - p) >= sizeof(buffer)) {
				fr_strerror_printf("Vendor name too long in attribute name \"%s\"", attribute);
				return NULL;
			}

			memcpy(buffer, p, (q - p));
			buffer[q - p] = '\0';

			vendor = dict_vendorbyname(buffer);
			if (!vendor) {
				fr_strerror_printf("Unknown vendor name in attribute name \"%s\"", attribute);
				return NULL;
			}

			p = q;
		}

		if (*p != '-') {
			fr_strerror_printf("Invalid text following vendor definition in attribute name \"%s\"", attribute);
			return NULL;
		}
		p++;
	}

	/*
	 *	Attr-%d
	 */
	if (strncasecmp(p, "Attr-", 5) != 0) {
		fr_strerror_printf("Invalid format in attribute name \"%s\"", attribute);
		return NULL;
	}

	attr = strtol(p + 5, &q, 10);

	/*
	 *	Invalid, or trailing text after number.
	 */
	if ((attr == 0) || *q) {
		fr_strerror_printf("Invalid value in attribute name \"%s\"", attribute);
		return NULL;
	}

	/*
	 *	Double-check the size of attr.
	 */
	if (vendor) {
		DICT_VENDOR *dv = dict_vendorbyvalue(vendor);

		if (!dv) {
			if (attr > 255) {
			attr_error:
				fr_strerror_printf("Invalid attribute number in attribute name \"%s\"", attribute);
				return NULL;
			}

		} else switch (dv->type) {
			case 1:
				if (attr > 255) goto attr_error;
				break;

			case 2:
				if (attr > 65535) goto attr_error;
				break;

			case 4:	/* Internal limitations! */
				if (attr > 65535) goto attr_error;
				break;

			default:
				fr_strerror_printf("Internal sanity check failed");
				return NULL;
		}
	}

	attr |= vendor << 16;

	/*
	 *	We've now parsed the attribute properly, Let's create
	 *	it.  This next stop also looks the attribute up in the
	 *	dictionary, and creates the appropriate type for it.
	 */
	if ((vp = paircreate(attr, PW_TYPE_OCTETS)) == NULL) {
		fr_strerror_printf("out of memory");
		return NULL;
	}

	vp->operator = (operator == 0) ? T_OP_EQ : operator;
	if (!value) return vp;

	size = strlen(value + 2);

	/*
	 *	We may be reading something like Attr-5.  i.e.
	 *	who-ever wrote the text didn't understand it, but we
	 *	do.
	 */
	switch (vp->type) {
	default:
		if (size == (vp->length * 2)) break;
		vp->type = PW_TYPE_OCTETS;
		/* FALL-THROUGH */
		
	case PW_TYPE_OCTETS:
	case PW_TYPE_ABINARY:
		vp->length = size >> 1;
		if (vp->length > sizeof(vp->vp_octets)) {
			vp->length = sizeof(vp->vp_octets);
		}
		break;

	case PW_TYPE_STRING:
		vp->length = size >> 1;
		memset(&vp->vp_strvalue, 0, sizeof(vp->vp_strvalue));
		if (vp->length >= sizeof(vp->vp_strvalue)) {
			vp->length = sizeof(vp->vp_strvalue) - 1;
		}
		break;
	}

	if (fr_hex2bin(value + 2, vp->vp_octets, size) != vp->length) {
		fr_strerror_printf("Invalid hex string");
		free(vp);
		return NULL;
	}

	/*
	 *	Move contents around based on type.  This is
	 *	to work around the historical use of "lvalue".
	 */
	switch (vp->type) {
	case PW_TYPE_DATE:
	case PW_TYPE_IPADDR:
	case PW_TYPE_INTEGER:
		memcpy(&vp->lvalue, vp->vp_octets, sizeof(vp->lvalue));
		vp->vp_strvalue[0] = '\0';
		break;
		
	default:
		break;
	}
       
	return vp;
}
Beispiel #15
0
/*
 *	Verify the response entered by the user.
 */
static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
{
	rlm_otp_t *inst = instance;

	char const *username;
	int rc;
	otp_pwe_t pwe;
	VALUE_PAIR *vp;

	char challenge[OTP_MAX_CHALLENGE_LEN];	/* cf. authorize() */
	char passcode[OTP_MAX_PASSCODE_LEN + 1];

	challenge[0] = '\0';	/* initialize for otp_pw_valid() */

	/* User-Name attribute required. */
	if (!request->username) {
		RWDEBUG("Attribute \"User-Name\" required "
			"for authentication");

		return RLM_MODULE_INVALID;
	}

	username = request->username->vp_strvalue;

	pwe = otp_pwe_present(request);
	if (pwe == 0) {
		RWDEBUG("Attribute \"User-Password\" "
			"or equivalent required for authentication");

		return RLM_MODULE_INVALID;
	}

	/*
	 *	Retrieve the challenge (from State attribute).
	 */
	vp = pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY);
	if (vp) {
		char	gen_state[OTP_MAX_RADSTATE_LEN]; //!< State as hexits
		uint8_t	bin_state[OTP_MAX_RADSTATE_LEN];

		int32_t	then;		//!< State timestamp.
		size_t	elen;		//!< Expected State length.
		size_t	len;

		/*
		 *	Set expected State length (see otp_gen_state())
		 */
		elen = (inst->challenge_len * 2) + 8 + 8 + 32;

		if (vp->length != elen) {
			REDEBUG("Bad radstate for [%s]: length", username);
			return RLM_MODULE_INVALID;
		}

		/*
		 *	Verify the state.
		 */

		/*
		 *	Convert vp state (ASCII encoded hexits in opaque bin
		 *	string) back to binary.
		 *
		 *	There are notes in otp_radstate as to why the state
		 *	value is encoded as hexits.
		 */
		len = fr_hex2bin(bin_state, vp->vp_strvalue, vp->length);
		if (len != (vp->length / 2)) {
			REDEBUG("bad radstate for [%s]: not hex", username);

			return RLM_MODULE_INVALID;
		}

		/*
		 *	Extract data from State
		 */
		memcpy(challenge, bin_state, inst->challenge_len);

		/*
		 *	Skip flag data
		 */
		memcpy(&then, bin_state + inst->challenge_len + 4, 4);

		/*
		 *	Generate new state from returned input data
		 */
		otp_gen_state(gen_state, challenge, inst->challenge_len, 0,
			      then, inst->hmac_key);

		/*
		 *	Compare generated state (in hex form)
		 *	against generated state (in hex form)
		 *	to verify hmac.
		 */
		if (memcmp(gen_state, vp->vp_octets, vp->length)) {
			REDEBUG("bad radstate for [%s]: hmac", username);

			return RLM_MODULE_REJECT;
		}

		/*
		 *	State is valid, but check expiry.
		 */
		then = ntohl(then);
		if (time(NULL) - then > inst->challenge_delay) {
			REDEBUG("bad radstate for [%s]: expired",username);

			return RLM_MODULE_REJECT;
		}
	}

	/* do it */
	rc = otp_pw_valid(request, pwe, challenge, inst, passcode);

	/* Add MPPE data as needed. */
	if (rc == RLM_MODULE_OK) {
		otp_mppe(request, pwe, inst, passcode);
	}

	return rc;
}
Beispiel #16
0
VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value)
{
	char		*p, *s=0;
	const char	*cp, *cs;
	int		x;
	size_t		length;
	DICT_VALUE	*dval;

	if (!value) return NULL;

	/*
	 *	Even for integers, dates and ip addresses we
	 *	keep the original string in vp->vp_strvalue.
	 */
	if (vp->type != PW_TYPE_TLV) {
		strlcpy(vp->vp_strvalue, value, sizeof(vp->vp_strvalue));
		vp->length = strlen(vp->vp_strvalue);
	}

	switch(vp->type) {
		case PW_TYPE_STRING:
			/*
			 *	Do escaping here
			 */
			p = vp->vp_strvalue;
			cp = value;
			length = 0;

			while (*cp && (length < (sizeof(vp->vp_strvalue) - 1))) {
				char c = *cp++;

				if (c == '\\') {
					switch (*cp) {
					case 'r':
						c = '\r';
						cp++;
						break;
					case 'n':
						c = '\n';
						cp++;
						break;
					case 't':
						c = '\t';
						cp++;
						break;
					case '"':
						c = '"';
						cp++;
						break;
					case '\'':
						c = '\'';
						cp++;
						break;
					case '\\':
						c = '\\';
						cp++;
						break;
					case '`':
						c = '`';
						cp++;
						break;
					case '\0':
						c = '\\'; /* no cp++ */
						break;
					default:
						if ((cp[0] >= '0') &&
						    (cp[0] <= '9') &&
						    (cp[1] >= '0') &&
						    (cp[1] <= '9') &&
						    (cp[2] >= '0') &&
						    (cp[2] <= '9') &&
						    (sscanf(cp, "%3o", &x) == 1)) {
							c = x;
							cp += 3;
						} /* else just do '\\' */
					}
				}
				*p++ = c;
				length++;
			}
			vp->vp_strvalue[length] = '\0';
			vp->length = length;
			break;

		case PW_TYPE_IPADDR:
			/*
			 *	It's a comparison, not a real IP.
			 */
			if ((vp->operator == T_OP_REG_EQ) ||
			    (vp->operator == T_OP_REG_NE)) {
				break;
			}

			/*
			 *	FIXME: complain if hostname
			 *	cannot be resolved, or resolve later!
			 */
			s = NULL;
			if ((p = strrchr(value, '+')) != NULL && !p[1]) {
				cs = s = strdup(value);
				if (!s) return NULL;
				p = strrchr(s, '+');
				*p = 0;
				vp->flags.addport = 1;
			} else {
				p = NULL;
				cs = value;
			}

			{
				fr_ipaddr_t ipaddr;

				if (ip_hton(cs, AF_INET, &ipaddr) < 0) {
					fr_strerror_printf("Failed to find IP address for %s", cs);
					free(s);
					return NULL;
				}

				vp->vp_ipaddr = ipaddr.ipaddr.ip4addr.s_addr;
			}
			free(s);
			vp->length = 4;
			break;

		case PW_TYPE_BYTE:
			vp->length = 1;

			/*
			 *	Note that ALL integers are unsigned!
			 */
			vp->vp_integer = getint(value, &p);
			if (!*p) {
				if (vp->vp_integer > 255) {
					fr_strerror_printf("Byte value \"%s\" is larger than 255", value);
					return NULL;
				}
				break;
			}
			if (check_for_whitespace(p)) break;
			goto check_for_value;

		case PW_TYPE_SHORT:
			/*
			 *	Note that ALL integers are unsigned!
			 */
			vp->vp_integer = getint(value, &p);
			vp->length = 2;
			if (!*p) {
				if (vp->vp_integer > 65535) {
					fr_strerror_printf("Byte value \"%s\" is larger than 65535", value);
					return NULL;
				}
				break;
			}
			if (check_for_whitespace(p)) break;
			goto check_for_value;

		case PW_TYPE_INTEGER:
			/*
			 *	Note that ALL integers are unsigned!
			 */
			vp->vp_integer = getint(value, &p);
			vp->length = 4;
			if (!*p) break;
			if (check_for_whitespace(p)) break;

	check_for_value:
			/*
			 *	Look for the named value for the given
			 *	attribute.
			 */
			if ((dval = dict_valbyname(vp->attribute, value)) == NULL) {
				fr_strerror_printf("Unknown value %s for attribute %s",
					   value, vp->name);
				return NULL;
			}
			vp->vp_integer = dval->value;
			break;

		case PW_TYPE_DATE:
		  	{
				/*
				 *	time_t may be 64 bits, whule vp_date
				 *	MUST be 32-bits.  We need an
				 *	intermediary variable to handle
				 *	the conversions.
				 */
				time_t date;

				if (gettime(value, &date) < 0) {
					fr_strerror_printf("failed to parse time string "
						   "\"%s\"", value);
					return NULL;
				}

				vp->vp_date = date;
			}
			vp->length = 4;
			break;

		case PW_TYPE_ABINARY:
#ifdef ASCEND_BINARY
			if (strncasecmp(value, "0x", 2) == 0) {
				vp->type = PW_TYPE_OCTETS;
				goto do_octets;
			}

		  	if (ascend_parse_filter(vp) < 0 ) {
				char buffer[256];

				snprintf(buffer, sizeof(buffer), "failed to parse Ascend binary attribute: %s", fr_strerror());
				fr_strerror_printf("%s", buffer);
				return NULL;
			}
			break;

			/*
			 *	If Ascend binary is NOT defined,
			 *	then fall through to raw octets, so that
			 *	the user can at least make them by hand...
			 */
	do_octets:
#endif
			/* raw octets: 0x01020304... */
		case PW_TYPE_OCTETS:
			if (strncasecmp(value, "0x", 2) == 0) {
				uint8_t *us;
				cp = value + 2;
				us = vp->vp_octets;
				vp->length = 0;


				/*
				 *	There is only one character,
				 *	die.
				 */
				if ((strlen(cp) & 0x01) != 0) {
					fr_strerror_printf("Hex string is not an even length string.");
					return NULL;
				}


				while (*cp &&
				       (vp->length < MAX_STRING_LEN)) {
					unsigned int tmp;

					if (sscanf(cp, "%02x", &tmp) != 1) {
						fr_strerror_printf("Non-hex characters at %c%c", cp[0], cp[1]);
						return NULL;
					}

					cp += 2;
					*(us++) = tmp;
					vp->length++;
				}
			}
			break;

		case PW_TYPE_IFID:
			if (ifid_aton(value, (void *) &vp->vp_ifid) == NULL) {
				fr_strerror_printf("failed to parse interface-id "
					   "string \"%s\"", value);
				return NULL;
			}
			vp->length = 8;
			break;

		case PW_TYPE_IPV6ADDR:
			{
				fr_ipaddr_t ipaddr;

				if (ip_hton(value, AF_INET6, &ipaddr) < 0) {
					char buffer[1024];

					strlcpy(buffer, fr_strerror(), sizeof(buffer));

					fr_strerror_printf("failed to parse IPv6 address "
                                                           "string \"%s\": %s", value, buffer);
					return NULL;
				}
				vp->vp_ipv6addr = ipaddr.ipaddr.ip6addr;
				vp->length = 16; /* length of IPv6 address */
			}
			break;

		case PW_TYPE_IPV6PREFIX:
			p = strchr(value, '/');
			if (!p || ((p - value) >= 256)) {
				fr_strerror_printf("invalid IPv6 prefix "
					   "string \"%s\"", value);
				return NULL;
			} else {
				unsigned int prefix;
				char buffer[256], *eptr;

				memcpy(buffer, value, p - value);
				buffer[p - value] = '\0';

				if (inet_pton(AF_INET6, buffer, vp->vp_octets + 2) <= 0) {
					fr_strerror_printf("failed to parse IPv6 address "
						   "string \"%s\"", value);
					return NULL;
				}

				prefix = strtoul(p + 1, &eptr, 10);
				if ((prefix > 128) || *eptr) {
					fr_strerror_printf("failed to parse IPv6 address "
						   "string \"%s\"", value);
					return NULL;
				}
				vp->vp_octets[1] = prefix;
			}
			vp->vp_octets[0] = '\0';
			vp->length = 16 + 2;
			break;

		case PW_TYPE_ETHERNET:
			{
				const char *c1, *c2;

				length = 0;
				cp = value;
				while (*cp) {
					if (cp[1] == ':') {
						c1 = hextab;
						c2 = memchr(hextab, tolower((int) cp[0]), 16);
						cp += 2;
					} else if ((cp[1] != '\0') &&
						   ((cp[2] == ':') ||
						    (cp[2] == '\0'))) {
						   c1 = memchr(hextab, tolower((int) cp[0]), 16);
						   c2 = memchr(hextab, tolower((int) cp[1]), 16);
						   cp += 2;
						   if (*cp == ':') cp++;
					} else {
						c1 = c2 = NULL;
					}
					if (!c1 || !c2 || (length >= sizeof(vp->vp_ether))) {
						fr_strerror_printf("failed to parse Ethernet address \"%s\"", value);
						return NULL;
					}
					vp->vp_ether[length] = ((c1-hextab)<<4) + (c2-hextab);
					length++;
				}
			}
			vp->length = 6;
			break;

		case PW_TYPE_COMBO_IP:
			if (inet_pton(AF_INET6, value, vp->vp_strvalue) > 0) {
				vp->type = PW_TYPE_IPV6ADDR;
				vp->length = 16; /* length of IPv6 address */
				vp->vp_strvalue[vp->length] = '\0';

			} else {
				fr_ipaddr_t ipaddr;

				if (ip_hton(value, AF_INET, &ipaddr) < 0) {
					fr_strerror_printf("Failed to find IPv4 address for %s", value);
					return NULL;
				}

				vp->type = PW_TYPE_IPADDR;
				vp->vp_ipaddr = ipaddr.ipaddr.ip4addr.s_addr;
				vp->length = 4;
			}
			break;

		case PW_TYPE_SIGNED: /* Damned code for 1 WiMAX attribute */
			vp->vp_signed = (int32_t) strtol(value, &p, 10);
			vp->length = 4;
			break;

		case PW_TYPE_TLV: /* don't use this! */
			if (strncasecmp(value, "0x", 2) != 0) {
				fr_strerror_printf("Invalid TLV specification");
				return NULL;
			}
			length = strlen(value + 2) / 2;
			if (vp->length < length) {
				free(vp->vp_tlv);
				vp->vp_tlv = NULL;
			}
			vp->vp_tlv = malloc(length);
			if (!vp->vp_tlv) {
				fr_strerror_printf("No memory");
				return NULL;
			}
			if (fr_hex2bin(value + 2, vp->vp_tlv,
				       length) != length) {
				fr_strerror_printf("Invalid hex data in TLV");
				return NULL;
			}
			vp->length = length;
			break;

			/*
			 *  Anything else.
			 */
		default:
			fr_strerror_printf("unknown attribute type %d", vp->type);
			return NULL;
	}

	return vp;
}
Beispiel #17
0
/*
 *	Do the MS-CHAP stuff.
 *
 *	This function is here so that all of the MS-CHAP related
 *	authentication is in one place, and we can perhaps later replace
 *	it with code to call winbindd, or something similar.
 */
static int do_mschap(rlm_mschap_t *inst,
		     REQUEST *request, VALUE_PAIR *password,
		     uint8_t *challenge, uint8_t *response,
		     uint8_t *nthashhash)
{
	int		do_ntlm_auth = 0;
	uint8_t		calculated[24];
	VALUE_PAIR	*vp = NULL;

	/*
	 *	If we have ntlm_auth configured, use it unless told
	 *	otherwise
	 */
	if (inst->ntlm_auth) do_ntlm_auth = 1;

	/*
	 *	If we have an ntlm_auth configuration, then we may
	 *	want to use it.
	 */
	vp = pairfind(request->config_items,
		      PW_MS_CHAP_USE_NTLM_AUTH);
	if (vp) do_ntlm_auth = vp->vp_integer;

	/*
	 *	No ntlm_auth configured, attribute to tell us to
	 *	use it exists, and we're told to use it.  We don't
	 *	know what to do...
	 */
	if (!inst->ntlm_auth && do_ntlm_auth) {
		RDEBUG2("Asked to use ntlm_auth, but it was not configured in the mschap{} section.");
		return -1;
	}

	/*
	 *	Do normal authentication.
	 */
	if (!do_ntlm_auth) {
		/*
		 *	No password: can't do authentication.
		 */
		if (!password) {
			RDEBUG2("FAILED: No NT/LM-Password.  Cannot perform authentication.");
			return -1;
		}

		smbdes_mschap(password->vp_strvalue, challenge, calculated);
		if (memcmp(response, calculated, 24) != 0) {
			return -1;
		}

		/*
		 *	If the password exists, and is an NT-Password,
		 *	then calculate the hash of the NT hash.  Doing this
		 *	here minimizes work for later.
		 */
		if (password && (password->attribute == PW_NT_PASSWORD)) {
			fr_md4_calc(nthashhash, password->vp_octets, 16);
		} else {
			memset(nthashhash, 0, 16);
		}
	} else {		/* run ntlm_auth */
		int	result;
		char	buffer[256];

		memset(nthashhash, 0, 16);

		/*
		 *	Run the program, and expect that we get 16
		 */
		result = radius_exec_program(inst->ntlm_auth, request,
					     TRUE, /* wait */
					     buffer, sizeof(buffer),
					     NULL, NULL, 1);
		if (result != 0) {
			RDEBUG2("External script failed.");
			return -1;
		}

		/*
		 *	Parse the answer as an nthashhash.
		 *
		 *	ntlm_auth currently returns:
		 *	NT_KEY: 000102030405060708090a0b0c0d0e0f
		 */
		if (memcmp(buffer, "NT_KEY: ", 8) != 0) {
			RDEBUG2("Invalid output from ntlm_auth: expecting NT_KEY");
			return -1;
		}

		/*
		 *	Check the length.  It should be at least 32,
		 *	with an LF at the end.
		 */
		if (strlen(buffer + 8) < 32) {
			RDEBUG2("Invalid output from ntlm_auth: NT_KEY has unexpected length");
			return -1;
		}

		/*
		 *	Update the NT hash hash, from the NT key.
		 */
		if (fr_hex2bin(buffer + 8, nthashhash, 16) != 16) {
			RDEBUG2("Invalid output from ntlm_auth: NT_KEY has non-hex values");
			return -1;
		}
	}

	return 0;
}