]> git.sur5r.net Git - openldap/commitdiff
Implementation of SASL authorization.
authorMark Adamson <adamson@openldap.org>
Thu, 21 Sep 2000 17:32:54 +0000 (17:32 +0000)
committerMark Adamson <adamson@openldap.org>
Thu, 21 Sep 2000 17:32:54 +0000 (17:32 +0000)
12 files changed:
doc/man/man5/slapd.conf.5
libraries/libldap/open.c
servers/slapd/Makefile.in
servers/slapd/bind.c
servers/slapd/config.c
servers/slapd/connection.c
servers/slapd/daemon.c
servers/slapd/proto-slap.h
servers/slapd/sasl.c
servers/slapd/saslauthz.c [new file with mode: 0644]
servers/slapd/slap.h
servers/slapd/tools/mimic.c

index 8d92c3145255b188fd5d545ce75cb285026eefe8..bd36aca34d85557cccc6a29e94603102fd1cb89e 100644 (file)
@@ -335,6 +335,63 @@ The
 property specifies the maximum security layer receive buffer
 size allowed.  0 disables security layers.  The default is 65536.
 .TP
+.B saslregexp <match> <replace>
+Used by the SASL authorization mechanism to convert a SASL authenticated 
+username to an LDAP DN. When an authorization request is received, the SASL 
+.B USERNAME, REALM, 
+and
+.B MECHANISM
+are taken, when available, and combined into a SASL name of the 
+form
+.RS
+.RS
+.TP
+.B uid=<UID>[+realm=<REALM>][,cn=<MECH>],cn=AUTHZ
+
+.RE
+This SASL name is then compared against the
+.B match
+regular expression, and if the match is successful, the SASL name is
+replaced with the
+.B replace
+string. If there are wildcard strings in the 
+.B match
+regular expression that are enclosed in parenthesis, e.g. 
+.RS
+.RS
+.TP
+.B uid=(.*)+realm=.*
+
+.RE
+.RE
+then the portion of the SASL name that matched the wildcard will be stored
+in the numbered placeholder variable $1. If there are other wildcard strings
+in parenthesis, the matching strings will be in $2, $3, etc. up to $9. The 
+placeholders can then be used in the 
+.B replace
+string, e.g. 
+.RS
+.RS
+.TP
+.B cn=$1,ou=Accounts,dc=$2,dc=$4. 
+
+.RE
+.RE
+The replaced SASL name can be either a DN or an LDAP URI. If the latter, the slapd
+server will use the URI to search its own database, and if the search returns 
+exactly one entry, the SASL name is replaced by the DN of that entry.
+Multiple 
+.B saslregexp 
+options can be given in the configuration file to allow for multiple matching 
+and replacement patterns. The matching patterns are checked in the order they 
+appear in the file, stopping at the first successful match.
+.LP
+.B Caution:
+Because the plus sign + is a character recognized by the regular expression engine,
+and it will appear in SASL names that include a REALM, be careful to escape the
+plus sign with a double backslash \\\\+ to remove the character's special meaning.
+.RE
+.TP
 .B schemacheck { on | off }
 Turn schema checking on or off. The default is on.
 .TP
index b62a2ffa9fb264c0d8420370444948271c775172..1cdba63ecd9fa9ea06b788e431b958e5f98880a4 100644 (file)
@@ -336,3 +336,52 @@ ldap_int_open_connection(
 
        return( 0 );
 }
+
+
+int ldap_open_internal_connection( LDAP **ldp, ber_socket_t *fdp )
+{
+       int rc;
+       LDAPConn *c;
+       LDAPRequest *lr;
+
+       rc = ldap_create( ldp );
+       if( rc != LDAP_SUCCESS ) {
+               *ldp = NULL;
+               return( rc );
+       }
+
+       /* Make it appear that a search request, msgid 0, was sent */
+       lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ));
+       if( lr == NULL ) {
+               ldap_unbind( *ldp );
+               *ldp = NULL;
+               return( LDAP_NO_MEMORY );
+       }
+       memset(lr, 0, sizeof( LDAPRequest ));
+       lr->lr_msgid = 0;
+       lr->lr_status = LDAP_REQST_INPROGRESS;
+       lr->lr_res_errno = LDAP_SUCCESS;
+       (*ldp)->ld_requests = lr;
+
+       /* Attach the passed socket as the *LDAP's connection */
+       c = ldap_new_connection( *ldp, NULL, 1, 0, NULL);
+       if( c == NULL ) {
+               ldap_unbind( *ldp );
+               *ldp = NULL;
+               return( LDAP_NO_MEMORY );
+       }
+       ber_sockbuf_ctrl( c->lconn_sb, LBER_SB_OPT_SET_FD, fdp );
+       ber_sockbuf_add_io( c->lconn_sb, &ber_sockbuf_io_tcp,
+         LBER_SBIOD_LEVEL_PROVIDER, NULL );
+       (*ldp)->ld_defconn = c;
+
+       /* Add the connection to the *LDAP's select pool */
+       ldap_mark_select_read( *ldp, c->lconn_sb );
+       ldap_mark_select_write( *ldp, c->lconn_sb );
+
+       /* Make this connection an LDAP V3 protocol connection */
+       rc = LDAP_VERSION3;
+       ldap_set_option( *ldp, LDAP_OPT_PROTOCOL_VERSION, &rc );
+
+       return( LDAP_SUCCESS );
+}
index eb54e1542e97839ddd67d274e6e9aaec6089c3a2..c2765718acd3c4b566b0db57f807891fa467aaf0 100644 (file)
@@ -16,7 +16,7 @@ SRCS  = main.c daemon.c connection.c search.c filter.c add.c charray.c \
                phonetic.c acl.c str2filter.c aclparse.c init.c user.c \
                repl.c lock.c controls.c extended.c kerberos.c passwd.c \
                schema.c schema_check.c schema_init.c schema_prep.c \
