mirror of
https://git.openldap.org/openldap/openldap.git
synced 2026-01-01 04:29:35 -05:00
Implementation of SASL authorization.
This commit is contained in:
parent
c9de004a86
commit
bf1ee530ea
12 changed files with 810 additions and 62 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ) {
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 ) {
|
||||
|
|
|
|||
564
servers/slapd/saslauthz.c
Normal file
564
servers/slapd/saslauthz.c
Normal file
|
|
@ -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( ®->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( ®->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 );
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue