Add Novell's eDir OTP patch to branch_1_1 also (previously committed to cvs)
authorpnixon <pnixon>
Sun, 13 May 2007 22:00:28 +0000 (22:00 +0000)
committerpnixon <pnixon>
Sun, 13 May 2007 22:00:28 +0000 (22:00 +0000)
src/modules/rlm_ldap/edir_ldapext.c
src/modules/rlm_ldap/rlm_ldap.c

index 1e4a128..31eefa7 100644 (file)
 
 #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
 
 
 /* ------------------------------------------------------------------------
@@ -361,3 +368,285 @@ Cleanup:
        /* 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 */
+
index 1beb87c..3460cfa 100644 (file)
@@ -245,6 +245,17 @@ int nmasldap_get_password(
 
 #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;
@@ -523,6 +534,9 @@ ldap_instantiate(CONF_SECTION * conf, void **instance)
         * ('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){
@@ -597,6 +611,10 @@ ldap_instantiate(CONF_SECTION * conf, void **instance)
                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.");
@@ -606,7 +624,11 @@ ldap_instantiate(CONF_SECTION * conf, void **instance)
        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)
@@ -633,6 +655,9 @@ ldap_instantiate(CONF_SECTION * conf, void **instance)
                        }
                }
        }
+#ifdef NOVELL
+       inst->atts[atts_num - 1] = "sasdefaultloginsequence";
+#endif
        inst->atts[atts_num] = NULL;
 
        DEBUG("conns: %p",inst->conns);
@@ -1565,9 +1590,33 @@ ldap_authorize(void *instance, REQUEST * request)
                        }
                }                       
 #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...");
@@ -1764,28 +1813,157 @@ ldap_authenticate(void *instance, REQUEST * request)
                               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){