Example #1
0
/*
 *	Run a virtual server auth and postauth
 *
 */
int rad_virtual_server(REQUEST *request)
{
	VALUE_PAIR *vp;
	int result;

	/*
	 *	We currently only handle AUTH packets here.
	 *	This could be expanded to handle other packets as well if required.
	 */
	rad_assert(request->packet->code == PW_AUTHENTICATION_REQUEST);

	result = rad_authenticate(request);

        if (request->reply->code == PW_AUTHENTICATION_REJECT) {
                pairdelete(&request->config_items, PW_POST_AUTH_TYPE, 0, TAG_ANY);
                vp = radius_pairmake(request, &request->config_items,
                                     "Post-Auth-Type", "Reject",
                                     T_OP_SET);
                if (vp) rad_postauth(request);
        }

        if (request->reply->code == PW_AUTHENTICATION_ACK) {
                rad_postauth(request);
        }

	return result;
}
Example #2
0
/*
 *	Set the REDISN user name.
 *
 *	We don't call the escape function here. The resulting string
 *	will be escaped later in the queries xlat so we don't need to
 *	escape it twice. (it will make things wrong if we have an
 *	escape candidate character in the username)
 */
int redisn_set_user(REDIS_INST *inst, REQUEST *request, char *redisnusername, const char *username)
{
	VALUE_PAIR *vp=NULL;
	char tmpuser[MAX_STRING_LEN];

	tmpuser[0] = '\0';
	redisnusername[0]= '\0';

	/* Remove any user attr we added previously */
	pairdelete(&request->packet->vps, PW_REDIS_USER_NAME, 0, TAG_ANY);

	if (username != NULL) {
		strlcpy(tmpuser, username, sizeof(tmpuser));
	} else if (strlen(inst->query_user)) {
		radius_xlat(tmpuser, sizeof(tmpuser), inst->query_user, request, NULL, inst);
	} else {
		return 0;
	}

	strlcpy(redisnusername, tmpuser, MAX_STRING_LEN);
	RDEBUG2("redisn_set_user escaped user --> '%s'", redisnusername);
	vp = radius_pairmake(request, &request->packet->vps,
			     "REDISN-User-Name", NULL, 0);
	if (!vp) {
		radlog(L_ERR, "%s", fr_strerror());
		return -1;
	}

	strlcpy(vp->vp_strvalue, tmpuser, sizeof(vp->vp_strvalue));
	vp->length = strlen(vp->vp_strvalue);

	return 0;

}
Example #3
0
/*
 *	Add MPPE attributes to the reply.
 */
static void mppe_add_reply(REQUEST *request,
			   const char* name, const uint8_t * value, int len)
{
       VALUE_PAIR *vp;
       vp = radius_pairmake(request, &request->reply->vps, name, "", T_OP_EQ);
       if (!vp) {
	       RDEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, fr_strerror());
	       return;
       }

       memcpy(vp->vp_octets, value, len);
       vp->length = len;
}
Example #4
0
/*
 *	mschap_authorize() - authorize user if we can authenticate
 *	it later. Add Auth-Type attribute if present in module
 *	configuration (usually Auth-Type must be "MS-CHAP")
 */
static int mschap_authorize(void * instance, REQUEST *request)
{
#define inst ((rlm_mschap_t *)instance)
	VALUE_PAIR *challenge = NULL, *response = NULL;

	challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
	if (!challenge) {
		return RLM_MODULE_NOOP;
	}

	response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
	if (!response)
		response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE);

	/*
	 *	Nothing we recognize.  Don't do anything.
	 */
	if (!response) {
		RDEBUG2("Found MS-CHAP-Challenge, but no MS-CHAP-Response.");
		return RLM_MODULE_NOOP;
	}

	if (pairfind(request->config_items, PW_AUTH_TYPE)) {
		RDEBUG2("Found existing Auth-Type.  Not changing it.");
		return RLM_MODULE_NOOP;
	}

	RDEBUG2("Found MS-CHAP attributes.  Setting 'Auth-Type  = %s'", inst->xlat_name);

	/*
	 *	Set Auth-Type to MS-CHAP.  The authentication code
	 *	will take care of turning clear-text passwords into
	 *	NT/LM passwords.
	 */
	if (!radius_pairmake(request, &request->config_items,
			     "Auth-Type", inst->auth_type, T_OP_EQ)) {
		return RLM_MODULE_FAIL;
	}

	return RLM_MODULE_OK;
#undef inst
}
static int eap_example_server_step(EAP_HANDLER *handler)
{
	int res, process = 0;
	REQUEST *request = handler->request;

	res = eap_server_sm_step(handler->server_ctx.eap);

	if (handler->server_ctx.eap_if->eapReq) {
		DEBUG("==> Request");
		process = 1;
		handler->server_ctx.eap_if->eapReq = 0;
	}

	if (handler->server_ctx.eap_if->eapSuccess) {
		DEBUG("==> Success");
		process = 1;
		res = 0;

		if (handler->server_ctx.eap_if->eapKeyAvailable) {
			int length = handler->server_ctx.eap_if->eapKeyDataLen;
			VALUE_PAIR *vp;

			if (length > 64) {
				length = 32;
			} else {
				length /= 2;
				/*
				 *	FIXME: Length is zero?
				 */
			}

			vp = radius_pairmake(request, &request->reply->vps,
					     "MS-MPPE-Recv-Key", "", T_OP_EQ);
			if (vp) {
				memcpy(vp->vp_octets,
				       handler->server_ctx.eap_if->eapKeyData,
				       length);
				vp->length = length;
			}
			
			vp = radius_pairmake(request, &request->reply->vps,
					     "MS-MPPE-Send-Key", "", T_OP_EQ);
			if (vp) {
				memcpy(vp->vp_octets,
				       handler->server_ctx.eap_if->eapKeyData + length,
				       length);
				vp->length = length;
			}
		}
	}

	if (handler->server_ctx.eap_if->eapFail) {
		DEBUG("==> Fail");
		process = 1;
	}

	if (process) {
		if (wpabuf_head(handler->server_ctx.eap_if->eapReqData)) {
			if (!eap_req2vp(handler)) return -1;
		} else {
			return -1;
		}
	}

	return res;
}
/*
 *	Before trusting a certificate, you must make sure that the
 *	certificate is 'valid'. There are several steps that your
 *	application can take in determining if a certificate is
 *	valid. Commonly used steps are:
 *
 *	1.Verifying the certificate's signature, and verifying that
 *	the certificate has been issued by a trusted Certificate
 *	Authority.
 *
 *	2.Verifying that the certificate is valid for the present date
 *	(i.e. it is being presented within its validity dates).
 *
 *	3.Verifying that the certificate has not been revoked by its
 *	issuing Certificate Authority, by checking with respect to a
 *	Certificate Revocation List (CRL).
 *
 *	4.Verifying that the credentials presented by the certificate
 *	fulfill additional requirements specific to the application,
 *	such as with respect to access control lists or with respect
 *	to OCSP (Online Certificate Status Processing).
 *
 *	NOTE: This callback will be called multiple times based on the
 *	depth of the root certificate chain
 */
