]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Rework eDirectory Universal Password lookup to be async
authorNick Porter <nick@portercomputing.co.uk>
Fri, 21 Apr 2023 10:33:10 +0000 (11:33 +0100)
committerNick Porter <nick@portercomputing.co.uk>
Fri, 21 Apr 2023 10:33:10 +0000 (11:33 +0100)
src/lib/ldap/base.h
src/lib/ldap/edir.c
src/modules/rlm_ldap/rlm_ldap.c
src/modules/rlm_ldap/rlm_ldap.h

index e3b78f0857e536e6fe9b4eae1d6e4971e7219ae0..99e7a8cb3f1b5eec4511b2481adc1694b40c8397 100644 (file)
@@ -810,7 +810,8 @@ int         fr_ldap_conn_directory_alloc_async(fr_ldap_connection_t *ldap_conn);
 /*
  *     edir.c - Edirectory integrations
  */
-int            fr_ldap_edir_get_password(LDAP *ld, char const *dn, char *password, size_t *passlen);
+int            fr_ldap_edir_get_password(rlm_rcode_t *p_result, request_t *request, char const *dn,
+                                         fr_ldap_thread_trunk_t *ttrunk, fr_dict_attr_t const *password_da);
 
 char const     *fr_ldap_edir_errstr(int code);
 
index 334468ebfa2e6f38ea3ec4c62b9d29c65494b79f..bb181263462d109c62fcc2dda9d046ca7595c202 100644 (file)
@@ -37,7 +37,6 @@ USES_APPLE_DEPRECATED_API
 #define NMAS_E_BASE    (-1600)
 
 #define NMAS_E_FRAG_FAILURE            (NMAS_E_BASE-31)        /* -1631 0xFFFFF9A1 */
-#define NMAS_E_BUFFER_OVERFLOW         (NMAS_E_BASE-33)        /* -1633 0xFFFFF99F */
 #define NMAS_E_SYSTEM_RESOURCES                (NMAS_E_BASE-34)        /* -1634 0xFFFFF99E */
 #define NMAS_E_INSUFFICIENT_MEMORY     (NMAS_E_BASE-35)        /* -1635 0xFFFFF99D */
 #define NMAS_E_NOT_SUPPORTED           (NMAS_E_BASE-36)        /* -1636 0xFFFFF99C */
@@ -52,6 +51,14 @@ USES_APPLE_DEPRECATED_API
 
 #define NMAS_LDAP_EXT_VERSION 1
 
+typedef struct {
+       fr_ldap_query_t         *query;
+       fr_ldap_thread_trunk_t  *ttrunk;
+       char const              *reqoid;
+       struct berval           *dn;
+       fr_dict_attr_t const    *password_da;
+} ldap_edir_ctx_t;
+
 /** Takes the object DN and BER encodes the data into the BER value which is used as part of the request
  *
  @verbatim
@@ -145,43 +152,65 @@ static int ber_decode_login_data(struct berval *reply_bv, int *server_version, v
        }
 
 finish:
-
        if (reply_ber) ber_free(reply_ber, 1);
 
        return err;
 }
 
-/** Attempt to retrieve the universal password from Novell eDirectory
+/** Submit LDAP extended operation to retrieve Universal Password
  *
- * @param[in] ld LDAP handle.
- * @param[in] dn of user we want to retrieve the password for.
- * @param[out] password Where to write the retrieved password.
- * @param[out] passlen Length of data written to the password buffer.
- * @return
- *     - 0 on success.
- *     - < 0 on failure.
+ * @param p_result     Result of current operation.
+ * @param priority     Unused.
+ * @param request      Current request.
+ * @param uctx         eDir lookup context.
+ * @return One of the RLM_MODULE_* values.
  */
