summaryrefslogtreecommitdiffstats
path: root/contrib/slapd-modules/authzid/authzid.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/slapd-modules/authzid/authzid.c')
-rw-r--r--contrib/slapd-modules/authzid/authzid.c390
1 files changed, 390 insertions, 0 deletions
diff --git a/contrib/slapd-modules/authzid/authzid.c b/contrib/slapd-modules/authzid/authzid.c
new file mode 100644
index 0000000..37264bf
--- /dev/null
+++ b/contrib/slapd-modules/authzid/authzid.c
@@ -0,0 +1,390 @@
+/* authzid.c - RFC 3829 Authzid Control */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2010-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+/*
+ * RFC 3829 Authzid
+ *
+ * must be instantiated as a global overlay
+ */
+
+#include "portable.h"
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+#include "ac/string.h"
+
+typedef struct authzid_conn_t {
+ Connection *conn;
+ int refcnt;
+ char authzid_flag;
+} authzid_conn_t;
+
+static ldap_pvt_thread_mutex_t authzid_mutex;
+static Avlnode *authzid_tree;
+
+static int
+authzid_conn_cmp( const void *c1, const void *c2 )
+{
+ const authzid_conn_t *ac1 = (const authzid_conn_t *)c1;
+ const authzid_conn_t *ac2 = (const authzid_conn_t *)c2;
+
+ return SLAP_PTRCMP( ac1->conn, ac2->conn );
+}
+
+static int
+authzid_conn_dup( void *c1, void *c2 )
+{
+ authzid_conn_t *ac1 = (authzid_conn_t *)c1;
+ authzid_conn_t *ac2 = (authzid_conn_t *)c2;
+
+ if ( ac1->conn == ac2->conn ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int authzid_cid;
+static slap_overinst authzid;
+
+static authzid_conn_t *
+authzid_conn_find( Connection *c )
+{
+ authzid_conn_t *ac = NULL, tmp = { 0 };
+
+ tmp.conn = c;
+ ac = (authzid_conn_t *)ldap_avl_find( authzid_tree, (caddr_t)&tmp, authzid_conn_cmp );
+ if ( ac == NULL || ( ac != NULL && ac->refcnt != 0 ) ) {
+ ac = NULL;
+ }
+ if ( ac ) {
+ ac->refcnt++;
+ }
+
+ return ac;
+}
+
+static authzid_conn_t *
+authzid_conn_get( Connection *c )
+{
+ authzid_conn_t *ac = NULL;
+
+ ldap_pvt_thread_mutex_lock( &authzid_mutex );
+ ac = authzid_conn_find( c );
+ if ( ac && ac->refcnt ) ac = NULL;
+ if ( ac ) ac->refcnt++;
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+
+ return ac;
+}
+
+static void
+authzid_conn_release( authzid_conn_t *ac )
+{
+ ldap_pvt_thread_mutex_lock( &authzid_mutex );
+ ac->refcnt--;
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+}
+
+static int
+authzid_conn_insert( Connection *c, char flag )
+{
+ authzid_conn_t *ac;
+ int rc;
+
+ ldap_pvt_thread_mutex_lock( &authzid_mutex );
+ ac = authzid_conn_find( c );
+ if ( ac ) {
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+ return -1;
+ }
+
+ ac = ch_malloc( sizeof( authzid_conn_t ) );
+ ac->conn = c;
+ ac->refcnt = 0;
+ ac->authzid_flag = flag;
+ rc = ldap_avl_insert( &authzid_tree, (caddr_t)ac,
+ authzid_conn_cmp, authzid_conn_dup );
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+
+ return rc;
+}
+
+static int
+authzid_conn_remove( Connection *c )
+{
+ authzid_conn_t *ac, *tmp;
+
+ ldap_pvt_thread_mutex_lock( &authzid_mutex );
+ ac = authzid_conn_find( c );
+ if ( !ac ) {
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+ return -1;
+ }
+ tmp = ldap_avl_delete( &authzid_tree, (caddr_t)ac, authzid_conn_cmp );
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+
+ assert( tmp == ac );
+ ch_free( ac );
+
+ return 0;
+}
+
+static int
+authzid_response(
+ Operation *op,
+ SlapReply *rs )
+{
+ LDAPControl **ctrls;
+ struct berval edn = BER_BVNULL;
+ ber_len_t len = 0;
+ int n = 0;
+
+ assert( rs->sr_tag = LDAP_RES_BIND );
+
+ if ( rs->sr_err == LDAP_SASL_BIND_IN_PROGRESS ) {
+ authzid_conn_t *ac = op->o_controls[ authzid_cid ];
+ if ( ac ) {
+ authzid_conn_release( ac );
+ } else {
+ (void)authzid_conn_insert( op->o_conn, op->o_ctrlflag[ authzid_cid ] );
+ }
+ return SLAP_CB_CONTINUE;
+ }
+
+ (void)authzid_conn_remove( op->o_conn );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( !BER_BVISEMPTY( &op->orb_edn ) ) {
+ edn = op->orb_edn;
+
+ } else if ( !BER_BVISEMPTY( &op->o_conn->c_dn ) ) {
+ edn = op->o_conn->c_dn;
+ }
+
+ if ( !BER_BVISEMPTY( &edn ) ) {
+ ber_tag_t save_tag = op->o_tag;
+ struct berval save_dn = op->o_dn;
+ struct berval save_ndn = op->o_ndn;
+ int rc;
+
+ /* pretend it's an extop without data,
+ * so it is treated as a generic write
+ */
+ op->o_tag = LDAP_REQ_EXTENDED;
+ op->o_dn = edn;
+ op->o_ndn = edn;
+ rc = backend_check_restrictions( op, rs, NULL );
+ op->o_tag = save_tag;
+ op->o_dn = save_dn;
+ op->o_ndn = save_ndn;
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_CONFIDENTIALITY_REQUIRED;
+ return SLAP_CB_CONTINUE;
+ }
+
+ len = STRLENOF("dn:") + edn.bv_len;
+ }
+
+ /* save original controls in sc_private;
+ * will be restored by sc_cleanup
+ */
+ if ( rs->sr_ctrls != NULL ) {
+ op->o_callback->sc_private = rs->sr_ctrls;
+ for ( ; rs->sr_ctrls[n] != NULL; n++ )
+ ;
+ }
+
+ ctrls = op->o_tmpalloc( sizeof( LDAPControl * )*( n + 2 ), op->o_tmpmemctx );
+ n = 0;
+ if ( rs->sr_ctrls ) {
+ for ( ; rs->sr_ctrls[n] != NULL; n++ ) {
+ ctrls[n] = rs->sr_ctrls[n];
+ }
+ }
+
+ /* anonymous: "", otherwise "dn:<dn>" */
+ ctrls[n] = op->o_tmpalloc( sizeof( LDAPControl ) + len + 1, op->o_tmpmemctx );
+ ctrls[n]->ldctl_oid = LDAP_CONTROL_AUTHZID_RESPONSE;
+ ctrls[n]->ldctl_iscritical = 0;
+ ctrls[n]->ldctl_value.bv_len = len;
+ ctrls[n]->ldctl_value.bv_val = (char *)&ctrls[n][1];
+ if ( len ) {
+ char *ptr;
+
+ ptr = lutil_strcopy( ctrls[n]->ldctl_value.bv_val, "dn:" );
+ ptr = lutil_strncopy( ptr, edn.bv_val, edn.bv_len );
+ }
+ ctrls[n]->ldctl_value.bv_val[len] = '\0';
+ ctrls[n + 1] = NULL;
+
+ rs->sr_ctrls = ctrls;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+authzid_cleanup(
+ Operation *op,
+ SlapReply *rs )
+{
+ if ( rs->sr_ctrls ) {
+ LDAPControl *ctrl;
+
+ /* if ours, cleanup */
+ ctrl = ldap_control_find( LDAP_CONTROL_AUTHZID_RESPONSE, rs->sr_ctrls, NULL );
+ if ( ctrl ) {
+ op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
+ rs->sr_ctrls = NULL;
+ }
+
+ if ( op->o_callback->sc_private != NULL ) {
+ rs->sr_ctrls = (LDAPControl **)op->o_callback->sc_private;
+ op->o_callback->sc_private = NULL;
+ }
+ }
+
+ op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
+ op->o_callback = NULL;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+authzid_op_bind(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_callback *sc;
+
+ if ( op->o_ctrlflag[ authzid_cid ] <= SLAP_CONTROL_IGNORED ) {
+ authzid_conn_t *ac = authzid_conn_get( op->o_conn );
+ if ( ac ) {
+ op->o_ctrlflag[ authzid_cid ] = ac->authzid_flag;
+ op->o_controls[ authzid_cid] = ac;
+ }
+ }
+
+ if ( op->o_ctrlflag[ authzid_cid ] > SLAP_CONTROL_IGNORED ) {
+ sc = op->o_callback;
+ op->o_callback = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
+ op->o_callback->sc_response = authzid_response;
+ op->o_callback->sc_cleanup = authzid_cleanup;
+ op->o_callback->sc_private = NULL;
+ op->o_callback->sc_writewait = NULL;
+ op->o_callback->sc_next = sc;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+parse_authzid_ctrl(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( op->o_ctrlflag[ authzid_cid ] != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "authzid control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "authzid control value not absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* drop ongoing requests */
+ (void)authzid_conn_remove( op->o_conn );
+
+ op->o_ctrlflag[ authzid_cid ] = ctrl->ldctl_iscritical ? SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+authzid_db_init( BackendDB *be, ConfigReply *cr )
+{
+ if ( !SLAP_ISGLOBALOVERLAY( be ) ) {
+ /* do not allow slapo-ppolicy to be global by now (ITS#5858) */
+ if ( cr ) {
+ snprintf( cr->msg, sizeof(cr->msg),
+ "slapo-authzid must be global" );
+ Debug( LDAP_DEBUG_ANY, "%s\n", cr->msg );
+ }
+ return 1;
+ }
+
+ int rc;
+
+ rc = register_supported_control( LDAP_CONTROL_AUTHZID_REQUEST,
+ SLAP_CTRL_GLOBAL|SLAP_CTRL_BIND|SLAP_CTRL_HIDE, NULL,
+ parse_authzid_ctrl, &authzid_cid );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "authzid_initialize: Failed to register control '%s' (%d)\n",
+ LDAP_CONTROL_AUTHZID_REQUEST, rc );
+ return rc;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Almost pointless, by now, since this overlay needs to be global,
+ * and global overlays deletion is not supported yet.
+ */
+static int
+authzid_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+#ifdef SLAP_CONFIG_DELETE
+ overlay_unregister_control( be, LDAP_CONTROL_AUTHZID_REQUEST );
+#endif /* SLAP_CONFIG_DELETE */
+
+ unregister_supported_control( LDAP_CONTROL_AUTHZID_REQUEST );
+
+ return 0;
+}
+
+static int
+authzid_initialize( void )
+{
+ ldap_pvt_thread_mutex_init( &authzid_mutex );
+
+ authzid.on_bi.bi_type = "authzid";
+
+ authzid.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ authzid.on_bi.bi_db_init = authzid_db_init;
+ authzid.on_bi.bi_db_destroy = authzid_db_destroy;
+ authzid.on_bi.bi_op_bind = authzid_op_bind;
+
+ return overlay_register( &authzid );
+}
+
+int
+init_module( int argc, char *argv[] )
+{
+ return authzid_initialize();
+}
+