static int cbtls_verify(int ok, X509_STORE_CTX *ctx)
{
	char subject[1024]; /* Used for the subject name */
	char issuer[1024]; /* Used for the issuer name */
	char common_name[1024];
	char cn_str[1024];
	char buf[64];
	EAP_HANDLER *handler = NULL;
	X509 *client_cert;
	X509 *issuer_cert;
	SSL *ssl;
	int err, depth, lookup;
	EAP_TLS_CONF *conf;
	int my_ok = ok;
	REQUEST *request;
	ASN1_INTEGER *sn = NULL;
	ASN1_TIME *asn_time = NULL;
#ifdef HAVE_OPENSSL_OCSP_H
	X509_STORE *ocsp_store = NULL;
#endif

	client_cert = X509_STORE_CTX_get_current_cert(ctx);
	err = X509_STORE_CTX_get_error(ctx);
	depth = X509_STORE_CTX_get_error_depth(ctx);

	lookup = depth;

	/*
	 *	Log client/issuing cert.  If there's an error, log
	 *	issuing cert.
	 */
	if ((lookup > 1) && !my_ok) lookup = 1;

	/*
	 * Retrieve the pointer to the SSL of the connection currently treated
	 * and the application specific data stored into the SSL object.
	 */
	ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
	handler = (EAP_HANDLER *)SSL_get_ex_data(ssl, 0);
	request = handler->request;
	conf = (EAP_TLS_CONF *)SSL_get_ex_data(ssl, 1);
#ifdef HAVE_OPENSSL_OCSP_H
	ocsp_store = (X509_STORE *)SSL_get_ex_data(ssl, 2);
#endif


	/*
	 *	Get the Serial Number
	 */
	buf[0] = '\0';
	sn = X509_get_serialNumber(client_cert);

	/*
	 *	For this next bit, we create the attributes *only* if
	 *	we're at the client or issuing certificate.
	 */
	if ((lookup <= 1) && sn && (sn->length < (sizeof(buf) / 2))) {
		char *p = buf;
		int i;

		for (i = 0; i < sn->length; i++) {
			sprintf(p, "%02x", (unsigned int)sn->data[i]);
			p += 2;
		}
		pairadd(&handler->certs,
			pairmake(cert_attr_names[EAPTLS_SERIAL][lookup], buf, T_OP_SET));
	}


	/*
	 *	Get the Expiration Date
	 */
	buf[0] = '\0';
	asn_time = X509_get_notAfter(client_cert);
	if ((lookup <= 1) && asn_time && (asn_time->length < MAX_STRING_LEN)) {
		memcpy(buf, (char*) asn_time->data, asn_time->length);
		buf[asn_time->length] = '\0';
		pairadd(&handler->certs,
			pairmake(cert_attr_names[EAPTLS_EXPIRATION][lookup], buf, T_OP_SET));
	}

	/*
	 *	Get the Subject & Issuer
	 */
	subject[0] = issuer[0] = '\0';
	X509_NAME_oneline(X509_get_subject_name(client_cert), subject,
			  sizeof(subject));
	subject[sizeof(subject) - 1] = '\0';
	if ((lookup <= 1) && subject[0] && (strlen(subject) < MAX_STRING_LEN)) {
		pairadd(&handler->certs,
			pairmake(cert_attr_names[EAPTLS_SUBJECT][lookup], subject, T_OP_SET));
	}

	X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), issuer,
			  sizeof(issuer));
	issuer[sizeof(issuer) - 1] = '\0';
	if ((lookup <= 1) && issuer[0] && (strlen(issuer) < MAX_STRING_LEN)) {
		pairadd(&handler->certs,
			pairmake(cert_attr_names[EAPTLS_ISSUER][lookup], issuer, T_OP_SET));
	}

	/*
	 *	Get the Common Name
	 */
	X509_NAME_get_text_by_NID(X509_get_subject_name(client_cert),
				  NID_commonName, common_name, sizeof(common_name));
	common_name[sizeof(common_name) - 1] = '\0';
	if ((lookup <= 1) && common_name[0] && (strlen(common_name) < MAX_STRING_LEN)) {
		pairadd(&handler->certs,
			pairmake(cert_attr_names[EAPTLS_CN][lookup], common_name, T_OP_SET));
	}

	/*
	 *	If the CRL has expired, that might still be OK.
	 */
	if (!my_ok &&
	    (conf->allow_expired_crl) &&
	    (err == X509_V_ERR_CRL_HAS_EXPIRED)) {
		my_ok = 1;
		X509_STORE_CTX_set_error( ctx, 0 );
	}

	if (!my_ok) {
		const char *p = X509_verify_cert_error_string(err);
		radlog(L_ERR,"--> verify error:num=%d:%s\n",err, p);
		radius_pairmake(request, &request->packet->vps,
				"Module-Failure-Message", p, T_OP_SET);
		return my_ok;
	}

	switch (ctx->error) {

	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
		radlog(L_ERR, "issuer= %s\n", issuer);
		break;
	case X509_V_ERR_CERT_NOT_YET_VALID:
	case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
		radlog(L_ERR, "notBefore=");
#if 0
		ASN1_TIME_print(bio_err, X509_get_notBefore(ctx->current_cert));
#endif
		break;
	case X509_V_ERR_CERT_HAS_EXPIRED:
	case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
		radlog(L_ERR, "notAfter=");
#if 0
		ASN1_TIME_print(bio_err, X509_get_notAfter(ctx->current_cert));
#endif
		break;
	}

	/*
	 *	If we're at the actual client cert, apply additional
	 *	checks.
	 */
	if (depth == 0) {
		/*
		 *	If the conf tells us to, check cert issuer
		 *	against the specified value and fail
		 *	verification if they don't match.
		 */
		if (conf->check_cert_issuer &&
		    (strcmp(issuer, conf->check_cert_issuer) != 0)) {
			radlog(L_AUTH, "rlm_eap_tls: Certificate issuer (%s) does not match specified value (%s)!", issuer, conf->check_cert_issuer);
 			my_ok = 0;
 		}

		/*
		 *	If the conf tells us to, check the CN in the
		 *	cert against xlat'ed value, but only if the
		 *	previous checks passed.
		 */
		if (my_ok && conf->check_cert_cn) {
			if (!radius_xlat(cn_str, sizeof(cn_str), conf->check_cert_cn, handler->request, NULL)) {
				radlog(L_ERR, "rlm_eap_tls (%s): xlat failed.",
				       conf->check_cert_cn);
				/* if this fails, fail the verification */
				my_ok = 0;
			} else {
				RDEBUG2("checking certificate CN (%s) with xlat'ed value (%s)", common_name, cn_str);
				if (strcmp(cn_str, common_name) != 0) {
					radlog(L_AUTH, "rlm_eap_tls: Certificate CN (%s) does not match specified value (%s)!", common_name, cn_str);
					my_ok = 0;
				}
			}
		} /* check_cert_cn */

#ifdef HAVE_OPENSSL_OCSP_H
		if (my_ok && conf->ocsp_enable){
			RDEBUG2("--> Starting OCSP Request");
			if(X509_STORE_CTX_get1_issuer(&issuer_cert, ctx, client_cert)!=1) {
				radlog(L_ERR, "Error: Couldn't get issuer_cert for %s", common_name);
			}
			my_ok = ocsp_check(ocsp_store, issuer_cert, client_cert, conf);
		}
#endif

		while (conf->verify_client_cert_cmd) {
			char filename[256];
			int fd;
			FILE *fp;

			snprintf(filename, sizeof(filename), "%s/%s.client.XXXXXXXX",
				 conf->verify_tmp_dir, progname);
			fd = mkstemp(filename);
			if (fd < 0) {
				RDEBUG("Failed creating file in %s: %s",
				       conf->verify_tmp_dir, strerror(errno));
				break;				       
			}

			fp = fdopen(fd, "w");
			if (!fp) {
				RDEBUG("Failed opening file %s: %s",
				       filename, strerror(errno));
				break;
			}

			if (!PEM_write_X509(fp, client_cert)) {
				fclose(fp);
				RDEBUG("Failed writing certificate to file");
				goto do_unlink;
			}
			fclose(fp);

			if (!radius_pairmake(request, &request->packet->vps,
					     "TLS-Client-Cert-Filename",
					     filename, T_OP_SET)) {
				RDEBUG("Failed creating TLS-Client-Cert-Filename");
				
				goto do_unlink;
			}

			RDEBUG("Verifying client certificate: %s",
			       conf->verify_client_cert_cmd);
			if (radius_exec_program(conf->verify_client_cert_cmd,
						request, 1, NULL, 0, 
						request->packet->vps,
						NULL, 1) != 0) {
				radlog(L_AUTH, "rlm_eap_tls: Certificate CN (%s) fails external verification!", common_name);
				my_ok = 0;
			} else {
				RDEBUG("Client certificate CN %s passed external validation", common_name);
			}

		do_unlink:
			unlink(filename);
			break;
		}


	} /* depth == 0 */

	if (debug_flag > 0) {
		RDEBUG2("chain-depth=%d, ", depth);
		RDEBUG2("error=%d", err);

		RDEBUG2("--> User-Name = %s", handler->identity);
		RDEBUG2("--> BUF-Name = %s", common_name);
		RDEBUG2("--> subject = %s", subject);
		RDEBUG2("--> issuer  = %s", issuer);
		RDEBUG2("--> verify return:%d", my_ok);
	}
	return my_ok;
}
Example #7
0
File: auth.c Project: ebichu/dd-wrt
/*
 *	Process and reply to an authentication request
 *
 *	The return value of this function isn't actually used right now, so
 *	it's not entirely clear if it is returning the right things. --Pac.
 */
