sasl.c 38.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* $OpenLDAP$ */
/*
 * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
 */

#include "portable.h"

#include <stdio.h>
#include <ac/stdlib.h>
#include <ac/string.h>

#include <lber.h>
#include <ldap_log.h>

#include "slap.h"

#ifdef HAVE_CYRUS_SASL
#include <limits.h>

#ifdef HAVE_SASL_SASL_H
#include <sasl/sasl.h>
#else
#include <sasl.h>
#endif

#include <lutil.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
28
#if SASL_VERSION_MAJOR >= 2
29
#include <sasl/saslplug.h>
30
31
32
33
34
#define	SASL_CONST const
#else
#define	SASL_CONST
#endif

Kurt Zeilenga's avatar
Kurt Zeilenga committed
35
36
#include "ldap_pvt.h"
#include "lber_pvt.h"
37
38
39
40
41
42
43

/* Flags for telling slap_sasl_getdn() what type of identity is being passed */
#define FLAG_GETDN_AUTHCID 2
#define FLAG_GETDN_AUTHZID 4

static sasl_security_properties_t sasl_secprops;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
44
45
46
47
48
49
50
int slap_sasl_config( int cargc, char **cargv, char *line,
	const char *fname, int lineno )
{
		/* set SASL proxy authorization policy */
		if ( strcasecmp( cargv[0], "sasl-authz-policy" ) == 0 ) {
			if ( cargc != 2 ) {
#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
51
				LDAP_LOG( CONFIG, CRIT,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
52
					   "%s: line %d: missing policy in \"sasl-authz-policy <policy>\" line\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
53
					   fname, lineno, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
54
55
56
57
58
59
60
61
62
63
#else
				Debug( LDAP_DEBUG_ANY,
	    "%s: line %d: missing policy in \"sasl-authz-policy <policy>\" line\n",
				    fname, lineno, 0 );
#endif

				return( 1 );
			}
			if ( slap_sasl_setpolicy( cargv[1] ) ) {
#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
64
				LDAP_LOG( CONFIG, CRIT,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
65
66
67
68
					   "%s: line %d: unable "
					   "to parse value \"%s\" "
					   "in \"sasl-authz-policy "
					   "<policy>\" line.\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
69
					   fname, lineno, cargv[1] );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#else
				Debug( LDAP_DEBUG_ANY,
				    	"%s: line %d: unable "
					"to parse value \"%s\" "
					"in \"sasl-authz-policy "
					"<policy>\" line\n",
    					fname, lineno, cargv[1] );
#endif
				return( 1 );
			}
			

		/* set SASL host */
		} else if ( strcasecmp( cargv[0], "sasl-host" ) == 0 ) {
			if ( cargc < 2 ) {
#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
86
				LDAP_LOG( CONFIG, CRIT,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
87
					   "%s: line %d: missing host in \"sasl-host <host>\" line\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
88
					   fname, lineno, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
89
90
91
92
93
94
95
96
97
98
99
#else
				Debug( LDAP_DEBUG_ANY,
	    "%s: line %d: missing host in \"sasl-host <host>\" line\n",
				    fname, lineno, 0 );
#endif

				return( 1 );
			}

			if ( global_host != NULL ) {
#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
100
				LDAP_LOG( CONFIG, CRIT,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
101
					   "%s: line %d: already set sasl-host!\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
102
					   fname, lineno, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#else
				Debug( LDAP_DEBUG_ANY,
					"%s: line %d: already set sasl-host!\n",
					fname, lineno, 0 );
#endif

				return 1;

			} else {
				global_host = ch_strdup( cargv[1] );
			}

		/* set SASL realm */
		} else if ( strcasecmp( cargv[0], "sasl-realm" ) == 0 ) {
			if ( cargc < 2 ) {
#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
119
				LDAP_LOG( CONFIG, CRIT,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
120
					   "%s: line %d: missing realm in \"sasl-realm <realm>\" line.\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
121
					   fname, lineno, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
122
123
124
125
126
127
128
129
130
131
132
#else
				Debug( LDAP_DEBUG_ANY,
	    "%s: line %d: missing realm in \"sasl-realm <realm>\" line\n",
				    fname, lineno, 0 );
#endif

				return( 1 );
			}

			if ( global_realm != NULL ) {
#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
133
				LDAP_LOG( CONFIG, CRIT,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
134
					   "%s: line %d: already set sasl-realm!\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
135
					   fname, lineno, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#else
				Debug( LDAP_DEBUG_ANY,
					"%s: line %d: already set sasl-realm!\n",
					fname, lineno, 0 );
#endif

				return 1;

			} else {
				global_realm = ch_strdup( cargv[1] );
			}

		} else if ( !strcasecmp( cargv[0], "sasl-regexp" ) 
			|| !strcasecmp( cargv[0], "saslregexp" ) )
		{
			int rc;
			if ( cargc != 3 ) {
#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
154
				LDAP_LOG( CONFIG, CRIT,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
155
156
					   "%s: line %d: need 2 args in "
					   "\"saslregexp <match> <replace>\"\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
157
					   fname, lineno, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#else
				Debug( LDAP_DEBUG_ANY, 
				"%s: line %d: need 2 args in \"saslregexp <match> <replace>\"\n",
				    fname, lineno, 0 );
#endif

				return( 1 );
			}
			rc = slap_sasl_regexp_config( cargv[1], cargv[2] );
			if ( rc ) {
				return rc;
			}

		/* SASL security properties */
		} else if ( strcasecmp( cargv[0], "sasl-secprops" ) == 0 ) {
			char *txt;

			if ( cargc < 2 ) {
#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
177
				LDAP_LOG( CONFIG, CRIT,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
178
179
					   "%s: line %d: missing flags in "
					   "\"sasl-secprops <properties>\" line\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
180
					   fname, lineno, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
181
182
183
184
185
186
187
188
189
190
191
192
#else
				Debug( LDAP_DEBUG_ANY,
	    "%s: line %d: missing flags in \"sasl-secprops <properties>\" line\n",
				    fname, lineno, 0 );
#endif

				return 1;
			}

			txt = slap_sasl_secprops( cargv[1] );
			if ( txt != NULL ) {
#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
193
				LDAP_LOG( CONFIG, CRIT,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
194
					   "%s: line %d sasl-secprops: %s\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
195
					   fname, lineno, txt );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
196
197
198
199
200
201
202
203
204
205
206
207
208
#else
				Debug( LDAP_DEBUG_ANY,
	    "%s: line %d: sasl-secprops: %s\n",
				    fname, lineno, txt );
#endif

				return 1;
			}
	    }

	    return LDAP_SUCCESS;
}

209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
static int
slap_sasl_log(
	void *context,
	int priority,
	const char *message) 
{
	Connection *conn = context;
	int level;
	const char * label;

	if ( message == NULL ) {
		return SASL_BADPARAM;
	}

	switch (priority) {
#if SASL_VERSION_MAJOR >= 2
	case SASL_LOG_NONE:
		level = LDAP_DEBUG_NONE;
		label = "None";
		break;
	case SASL_LOG_ERR:
		level = LDAP_DEBUG_ANY;
		label = "Error";
		break;
	case SASL_LOG_FAIL:
		level = LDAP_DEBUG_ANY;
		label = "Failure";
		break;
	case SASL_LOG_WARN:
		level = LDAP_DEBUG_TRACE;
		label = "Warning";
		break;
	case SASL_LOG_NOTE:
		level = LDAP_DEBUG_TRACE;
		label = "Notice";
		break;
	case SASL_LOG_DEBUG:
		level = LDAP_DEBUG_TRACE;
		label = "Debug";
		break;
	case SASL_LOG_TRACE:
		level = LDAP_DEBUG_TRACE;
		label = "Trace";
		break;
	case SASL_LOG_PASS:
		level = LDAP_DEBUG_TRACE;
		label = "Password Trace";
		break;
#else
	case SASL_LOG_ERR:
		level = LDAP_DEBUG_ANY;
		label = "Error";
		break;
	case SASL_LOG_WARNING:
		level = LDAP_DEBUG_TRACE;
		label = "Warning";
		break;
	case SASL_LOG_INFO:
		level = LDAP_DEBUG_TRACE;
		label = "Info";
		break;
#endif
	default:
		return SASL_BADPARAM;
	}

#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
276
277
	LDAP_LOG( TRANSPORT, ENTRY, 
		"SASL [conn=%ld] %s: %s\n", conn ? conn->c_connid : -1, label, message);
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
#else
	Debug( level, "SASL [conn=%ld] %s: %s\n",
		conn ? conn->c_connid: -1,
		label, message );
#endif


	return SASL_OK;
}


/* Take any sort of identity string and return a DN with the "dn:" prefix. The
   string returned in *dn is in its own allocated memory, and must be free'd 
   by the calling process.
   -Mark Adamson, Carnegie Mellon
Kurt Zeilenga's avatar
Kurt Zeilenga committed
293
294
295
296

   The "dn:" prefix is no longer used anywhere inside slapd. It is only used
   on strings passed in directly from SASL.
   -Howard Chu, Symas Corp.
297
298
299
300
301
*/

#define	SET_DN	1
#define	SET_U	2

Kurt Zeilenga's avatar
Kurt Zeilenga committed
302
static struct berval ext_bv = BER_BVC( "EXTERNAL" );
303

Kurt Zeilenga's avatar
Kurt Zeilenga committed
304
int slap_sasl_getdn( Connection *conn, char *id, int len,
305
306
307
	char *user_realm, struct berval *dn, int flags )
{
	char *c1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
308
	int rc, is_dn = 0, do_norm = 1;
309
310
311
312
	sasl_conn_t *ctx;
	struct berval dn2;

#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
313
	LDAP_LOG( TRANSPORT, ENTRY, 
314
		"slap_sasl_getdn: conn %d id=%s\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
315
		conn ? conn->c_connid : -1, id ? (*id ? id : "<empty>") : "NULL", 0 );
316
317
318
319
320
321
322
323
#else
	Debug( LDAP_DEBUG_ARGS, "slap_sasl_getdn: id=%s\n", 
      id?(*id?id:"<empty>"):"NULL",0,0 );
#endif

	dn->bv_val = NULL;
	dn->bv_len = 0;

324
325
326
327
328
329
330
331
332
333
	if ( id ) {
		if ( len == 0 ) len = strlen( id );

		/* Blatantly anonymous ID */
		if ( len == sizeof("anonymous") - 1 &&
			!strcasecmp( id, "anonymous" ) ) {
			return( LDAP_SUCCESS );
		}
	} else {
		len = 0;
334
	}
335

336
337
	ctx = conn->c_sasl_context;

338
339
340
341
342
	/* An authcID needs to be converted to authzID form. Set the
	 * values directly into *dn; they will be normalized later. (and
	 * normalizing always makes a new copy.) An ID from a TLS certificate
	 * is already normalized, so copy it and skip normalization.
	 */
343
	if( flags & FLAG_GETDN_AUTHCID ) {
344
#ifdef HAVE_TLS
Kurt Zeilenga's avatar
Kurt Zeilenga committed
345
346
347
348
		if( conn->c_is_tls &&
			conn->c_sasl_bind_mech.bv_len == ext_bv.bv_len &&
			strcasecmp( ext_bv.bv_val, conn->c_sasl_bind_mech.bv_val ) == 0 )
		{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
349
			/* X.509 DN is already normalized */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
350
			do_norm = 0;
351
			is_dn = SET_DN;
352
			ber_str2bv( id, len, 1, dn );
353

354
355
356
		} else
#endif
		{
357
358
			/* convert to u:<username> form */
			is_dn = SET_U;
359
360
			dn->bv_val = id;
			dn->bv_len = len;
361
362
363
364
365
		}
	}
	if( !is_dn ) {
		if( !strncasecmp( id, "u:", sizeof("u:")-1 )) {
			is_dn = SET_U;
366
367
			dn->bv_val = id+2;
			dn->bv_len = len-2;
368
369
		} else if ( !strncasecmp( id, "dn:", sizeof("dn:")-1) ) {
			is_dn = SET_DN;
370
371
			dn->bv_val = id+3;
			dn->bv_len = len-3;
372
373
374
		}
	}

375
376
	/* No other possibilities from here */
	if( !is_dn ) {
377
378
379
380
381
382
383
		dn->bv_val = NULL;
		dn->bv_len = 0;
		return( LDAP_INAPPROPRIATE_AUTH );
	}

	/* Username strings */
	if( is_dn == SET_U ) {
384
		char *p, *realm;
385
386
		len = dn->bv_len + sizeof("uid=")-1 + sizeof(",cn=auth")-1;

387
		/* username may have embedded realm name */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
388
		if( ( realm = strchr( dn->bv_val, '@') ) ) {
389
390
391
			*realm++ = '\0';
			len += sizeof(",cn=")-2;
		} else if( user_realm && *user_realm ) {
392
393
394
395
396
397
398
399
400
 			len += strlen( user_realm ) + sizeof(",cn=")-1;
		}

		if( conn->c_sasl_bind_mech.bv_len ) {
			len += conn->c_sasl_bind_mech.bv_len + sizeof(",cn=")-1;
		}

		/* Build the new dn */
		c1 = dn->bv_val;
401
402
403
404
405
406
407
408
409
410
411
		dn->bv_val = SLAP_MALLOC( len+1 );
		if( dn->bv_val == NULL ) {
#ifdef NEW_LOGGING
			LDAP_LOG( TRANSPORT, ERR, 
				"slap_sasl_getdn: SLAP_MALLOC failed", 0, 0, 0 );
#else
			Debug( LDAP_DEBUG_ANY, 
				"slap_sasl_getdn: SLAP_MALLOC failed", 0, 0, 0 );
#endif
			return LDAP_OTHER;
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
412
413
		p = lutil_strcopy( dn->bv_val, "uid=" );
		p = lutil_strncopy( p, c1, dn->bv_len );
414

415
416
		if( realm ) {
			int rlen = dn->bv_len - ( realm - c1 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
417
418
			p = lutil_strcopy( p, ",cn=" );
			p = lutil_strncopy( p, realm, rlen );
419
420
			realm[-1] = '@';
		} else if( user_realm && *user_realm ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
421
422
			p = lutil_strcopy( p, ",cn=" );
			p = lutil_strcopy( p, user_realm );
423
		}
424

425
		if( conn->c_sasl_bind_mech.bv_len ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
426
427
			p = lutil_strcopy( p, ",cn=" );
			p = lutil_strcopy( p, conn->c_sasl_bind_mech.bv_val );
428
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
429
		p = lutil_strcopy( p, ",cn=auth" );
430
431
432
		dn->bv_len = p - dn->bv_val;

#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
433
434
		LDAP_LOG( TRANSPORT, ENTRY, 
			"slap_sasl_getdn: u:id converted to %s.\n", dn->bv_val, 0, 0 );
435
436
437
438
439
#else
		Debug( LDAP_DEBUG_TRACE, "getdn: u:id converted to %s\n", dn->bv_val,0,0 );
#endif
	}

440
441
442
443
444
445
	/* All strings are in DN form now. Normalize if needed. */
	if ( do_norm ) {
		rc = dnNormalize2( NULL, dn, &dn2 );

		/* User DNs were constructed above and must be freed now */
		if ( is_dn == SET_U )
446
			ch_free( dn->bv_val );
447
448
449
450
451
452
453
454
455
456
457
458
459
460

		if ( rc != LDAP_SUCCESS ) {
			dn->bv_val = NULL;
			dn->bv_len = 0;
			return rc;
		}
		*dn = dn2;
	}

	/* Run thru regexp */
	slap_sasl2dn( conn, dn, &dn2 );
	if( dn2.bv_val ) {
		ch_free( dn->bv_val );
		*dn = dn2;
461
#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
462
463
		LDAP_LOG( TRANSPORT, ENTRY, 
			"slap_sasl_getdn: dn:id converted to %s.\n", dn->bv_val, 0, 0 );
464
#else
465
466
		Debug( LDAP_DEBUG_TRACE, "getdn: dn:id converted to %s\n",
			dn->bv_val, 0, 0 );
467
#endif
468
469
470
471
472
473
	}

	return( LDAP_SUCCESS );
}

#if SASL_VERSION_MAJOR >= 2
Kurt Zeilenga's avatar
Kurt Zeilenga committed
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
static const char *slap_propnames[] = {
	"*slapConn", "*authcDN", "*authzDN", NULL };

static Filter *generic_filter;

#define	PROP_CONN	0
#define	PROP_AUTHC	1
#define	PROP_AUTHZ	2

typedef struct lookup_info {
	int last;
	int flags;
	const struct propval *list;
	sasl_server_params_t *sparams;
} lookup_info;

static int
sasl_ap_lookup(
	BackendDB *be,
	Connection *conn,
	Operation *op,
	Entry *e,
	AttributeName *an,
	int attrsonly,
	LDAPControl **ctrls )
{
	BerVarray bv;
	AttributeDescription *ad;
	Attribute *a;
	const char *text;
	int rc, i;
	slap_callback *tmp = op->o_callback;
	lookup_info *sl = tmp->sc_private;

	for( i = 0; i < sl->last; i++ ) {
		const char *name = sl->list[i].name;

		if ( name[0] == '*' ) {
			if ( sl->flags & SASL_AUXPROP_AUTHZID ) continue;
			name++;
		} else if ( !(sl->flags & SASL_AUXPROP_AUTHZID ) )
			continue;

		if ( sl->list[i].values ) {
			if ( !(sl->flags & SASL_AUXPROP_OVERRIDE) ) continue;
		}
		ad = NULL;
		rc = slap_str2ad( name, &ad, &text );
		if ( rc != LDAP_SUCCESS ) {
#ifdef NEW_LOGGING
			LDAP_LOG( TRANSPORT, DETAIL1, 
				"slap_auxprop: str2ad(%s): %s\n", name, text, 0 );
#else
			Debug( LDAP_DEBUG_TRACE,
				"slap_auxprop: str2ad(%s): %s\n", name, text, 0 );
#endif
			continue;
		}
		a = attr_find( e->e_attrs, ad );
		if ( !a ) continue;
		if ( ! access_allowed( be, conn, op, e, ad, NULL, ACL_AUTH, NULL ) )
			continue;
		if ( sl->list[i].values && ( sl->flags & SASL_AUXPROP_OVERRIDE ) )
			sl->sparams->utils->prop_erase( sl->sparams->propctx, sl->list[i].name );
		for ( bv = a->a_vals; bv->bv_val; bv++ ) {
			sl->sparams->utils->prop_set( sl->sparams->propctx, sl->list[i].name,
				bv->bv_val, bv->bv_len );
		}
	}
	return LDAP_SUCCESS;
}
545
546
547
548
549
550
551
552
553

static void
slap_auxprop_lookup(
	void *glob_context,
	sasl_server_params_t *sparams,
	unsigned flags,
	const char *user,
	unsigned ulen)
{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
554
	int rc, i, doit=0;
555
	struct berval dn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
	Connection *conn = NULL;
	lookup_info sl;

	sl.list = sparams->utils->prop_get( sparams->propctx );
	sl.sparams = sparams;
	sl.flags = flags;

	/* Find our DN and conn first */
	for( i = 0, sl.last = 0; sl.list[i].name; i++ ) {
		if ( sl.list[i].name[0] == '*' ) {
			if ( !strcmp( sl.list[i].name, slap_propnames[PROP_CONN] ) ) {
				if ( sl.list[i].values && sl.list[i].values[0] )
					AC_MEMCPY( &conn, sl.list[i].values[0], sizeof( conn ) );
				if ( !sl.last ) sl.last = i;
			}
571
			if ( (flags & SASL_AUXPROP_AUTHZID) &&
Kurt Zeilenga's avatar
Kurt Zeilenga committed
572
573
574
575
576
				!strcmp( sl.list[i].name, slap_propnames[PROP_AUTHZ] ) ) {

				if ( sl.list[i].values && sl.list[i].values[0] )
					AC_MEMCPY( &dn, sl.list[i].values[0], sizeof( dn ) );
				if ( !sl.last ) sl.last = i;
577
578
				break;
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
579
580
581
582
			if ( !strcmp( sl.list[i].name, slap_propnames[PROP_AUTHC] ) ) {
				if ( !sl.last ) sl.last = i;
				if ( sl.list[i].values && sl.list[i].values[0] ) {
					AC_MEMCPY( &dn, sl.list[i].values[0], sizeof( dn ) );
583
584
585
586
					if ( !(flags & SASL_AUXPROP_AUTHZID) )
						break;
				}
			}
587
588
589
		}
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
590
591
592
	/* Now see what else needs to be fetched */
	for( i = 0; i < sl.last; i++ ) {
		const char *name = sl.list[i].name;
593
594
595
596
597
598
599

		if ( name[0] == '*' ) {
			if ( flags & SASL_AUXPROP_AUTHZID ) continue;
			name++;
		} else if ( !(flags & SASL_AUXPROP_AUTHZID ) )
			continue;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
600
		if ( sl.list[i].values ) {
601
602
			if ( !(flags & SASL_AUXPROP_OVERRIDE) ) continue;
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
		doit = 1;
	}

	if (doit) {
		Backend *be;
		Operation op = {0};
		slap_callback cb = { slap_cb_null_response,
			slap_cb_null_sresult, sasl_ap_lookup, NULL };

		cb.sc_private = &sl;

		be = select_backend( &dn, 0, 1 );

		if ( be && be->be_search ) {
			op.o_tag = LDAP_REQ_SEARCH;
			op.o_protocol = LDAP_VERSION3;
			op.o_ndn = conn->c_ndn;
			op.o_callback = &cb;
			op.o_time = slap_get_time();
			op.o_do_not_cache = 1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
623
			op.o_threadctx = conn->c_sasl_bindop->o_threadctx;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
624
625
626
627

			(*be->be_search)( be, conn, &op, NULL, &dn,
				LDAP_SCOPE_BASE, LDAP_DEREF_NEVER, 1, 0,
				generic_filter, NULL, NULL, 0 );
628
		}
629
	}
630
}
631

632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
static sasl_auxprop_plug_t slap_auxprop_plugin = {
	0,	/* Features */
	0,	/* spare */
	NULL,	/* glob_context */
	NULL,	/* auxprop_free */
	slap_auxprop_lookup,
	"slapd",	/* name */
	NULL	/* spare */
};

static int
slap_auxprop_init(
	const sasl_utils_t *utils,
	int max_version,
	int *out_version,
	sasl_auxprop_plug_t **plug,
	const char *plugname)
{
	if ( !out_version | !plug ) return SASL_BADPARAM;

	if ( max_version < SASL_AUXPROP_PLUG_VERSION ) return SASL_BADVERS;

	*out_version = SASL_AUXPROP_PLUG_VERSION;
	*plug = &slap_auxprop_plugin;
	return SASL_OK;
657
658
}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
typedef struct checkpass_info {
	int rc;
	struct berval cred;
} checkpass_info;

static int
sasl_cb_checkpass(
	BackendDB *be,
	Connection *conn,
	Operation *op,
	Entry *e,
	AttributeName *an,
	int attrsonly,
	LDAPControl **ctrls )
{
	slap_callback *tmp = op->o_callback;
	checkpass_info *ci = tmp->sc_private;
	Attribute *a;
	struct berval *bv;
	
	ci->rc = SASL_NOVERIFY;

	a = attr_find( e->e_attrs, slap_schema.si_ad_userPassword );
	if ( !a ) return 0;
	if ( ! access_allowed( be, conn, op, e, slap_schema.si_ad_userPassword,
		NULL, ACL_AUTH, NULL ) ) return 0;

	for ( bv = a->a_vals; bv->bv_val != NULL; bv++ ) {
		if ( !lutil_passwd( bv, &ci->cred, NULL ) ) {
			ci->rc = SASL_OK;
			break;
		}
	}
	return 0;
}

695
696
697
698
699
700
701
702
703
704
static int
slap_sasl_checkpass(
	sasl_conn_t *sconn,
	void *context,
	const char *username,
	const char *pass,
	unsigned passlen,
	struct propctx *propctx)
{
	Connection *conn = (Connection *)context;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
705
	struct berval dn;
706
	int rc;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
707
708
	Backend *be;
	checkpass_info ci;
709

Kurt Zeilenga's avatar
Kurt Zeilenga committed
710
	ci.rc = SASL_NOUSER;
711

Kurt Zeilenga's avatar
Kurt Zeilenga committed
712
713
714
	/* SASL will fallback to its own mechanisms if we don't
	 * find an answer here.
	 */
715

Kurt Zeilenga's avatar
Kurt Zeilenga committed
716
717
	rc = slap_sasl_getdn( conn, (char *)username, 0, NULL, &dn,
		FLAG_GETDN_AUTHCID );
718
719
720
721
722
723
724
725
726
727
728
729
730
731
	if ( rc != LDAP_SUCCESS ) {
		sasl_seterror( sconn, 0, ldap_err2string( rc ) );
		return SASL_NOUSER;
	}

	if ( dn.bv_len == 0 ) {
		sasl_seterror( sconn, 0,
			"No password is associated with the Root DSE" );
		if ( dn.bv_val != NULL ) {
			ch_free( dn.bv_val );
		}
		return SASL_NOUSER;
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
	be = select_backend( &dn, 0, 1 );
	if ( be && be->be_search ) {
		Operation op = {0};
		slap_callback cb = { slap_cb_null_response,
			slap_cb_null_sresult, sasl_cb_checkpass, NULL };

		ci.cred.bv_val = (char *)pass;
		ci.cred.bv_len = passlen;

		cb.sc_private = &ci;
		op.o_tag = LDAP_REQ_SEARCH;
		op.o_protocol = LDAP_VERSION3;
		op.o_ndn = conn->c_ndn;
		op.o_callback = &cb;
		op.o_time = slap_get_time();
		op.o_do_not_cache = 1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
748
		op.o_threadctx = conn->c_sasl_bindop->o_threadctx;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
749
750
751
752

		(*be->be_search)( be, conn, &op, NULL, &dn,
			LDAP_SCOPE_BASE, LDAP_DEREF_NEVER, 1, 0,
			generic_filter, NULL, NULL, 0 );
753
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
754
	if ( ci.rc != SASL_OK ) {
755
756
757
758
759
760
		sasl_seterror( sconn, 0,
			ldap_err2string( LDAP_INVALID_CREDENTIALS ) );
	}

	ch_free( dn.bv_val );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
761
	return ci.rc;
762
763
}

764
765
766
767
768
769
/* Convert a SASL authcid or authzid into a DN. Store the DN in an
 * auxiliary property, so that we can refer to it in sasl_authorize
 * without interfering with anything else. Also, the SASL username
 * buffer is constrained to 256 characters, and our DNs could be
 * much longer (totally arbitrary length)...
 */
770
771
772
773
774
775
776
777
778
779
780
781
782
static int
slap_sasl_canonicalize(
	sasl_conn_t *sconn,
	void *context,
	const char *in,
	unsigned inlen,
	unsigned flags,
	const char *user_realm,
	char *out,
	unsigned out_max,
	unsigned *out_len)
{
	Connection *conn = (Connection *)context;
783
784
	struct propctx *props = sasl_auxprop_getctx( sconn );
	struct propval auxvals[3];
785
	struct berval dn;
786
787
	int rc, which;
	const char *names[2];
788
789
790
791

	*out_len = 0;

#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
792
	LDAP_LOG( TRANSPORT, ENTRY, 
793
		"slap_sasl_canonicalize: conn %d %s=\"%s\"\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
794
795
		conn ? conn->c_connid : -1,
		(flags & SASL_CU_AUTHID) ? "authcid" : "authzid", in ? in : "<empty>");
796
797
798
799
#else
	Debug( LDAP_DEBUG_ARGS, "SASL Canonicalize [conn=%ld]: "
		"%s=\"%s\"\n",
			conn ? conn->c_connid : -1,
800
			(flags & SASL_CU_AUTHID) ? "authcid" : "authzid",
801
802
803
			in ? in : "<empty>" );
#endif

804
805
806
807
808
809
810
811
812
813
814
815
	/* If name is too big, just truncate. We don't care, we're
	 * using DNs, not the usernames.
	 */
	if ( inlen > out_max )
		inlen = out_max-1;

	/* See if we need to add request, can only do it once */
	prop_getnames( props, slap_propnames, auxvals );
	if ( !auxvals[0].name )
		prop_request( props, slap_propnames );

	if ( flags & SASL_CU_AUTHID )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
816
		which = PROP_AUTHC;
817
	else
Kurt Zeilenga's avatar
Kurt Zeilenga committed
818
		which = PROP_AUTHZ;
819

Kurt Zeilenga's avatar
Kurt Zeilenga committed
820
821
822
823
824
825
826
	/* Need to store the Connection for auxprop_lookup */
	if ( !auxvals[PROP_CONN].values ) {
		names[0] = slap_propnames[PROP_CONN];
		names[1] = NULL;
		prop_set( props, names[0], (char *)&conn, sizeof( conn ) );
	}
		
827
828
829
830
	/* Already been here? */
	if ( auxvals[which].values )
		goto done;

831
832
833
834
835
836
837
838
839
840
	/* Normally we require an authzID to have a u: or dn: prefix.
	 * However, SASL frequently gives us an authzID that is just
	 * an exact copy of the authcID, without a prefix. We need to
	 * detect and allow this condition. If SASL calls canonicalize
	 * with SASL_CU_AUTHID|SASL_CU_AUTHZID this is a no-brainer.
	 * But if it's broken into two calls, we need to remember the
	 * authcID so that we can compare the authzID later. We store
	 * the authcID temporarily in conn->c_sasl_dn. We necessarily
	 * finish Canonicalizing before Authorizing, so there is no
	 * conflict with slap_sasl_authorize's use of this temp var.
841
	 */
842
	if ( flags == SASL_CU_AUTHID ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
843
		conn->c_sasl_dn.bv_val = (char *) in;
844
845
846
847
848
	} else if ( flags == SASL_CU_AUTHZID && conn->c_sasl_dn.bv_val ) {
		rc = strcmp( in, conn->c_sasl_dn.bv_val );
		conn->c_sasl_dn.bv_val = NULL;
		/* They were equal, no work needed */
		if ( !rc ) goto done;
849
850
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
851
	rc = slap_sasl_getdn( conn, (char *)in, inlen, (char *)user_realm, &dn,
852
		(flags & SASL_CU_AUTHID) ? FLAG_GETDN_AUTHCID : FLAG_GETDN_AUTHZID );
853
854
855
856
857
	if ( rc != LDAP_SUCCESS ) {
		sasl_seterror( sconn, 0, ldap_err2string( rc ) );
		return SASL_NOAUTHZ;
	}		

858
859
	names[0] = slap_propnames[which];
	names[1] = NULL;
860

861
862
	prop_set( props, names[0], (char *)&dn, sizeof( dn ) );
		
863
#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
864
	LDAP_LOG( TRANSPORT, ENTRY, 
865
		"slap_sasl_canonicalize: conn %d %s=\"%s\"\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
866
		conn ? conn->c_connid : -1, names[0]+1, dn.bv_val );
867
868
869
870
#else
	Debug( LDAP_DEBUG_ARGS, "SASL Canonicalize [conn=%ld]: "
		"%s=\"%s\"\n",
			conn ? conn->c_connid : -1,
871
			names[0]+1, dn.bv_val );
872
#endif
873
874
875
876
done:	AC_MEMCPY( out, in, inlen );
	out[inlen] = '\0';

	*out_len = inlen;
877
878
879
880
881
882
883
884
885
886
887
888
889
890

	return SASL_OK;
}

static int
slap_sasl_authorize(
	sasl_conn_t *sconn,
	void *context,
	char *requested_user,
	unsigned rlen,
	char *auth_identity,
	unsigned alen,
	const char *def_realm,
	unsigned urlen,
891
	struct propctx *props)
892
893
{
	Connection *conn = (Connection *)context;
894
	struct propval auxvals[3];
895
	struct berval authcDN, authzDN;
896
	int rc;
897
898

#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
899
	LDAP_LOG( TRANSPORT, ENTRY, 
900
		"slap_sasl_authorize: conn %d authcid=\"%s\" authzid=\"%s\"\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
901
		conn ? conn->c_connid : -1, auth_identity, requested_user);
902
903
904
905
906
#else
	Debug( LDAP_DEBUG_ARGS, "SASL Authorize [conn=%ld]: "
		"authcid=\"%s\" authzid=\"%s\"\n",
		conn ? conn->c_connid : -1, auth_identity, requested_user );
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
907
908
909
910
911
	if ( conn->c_sasl_dn.bv_val ) {
		ch_free( conn->c_sasl_dn.bv_val );
		conn->c_sasl_dn.bv_val = NULL;
		conn->c_sasl_dn.bv_len = 0;
	}
912

Kurt Zeilenga's avatar
Kurt Zeilenga committed
913
914
	/* Skip PROP_CONN */
	prop_getnames( props, slap_propnames+1, auxvals );
915
	
Kurt Zeilenga's avatar
Kurt Zeilenga committed
916
917
	AC_MEMCPY( &authcDN, auxvals[0].values[0], sizeof(authcDN) );

918
	/* Nothing to do if no authzID was given */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
919
920
	if ( !auxvals[1].name || !auxvals[1].values ) {
		conn->c_sasl_dn = authcDN;
921
		return SASL_OK;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
922
	}
923
924
	
	AC_MEMCPY( &authzDN, auxvals[1].values[0], sizeof(authzDN) );
925

Kurt Zeilenga's avatar
Kurt Zeilenga committed
926
	rc = slap_sasl_authorized( conn, &authcDN, &authzDN );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
927
	ch_free( authcDN.bv_val );
928
929
	if ( rc != LDAP_SUCCESS ) {
#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
930
931
932
		LDAP_LOG( TRANSPORT, INFO, 
			"slap_sasl_authorize: conn %ld  authorization disallowed (%d)\n",
			(long)(conn ? conn->c_connid : -1), rc, 0 );
933
934
935
936
937
938
939
#else
		Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
			" authorization disallowed (%d)\n",
			(long) (conn ? conn->c_connid : -1), rc, 0 );
#endif

		sasl_seterror( sconn, 0, "not authorized" );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
940
		ch_free( authzDN.bv_val );
941
942
943
		return SASL_NOAUTHZ;
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
944
945
	conn->c_sasl_dn = authzDN;

946
#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
947
948
949
	LDAP_LOG( TRANSPORT, ENTRY, 
		"slap_sasl_authorize: conn %d authorization allowed\n",
		(long)(conn ? conn->c_connid : -1), 0, 0 );
950
951
952
953
954
955
956
957
958
959
960
#else
	Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
		" authorization allowed\n",
		(long) (conn ? conn->c_connid : -1), 0, 0 );
#endif
	return SASL_OK;
} 
#else
static int
slap_sasl_authorize(
	void *context,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
961
962
	char *authcid,
	char *authzid,
963
964
965
966
	const char **user,
	const char **errstr)
{
	struct berval authcDN, authzDN;
967
	int rc;
968
	Connection *conn = context;
969
	char *realm;
970
971

	*user = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
972
973
974
975
976
	if ( conn->c_sasl_dn.bv_val ) {
		ch_free( conn->c_sasl_dn.bv_val );
		conn->c_sasl_dn.bv_val = NULL;
		conn->c_sasl_dn.bv_len = 0;
	}
977
978

#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
979
980
981
982
	LDAP_LOG( TRANSPORT, ENTRY, 
		"slap_sasl_authorize: conn %d	 authcid=\"%s\" authzid=\"%s\"\n",
		conn ? conn->c_connid : -1, authcid ? authcid : "<empty>",
		authzid ? authzid : "<empty>" );
983
984
985
986
987
988
989
990
991
992
993
994
#else
	Debug( LDAP_DEBUG_ARGS, "SASL Authorize [conn=%ld]: "
		"authcid=\"%s\" authzid=\"%s\"\n",
		(long) (conn ? conn->c_connid : -1),
		authcid ? authcid : "<empty>",
		authzid ? authzid : "<empty>" );
#endif

	/* Figure out how much data we have for the dn */
	rc = sasl_getprop( conn->c_sasl_context, SASL_REALM, (void **)&realm );
	if( rc != SASL_OK && rc != SASL_NOTDONE ) {
#ifdef NEW_LOGGING
Kurt Zeilenga's avatar
Kurt Zeilenga committed
995
996
		LDAP_LOG( TRANSPORT, ERR,
			"slap_sasl_authorize: getprop(REALM) failed.\n", 0, 0, 0 );
997
998
999
1000
#else
		Debug(LDAP_DEBUG_TRACE,
			"authorize: getprop(REALM) failed!\n", 0,0,0);
#endif
For faster browsing, not all history is shown. View entire blame