Multiple calls to ber_printf seem to work better. Closes #106
[freeradius.git] / src / modules / rlm_ldap / edir_ldapext.c
index 1e4a128..0c48ee8 100644 (file)
@@ -1,8 +1,8 @@
-/* 
+/*
  * Copyright (C) 2002-2004 Novell, Inc.
  *
  * edir_ldapext.c  LDAP extension for reading eDirectory universal password
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as published
  * by the Free Software Foundation.
  *
  * To contact Novell about this file by physical or electronic mail, you may
  * find current contact  information at www.novell.com.
- */ 
+ *
+ * Copyright 2006 The FreeRADIUS Server Project.
+ */
+
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
 
 #include <ldap.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <strings.h>
+
 /* NMAS error codes */
 #define NMAS_E_BASE                       (-1600)
 
 
 #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
 
 
 /* ------------------------------------------------------------------------
@@ -125,8 +139,8 @@ int berEncodePasswordData(
                err = 0;
        }
 
-       /* 
-        * Convert the BER we just built to a berval that we'll send with the extended request. 
+       /*
+        * Convert the BER we just built to a berval that we'll send with the extended request.
         */
        if(ber_flatten(requestBer, requestBV) == LBER_ERROR)
        {
@@ -192,7 +206,7 @@ int berDecodeLoginData(
                                memcpy(retData, retOctStr, retOctStrLen);
                        }
                        else if (!err)
-                       {       
+                       {
                                err = NMAS_E_BUFFER_OVERFLOW;
                        }
 
@@ -303,7 +317,7 @@ int nmasldap_get_password(
        /* Do we have a good returned berval? */
        if(!replyBV)
        {
-               /* 
+               /*
                 * No; returned berval means we experienced a rather drastic error.
                 * Return operations error.
                 */
@@ -361,3 +375,289 @@ 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, "{i", RADAUTH_LDAP_EXT_VERSION);
+        rc = ber_printf(requestBer, "o", utf8ObjPtr, utf8ObjSize);
+        rc = ber_printf(requestBer, "o", utf8PwdPtr, utf8PwdSize);
+        rc = ber_printf(requestBer, "o", utf8SeqPtr, utf8SeqSize);
+        rc = ber_printf(requestBer, "o", 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 */
+