int rad_authenticate(REQUEST *request)
{
	VALUE_PAIR	*namepair;
#ifdef WITH_SESSION_MGMT
	VALUE_PAIR	*check_item;
#endif
	VALUE_PAIR	*auth_item = NULL;
	VALUE_PAIR	*module_msg;
	VALUE_PAIR	*tmp = NULL;
	int		result;
	const char	*password;
	char		autz_retry = 0;
	int		autz_type = 0;

	password = "";

#ifdef WITH_PROXY
	/*
	 *	If this request got proxied to another server, we need
	 *	to check whether it authenticated the request or not.
	 */
	if (request->proxy_reply) {
		switch (request->proxy_reply->code) {
		/*
		 *	Reply of ACCEPT means accept, thus set Auth-Type
		 *	accordingly.
		 */
		case PW_AUTHENTICATION_ACK:
			tmp = radius_paircreate(request,
						&request->config_items,
						PW_AUTH_TYPE, PW_TYPE_INTEGER);
			if (tmp) tmp->vp_integer = PW_AUTHTYPE_ACCEPT;
#ifdef WITH_POST_PROXY_AUTHORIZE
			if (mainconfig.post_proxy_authorize) break;
#endif
			goto authenticate;

		/*
		 *	Challenges are punted back to the NAS without any
		 *	further processing.
		 */
		case PW_ACCESS_CHALLENGE:
			request->reply->code = PW_ACCESS_CHALLENGE;
			return RLM_MODULE_OK;
		/*
		 *	ALL other replies mean reject. (this is fail-safe)
		 *
		 *	Do NOT do any authorization or authentication. They
		 *	are being rejected, so we minimize the amount of work
		 *	done by the server, by rejecting them here.
		 */
		case PW_AUTHENTICATION_REJECT:
			rad_authlog("Login incorrect (Home Server says so)",
				    request, 0);
			request->reply->code = PW_AUTHENTICATION_REJECT;
			return RLM_MODULE_REJECT;

		default:
			rad_authlog("Login incorrect (Home Server failed to respond)",
				    request, 0);
			return RLM_MODULE_REJECT;
		}
	}
#endif

	/*
	 *	Get the username from the request.
	 *
	 *	Note that namepair MAY be NULL, in which case there
	 *	is no User-Name attribute in the request.
	 */
	namepair = request->username;

	/*
	 *	Look for, and cache, passwords.
	 */
	if (!request->password) {
		request->password = pairfind(request->packet->vps,
					     PW_USER_PASSWORD);
	}

	/*
	 *	Discover which password we want to use.
	 */
	auth_item = request->password;
	if (auth_item) {
		password = (const char *)auth_item->vp_strvalue;

	} else {
		/*
		 *	Maybe there's a CHAP-Password?
		 */
		if ((auth_item = pairfind(request->packet->vps,
					  PW_CHAP_PASSWORD)) != NULL) {
			password = "******";

		} else {
			/*
			 *	No password we recognize.
			 */
			password = "******";
		}
	}
	request->password = auth_item;

	/*
	 *	Get the user's authorization information from the database
	 */
autz_redo:
	result = module_authorize(autz_type, request);
	switch (result) {
		case RLM_MODULE_NOOP:
		case RLM_MODULE_NOTFOUND:
		case RLM_MODULE_OK:
		case RLM_MODULE_UPDATED:
			break;
		case RLM_MODULE_HANDLED:
			return result;
		case RLM_MODULE_FAIL:
		case RLM_MODULE_INVALID:
		case RLM_MODULE_REJECT:
		case RLM_MODULE_USERLOCK:
		default:
			if ((module_msg = pairfind(request->packet->vps,
					PW_MODULE_FAILURE_MESSAGE)) != NULL) {
				char msg[MAX_STRING_LEN + 16];
				snprintf(msg, sizeof(msg), "Invalid user (%s)",
					 module_msg->vp_strvalue);
				rad_authlog(msg,request,0);
			} else {
				rad_authlog("Invalid user", request, 0);
			}
			request->reply->code = PW_AUTHENTICATION_REJECT;
			return result;
	}
	if (!autz_retry) {
		tmp = pairfind(request->config_items, PW_AUTZ_TYPE);
		if (tmp) {
			autz_type = tmp->vp_integer;
			RDEBUG2("Using Autz-Type %s",
				dict_valnamebyattr(PW_AUTZ_TYPE, autz_type));
			autz_retry = 1;
			goto autz_redo;
		}
	}

	/*
	 *	If we haven't already proxied the packet, then check
	 *	to see if we should.  Maybe one of the authorize
	 *	modules has decided that a proxy should be used. If
	 *	so, get out of here and send the packet.
	 */
	if (
#ifdef WITH_PROXY
	    (request->proxy == NULL) &&
#endif
	    ((tmp = pairfind(request->config_items, PW_PROXY_TO_REALM)) != NULL)) {
		REALM *realm;

		realm = realm_find2(tmp->vp_strvalue);

		/*
		 *	Don't authenticate, as the request is going to
		 *	be proxied.
		 */
		if (realm && realm->auth_pool) {
			return RLM_MODULE_OK;
		}

		/*
		 *	Catch users who set Proxy-To-Realm to a LOCAL
		 *	realm (sigh).  But don't complain if it is
		 *	*the* LOCAL realm.
		 */
		if (realm &&(strcmp(realm->name, "LOCAL") != 0)) {
			RDEBUG2("WARNING: You set Proxy-To-Realm = %s, but it is a LOCAL realm!  Cancelling proxy request.", realm->name);
		}

		if (!realm) {
			RDEBUG2("WARNING: You set Proxy-To-Realm = %s, but the realm does not exist!  Cancelling invalid proxy request.", tmp->vp_strvalue);
		}
	}

#ifdef WITH_PROXY
 authenticate:
#endif

	/*
	 *	Perhaps there is a Stripped-User-Name now.
	 */
	namepair = request->username;

	/*
	 *	Validate the user
	 */
	do {
		result = rad_check_password(request);
		if (result > 0) {
			/* don't reply! */
			return RLM_MODULE_HANDLED;
		}
	} while(0);

	/*
	 *	Failed to validate the user.
	 *
	 *	We PRESUME that the code which failed will clean up
	 *	request->reply->vps, to be ONLY the reply items it
	 *	wants to send back.
	 */
	if (result < 0) {
		RDEBUG2("Failed to authenticate the user.");
		request->reply->code = PW_AUTHENTICATION_REJECT;

		if ((module_msg = pairfind(request->packet->vps,PW_MODULE_FAILURE_MESSAGE)) != NULL){
			char msg[MAX_STRING_LEN+19];

			snprintf(msg, sizeof(msg), "Login incorrect (%s)",
				 module_msg->vp_strvalue);
			rad_authlog(msg, request, 0);
		} else {
			rad_authlog("Login incorrect", request, 0);
		}

		/* double check: maybe the secret is wrong? */
		if ((debug_flag > 1) && (auth_item != NULL) &&
				(auth_item->attribute == PW_USER_PASSWORD)) {
			uint8_t *p;

			p = (uint8_t *) auth_item->vp_strvalue;
			while (*p) {
				int size;

				size = fr_utf8_char(p);
				if (!size) {
					log_debug("  WARNING: Unprintable characters in the password.  Double-check the shared secret on the server and the NAS!");
					break;
				}
				p += size;
			}
		}
	}

#ifdef WITH_SESSION_MGMT
	if (result >= 0 &&
	    (check_item = pairfind(request->config_items, PW_SIMULTANEOUS_USE)) != NULL) {
		int r, session_type = 0;
		char		logstr[1024];
		char		umsg[MAX_STRING_LEN + 1];
		const char	*user_msg = NULL;

		tmp = pairfind(request->config_items, PW_SESSION_TYPE);
		if (tmp) {
			session_type = tmp->vp_integer;
			RDEBUG2("Using Session-Type %s",
				dict_valnamebyattr(PW_SESSION_TYPE, session_type));
		}

		/*
		 *	User authenticated O.K. Now we have to check
		 *	for the Simultaneous-Use parameter.
		 */
		if (namepair &&
		    (r = module_checksimul(session_type, request, check_item->vp_integer)) != 0) {
			char mpp_ok = 0;

			if (r == 2){
				/* Multilink attempt. Check if port-limit > simultaneous-use */
				VALUE_PAIR *port_limit;

				if ((port_limit = pairfind(request->reply->vps, PW_PORT_LIMIT)) != NULL &&
					port_limit->vp_integer > check_item->vp_integer){
					RDEBUG2("MPP is OK");
					mpp_ok = 1;
				}
			}
			if (!mpp_ok){
				if (check_item->vp_integer > 1) {
		  		snprintf(umsg, sizeof(umsg),
							"\r\nYou are already logged in %d times  - access denied\r\n\n",
							(int)check_item->vp_integer);
					user_msg = umsg;
				} else {
					user_msg = "\r\nYou are already logged in - access denied\r\n\n";
				}

				request->reply->code = PW_AUTHENTICATION_REJECT;

				/*
				 *	They're trying to log in too many times.
				 *	Remove ALL reply attributes.
				 */
				pairfree(&request->reply->vps);
				radius_pairmake(request, &request->reply->vps,
						"Reply-Message",
						user_msg, T_OP_SET);

				snprintf(logstr, sizeof(logstr), "Multiple logins (max %d) %s",
					check_item->vp_integer,
					r == 2 ? "[MPP attempt]" : "");
				rad_authlog(logstr, request, 1);

				result = -1;
			}
		}
	}
#endif

	/*
	 *	Result should be >= 0 here - if not, it means the user
	 *	is rejected, so we just process post-auth and return.
	 */
	if (result < 0) {
		return RLM_MODULE_REJECT;
	}

	/*
	 *	Add the port number to the Framed-IP-Address if
	 *	vp->addport is set.
	 */
	if (((tmp = pairfind(request->reply->vps,
			     PW_FRAMED_IP_ADDRESS)) != NULL) &&
	    (tmp->flags.addport != 0)) {
		VALUE_PAIR *vpPortId;

		/*
		 *  Find the NAS port ID.
		 */
		if ((vpPortId = pairfind(request->packet->vps,
					 PW_NAS_PORT)) != NULL) {
		  unsigned long tvalue = ntohl(tmp->vp_integer);
		  tmp->vp_integer = htonl(tvalue + vpPortId->vp_integer);
		  tmp->flags.addport = 0;
		  ip_ntoa(tmp->vp_strvalue, tmp->vp_integer);
		} else {
			RDEBUG2("WARNING: No NAS-Port attribute in request.  CANNOT return a Framed-IP-Address + NAS-Port.\n");
			pairdelete(&request->reply->vps, PW_FRAMED_IP_ADDRESS);
		}
	}

	/*
	 *	Set the reply to Access-Accept, if it hasn't already
	 *	been set to something.  (i.e. Access-Challenge)
	 */
	if (request->reply->code == 0)
	  request->reply->code = PW_AUTHENTICATION_ACK;

	if ((module_msg = pairfind(request->packet->vps,PW_MODULE_SUCCESS_MESSAGE)) != NULL){
		char msg[MAX_STRING_LEN+12];

		snprintf(msg, sizeof(msg), "Login OK (%s)",
			 module_msg->vp_strvalue);
		rad_authlog(msg, request, 1);
	} else {
		rad_authlog("Login OK", request, 1);
	}

	/*
	 *	Run the modules in the 'post-auth' section.
	 */
	result = rad_postauth(request);

	return result;
}
Example #8
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
}
Example #9
0
/*
 *	Generate the keys after the user has been authenticated.
 */
