#define NMAS_LDAP_EXT_VERSION 1
+/* OID of LDAP extension call to perform NMAS authentication */
+#define RADAUTH_OID_NMAS_AUTH_REQUEST "2.16.840.1.113719.1.510.100.1"
+#define RADAUTH_OID_NMAS_AUTH_REPLY "2.16.840.1.113719.1.510.100.2"
+
+#define RADAUTH_LDAP_EXT_VERSION 1
+
+#define REQUEST_CHALLENGED 1
/* ------------------------------------------------------------------------
/* Return the appropriate error/success code. */
return err;
} /* end of nmasldap_get_password */
+
+/* ------------------------------------------------------------------------
+ * berEncodeAuthData
+ * ==============================
+ * RequestBer contents:
+ * targetObjectDN OCTET STRING
+ * pwd OCTET STRING
+ * NasIP OCTET STRING
+ * stete OCTET STRING
+ *
+ * Description:
+ * This function takes the request BER value and input data items
+ * and BER encodes the data into the BER value
+ *
+ * ------------------------------------------------------------------------ */
+int berEncodeAuthData(
+ struct berval **requestBV,
+ char *objectDN,
+ char *pwd,
+ char *sequence,
+ char *NasIP,
+ char *state,
+ int *auth_state)
+{
+ int err = 0, rc=0;
+ BerElement *requestBer = NULL;
+
+ char * utf8ObjPtr = NULL;
+ int utf8ObjSize = 0;
+ char * utf8PwdPtr = NULL;
+ int utf8PwdSize = 0;
+ char * utf8NasIPPtr = NULL;
+ int utf8NasIPSize = 0;
+ char * utf8StatePtr = NULL;
+ int utf8StateSize = 0;
+ char * utf8SeqPtr = NULL;
+ int utf8SeqSize = 0;
+ int state_present = 0;
+
+ utf8ObjSize = strlen(objectDN)+1;
+ utf8ObjPtr = objectDN;
+
+ utf8PwdSize = strlen(pwd);
+ utf8PwdPtr = pwd;
+
+ utf8SeqSize = strlen(sequence)+1;
+ utf8SeqPtr = sequence;
+
+ utf8NasIPSize = strlen(NasIP)+1;
+ utf8NasIPPtr = NasIP;
+
+ /* Allocate a BerElement for the request parameters.*/
+ if((requestBer = ber_alloc()) == NULL)
+ {
+ err = NMAS_E_FRAG_FAILURE;
+ goto Cleanup;
+ }
+
+ /* BER encode the NMAS Version, the objectDN, and the password */
+ rc = ber_printf(requestBer, "{ioooo", RADAUTH_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize, utf8PwdPtr, utf8PwdSize, utf8SeqPtr, utf8SeqSize, utf8NasIPPtr, utf8NasIPSize);
+
+ if( *auth_state == -2)
+ {
+ utf8StateSize = strlen(state)+1;
+ utf8StatePtr = state;
+ state_present = 1;
+ rc = ber_printf(requestBer, "io}", state_present, utf8StatePtr, utf8StateSize);
+ }
+ else
+ {
+ rc = ber_printf(requestBer, "i}", state_present);
+ }
+
+ if (rc < 0)
+ {
+ err = NMAS_E_FRAG_FAILURE;
+ goto Cleanup;
+ }
+ else
+ {
+ err = 0;
+ }
+ /*
+ * Convert the BER we just built to a berval that we'll send with the extended request.
+ */
+ if(ber_flatten(requestBer, requestBV) == -1)
+ {
+ err = NMAS_E_FRAG_FAILURE;
+ goto Cleanup;
+ }
+
+Cleanup:
+
+ if(requestBer)
+ {
+ ber_free(requestBer, 1);
+ }
+
+ return err;
+} /* End of berEncodeAuthData */
+
+/* ------------------------------------------------------------------------
+ * berDecodeAuthData()
+ * ==============================
+ * ResponseBer contents:
+ * serverVersion INTEGER
+ * auth_state INTEGER
+ * challenge_data OCTET STRING
+ *
+ * Description:
+ * This function takes the reply BER Value and decodes the
+ * server version and return code and if a non null retData
+ * buffer was supplied, tries to decode the the return data and length
+ *
+ * ------------------------------------------------------------------------ */
+int berDecodeAuthData(
+ struct berval *replyBV,
+ int *errCode,
+ size_t *retDataLen,
+ char *retData,
+ int *auth_state )
+{
+ int rc=0, err = 0;
+ BerElement *replyBer = NULL;
+ struct berval challenge = {0};
+
+ if((replyBer = ber_init(replyBV)) == NULL)
+ {
+ err = NMAS_E_SYSTEM_RESOURCES; // fix err code
+ goto Cleanup;
+ }
+ if( (rc = ber_scanf(replyBer, "{ii", errCode, auth_state)) != -1)
+ {
+ if ( *auth_state != REQUEST_CHALLENGED )
+ {
+ if( (rc = ber_scanf(replyBer, "}")) != -1)
+ return err;
+ }
+ else
+ {
+ if( (rc = ber_scanf(replyBer, "o}", &challenge)) != -1)
+ {
+ if (*retDataLen >= challenge.bv_len)
+ {
+ memcpy(retData, challenge.bv_val, challenge.bv_len);
+ }
+ *retDataLen = challenge.bv_len;
+ }
+ }
+ }
+
+Cleanup:
+ if(replyBer)
+ {
+ ber_free(replyBer, 1);
+ }
+
+ return err;
+}/* End of berDecodeLoginData */
+
+/* -----------------------------------------------------------------------
+ * radLdapXtnNMASAuth()
+ * ==============================
+ *
+ * Description:
+ * This API attempts to perform NMAS authentication.
+ *
+ * ------------------------------------------------------------------------ */
+int radLdapXtnNMASAuth(
+ LDAP *ld,
+ char *objectDN,
+ char *pwd,
+ char *sequence,
+ char *NasIPaddr,
+ size_t *statesize,
+ char *state,
+ int *auth_state
+)
+{
+ int err = 0;
+
+ struct berval *requestBV = NULL;
+ char *replyOID = NULL;
+ struct berval *replyBV = NULL;
+ int errCode;
+ char *challenge;
+ size_t challengesize;
+
+ challengesize = *statesize;
+ challenge = (char *)malloc(challengesize+2);
+ if(challenge == NULL)
+ {
+ return NMAS_E_INSUFFICIENT_MEMORY;
+ }
+
+ /* Validate char parameters. */
+ if(objectDN == NULL || (strlen(objectDN) == 0) || statesize == NULL || NasIPaddr == NULL || ld == NULL)
+ {
+ return NMAS_E_INVALID_PARAMETER;
+ }
+
+ err = berEncodeAuthData(&requestBV, objectDN, pwd, sequence, NasIPaddr, state, auth_state);
+
+ if(err)
+ {
+ goto Cleanup;
+ }
+
+ /* Call the ldap_extended_operation (synchronously) */
+ if((err = ldap_extended_operation_s(ld, RADAUTH_OID_NMAS_AUTH_REQUEST, requestBV, NULL, NULL, &replyOID, &replyBV))!=0)
+ {
+ goto Cleanup;
+ }
+ /* Make sure there is a return OID */
+ if(!replyOID)
+ {
+ err = NMAS_E_NOT_SUPPORTED; // change error values
+ goto Cleanup;
+ }
+
+ /* Is this what we were expecting to get back. */
+ if(strcmp(replyOID, RADAUTH_OID_NMAS_AUTH_REPLY))
+ {
+ err = NMAS_E_NOT_SUPPORTED; // change return value
+ goto Cleanup;
+ }
+
+ /* Do we have a good returned berval? */
+ if(!replyBV)
+ {
+ /*
+ * No; returned berval means we experienced a rather drastic error.
+ * Return operations error.
+ */
+ err = NMAS_E_SYSTEM_RESOURCES; //change return value
+ goto Cleanup;
+ }
+ err = berDecodeAuthData(replyBV, &errCode, &challengesize, challenge, auth_state);
+
+/* errCode return error in case of AUTH-REJECT */
+ if (!err && challengesize!= 0)
+ {
+ if (*statesize >= challengesize+1 && challenge != NULL)
+ {
+ memcpy(state, challenge, challengesize);
+ state[challengesize] = 0; /* add null termination */
+ }
+ *statesize = challengesize; /* does not include null termination */
+ }
+
+Cleanup:
+ /* Free memory allocated for challenge */
+ if(challenge)
+ {
+ free(challenge);
+ }
+
+ if(replyBV)
+ {
+ ber_bvfree(replyBV);
+ }
+
+ /* Free the return OID string if one was returned. */
+ if(replyOID)
+ {
+ ldap_memfree(replyOID);
+ }
+
+ /* Free memory allocated while building the request ber and berval. */
+ if(requestBV)
+ {
+ ber_bvfree(requestBV);
+ }
+
+#ifdef NOT_N_PLAT_NLM
+ SetThreadGroupID(currentThreadGroupID);
+#endif
+
+ /* Return the appropriate error/success code. */
+ return err;
+}/* End of radLdapXtnNMASAuth */
+
#endif
+#ifdef NOVELL
+
+#define REQUEST_ACCEPTED 0
+#define REQUEST_CHALLENGED 1
+#define REQUEST_REJECTED 2
+#define MAX_CHALLENGE_LEN 128
+
+int radLdapXtnNMASAuth( LDAP *, char *, char *, char *, char *, size_t *, char *, int * );
+
+#endif
+
/* linked list of mappings between RADIUS attributes and LDAP attributes */
struct TLDAP_RADIUS {
char* attr;
* ('eDir-APC', '3') in config items list => eDirectory APC has been completed
*/
dict_addattr("eDir-APC", 0, PW_TYPE_STRING, -1, flags);
+
+dict_addattr("eDir-Auth-Option", 0, PW_TYPE_STRING, -1, flags);
+
#endif
if (inst->num_conns <= 0){
atts_num++;
if (inst->access_attr)
atts_num++;
+#ifdef NOVELL
+ atts_num++; /* eDirectory Authentication Option attribute */
+#endif
+
inst->atts = (char **)malloc(sizeof(char *)*(atts_num + 1));
if (inst->atts == NULL){
radlog(L_ERR, "rlm_ldap: Could not allocate memory. Aborting.");
pair = inst->check_item_map;
if (pair == NULL)
pair = inst->reply_item_map;
+#ifdef NOVELL
+ for(i=0;i<atts_num - 1;i++){
+#else
for(i=0;i<atts_num;i++){
+#endif
if (i <= check_map_num ){
inst->atts[i] = pair->attr;
if (i == check_map_num)
}
}
}
+#ifdef NOVELL
+ inst->atts[atts_num - 1] = "sasdefaultloginsequence";
+#endif
inst->atts[atts_num] = NULL;
DEBUG("conns: %p",inst->conns);
}
}
#endif
-
}
+#ifdef NOVELL
+ {
+ VALUE_PAIR *vp_auth_opt;
+ DICT_ATTR *dattr;
+ char **auth_option;
+ int auth_opt_attr;
+
+ dattr = dict_attrbyname("eDir-Auth-Option");
+ auth_opt_attr = dattr->attr;
+ if(pairfind(*check_pairs, auth_opt_attr) == NULL){
+ if ((auth_option = ldap_get_values(conn->ld, msg, "sasDefaultLoginSequence")) != NULL) {
+ if ((vp_auth_opt = paircreate(auth_opt_attr, PW_TYPE_STRING)) == NULL){
+ radlog(L_ERR, "rlm_ldap: Could not allocate memory. Aborting.");
+ ldap_msgfree(result);
+ ldap_release_conn(conn_id, inst->conns);
+ }
+ strcpy(vp_auth_opt->strvalue, auth_option[0]);
+ vp_auth_opt->length = strlen(auth_option[0]);
+ pairadd(&request->config_items, vp_auth_opt);
+ }else{
+ DEBUG("rlm_ldap: No default NMAS login sequence");
+ }
+ }
+ }
+#endif
DEBUG("rlm_ldap: looking for check items in directory...");
1, &res, NULL);
#else
- ld_user = ldap_connect(instance, user_dn, request->password->strvalue,
- 1, &res, &err);
-
- if(err != NULL){
- /* 'err' contains the LDAP connection error description */
- DEBUG("rlm_ldap: %s", err);
- pairadd(&request->reply->vps, pairmake("Reply-Message", err, T_OP_EQ));
- ldap_memfree((void *)err);
- }
-
/* Don't perform eDirectory APC again after attempting to bind here. */
{
int apc_attr;
DICT_ATTR *dattr;
VALUE_PAIR *vp_apc;
+ VALUE_PAIR *vp_auth_opt, *vp_state;
+ int auth_opt_attr;
+ char seq[256];
+ char host_ipaddr[32];
+ LDAP_CONN *conn1;
+ int auth_state = -1;
+ char *challenge = NULL;
+ int challenge_len = MAX_CHALLENGE_LEN;
+ char *state = NULL;
dattr = dict_attrbyname("eDir-APC");
apc_attr = dattr->attr;
vp_apc = pairfind(request->config_items, apc_attr);
if(vp_apc && vp_apc->strvalue[0] == '2')
vp_apc->strvalue[0] = '3';
+
+ res = 0;
+
+ dattr = dict_attrbyname("eDir-Auth-Option");
+ auth_opt_attr = dattr->attr;
+
+ vp_auth_opt = pairfind(request->config_items, auth_opt_attr);
+
+ if(vp_auth_opt )
+ {
+ DEBUG("rlm_ldap: ldap auth option = %s", vp_auth_opt->strvalue);
+ strncpy(seq, vp_auth_opt->strvalue, vp_auth_opt->length);
+ seq[vp_auth_opt->length] = '\0';
+ if( strcmp(seq, "<No Default>") ){
+
+ /* Get the client IP address to check for packet validity */
+ inet_ntop(AF_INET, &request->packet->src_ipaddr, host_ipaddr, sizeof(host_ipaddr));
+
+ /* challenge variable is used to receive the challenge from the
+ * Token method (if any) and also to send the state attribute
+ * in case the request packet is a reply to a challenge
+ */
+ challenge = rad_malloc(MAX_CHALLENGE_LEN);
+
+ /* If state attribute present in request it is a reply to challenge. */
+ if((vp_state = pairfind(request->packet->vps, PW_STATE))!= NULL ){
+ DEBUG("rlm_ldap: Response to Access-Challenge");
+ strncpy(challenge, vp_state->strvalue, sizeof(challenge));
+ challenge_len = vp_state->length;
+ challenge[challenge_len] = 0;
+ auth_state = -2;
+ }
+
+ if ((conn_id = ldap_get_conn(inst->conns, &conn1, inst)) == -1){
+ radlog(L_ERR, "rlm_ldap: All ldap connections are in use");
+ res = RLM_MODULE_FAIL;
+ }
+
+ if(!conn1){
+ radlog(L_ERR, "rlm_ldap: NULL connection handle passed");
+ return RLM_MODULE_FAIL;
+ }
+
+ if (conn1->failed_conns > MAX_FAILED_CONNS_START){
+ conn1->failed_conns++;
+ if (conn1->failed_conns >= MAX_FAILED_CONNS_END){
+ conn1->failed_conns = MAX_FAILED_CONNS_RESTART;
+ conn1->bound = 0;
+ }
+ }
+retry:
+ if (!conn1->bound || conn1->ld == NULL) {
+ DEBUG2("rlm_ldap: attempting LDAP reconnection");
+ if (conn1->ld){
+ DEBUG2("rlm_ldap: closing existing LDAP connection");
+ ldap_unbind_s(conn1->ld);
+ }
+ if ((conn1->ld = ldap_connect(instance, inst->login,inst->password, 0, &res, NULL)) == NULL) {
+ radlog(L_ERR, "rlm_ldap: (re)connection attempt failed");
+ conn1->failed_conns++;
+ return (RLM_MODULE_FAIL);
+ }
+ conn1->bound = 1;
+ conn1->failed_conns = 0;
+ }
+ DEBUG("rlm_ldap: Performing NMAS Authentication for user: %s, seq: %s \n", user_dn,seq);
+
+ res = radLdapXtnNMASAuth(conn1->ld, user_dn, request->password->strvalue, seq, host_ipaddr, &challenge_len, challenge, &auth_state );
+
+ switch(res){
+ case LDAP_SUCCESS:
+ ldap_release_conn(conn_id,inst->conns);
+ if ( auth_state == -1)
+ res = RLM_MODULE_FAIL;
+ if ( auth_state != REQUEST_CHALLENGED){
+ if (auth_state == REQUEST_ACCEPTED){
+ DEBUG("rlm_ldap: user %s authenticated succesfully",request->username->strvalue);
+ res = RLM_MODULE_OK;
+ }else if(auth_state == REQUEST_REJECTED){
+ DEBUG("rlm_ldap: user %s authentication failed",request->username->strvalue);
+ res = RLM_MODULE_REJECT;
+ }
+ }else{
+ /* Request challenged. Generate Reply-Message attribute with challenge data */
+ pairadd(&request->reply->vps,pairmake("Reply-Message", challenge, T_OP_EQ));
+ /* Generate state attribute */
+ state = rad_malloc(MAX_CHALLENGE_LEN);
+ (void) sprintf(state, "%s%s", challenge, challenge);
+ vp_state = paircreate(PW_STATE, PW_TYPE_OCTETS);
+ memcpy(vp_state->strvalue, state, strlen(state));
+ vp_state->length = strlen(state);
+ pairadd(&request->reply->vps, vp_state);
+ free(state);
+ /* Mark the packet as a Acceess-Challenge Packet */
+ request->reply->code = PW_ACCESS_CHALLENGE;
+ DEBUG("rlm_ldap: Sending Access-Challenge.");
+ res = RLM_MODULE_HANDLED;
+ }
+ if(challenge)
+ free(challenge);
+ return res;
+ case LDAP_SERVER_DOWN:
+ radlog(L_ERR, "rlm_ldap: nmas authentication failed: LDAP connection lost."); conn->failed_conns++;
+ if (conn->failed_conns <= MAX_FAILED_CONNS_START){
+ radlog(L_INFO, "rlm_ldap: Attempting reconnect");
+ conn->bound = 0;
+ goto retry;
+ }
+ if(challenge)
+ free(challenge);
+ return RLM_MODULE_FAIL;
+ default:
+ ldap_release_conn(conn_id,inst->conns);
+ if(challenge)
+ free(challenge);
+ return RLM_MODULE_FAIL;
+ }
+ }
+ }
}
+
+ ld_user = ldap_connect(instance, user_dn, request->password->strvalue,
+ 1, &res, &err);
+
+ if(err != NULL){
+ /* 'err' contains the LDAP connection error description */
+ DEBUG("rlm_ldap: %s", err);
+ pairadd(&request->reply->vps, pairmake("Reply-Message", err, T_OP_EQ));
+ ldap_memfree((void *)err);
+ }
+
#endif
if (ld_user == NULL){