3 * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
4 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
13 #include "proto-slap.h"
18 #ifdef HAVE_CYRUS_SASL
28 static sasl_security_properties_t sasl_secprops;
37 Connection *conn = context;
41 if ( message == NULL ) {
47 level = LDAP_DEBUG_ANY;
50 case SASL_LOG_WARNING:
51 level = LDAP_DEBUG_TRACE;
55 level = LDAP_DEBUG_TRACE;
62 Debug( level, "SASL [conn=%d] %s: %s\n",
63 conn ? conn->c_connid: -1,
70 /* Take any sort of identity string and return a DN with the "dn:" prefix. The
71 string returned in *dnptr is in its own allocated memory, and must be free'd
72 by the calling process.
73 -Mark Adamson, Carnegie Mellon
76 int slap_sasl_getdn( Connection *conn, char *id, char **dnptr, int flags )
78 char *c, *c1, *dn=NULL;
83 Debug( LDAP_DEBUG_ARGS, "slap_sasl_getdn: id=%s\n",
84 id?(*id?id:"<empty>"):"NULL",0,0 );
86 /* Blatantly anonymous ID */
87 len = strlen( "anonymous" );
88 if( id && !strncasecmp( id, "anonymous", len) &&
89 ( id[len] == '\0' || id[len] == '@' ) ) {
91 return( LDAP_SUCCESS );
93 ctx = conn->c_sasl_context;
97 /* An authcID will need to be prefixed with u: */
98 if( flags & FLAG_GETDN_AUTHCID ) {
99 dn = ch_realloc( dn, len+3 );
100 memmove( dn+2, dn, len+1 );
106 /* An authzID must be properly prefixed */
107 if( flags & FLAG_GETDN_AUTHZID && strncasecmp( dn, "u:", 2 ) &&
108 strncasecmp( dn, "dn:", 3 ) ) {
111 return( LDAP_INAPPROPRIATE_AUTH );
114 /* Username strings */
115 len1 = strlen( ",cn=authzid" );
116 if( !strncasecmp( dn, "u:", 2 ) ) {
117 len += strlen( "dn:uid=" ) + len1;
119 /* Figure out how much data we have for the dn */
120 rc = sasl_getprop( ctx, SASL_REALM, (void **)&c );
121 if( rc != SASL_OK ) {
122 Debug(LDAP_DEBUG_TRACE,
123 "getdn: getprop(REALM) failed!\n", 0,0,0);
126 return( LDAP_OPERATIONS_ERROR );
129 len += strlen( c ) + strlen(",cn=" );
131 if( conn->c_sasl_bind_mech ) {
132 len += strlen( conn->c_sasl_bind_mech ) + strlen( ",cn=mech" );
135 /* Build the new dn */
137 dn = ch_malloc( len );
138 len = sprintf( dn, "dn:uid=%s", c1+2 );
142 len += sprintf( dn+len, ",cn=%s", c );
144 if( conn->c_sasl_bind_mech ) {
145 len += sprintf( dn+len, ",cn=%s", conn->c_sasl_bind_mech );
147 strcpy( dn+len, ",cn=authzid" );
149 Debug( LDAP_DEBUG_TRACE, "getdn: u:id converted to %s\n", dn,0,0 );
152 /* DN strings that are a cn=authzid identity to run through regexp */
153 if( !strncasecmp( dn, "dn:", 3) && ( ( flags & FLAG_GETDN_FINAL ) == 0 ) ) {
154 c1 = slap_sasl2dn( dn + 3 );
158 /* Reaffix the dn: prefix if it was removed */
159 if( strncasecmp( dn, "dn:", 3) ) {
161 dn = ch_malloc( strlen( c1 ) + 4 );
162 sprintf( dn, "dn:%s", c1 );
165 Debug( LDAP_DEBUG_TRACE, "getdn: dn:id converted to %s\n", dn,0,0 );
169 if( ( flags & FLAG_GETDN_FINAL ) == 0 ) {
174 return( LDAP_SUCCESS );
187 char *authcDN, *authzDN;
189 Connection *conn = context;
193 Debug( LDAP_DEBUG_ARGS, "SASL Authorize [conn=%ld]: "
194 "authcid=\"%s\" authzid=\"%s\"\n",
195 (long) (conn ? conn->c_connid : -1),
196 authcid ? authcid : "<empty>",
197 authzid ? authzid : "<empty>" );
199 /* Convert the identities to DN's. If no authzid was given, client will
200 be bound as the DN matching their username */
201 rc = slap_sasl_getdn( conn, (char *)authcid, &authcDN, FLAG_GETDN_AUTHCID );
202 if( rc != LDAP_SUCCESS ) {
203 *errstr = ldap_err2string( rc );
206 if( ( authzid == NULL ) || !strcmp( authcid,authzid ) ) {
207 Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
208 "Using authcDN=%s\n", (long) (conn ? conn->c_connid : -1), authcDN,0 );
213 rc = slap_sasl_getdn( conn, (char *)authzid, &authzDN, FLAG_GETDN_AUTHZID );
214 if( rc != LDAP_SUCCESS ) {
216 *errstr = ldap_err2string( rc );
220 rc = slap_sasl_authorized( authcDN, authzDN );
222 Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
223 " authorization disallowed (%d)\n",
224 (long) (conn ? conn->c_connid : -1), rc, 0 );
225 *errstr = "not authorized";
231 Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
232 " authorization allowed\n",
233 (long) (conn ? conn->c_connid : -1), 0, 0 );
243 slap_sasl_err2ldap( int saslerr )
249 rc = LDAP_SASL_BIND_IN_PROGRESS;
258 rc = LDAP_AUTH_METHOD_NOT_SUPPORTED;
261 rc = LDAP_INVALID_CREDENTIALS;
264 rc = LDAP_INSUFFICIENT_ACCESS;
268 rc = LDAP_INAPPROPRIATE_AUTH;
280 int slap_sasl_init( void )
282 #ifdef HAVE_CYRUS_SASL
284 static sasl_callback_t server_callbacks[] = {
285 { SASL_CB_LOG, &slap_sasl_log, NULL },
286 { SASL_CB_LIST_END, NULL, NULL }
296 ldap_pvt_sasl_mutex_new,
297 ldap_pvt_sasl_mutex_lock,
298 ldap_pvt_sasl_mutex_unlock,
299 ldap_pvt_sasl_mutex_dispose );
301 /* should provide callbacks for logging */
302 /* server name should be configurable */
303 rc = sasl_server_init( server_callbacks, "slapd" );
305 if( rc != SASL_OK ) {
306 Debug( LDAP_DEBUG_ANY, "sasl_server_init failed\n",
311 Debug( LDAP_DEBUG_TRACE, "slap_sasl_init: initialized!\n",
314 /* default security properties */
315 memset( &sasl_secprops, '\0', sizeof(sasl_secprops) );
316 sasl_secprops.max_ssf = INT_MAX;
317 sasl_secprops.maxbufsize = 65536;
318 sasl_secprops.security_flags = SASL_SEC_NOPLAINTEXT|SASL_SEC_NOANONYMOUS;
324 int slap_sasl_destroy( void )
326 #ifdef HAVE_CYRUS_SASL
332 int slap_sasl_open( Connection *conn )
334 int sc = LDAP_SUCCESS;
336 #ifdef HAVE_CYRUS_SASL
337 sasl_conn_t *ctx = NULL;
338 sasl_callback_t *session_callbacks;
340 assert( conn->c_sasl_context == NULL );
341 assert( conn->c_sasl_extra == NULL );
343 conn->c_sasl_layers = 0;
346 ch_calloc( 3, sizeof(sasl_callback_t));
347 conn->c_sasl_extra = session_callbacks;
349 session_callbacks[0].id = SASL_CB_LOG;
350 session_callbacks[0].proc = &slap_sasl_log;
351 session_callbacks[0].context = conn;
353 session_callbacks[1].id = SASL_CB_PROXY_POLICY;
354 session_callbacks[1].proc = &slap_sasl_authorize;
355 session_callbacks[1].context = conn;
357 session_callbacks[2].id = SASL_CB_LIST_END;
358 session_callbacks[2].proc = NULL;
359 session_callbacks[2].context = NULL;
361 if( global_host == NULL ) {
362 global_host = ldap_pvt_get_fqdn( NULL );
365 /* create new SASL context */
366 sc = sasl_server_new( "ldap", global_host, global_realm,
367 session_callbacks, SASL_SECURITY_LAYER, &ctx );
369 if( sc != SASL_OK ) {
370 Debug( LDAP_DEBUG_ANY, "sasl_server_new failed: %d\n",
375 conn->c_sasl_context = ctx;
377 if( sc == SASL_OK ) {
378 sc = sasl_setprop( ctx,
379 SASL_SEC_PROPS, &sasl_secprops );
381 if( sc != SASL_OK ) {
382 Debug( LDAP_DEBUG_ANY, "sasl_setprop failed: %d\n",
384 slap_sasl_close( conn );
389 sc = slap_sasl_err2ldap( sc );
394 int slap_sasl_external(
397 const char *auth_id )
399 #ifdef HAVE_CYRUS_SASL
401 sasl_conn_t *ctx = conn->c_sasl_context;
402 sasl_external_properties_t extprops;
405 return LDAP_UNAVAILABLE;
408 memset( &extprops, '\0', sizeof(extprops) );
410 extprops.auth_id = (char *) auth_id;
412 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL,
413 (void *) &extprops );
415 if ( sc != SASL_OK ) {
423 int slap_sasl_reset( Connection *conn )
425 #ifdef HAVE_CYRUS_SASL
426 sasl_conn_t *ctx = conn->c_sasl_context;
431 /* must return "anonymous" */
435 char ** slap_sasl_mechs( Connection *conn )
439 #ifdef HAVE_CYRUS_SASL
440 sasl_conn_t *ctx = conn->c_sasl_context;
446 sc = sasl_listmech( ctx,
447 NULL, NULL, ",", NULL,
448 &mechstr, NULL, NULL );
450 if( sc != SASL_OK ) {
451 Debug( LDAP_DEBUG_ANY, "slap_sasl_listmech failed: %d\n",
456 mechs = str2charray( mechstr, "," );
465 int slap_sasl_close( Connection *conn )
467 #ifdef HAVE_CYRUS_SASL
468 sasl_conn_t *ctx = conn->c_sasl_context;
471 sasl_dispose( &ctx );
474 conn->c_sasl_context = NULL;
476 free( conn->c_sasl_extra );
477 conn->c_sasl_extra = NULL;
494 #ifdef HAVE_CYRUS_SASL
495 sasl_conn_t *ctx = conn->c_sasl_context;
496 struct berval response;
501 Debug(LDAP_DEBUG_ARGS,
502 "==> sasl_bind: dn=\"%s\" mech=%s datalen=%d\n", dn,
503 conn->c_sasl_bind_in_progress ? "<continuing>":conn->c_sasl_bind_mech,
504 cred ? cred->bv_len : 0 );
507 send_ldap_result( conn, op, LDAP_UNAVAILABLE,
508 NULL, "SASL unavailable on this session", NULL, NULL );
512 if ( !conn->c_sasl_bind_in_progress ) {
513 sc = sasl_server_start( ctx,
514 conn->c_sasl_bind_mech,
515 cred->bv_val, cred->bv_len,
516 (char **)&response.bv_val, &reslen, &errstr );
519 sc = sasl_server_step( ctx,
520 cred->bv_val, cred->bv_len,
521 (char **)&response.bv_val, &reslen, &errstr );
524 response.bv_len = reslen;
526 if ( sc == SASL_OK ) {
527 char *username = NULL;
529 sc = sasl_getprop( ctx,
530 SASL_USERNAME, (void **)&username );
532 if ( sc != SASL_OK ) {
533 Debug(LDAP_DEBUG_TRACE,
534 "slap_sasl_bind: getprop(USERNAME) failed!\n",
537 send_ldap_result( conn, op, rc = slap_sasl_err2ldap( sc ),
538 NULL, "no SASL username", NULL, NULL );
541 rc = slap_sasl_getdn( conn, username, edn, FLAG_GETDN_FINAL );
543 if( rc == LDAP_SUCCESS ) {
545 sasl_ssf_t *ssf = NULL;
546 (void) sasl_getprop( ctx, SASL_SSF, (void *)&ssf );
547 *ssfp = ssf ? *ssf : 0;
550 ldap_pvt_thread_mutex_lock( &conn->c_mutex );
551 conn->c_sasl_layers++;
552 ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
555 /* Store the authorization DN as a subjectDN */
560 (*edn)[i-3] = (*edn)[i];
561 } while( (*edn)[i] );
564 send_ldap_sasl( conn, op, rc,
565 NULL, NULL, NULL, NULL,
566 response.bv_len ? &response : NULL );
569 send_ldap_result( conn, op, rc,
570 NULL, errstr, NULL, NULL );
574 } else if ( sc == SASL_CONTINUE ) {
575 send_ldap_sasl( conn, op, rc = LDAP_SASL_BIND_IN_PROGRESS,
576 NULL, NULL, NULL, NULL, &response );
579 send_ldap_result( conn, op, rc = slap_sasl_err2ldap( sc ),
580 NULL, errstr, NULL, NULL );
583 Debug(LDAP_DEBUG_TRACE, "<== slap_sasl_bind: rc=%d\n", rc, 0, 0);
586 send_ldap_result( conn, op, rc = LDAP_UNAVAILABLE,
587 NULL, "SASL not supported", NULL, NULL );
593 char* slap_sasl_secprops( const char *in )
595 #ifdef HAVE_CYRUS_SASL
596 int rc = ldap_pvt_sasl_secprops( in, &sasl_secprops );
598 return rc == LDAP_SUCCESS ? NULL : "Invalid security properties";
600 return "SASL not supported";