static int wimax_postauth(void *instance, REQUEST *request)
{
    rlm_wimax_t *inst = instance;
    VALUE_PAIR *msk, *emsk, *vp;
    VALUE_PAIR *mn_nai, *ip, *fa_rk;
    HMAC_CTX hmac;
    unsigned int rk1_len, rk2_len, rk_len;
    int rk_lifetime = 3600;	/* ? */
    uint32_t mip_spi;
    uint8_t usage_data[24];
    uint8_t mip_rk_1[EVP_MAX_MD_SIZE], mip_rk_2[EVP_MAX_MD_SIZE];
    uint8_t mip_rk[2 * EVP_MAX_MD_SIZE];

    msk = pairfind(request->reply->vps, 1129);
    emsk = pairfind(request->reply->vps, 1130);
    if (!msk || !emsk) {
        RDEBUG("No EAP-MSK or EAP-EMSK.  Cannot create WiMAX keys.");
        return RLM_MODULE_NOOP;
    }

    /*
     *	If we delete the MS-MPPE-*-Key attributes, then add in
     *	the WiMAX-MSK so that the client has a key available.
     */
    if (inst->delete_mppe_keys) {
        pairdelete(&request->reply->vps, ((311 << 16) | 16));
        pairdelete(&request->reply->vps, ((311 << 16) | 17));

        vp = radius_pairmake(request, &request->reply->vps, "WiMAX-MSK", "0x00", T_OP_EQ);
        if (vp) {
            memcpy(vp->vp_octets, msk->vp_octets, msk->length);
            vp->length = msk->length;
        }
    }

    /*
     *	Initialize usage data.
     */
    memcpy(usage_data, "*****@*****.**", 21);	/* with trailing \0 */
    usage_data[21] = 0x02;
    usage_data[22] = 0x00;
    usage_data[23] = 0x01;

    /*
     *	MIP-RK-1 = HMAC-SSHA256(EMSK, usage-data | 0x01)
     */
    HMAC_CTX_init(&hmac);
    HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->length, EVP_sha256(), NULL);

    HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data));
    HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);

    /*
     *	MIP-RK-2 = HMAC-SSHA256(EMSK, MIP-RK-1 | usage-data | 0x01)
     */
    HMAC_CTX_init(&hmac);
    HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->length, EVP_sha256(), NULL);

    HMAC_Update(&hmac, (const uint8_t *) &mip_rk_1, rk1_len);
    HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data));
    HMAC_Final(&hmac, &mip_rk_2[0], &rk2_len);

    vp = pairfind(request->reply->vps, PW_SESSION_TIMEOUT);
    if (vp) rk_lifetime = vp->vp_integer;

    memcpy(mip_rk, mip_rk_1, rk1_len);
    memcpy(mip_rk + rk1_len, mip_rk_2, rk2_len);
    rk_len = rk1_len + rk2_len;

    /*
     *	MIP-SPI = HMAC-SSHA256(MIP-RK, "SPI CMIP PMIP");
     */
    HMAC_CTX_init(&hmac);
    HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha256(), NULL);

    HMAC_Update(&hmac, (const uint8_t *) "SPI CMIP PMIP", 12);
    HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);

    /*
     *	Take the 4 most significant octets.
     *	If less than 256, add 256.
     */
    mip_spi = ((mip_rk_1[0] << 24) | (mip_rk_1[1] << 16) |
               (mip_rk_1[2] << 8) | mip_rk_1[3]);
    if (mip_spi < 256) mip_spi += 256;

    if (debug_flag) {
        int len = rk_len;
        char buffer[512];

        if (len > 128) len = 128; /* buffer size */

        fr_bin2hex(mip_rk, buffer, len);
        radlog_request(L_DBG, 0, request, "MIP-RK = 0x%s", buffer);
        radlog_request(L_DBG, 0, request, "MIP-SPI = %08x",
                       ntohl(mip_spi));
    }

    /*
     *	FIXME: Perform SPI collision prevention
     */

    /*
     *	Calculate mobility keys
     */
    mn_nai = pairfind(request->packet->vps, 1900);
    if (!mn_nai) mn_nai = pairfind(request->reply->vps, 1900);
    if (!mn_nai) {
        RDEBUG("WARNING: WiMAX-MN-NAI was not found in the request or in the reply.");
        RDEBUG("WARNING: We cannot calculate MN-HA keys.");
    }

    /*
     *	WiMAX-IP-Technology
     */
    vp = NULL;
    if (mn_nai) vp = pairfind(request->reply->vps, WIMAX2ATTR(23));
    if (!vp) {
        RDEBUG("WARNING: WiMAX-IP-Technology not found in reply.");
        RDEBUG("WARNING: Not calculating MN-HA keys");
    }

    if (vp) switch (vp->vp_integer) {
        case 2:			/* PMIP4 */
            /*
             *	Look for WiMAX-hHA-IP-MIP4
             */
            ip = pairfind(request->reply->vps, WIMAX2ATTR(6));
            if (!ip) {
                RDEBUG("WARNING: WiMAX-hHA-IP-MIP4 not found.  Cannot calculate MN-HA-PMIP4 key");
                break;
            }

            /*
             *	MN-HA-PMIP4 =
             *	   H(MIP-RK, "PMIP4 MN HA" | HA-IPv4 | MN-NAI);
             */
            HMAC_CTX_init(&hmac);
            HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);

            HMAC_Update(&hmac, (const uint8_t *) "PMIP4 MN HA", 11);
            HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4);
            HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);
            HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);

            /*
             *	Put MN-HA-PMIP4 into WiMAX-MN-hHA-MIP4-Key
             */
            vp = pairfind(request->reply->vps, WIMAX2ATTR(10));
            if (!vp) {
                vp = radius_paircreate(request, &request->reply->vps,
                                       WIMAX2ATTR(10), PW_TYPE_OCTETS);
            }
            if (!vp) {
                RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-Key");
                break;
            }
            memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
            vp->length = rk1_len;

            /*
             *	Put MN-HA-PMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI
             */
            vp = pairfind(request->reply->vps, WIMAX2ATTR(11));
            if (!vp) {
                vp = radius_paircreate(request, &request->reply->vps,
                                       WIMAX2ATTR(11), PW_TYPE_INTEGER);
            }
            if (!vp) {
                RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-SPI");
                break;
            }
            vp->vp_integer = mip_spi + 1;
            break;

        case 3:			/* CMIP4 */
            /*
             *	Look for WiMAX-hHA-IP-MIP4
             */
            ip = pairfind(request->reply->vps, WIMAX2ATTR(6));
            if (!ip) {
                RDEBUG("WARNING: WiMAX-hHA-IP-MIP4 not found.  Cannot calculate MN-HA-CMIP4 key");
                break;
            }

            /*
             *	MN-HA-CMIP4 =
             *	   H(MIP-RK, "CMIP4 MN HA" | HA-IPv4 | MN-NAI);
             */
            HMAC_CTX_init(&hmac);
            HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);

            HMAC_Update(&hmac, (const uint8_t *) "CMIP4 MN HA", 11);
            HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4);
            HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);
            HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);

            /*
             *	Put MN-HA-CMIP4 into WiMAX-MN-hHA-MIP4-Key
             */
            vp = pairfind(request->reply->vps, WIMAX2ATTR(10));
            if (!vp) {
                vp = radius_paircreate(request, &request->reply->vps,
                                       WIMAX2ATTR(10), PW_TYPE_OCTETS);
            }
            if (!vp) {
                RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-Key");
                break;
            }
            memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
            vp->length = rk1_len;

            /*
             *	Put MN-HA-CMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI
             */
            vp = pairfind(request->reply->vps, WIMAX2ATTR(11));
            if (!vp) {
                vp = radius_paircreate(request, &request->reply->vps,
                                       WIMAX2ATTR(11), PW_TYPE_INTEGER);
            }
            if (!vp) {
                RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-SPI");
                break;
            }
            vp->vp_integer = mip_spi;
            break;

        case 4:			/* CMIP6 */
            /*
             *	Look for WiMAX-hHA-IP-MIP6
             */
            ip = pairfind(request->reply->vps, WIMAX2ATTR(7));
            if (!ip) {
                RDEBUG("WARNING: WiMAX-hHA-IP-MIP6 not found.  Cannot calculate MN-HA-CMIP6 key");
                break;
            }

            /*
             *	MN-HA-CMIP6 =
             *	   H(MIP-RK, "CMIP6 MN HA" | HA-IPv6 | MN-NAI);
             */
            HMAC_CTX_init(&hmac);
            HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);

            HMAC_Update(&hmac, (const uint8_t *) "CMIP6 MN HA", 11);
            HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipv6addr, 16);
            HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);
            HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);

            /*
             *	Put MN-HA-CMIP6 into WiMAX-MN-hHA-MIP6-Key
             */
            vp = pairfind(request->reply->vps, WIMAX2ATTR(12));
            if (!vp) {
                vp = radius_paircreate(request, &request->reply->vps,
                                       WIMAX2ATTR(12), PW_TYPE_OCTETS);
            }
            if (!vp) {
                RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP6-Key");
                break;
            }
            memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
            vp->length = rk1_len;

            /*
             *	Put MN-HA-CMIP6-SPI into WiMAX-MN-hHA-MIP6-SPI
             */
            vp = pairfind(request->reply->vps, WIMAX2ATTR(13));
            if (!vp) {
                vp = radius_paircreate(request, &request->reply->vps,
                                       WIMAX2ATTR(13), PW_TYPE_INTEGER);
            }
            if (!vp) {
                RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP6-SPI");
                break;
            }
            vp->vp_integer = mip_spi + 2;
            break;

        default:
            break;		/* do nothing */
        }

    /*
     *	Generate FA-RK, if requested.
     *
     *	FA-RK= H(MIP-RK, "FA-RK")
     */
    fa_rk = pairfind(request->reply->vps, WIMAX2ATTR(14));
    if (fa_rk && (fa_rk->length == 0)) {
        HMAC_CTX_init(&hmac);
        HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);

        HMAC_Update(&hmac, (const uint8_t *) "FA-RK", 5);

        HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);

        memcpy(fa_rk->vp_octets, &mip_rk_1[0], rk1_len);
        fa_rk->length = rk1_len;
    }

    /*
     *	Create FA-RK-SPI, which is really SPI-CMIP4, which is
     *	really MIP-SPI.  Clear?  Of course.  This is WiMAX.
     */
    if (fa_rk) {
        vp = pairfind(request->reply->vps, WIMAX2ATTR(61));
        if (!vp) {
            vp = radius_paircreate(request, &request->reply->vps,
                                   WIMAX2ATTR(61), PW_TYPE_INTEGER);
        }
        if (!vp) {
            RDEBUG("WARNING: Failed creating WiMAX-FA-RK-SPI");
        } else {
            vp->vp_integer = mip_spi;
        }
    }

    /*
     *	Generate MN-FA = H(FA-RK, "MN FA" | FA-IP | MN-NAI)
     */
    ip = pairfind(request->reply->vps, 1901);
    if (fa_rk && ip && mn_nai) {
        HMAC_CTX_init(&hmac);
        HMAC_Init_ex(&hmac, fa_rk->vp_octets, fa_rk->length,
                     EVP_sha1(), NULL);

        HMAC_Update(&hmac, (const uint8_t *) "MN FA", 5);
        HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4);
        HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);

        HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);

        vp = radius_paircreate(request, &request->reply->vps,
                               1902, PW_TYPE_OCTETS);
        if (!vp) {
            RDEBUG("WARNING: Failed creating WiMAX-MN-FA");
        } else {
            memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
            vp->length = rk1_len;
        }
    }

    /*
     *	Give additional information about requests && responses
     *
     *	WiMAX-RRQ-MN-HA-SPI
     */
    vp = pairfind(request->packet->vps, WIMAX2ATTR(20));
    if (vp) {
        RDEBUG("Client requested MN-HA key: Should use SPI to look up key from storage.");
        if (!mn_nai) {
            RDEBUG("WARNING: MN-NAI was not found!");
        }

        /*
         *	WiMAX-RRQ-HA-IP
         */
        if (!pairfind(request->packet->vps, WIMAX2ATTR(18))) {
            RDEBUG("WARNING: HA-IP was not found!");
        }


        /*
         *	WiMAX-HA-RK-Key-Requested
         */
        vp = pairfind(request->packet->vps, WIMAX2ATTR(58));
        if (vp && (vp->vp_integer == 1)) {
            RDEBUG("Client requested HA-RK: Should use IP to look it up from storage.");
        }
    }

    /*
     *	Wipe the context of all sensitive information.
     */
    HMAC_CTX_cleanup(&hmac);

    return RLM_MODULE_UPDATED;
}
/*
 *	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
}
/*
 *	member of the radius group?
 */