-int fr_ldap_edir_get_password(LDAP *ld, char const *dn, char *password, size_t *passlen)
+static unlang_action_t ldap_edir_get_password_start(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request,
+                                                   void *uctx)
 {
-       int err = 0;
-       struct berval *request_bv = NULL;
-       char *reply_oid = NULL;
-       struct berval *reply_bv = NULL;
-       int server_version;
-       size_t bufsize;
-       char buffer[256];
-
-       /* Validate  parameters. */
-       if (!dn || !*dn || !passlen || !ld) {
-               return NMAS_E_INVALID_PARAMETER;
+       ldap_edir_ctx_t *edir_ctx = talloc_get_type_abort(uctx, ldap_edir_ctx_t);
+       return fr_ldap_trunk_extended(p_result, edir_ctx, &edir_ctx->query, request, edir_ctx->ttrunk,
+                                     edir_ctx->reqoid, edir_ctx->dn, NULL, NULL);
+}
+
+/** Handle results of retrieving Universal Password
+ *
+ * @param p_result     Result of current operation.
+ * @param priority     Unused.
+ * @param request      Current request.
+ * @param uctx         eDir lookup context.
+ * @return One of the RLM_MODULE_* values.
+ */
+static unlang_action_t ldap_edir_get_password_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request,
+                                                    void *uctx)
+{
+       ldap_edir_ctx_t *edir_ctx = talloc_get_type_abort(uctx, ldap_edir_ctx_t);
+       fr_ldap_query_t *query = edir_ctx->query;
+       rlm_rcode_t     rcode = RLM_MODULE_OK;
+       fr_pair_t       *vp;
+       char            *reply_oid = NULL;
+       struct berval   *reply_bv = NULL;
+       size_t          bufsize;
+       char            buffer[256];
+       int             err = 0;
+       int             server_version;
+
+       switch (query->ret){
+       case LDAP_SUCCESS:
+               break;
+
+       default:
+               REDEBUG("Failed retrieving Universal Password");
+               rcode = RLM_MODULE_FAIL;
+               goto finish;
        }
 
-       err = ber_encode_request_data(dn, &request_bv);
-       if (err) goto finish;
+       err = ldap_parse_extended_result(query->ldap_conn->handle, query->result, &reply_oid, &reply_bv, false);
 
-       /* Call the ldap_extended_operation (synchronously) */
-       err = ldap_extended_operation_s(ld, NMASLDAP_GET_PASSWORD_REQUEST, request_bv, NULL, NULL, &reply_oid, &reply_bv);
-       if (err) goto finish;
+       switch (err) {
+       case LDAP_SUCCESS:
+               break;
+       }
 
        /* Make sure there is a return OID */
        if (!reply_oid) {
@@ -214,31 +243,88 @@ int fr_ldap_edir_get_password(LDAP *ld, char const *dn, char *password, size_t *
                goto finish;
        }
 
-       if (bufsize > *passlen) {
-               err = NMAS_E_BUFFER_OVERFLOW;
-               goto finish;
-       }
+       /*
+        *      Add Password.Cleartext attribute to the request
+        */
+       MEM(pair_update_control(&vp, edir_ctx->password_da) >= 0);
+       fr_pair_value_bstrndup(vp, buffer, bufsize, true);
 
-       memcpy(password, buffer, bufsize);
-       password[bufsize] = '\0';
-       *passlen = bufsize;
+       if (RDEBUG_ENABLED3) {
+               RDEBUG3("Added eDirectory password.  control.%pP", vp);
+       } else {
+               RDEBUG2("Added eDirectory password");
+       }
 
 finish:
-       if (reply_bv) {
-               ber_bvfree(reply_bv);
+       /*
+        *      Free any libldap allocated resources.
+        */
+       if (reply_bv) ber_bvfree(reply_bv);
+       if (reply_oid) ldap_memfree(reply_oid);
+
+       if (err) {
+               REDEBUG("Failed to retrieve eDirectory password: (%i) %s", err, fr_ldap_edir_errstr(err));
+               rcode = RLM_MODULE_FAIL;
        }
 
-       /* Free the return OID string if one was returned. */
-       if (reply_oid) {
-               ldap_memfree(reply_oid);
+       RETURN_MODULE_RCODE(rcode);
+}
+
+/** Cancel an in progress Universal Password lookup
+ *
+ */
+static void ldap_edir_get_password_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
+{
+       ldap_edir_ctx_t *edir_ctx = talloc_get_type_abort(uctx, ldap_edir_ctx_t);
+
+       if (!edir_ctx->query || !edir_ctx->query->treq) return;
+
+       fr_trunk_request_signal_cancel(edir_ctx->query->treq);
+}
+
+/** Initiate retrieval of the universal password from Novell eDirectory
+ *
+ * @param[in,out] p_result     Current result code.
+ * @param[in] request          Current request.
+ * @param[in] dn               of the user whose password is to be retrieved.
+ * @param[in] ttrunk           on which to send the LDAP request.
+ * @param[in] password_da      DA to use when creating password attribute.
+ * @return
+ *     - 0 on success.
+ *     - < 0 on failure.
+ */
+int fr_ldap_edir_get_password(rlm_rcode_t *p_result, request_t *request, char const *dn,
+                             fr_ldap_thread_trunk_t *ttrunk, fr_dict_attr_t const *password_da)
+{
+       ldap_edir_ctx_t *edir_ctx;
+       int             err = 0;
+
+       if (!dn || !*dn) {
+               REDEBUG("Missing DN");
+               RETURN_MODULE_FAIL;
        }
 
-       /* Free memory allocated while building the request ber and berval. */
-       if (request_bv) {
-               ber_bvfree(request_bv);
+       MEM(edir_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), ldap_edir_ctx_t));
+
+       *edir_ctx = (ldap_edir_ctx_t) {
+               .reqoid = NMASLDAP_GET_PASSWORD_REQUEST,
+               .ttrunk = ttrunk,
+               .password_da = password_da
+       };
+
+       err = ber_encode_request_data(dn, &edir_ctx->dn);
+       if (err) {
+               REDEBUG("Failed to encode user DN: %s", fr_ldap_edir_errstr(err));
+       fail:
+               talloc_free(edir_ctx);
+               RETURN_MODULE_FAIL;
        }
 
-       return err;
+       if (unlang_function_push(request, ldap_edir_get_password_start, ldap_edir_get_password_resume,
+                                ldap_edir_get_password_cancel, ~FR_SIGNAL_CANCEL,
+                                UNLANG_SUB_FRAME, edir_ctx) < 0) goto fail;
+
+       return UNLANG_ACTION_PUSHED_CHILD;
 }
 
 char const *fr_ldap_edir_errstr(int code)
@@ -247,9 +333,6 @@ char const *fr_ldap_edir_errstr(int code)
        case NMAS_E_FRAG_FAILURE:
                return "BER manipulation failed";
 
-       case NMAS_E_BUFFER_OVERFLOW:
-               return "Insufficient buffer space to write retrieved password";
-
        case NMAS_E_SYSTEM_RESOURCES:
        case NMAS_E_INSUFFICIENT_MEMORY:
                return "Insufficient memory or system resources";
index 906d32bb1a0d3210726599869d0803265ac83d71..1e07003f90d3b50f46aee21f462522cb3f9d6793 100644 (file)
@@ -1451,52 +1451,46 @@ static unlang_action_t mod_authorize_resume(rlm_rcode_t *p_result, UNUSED int *p
                 *      Retrieve Universal Password if we use eDirectory
                 */
                if (inst->edir) {
-                       fr_pair_t       *vp;
-                       int             res = 0;
-                       char            password[256];
-                       size_t          pass_size = sizeof(password);
-                       char const      *dn = rlm_find_user_dn_cached(request);;
+                       autz_ctx->dn = rlm_find_user_dn_cached(request);;
 
                        /*
                         *      Retrive universal password
                         */
-                       res = fr_ldap_edir_get_password(handle, dn, password, &pass_size);
-                       if (res != 0) {
-                               REDEBUG("Failed to retrieve eDirectory password: (%i) %s", res, fr_ldap_edir_errstr(res));
+                       REPEAT_MOD_AUTHORIZE_RESUME;
+                       if (fr_ldap_edir_get_password(p_result, request, autz_ctx->dn, autz_ctx->ttrunk,
+                                                     attr_cleartext_password) < 0) {
                                rcode = RLM_MODULE_FAIL;
-
                                goto finish;
                        }
+                       autz_ctx->status = LDAP_AUTZ_EDIR_BIND;
+                       return UNLANG_ACTION_PUSHED_CHILD;
+               }
+               FALL_THROUGH;
 
-                       /*
-                        *      Add Password.Cleartext attribute to the request
-                        */
-                       MEM(pair_update_control(&vp, attr_cleartext_password) >= 0);
-                       fr_pair_value_bstrndup(vp, password, pass_size, true);
-
-                       if (RDEBUG_ENABLED3) {
-                               RDEBUG3("Added eDirectory password.  control.%pP", vp);
-                       } else {
-                               RDEBUG2("Added eDirectory password");
-                       }
+       case LDAP_AUTZ_EDIR_BIND:
+               if (*p_result != RLM_MODULE_OK) {
+                       rcode = *p_result;
+                       goto finish;
+               }
 
-                       if (inst->edir_autz) {
-                               fr_ldap_thread_t *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data,
+               if (inst->edir && inst->edir_autz) {
+                       fr_pair_t       *password = fr_pair_find_by_da(&request->control_pairs,
+                                                                      NULL, attr_cleartext_password);
+                       fr_ldap_thread_t *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data,
                                                                         fr_ldap_thread_t);
 
-                               RDEBUG2("Binding as user for eDirectory authorization checks");
-                               /*
-                                *      Bind as the user
-                                */
-                               if (fr_ldap_bind_auth_async(request, thread, dn, vp->vp_strvalue) < 0) {
-                                       rcode = RLM_MODULE_FAIL;
-                                       goto finish;
-                               }
+                       RDEBUG2("Binding as %s for eDirectory authorization checks", autz_ctx->dn);
+                       /*
+                        *      Bind as the user
+                        */
+                       if (fr_ldap_bind_auth_async(request, thread, autz_ctx->dn, password->vp_strvalue) < 0) {
+                               rcode = RLM_MODULE_FAIL;
+                               goto finish;
+                       }
 
-                               rcode = unlang_interpret_synchronous(unlang_interpret_event_list(request), request);
+                       rcode = unlang_interpret_synchronous(unlang_interpret_event_list(request), request);
 
-                               if (rcode != RLM_MODULE_OK) goto finish;
-                       }
+                       if (rcode != RLM_MODULE_OK) goto finish;
                }
                FALL_THROUGH;
 
index b8e0ea2ebc0a75cd93a5e6becc952dcf3e0ae30c..7f87074065c2c3141b5d62e49f66f8e1768bd883 100644 (file)
@@ -146,6 +146,7 @@ typedef enum {
        LDAP_AUTZ_GROUP,
        LDAP_AUTZ_POST_GROUP,
 #ifdef WITH_EDIR
+       LDAP_AUTZ_EDIR_BIND,
        LDAP_AUTZ_POST_EDIR,
 #endif
        LDAP_AUTZ_POST_DEFAULT_PROFILE,
@@ -168,6 +169,7 @@ typedef struct {
        struct berval           **profile_values;
        int                     value_idx;
        char                    *profile_value;
+       char const              *dn;
 } ldap_autz_ctx_t;
 
 extern HIDDEN fr_dict_attr_t const *attr_cleartext_password;