-               schemaparse.c ad.c at.c mr.c syntax.c oc.c \
+               schemaparse.c ad.c at.c mr.c syntax.c oc.c saslauthz.c \
                monitor.c configinfo.c starttls.c index.c sets.c\
                root_dse.c sasl.c module.c suffixalias.c $(@PLAT@_SRCS)
 
@@ -27,7 +27,7 @@ OBJS  = main.o daemon.o connection.o search.o filter.o add.o charray.o \
                phonetic.o acl.o str2filter.o aclparse.o init.o user.o \
                repl.o lock.o controls.o extended.o kerberos.o passwd.o \
                schema.o schema_check.o schema_init.o schema_prep.o \
-               schemaparse.o ad.o at.o mr.o syntax.o oc.o \
+               schemaparse.o ad.o at.o mr.o syntax.o oc.o saslauthz.o \
                monitor.o configinfo.o starttls.o index.o sets.o\
                root_dse.o sasl.o module.o suffixalias.o $(@PLAT@_OBJS)
 
index 4d96e0431efc685f3020e22762a0880268f10b28..e88001e4d2011053a19a2f1e0dd16c890783874a 100644 (file)
@@ -37,7 +37,6 @@ do_bind(
        ber_int_t               version;
        ber_tag_t method;
        char            *mech;
-       char            *saslmech;
        char            *dn;
        char *ndn;
        ber_tag_t       tag;
@@ -204,49 +203,42 @@ do_bind(
                }
 
                ldap_pvt_thread_mutex_lock( &conn->c_mutex );
-
-               if ( conn->c_sasl_bind_mech != NULL ) {
-                       /* SASL bind is in progress */
-                       saslmech = NULL;
-
+               if ( conn->c_sasl_bind_in_progress ) {
                        if((strcmp(conn->c_sasl_bind_mech, mech) != 0)) {
-                               /* mechanism changed */
+                               /* mechanism changed between bind steps */
                                slap_sasl_reset(conn);
                        }
-
-                       free( conn->c_sasl_bind_mech );
-                       conn->c_sasl_bind_mech = NULL;
-
-#ifdef LDAP_DEBUG
                } else {
-                       /* SASL bind is NOT in progress */
-                       saslmech = mech;
-                       assert( conn->c_sasl_bind_mech == NULL );
-#endif
+                       conn->c_sasl_bind_mech = mech;
+                       mech = NULL;
                }
-
                ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
 
                edn = NULL;
-               rc = slap_sasl_bind( conn, op, dn, ndn, saslmech, &cred,
-                       &edn, &ssf );
+               rc = slap_sasl_bind( conn, op, dn, ndn, &cred, &edn, &ssf );
 
+               ldap_pvt_thread_mutex_lock( &conn->c_mutex );
                if( rc == LDAP_SUCCESS ) {
-                       ldap_pvt_thread_mutex_lock( &conn->c_mutex );
                        conn->c_dn = edn;
-                       conn->c_authmech = mech;
+                       conn->c_authmech = conn->c_sasl_bind_mech;
+                       conn->c_sasl_bind_mech = NULL;
+                       conn->c_sasl_bind_in_progress = 0;
                        if( ssf ) conn->c_sasl_layers++;
                        conn->c_sasl_ssf = ssf;
                        if( ssf > conn->c_ssf ) {
                                conn->c_ssf = ssf;
                        }
-                       ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
-
                } else if ( rc == LDAP_SASL_BIND_IN_PROGRESS ) {
-                       conn->c_sasl_bind_mech = mech;
-               }
+                       conn->c_sasl_bind_in_progress = 1;
 
-               mech = NULL;
+               } else {
+                       if ( conn->c_sasl_bind_mech ) {
+                               free( conn->c_sasl_bind_mech );
+                               conn->c_sasl_bind_mech = NULL;
+                       }
+                       conn->c_sasl_bind_in_progress = 0;
+               }
+               ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
 
                goto cleanup;
 
@@ -255,14 +247,10 @@ do_bind(
                ldap_pvt_thread_mutex_lock( &conn->c_mutex );
 
                if ( conn->c_sasl_bind_mech != NULL ) {
-                       assert( conn->c_sasl_bind_in_progress );
-
                        free(conn->c_sasl_bind_mech);
                        conn->c_sasl_bind_mech = NULL;
-
-               } else {
-                       assert( !conn->c_sasl_bind_in_progress );
                }
+               conn->c_sasl_bind_in_progress = 0;
 
                slap_sasl_reset( conn );
                ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
@@ -416,16 +404,6 @@ do_bind(
        }
 
 cleanup:
-       if( rc != LDAP_SASL_BIND_IN_PROGRESS ) {
-               ldap_pvt_thread_mutex_lock( &conn->c_mutex );
-
-               /* dispose of mech */
-               free( conn->c_sasl_bind_mech );
-               conn->c_sasl_bind_mech = NULL;
-
-               ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
-       }
-
        if( dn != NULL ) {
                free( dn );
        }
index 6a2a9984214927c7c3b1ff21ba28cb4eb2db19e9..ff6e7162c1af0a04e893f8077648a4f80ef5d564 100644 (file)
@@ -44,6 +44,9 @@ char          *default_search_nbase = NULL;
 char   *slapd_pid_file  = NULL;
 char   *slapd_args_file = NULL;
 
+int nSaslRegexp = 0;
+SaslRegexp_t *SaslRegexp = NULL;
+
 static char    *fp_getline(FILE *fp, int *lineno);
 static void    fp_getline_init(int *lineno);
 static int     fp_parse_line(char *line, int *argcp, char **argv);
@@ -1108,6 +1111,17 @@ read_config( const char *fname )
 
 #endif
 
+               } else if ( !strcasecmp( cargv[0], "saslregexp" ) ) {
+                       if ( cargc != 3 ) {
+                               Debug( LDAP_DEBUG_ANY, 
+                               "%s: line %d: need 2 args in \"saslregexp <match> <replace>\"\n",
+                                   fname, lineno, 0 );
+                               return( 1 );
+                       }
+                       rc = slap_sasl_regexp_config( cargv[1], cargv[2] );
+                       if ( rc )
+                               return rc;
+
                /* pass anything else to the current backend info/db config routine */
                } else {
                        if ( bi != NULL ) {
index d03a7c9a790b61a895d434a5affb3560ffb34149..4a77ce00e439f120c40e1df73ae182f96929abf4 100644 (file)
@@ -16,7 +16,6 @@
 #include <ac/time.h>
 
 #include "ldap_pvt.h"
-
 #include "slap.h"
 
 /* protected by connections_mutex */
@@ -1258,3 +1257,62 @@ int connection_write(ber_socket_t s)
        ldap_pvt_thread_mutex_unlock( &connections_mutex );
        return 0;
 }
+
+
+/*
+ * Create client side and server side connection structures, connected to
+ * one another, for the front end to use for searches on arbitrary back ends.
+ */
+
+int connection_internal_open( Connection **conn, LDAP **ldp, char *id )
+{
+       int rc;
+       ber_socket_t fd[2] = {-1,-1};
+       Operation *op;
+
+
+       *conn=NULL;
+       *ldp=NULL;
+
+       rc = pipe( fd );
+       if( rc == -1 )
+               return( LDAP_OPERATIONS_ERROR );
+
+       rc = connection_init( fd[1], "INT", "localhost", 
+      "localhost:0", "localhost:00", 0, 256, id );
+       if( rc < 0 ) {
+               close( fd[0] );
+               close( fd[1] );
+               return( LDAP_OPERATIONS_ERROR );
+       }
+       slapd_add_internal( fd[1] );
+
+       /* A search operation, number 0 */
+       op = slap_op_alloc( NULL, 0, LDAP_REQ_SEARCH, 0);
+       op->o_ndn = ch_strdup( id );
+       op->o_protocol = LDAP_VERSION3;
+
+       (*conn) = connection_get( fd[1] );
+       (*conn)->c_ops = op;
+    (*conn)->c_conn_state = SLAP_C_ACTIVE;
+
+
+       /* Create the client side of the connection */
+       rc = ldap_open_internal_connection( ldp, &(fd[0]) );
+       if( rc != LDAP_SUCCESS ) {
+               close( fd[0] );
+               return( LDAP_OPERATIONS_ERROR );
+       }
+
+       /* The connection_get() will have locked the connection's mutex */
+       pthread_mutex_unlock(  &((*conn)->c_mutex) );
+
+       return( LDAP_SUCCESS );
+}
+
+
+void connection_internal_close( Connection *conn )
+{
+       connection_closing( conn );
+       connection_close( conn );
+}
index bca5aed17b2b2f358647836dfacc36c3e1ec8a5d..14f3f168d931948ecf21c8c7084bfa93962d0b8e 100644 (file)
@@ -1304,3 +1304,8 @@ slap_sig_wake( int sig )
        /* reinstall self */
        (void) SIGNAL_REINSTALL( sig, slap_sig_wake );
 }
+
+
+void slapd_add_internal(ber_socket_t s) {
+       slapd_add(s);
+}
index 955a05bb75f353b1c55f8a69168beea41ed0ea0a..ca1a3e49b428e5bcd2bf20b1f43a8ea1cbc3955f 100644 (file)
@@ -542,7 +542,7 @@ LDAP_SLAPD_F (int) slap_sasl_close( Connection *c );
 LDAP_SLAPD_F (int) slap_sasl_bind LDAP_P((
        Connection *conn, Operation *op, 
        const char *dn, const char *ndn,
-       const char *mech, struct berval *cred,
+       struct berval *cred,
        char **edn, slap_ssf_t *ssf ));
 
 /* oc.c */
index 10dc15c097beb785dc7c4c9f6ae1faac139a8286..af9687f753a46597858140d26e7129fd606af435 100644 (file)
@@ -74,6 +74,8 @@ slap_sasl_authorize(
        const char **user,
        const char **errstr)
 {
+       char *cuser;
+       int rc;
        Connection *conn = context;
 
        *user = NULL;
@@ -97,7 +99,6 @@ slap_sasl_authorize(
        if ( authzid == NULL || *authzid == '\0' ||
                strcmp( authcid, authzid ) == 0 )
        {
-               char* cuser;
                size_t len = sizeof("u:") + strlen( authcid );
 
                cuser = ch_malloc( len );
@@ -114,13 +115,18 @@ slap_sasl_authorize(
                return SASL_OK;
        }
 
-       Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
-               "\"%s\" as \"%s\" disallowed. No policy.\n", 
-               (long) (conn ? conn->c_connid : -1),
-               authcid, authzid );
+       rc = slap_sasl_authorized( conn, authcid, authzid );
+       Debug( LDAP_DEBUG_TRACE, "SASL Authorization returned %d\n", rc,0,0);
+       if( rc ) {
+               *errstr = "not authorized";
+               return SASL_NOAUTHZ;
+       }
 
-       *errstr = "no proxy policy";
-    return SASL_NOAUTHZ;
+       cuser = ch_strdup( authzid );
+       dn_normalize( cuser );
+       *errstr = NULL;
+       *user = cuser;
+       return SASL_OK;
 }
 
 
@@ -373,7 +379,6 @@ int slap_sasl_bind(
     Operation           *op,  
     const char          *dn,  
     const char          *ndn,
-    const char          *mech,
     struct berval       *cred,
        char                            **edn,
        slap_ssf_t                      *ssfp )
@@ -388,8 +393,9 @@ int slap_sasl_bind(
        int sc;
 
        Debug(LDAP_DEBUG_ARGS,
-               "==> sasl_bind: dn=\"%s\" mech=%s datalen=%d\n",
-               dn, mech ? mech : "<continuing>", cred ? cred->bv_len : 0 );
+         "==> sasl_bind: dn=\"%s\" mech=%s datalen=%d\n", dn,
+         conn->c_sasl_bind_in_progress ? "<continuing>":conn->c_sasl_bind_mech,
+         cred ? cred->bv_len : 0 );
 
        if( ctx == NULL ) {
                send_ldap_result( conn, op, LDAP_UNAVAILABLE,
@@ -397,9 +403,9 @@ int slap_sasl_bind(
                return rc;
        }
 
-       if ( mech != NULL ) {
+       if ( !conn->c_sasl_bind_in_progress ) {
                sc = sasl_server_start( ctx,
-                       mech,
+                       conn->c_sasl_bind_mech,
                        cred->bv_val, cred->bv_len,
                        (char **)&response.bv_val, &reslen, &errstr );
 
@@ -480,11 +486,6 @@ int slap_sasl_bind(
                                Debug(LDAP_DEBUG_TRACE, "<== slap_sasl_bind: authzdn: \"%s\"\n",
                                        *edn, 0, 0);
 
-                       } else {
-                               rc = LDAP_INAPPROPRIATE_AUTH;
-                               errstr = "authorization disallowed";
-                               Debug(LDAP_DEBUG_TRACE, "<== slap_sasl_bind: %s\n",
-                                       errstr, 0, 0);
                        }
 
                        if( rc == LDAP_SUCCESS ) {
diff --git a/servers/slapd/saslauthz.c b/servers/slapd/saslauthz.c
new file mode 100644 (file)
index 0000000..f762f20
--- /dev/null
@@ -0,0 +1,564 @@
+/*
+ * Copyright (c) 2000, Mark Adamson, Carnegie Mellon.  All rights reserved.
+ * This software is not subject to any license of Carnegie Mellon University.
+ *
+ * Redistribution and use in source and binary forms are permitted without 
+ * restriction or fee of any kind as long as this notice is preserved.
+ *
+ * The name "Carnegie Mellon" must not be used to endorse or promote
+ * products derived from this software without prior written permission.
+ *
+ */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+#include <stdio.h>
+
+#define SLAPD_TOOLS
+#include "slap.h"
+#undef SLAPD_TOOLS
+#include "proto-slap.h"
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#elif defined (HAVE_STRING_H)
+#include <string.h>
+#endif
+
+#ifdef HAVE_CYRUS_SASL
+#include <limits.h>
+#include <sasl.h>
+#include <ldap_pvt.h>
+
+extern int nSaslRegexp;
+extern SaslRegexp_t *SaslRegexp;
+#endif
+
+
+
+
+
+/* URI format:  ldap://<host>/<base>[?[<attrs>][?[<scope>][?[<filter>]]]]   */
+
+int slap_parseURI( char *uri, char **searchbase, int *scope, Filter **filter )
+{
+       char *start, *end;
+
+
+       assert( uri != NULL );
+       *searchbase = NULL;
+       *scope = -1;
+       *filter = NULL;
+
+       Debug( LDAP_DEBUG_TRACE, "slap_parseURI: parsing %s\n", uri, 0, 0 );
+
+       /* If it does not look like a URI, assume it is a DN */
+       if( strncasecmp( uri, "ldap://", 7 ) ) {
+               *searchbase = ch_strdup( uri );
+               dn_normalize( *searchbase );
+               *scope = LDAP_SCOPE_BASE;
+               return( LDAP_SUCCESS );
+       }
+
+       end = index( uri + 7, '/' );
+       if ( end == NULL )
+               return( LDAP_PROTOCOL_ERROR );
+
+       /* could check the hostname here */
+
+       /* Grab the searchbase */
+       start = end+1;
+       end = index( start, '?' );
+       if( end == NULL ) {
+               *searchbase = ch_strdup( start );
+               dn_normalize( *searchbase );
+               return( LDAP_SUCCESS );
+       }
+       *end = '\0';
+       *searchbase = ch_strdup( start );
+       *end = '?';
+       dn_normalize( *searchbase );
+
+       /* Skip the attrs */
+       start = end+1;
+       end = index( start, '?' );
+       if( end == NULL ) {
+               return( LDAP_SUCCESS );
+       }
+
+       /* Grab the scope */
+       start = end+1;
+       if( !strncasecmp( start, "base?", 5 )) {
+               *scope = LDAP_SCOPE_BASE;
+               start += 5;
+       }
+       else if( !strncasecmp( start, "one?", 4 )) {
+               *scope = LDAP_SCOPE_ONELEVEL;
+               start += 4;
+       }
+       else if( !strncasecmp( start, "sub?", 3 )) {
+               *scope = LDAP_SCOPE_SUBTREE;
+               start += 4;
+       }
+       else {
+               ch_free( *searchbase );
+               *searchbase = NULL;
+               return( LDAP_PROTOCOL_ERROR );
+       }
+
+       /* Grab the filter */
+       *filter = str2filter( start );
+
+       return( LDAP_SUCCESS );
+}
+
+
+
+
+
+int slap_sasl_regexp_config( const char *match, const char *replace )
+{
+#ifdef HAVE_CYRUS_SASL
+       const char *c;
+       int rc, n;
+       SaslRegexp_t *reg;
+
+       SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
+         (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
+       reg = &( SaslRegexp[nSaslRegexp] );
+       reg->match = ch_strdup( match );
+       reg->replace = ch_strdup( replace );
+       dn_normalize( reg->match );
+       dn_normalize( reg->replace );
+
+       /* Precompile matching pattern */
+       rc = regcomp( &reg->workspace, reg->match, REG_EXTENDED|REG_ICASE );
+       if ( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+               "SASL match pattern %s could not be compiled by regexp engine\n",
+               reg->match, 0, 0 );
+               return( LDAP_OPERATIONS_ERROR );
+       }
+
+       /* Precompile replace pattern. Find the $<n> placeholders */
+       reg->offset[0] = -2;
+       n = 1;
+       for ( c = reg->replace;  *c;  c++ ) {
+               if ( *c == '\\' ) {
+                       c++;
+                       continue;
+               }
+               if ( *c == '$' ) {
+                       if ( n == SASLREGEX_REPLACE ) {
+                               Debug( LDAP_DEBUG_ANY,
+                                  "SASL replace pattern %s has too many $n placeholders (max %d)\n",
+                                  reg->replace, SASLREGEX_REPLACE, 0 );
+                               return( LDAP_OPERATIONS_ERROR );
+                       }
+                       reg->offset[n] = c - reg->replace;
+                       n++;
+               }
+       }
+
+       /* Final placeholder, after the last $n */
+       reg->offset[n] = c - reg->replace;
+       n++;
+       reg->offset[n] = -1;
+
+       nSaslRegexp++;
+#endif
+       return( LDAP_SUCCESS );
+}
+
+
+
+
+
+#ifdef HAVE_CYRUS_SASL
+
+
+
+/* Take the passed in SASL name and attempt to convert it into an
+   LDAP URI to find the matching LDAP entry, using the pattern matching
+   strings given in the saslregexp config file directive(s) */
+static
+char *slap_sasl_regexp( char *saslname )
+{
+       char *uri=NULL;
+       int i, n, len, insert;
+       SaslRegexp_t *reg;
+
+
+       Debug( LDAP_DEBUG_TRACE, "slap_sasl_regexp: converting SASL name %s\n",
+          saslname, 0, 0 );
+       if (( saslname == NULL ) || ( nSaslRegexp == 0 ))
+               return( NULL );
+
+       /* Match the normalized SASL name to the saslregexp patterns */
+       for( reg = SaslRegexp,i=0;  i<nSaslRegexp;  i++,reg++ ) {
+               if ( regexec( &reg->workspace, saslname, SASLREGEX_REPLACE,
+                 reg->strings, 0)  == REG_OK )
+                       break;
+       }
+
+       if( i >= nSaslRegexp )
+               return( NULL );
+
+       /*
+        * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
+        * replace pattern of the form "x$1y$2z". The returned string needs
+        * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
+        */
+
+
+       /* Get the total length of the final URI */
+
+       n=1;
+       len = 0;
+       while( reg->offset[n] >= 0 ) {
+               /* Len of next section from replacement string (x,y,z above) */
+               len += reg->offset[n] - reg->offset[n-1] - 2;
+               if( reg->offset[n+1] < 0)
+                       break;
+
+               /* Len of string from saslname that matched next $i  (b,d above) */
+               i = reg->replace[ reg->offset[n] + 1 ]  - '0';
+               len += reg->strings[i].rm_eo - reg->strings[i].rm_so;
+               n++;
+       }
+       uri = ch_malloc( len + 1 );
+
+       /* Fill in URI with replace string, replacing $i as we go */
+       n=1;
+       insert = 0;
+       while( reg->offset[n] >= 0) {
+               /* Paste in next section from replacement string (x,y,z above) */
+               len = reg->offset[n] - reg->offset[n-1] - 2;
+               strncpy( uri+insert, reg->replace + reg->offset[n-1] + 2, len);
+               insert += len;
+               if( reg->offset[n+1] < 0)
+                       break;
+
+               /* Paste in string from saslname that matched next $i  (b,d above) */
+               i = reg->replace[ reg->offset[n] + 1 ]  - '0';
+               len = reg->strings[i].rm_eo - reg->strings[i].rm_so;
+               strncpy( uri+insert, saslname + reg->strings[i].rm_so, len );
+               insert += len;
+
+               n++;
+       }
+
+       uri[insert] = '\0';
+       Debug( LDAP_DEBUG_TRACE,
+          "slap_sasl_regexp: converted SASL name to %s\n", uri, 0, 0 );
+       return( uri );
+}
+
+
+
+
+
+/*
+ * Given a SASL name (e.g. "UID=name+REALM=company,cn=GSSAPI,cn=AUTHZ")
+ * return the LDAP DN to which it matches. The SASL regexp rules in the config
+ * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
+ * search with scope=base), just return the URI (or its searchbase). Otherwise
+ * an internal search must be done, and if that search returns exactly one
+ * entry, return the DN of that one entry.
+ */
+
+static
+char *slap_sasl2dn( char *saslname )
+{
+       char *uri=NULL, *searchbase=NULL, *DN=NULL;
+       int rc, scope;
+       Backend *be;
+       Filter *filter=NULL;
+       Connection *conn=NULL;
+       LDAP *client=NULL;
+       LDAPMessage *res=NULL, *msg;
+
+
+       Debug( LDAP_DEBUG_TRACE,
+         "==>slap_sasl2dn: Converting SASL name %s to a DN\n", saslname, 0,0 );
+
+       /* Convert the SASL name into an LDAP URI */
+       uri = slap_sasl_regexp( saslname );
+       if( uri == NULL )
+               goto FINISHED;
+
+       rc = slap_parseURI( uri, &searchbase, &scope, &filter );
+       if( rc )
+               goto FINISHED;
+
+       /* Massive shortcut: search scope == base */
+       if( scope == LDAP_SCOPE_BASE ) {
+               DN = ch_strdup( searchbase );
+               goto FINISHED;
+       }
+
+       /* Must do an internal search */
+
+       Debug( LDAP_DEBUG_TRACE,
+          "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
+          searchbase, scope, 0 );
+
+       be = select_backend( searchbase );
+       if(( be == NULL ) || ( be->be_search == NULL))
+               goto FINISHED;
+       searchbase = suffix_alias( be, searchbase );
+
+       rc = connection_internal_open( &conn, &client, saslname );
+       if( rc != LDAP_SUCCESS )
+               goto FINISHED;
+
+       (*be->be_search)( be, conn, conn->c_ops, /*base=*/NULL, searchbase,
+          scope, /*deref=*/1, /*sizelimit=*/1, /*time=*/0, filter, /*fstr=*/NULL,
+          /*attrs=*/NULL, /*attrsonly=*/0 );
+
+
+       /* Read the client side of the internal search */
+       rc = ldap_result( client, LDAP_RES_ANY, LDAP_MSG_ALL, NULL, &res );
+       if( rc == -1 )
+               goto FINISHED;
+
+       /* Make sure exactly one entry was returned */
+       rc = ldap_count_entries( client, res );
+       Debug( LDAP_DEBUG_TRACE,
+          "slap_sasl2dn: search DN returned %d entries\n", rc,0,0 );
+       if( rc != 1 )
+               goto FINISHED;
+
+       msg = ldap_first_entry( client, res );
+       DN = ldap_get_dn( client, msg );
+
+FINISHED:
+       if( searchbase ) ch_free( searchbase );
+       if( filter ) filter_free( filter );
+       if( uri ) ch_free( uri );
+       if( conn ) connection_internal_close( conn );
+       if( res ) ldap_msgfree( res );
+       if( client  ) ldap_unbind( client );
+       if( DN ) dn_normalize( DN );
+       Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
+          DN ? DN : "<nothing>", 0, 0 );
+       return( DN );
+}
+
+
+
+
+
+/*
+ * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
+ * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
+ * the rule must be used as an internal search for entries. If that search
+ * returns the *assertDN entry, the match is successful.
+ */
+
+static
+int slap_sasl_match( char *rule, char *assertDN, char *authc )
+{
+       char *searchbase=NULL, *dn=NULL;
+       int rc, scope;
+       Backend *be;
+       Filter *filter=NULL;
+       Connection *conn=NULL;
+       LDAP *client=NULL;
+       LDAPMessage *res=NULL, *msg;
+
+
+       Debug( LDAP_DEBUG_TRACE,
+          "===>slap_sasl_match: comparing DN %s to rule %s\n", assertDN, rule, 0 );
+
+       rc = slap_parseURI( rule, &searchbase, &scope, &filter );
+       if( rc != LDAP_SUCCESS )
+               goto CONCLUDED;
+
+       /* Massive shortcut: search scope == base */
+       if( scope == LDAP_SCOPE_BASE ) {
+               dn_normalize( searchbase );
+               if( strcmp( searchbase, assertDN ) == 0 )
+                       rc = LDAP_SUCCESS;
+               else
+                       rc = LDAP_INAPPROPRIATE_AUTH;
+               goto CONCLUDED;
+       }
+
+       /* Must run an internal search. */
+
+       Debug( LDAP_DEBUG_TRACE,
+          "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
+          searchbase, scope, 0 );
+
+       be = select_backend( searchbase );
+       if(( be == NULL ) || ( be->be_search == NULL)) {
+               rc = LDAP_INAPPROPRIATE_AUTH;
+               goto CONCLUDED;
+       }
+       searchbase = suffix_alias( be, searchbase );
+
+       /* Make an internal connection on which to run the search */
+       rc = connection_internal_open( &conn, &client, authc );
+       if( rc != LDAP_SUCCESS )
+               goto CONCLUDED;
+
+       (*be->be_search)( be, conn, conn->c_ops, /*base=*/NULL, searchbase,
+          scope, /*deref=*/1, /*sizelimit=*/0, /*time=*/0, filter, /*fstr=*/NULL,
+          /*attrs=*/NULL, /*attrsonly=*/0 );
+
+
+       /* On the client side of the internal search, read the results. Check
+          if the assertDN matches any of the DN's returned by the search */
+       rc = ldap_result( client, LDAP_RES_ANY, LDAP_MSG_ALL, NULL, &res );
+       if( rc == -1 )
+               goto CONCLUDED;
+
+       for( msg=ldap_first_entry( client, res );
+             msg;
+             msg=ldap_next_entry( client, msg ) )   {
+               dn = ldap_get_dn( client, msg );
+               dn_normalize( dn );
+               rc = strcmp( dn, assertDN );
+               ch_free( dn );
+               if( rc == 0 ) {
+                       rc = LDAP_SUCCESS;
+                       goto CONCLUDED;
+               }
+       }
+       rc = LDAP_INAPPROPRIATE_AUTH;
+
+CONCLUDED:
+       if( searchbase ) ch_free( searchbase );
+       if( filter ) filter_free( filter );
+       if( conn ) connection_internal_close( conn );
+       if( res ) ldap_msgfree( res );
+       if( client  ) ldap_unbind( client );
+       Debug( LDAP_DEBUG_TRACE,
+          "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
+       return( rc );
+}
+
+
+
+
+
+/*
+ * This function answers the question, "Can this ID authorize to that ID?",
+ * based on authorization rules. The rules are stored in the *searchDN, in the
+ * attribute named by *attr. If any of those rules map to the *assertDN, the
+ * authorization is approved.
+ */
+
+static int
+slap_sasl_check_authz(char *searchDN, char *assertDN, char *attr, char *authc)
+{
+       const char *errmsg;
+       int i, rc;
+       struct berval **vals=NULL;
+       AttributeDescription *ad=NULL;
+
+
+       Debug( LDAP_DEBUG_TRACE,
+          "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
+          assertDN, attr, searchDN);
+       rc = slap_str2ad( attr, &ad, &errmsg );
+       if( rc != LDAP_SUCCESS )
+               goto COMPLETE;
+
+       rc = backend_attribute( NULL, NULL, NULL, NULL, searchDN, ad, &vals );
+       if( rc != LDAP_SUCCESS )
+               goto COMPLETE;
+
+       /* Check if the *assertDN matches any **vals */
+       for( i=0; vals[i] != NULL; i++ ) {
+               rc = slap_sasl_match( vals[i]->bv_val, assertDN, authc );
+               if ( rc == LDAP_SUCCESS )
+                       goto COMPLETE;
+       }
+       rc = LDAP_INAPPROPRIATE_AUTH;
+
+COMPLETE:
+       if( vals ) ber_bvecfree( vals );
+       if( ad ) ad_free( ad, 1 );
+
+       Debug( LDAP_DEBUG_TRACE,
+          "<==slap_sasl_check_authz: %s check returning %d\n", attr, rc, 0);
+       return( rc );
+}
+
+
+
+#endif  /* HAVE_CYRUS_SASL */
+
+
+
+
+
+/* Check if a bind can SASL authorize to another identity. */
+
+int slap_sasl_authorized( Connection *conn, char *authcid, char *authzid )
+{
+       int rc;
+       char *saslname=NULL,*authcDN=NULL,*realm=NULL, *authzDN=NULL;
+
+#ifdef HAVE_CYRUS_SASL
+       Debug( LDAP_DEBUG_TRACE,
+          "==>slap_sasl_authorized: can %s become %s?\n", authcid, authzid, 0 );
+
+       /* Create a complete SASL name for the SASL regexp patterns */
+
+       sasl_getprop( conn->c_sasl_context, SASL_REALM, (void **)&realm );
+
+       /* Allocate space */
+       rc = strlen("uid=+realm=,cn=,cn=AUTHZ ");
+       if ( realm ) rc += strlen( realm );
+       if ( authcid ) rc += strlen( authcid );
+       rc += strlen( conn->c_sasl_bind_mech );
+       saslname = ch_malloc( rc );
+
+       /* Build the SASL name with whatever we have, and normalize it */
+       saslname[0] = '\0';
+       rc = 0;
+       if ( authcid )
+               rc += sprintf( saslname+rc, "%sUID=%s", rc?",":"", authcid);
+       if ( realm )
+               rc += sprintf( saslname+rc, "%sREALM=%s", rc?"+":"", realm);
+       if ( conn->c_sasl_bind_mech )
+               rc += sprintf( saslname+rc, "%sCN=%s", rc?",":"",
+                  conn->c_sasl_bind_mech);
+       sprintf( saslname+rc, "%sCN=AUTHZ", rc?",":"");
+       dn_normalize( saslname );
+
+       authcDN = slap_sasl2dn( saslname );
+       if( authcDN == NULL )
+               goto DONE;
+
+       /* Normalize the name given by the clientside of the connection */
+       authzDN = ch_strdup( authzid );
+       dn_normalize( authzDN );
+
+
+       /* Check source rules */
+       rc = slap_sasl_check_authz( authcDN, authzDN, SASL_AUTHZ_SOURCE_ATTR,
+          authcDN );
+       if( rc == LDAP_SUCCESS )
+               goto DONE;
+
+       /* Check destination rules */
+       rc = slap_sasl_check_authz( authzDN, authcDN, SASL_AUTHZ_DEST_ATTR,
+          authcDN );
+       if( rc == LDAP_SUCCESS )
+               goto DONE;
+
+#endif
+       rc = LDAP_INAPPROPRIATE_AUTH;
+
+DONE:
+       if( saslname ) ch_free( saslname );
+       if( authcDN ) ch_free( authcDN );
+       if( authzDN ) ch_free( authzDN );
+       Debug( LDAP_DEBUG_TRACE, "<== slap_sasl_authorized: return %d\n",rc,0,0 );
+       return( rc );
+}
index 69007e65e907f3ddf31a18fa1c04323241f89937..cb1e733c17910ae5d7c67db845c135766e44171c 100644 (file)
@@ -1176,6 +1176,20 @@ typedef struct slap_conn {
 #define Statslog( level, fmt, connid, opid, arg1, arg2, arg3 )
 #endif
 
+
+#define SASLREGEX_REPLACE 10
+#define SASL_AUTHZ_SOURCE_ATTR "saslAuthzTo"
+#define SASL_AUTHZ_DEST_ATTR "saslAuthzFrom"
+
+typedef struct sasl_regexp {
+  char *match;                            /* regexp match pattern */
+  char *replace;                          /* regexp replace pattern */
+  regex_t workspace;                      /* workspace for regexp engine */
+  regmatch_t strings[SASLREGEX_REPLACE];  /* strings matching $1,$2 ... */
+  int offset[SASLREGEX_REPLACE+2];        /* offsets of $1,$2... in *replace */
+} SaslRegexp_t;
+
+
 LDAP_END_DECL
 
 #include "proto-slap.h"
index 555a1b31cc32c697cf0397460fe73eacd6192476..08b73f459deab67141ba9d3f60a2046b8141da4a 100644 (file)
@@ -152,7 +152,15 @@ char * slap_sasl_secprops( const char *in )
        return NULL;
 }
 
+
+int slap_sasl_regexp_config( const char *match, const char *replace )
+{
+  return(0);
+}
+
+
 void connection2anonymous( Connection *c )
 {
        assert(0);
 }
+