static int od_authorize(UNUSED void *instance, REQUEST *request)
{
	char *name = NULL;
	struct passwd *userdata = NULL;
	struct group *groupdata = NULL;
	int ismember = 0;
	RADCLIENT *rad_client = NULL;
	uuid_t uuid;
	uuid_t guid_sacl;
	uuid_t guid_nasgroup;
	int err;
	char host_ipaddr[128] = {0};
	
	if (!request || !request->username) {
		RDEBUG("OpenDirectory requires a User-Name attribute.");
		return RLM_MODULE_NOOP;
	}
	
	/* resolve SACL */
	uuid_clear(guid_sacl);
	groupdata = getgrnam(kRadiusSACLName);
	if (groupdata != NULL) {
		err = mbr_gid_to_uuid(groupdata->gr_gid, guid_sacl);
		if (err != 0) {
			radlog(L_ERR, "rlm_opendirectory: The group \"%s\" does not have a GUID.", kRadiusSACLName);
			return RLM_MODULE_FAIL;
		}		
	}
	else {
		RDEBUG("The SACL group \"%s\" does not exist on this system.", kRadiusSACLName);
	}
	
	/* resolve client access list */
	uuid_clear(guid_nasgroup);

	rad_client = request->client;
#if 0
	if (rad_client->community[0] != '\0' )
	{
		/*
		 *	The "community" can be a GUID (Globally Unique ID) or
		 *	a group name
		 */
		if (uuid_parse(rad_client->community, guid_nasgroup) != 0) {
			/* attempt to resolve the name */
			groupdata = getgrnam(rad_client->community);
			if (groupdata == NULL) {
				radlog(L_AUTH, "rlm_opendirectory: The group \"%s\" does not exist on this system.", rad_client->community);
				return RLM_MODULE_FAIL;
			}
			err = mbr_gid_to_uuid(groupdata->gr_gid, guid_nasgroup);
			if (err != 0) {
				radlog(L_AUTH, "rlm_opendirectory: The group \"%s\" does not have a GUID.", rad_client->community);
				return RLM_MODULE_FAIL;
			}
		}
	}
	else
#endif
	{
		if (rad_client == NULL) {
			RDEBUG("The client record could not be found for host %s.",
					ip_ntoh(&request->packet->src_ipaddr,
						host_ipaddr, sizeof(host_ipaddr)));
		}
		else {
			RDEBUG("The host %s does not have an access group.",
					ip_ntoh(&request->packet->src_ipaddr,
						host_ipaddr, sizeof(host_ipaddr)));
		}
	}
	
	if (uuid_is_null(guid_sacl) && uuid_is_null(guid_nasgroup)) {
		RDEBUG("no access control groups, all users allowed.");
    	if (pairfind(request->config_items, PW_AUTH_TYPE) == NULL) {
    		pairadd(&request->config_items, pairmake("Auth-Type", kAuthType, T_OP_EQ));
    		RDEBUG("Setting Auth-Type = %s", kAuthType);
		}
		return RLM_MODULE_OK;
	}

	/* resolve user */
	uuid_clear(uuid);
	name = (char *)request->username->vp_strvalue;
	rad_assert(name != NULL);

	userdata = getpwnam(name);
	if (userdata != NULL) {
		err = mbr_uid_to_uuid(userdata->pw_uid, uuid);
		if (err != 0)
			uuid_clear(uuid);
	}
	
	if (uuid_is_null(uuid)) {
		radius_pairmake(request, &request->packet->vps,
				"Module-Failure-Message", "Could not get the user's uuid", T_OP_EQ);
		return RLM_MODULE_NOTFOUND;
	}
	
	if (!uuid_is_null(guid_sacl)) {
		err = mbr_check_service_membership(uuid, kRadiusServiceName, &ismember);
		if (err != 0) {
			radius_pairmake(request, &request->packet->vps,
					"Module-Failure-Message", "Failed to check group membership", T_OP_EQ);
			return RLM_MODULE_FAIL;
		}
		
		if (ismember == 0) {
			radius_pairmake(request, &request->packet->vps,
					"Module-Failure-Message", "User is not authorized", T_OP_EQ);
			return RLM_MODULE_USERLOCK;
		}
	}
	
	if (!uuid_is_null(guid_nasgroup)) {
		err = mbr_check_membership_refresh(uuid, guid_nasgroup, &ismember);
		if (err != 0) {
			radius_pairmake(request, &request->packet->vps,
					"Module-Failure-Message", "Failed to check group membership", T_OP_EQ);
			return RLM_MODULE_FAIL;
		}
		
		if (ismember == 0) {
			radius_pairmake(request, &request->packet->vps,
					"Module-Failure-Message", "User is not authorized", T_OP_EQ);
			return RLM_MODULE_USERLOCK;
		}
	}
	
	if (pairfind(request->config_items, PW_AUTH_TYPE) == NULL) {
		pairadd(&request->config_items, pairmake("Auth-Type", kAuthType, T_OP_EQ));
		RDEBUG("Setting Auth-Type = %s", kAuthType);
	}

	return RLM_MODULE_OK;
}