1/* $NetBSD: saslauthz.c,v 1.1.1.3 2010/12/12 15:22:40 adam Exp $ */ 2 3/* OpenLDAP: pkg/ldap/servers/slapd/saslauthz.c,v 1.163.2.12 2010/04/13 20:23:18 kurt Exp */ 4/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1998-2010 The OpenLDAP Foundation. 7 * Portions Copyright 2000 Mark Adamson, Carnegie Mellon. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted only as authorized by the OpenLDAP 12 * Public License. 13 * 14 * A copy of this license is available in the file LICENSE in the 15 * top-level directory of the distribution or, alternatively, at 16 * <http://www.OpenLDAP.org/license.html>. 17 */ 18 19#include "portable.h" 20 21#include <stdio.h> 22#ifdef HAVE_LIMITS_H 23#include <limits.h> 24#endif 25 26#include <ac/stdlib.h> 27#include <ac/string.h> 28#include <ac/ctype.h> 29 30#include "slap.h" 31 32#include "lutil.h" 33 34#define SASLREGEX_REPLACE 10 35 36#define LDAP_X_SCOPE_EXACT ((ber_int_t) 0x0010) 37#define LDAP_X_SCOPE_REGEX ((ber_int_t) 0x0020) 38#define LDAP_X_SCOPE_CHILDREN ((ber_int_t) 0x0030) 39#define LDAP_X_SCOPE_SUBTREE ((ber_int_t) 0x0040) 40#define LDAP_X_SCOPE_ONELEVEL ((ber_int_t) 0x0050) 41#define LDAP_X_SCOPE_GROUP ((ber_int_t) 0x0060) 42#define LDAP_X_SCOPE_USERS ((ber_int_t) 0x0070) 43 44/* 45 * IDs in DNauthzid form can now have a type specifier, that 46 * influences how they are used in related operations. 47 * 48 * syntax: dn[.{exact|regex}]:<val> 49 * 50 * dn.exact: the value must pass normalization and is used 51 * in exact DN match. 52 * dn.regex: the value is treated as a regular expression 53 * in matching DN values in authz{To|From} 54 * attributes. 55 * dn: for backwards compatibility reasons, the value 56 * is treated as a regular expression, and thus 57 * it is not normalized nor validated; it is used 58 * in exact or regex comparisons based on the 59 * context. 60 * 61 * IDs in DNauthzid form can now have a type specifier, that 62 * influences how they are used in related operations. 63 * 64 * syntax: u[.mech[/realm]]:<val> 65 * 66 * where mech is a SIMPLE, AUTHZ, or a SASL mechanism name 67 * and realm is mechanism specific realm (separate to those 68 * which are representable as part of the principal). 69 */ 70 71typedef struct sasl_regexp { 72 char *sr_match; /* regexp match pattern */ 73 char *sr_replace; /* regexp replace pattern */ 74 regex_t sr_workspace; /* workspace for regexp engine */ 75 int sr_offset[SASLREGEX_REPLACE+2]; /* offsets of $1,$2... in *replace */ 76} SaslRegexp_t; 77 78static int nSaslRegexp = 0; 79static SaslRegexp_t *SaslRegexp = NULL; 80 81#ifdef SLAP_AUTH_REWRITE 82#include "rewrite.h" 83struct rewrite_info *sasl_rwinfo = NULL; 84#define AUTHID_CONTEXT "authid" 85#endif /* SLAP_AUTH_REWRITE */ 86 87/* What SASL proxy authorization policies are allowed? */ 88#define SASL_AUTHZ_NONE 0x00 89#define SASL_AUTHZ_FROM 0x01 90#define SASL_AUTHZ_TO 0x02 91#define SASL_AUTHZ_AND 0x10 92 93static const char *policy_txt[] = { 94 "none", "from", "to", "any" 95}; 96 97static int authz_policy = SASL_AUTHZ_NONE; 98 99static int 100slap_sasl_match( Operation *opx, struct berval *rule, 101 struct berval *assertDN, struct berval *authc ); 102 103int slap_sasl_setpolicy( const char *arg ) 104{ 105 int rc = LDAP_SUCCESS; 106 107 if ( strcasecmp( arg, "none" ) == 0 ) { 108 authz_policy = SASL_AUTHZ_NONE; 109 } else if ( strcasecmp( arg, "from" ) == 0 ) { 110 authz_policy = SASL_AUTHZ_FROM; 111 } else if ( strcasecmp( arg, "to" ) == 0 ) { 112 authz_policy = SASL_AUTHZ_TO; 113 } else if ( strcasecmp( arg, "both" ) == 0 || strcasecmp( arg, "any" ) == 0 ) { 114 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO; 115 } else if ( strcasecmp( arg, "all" ) == 0 ) { 116 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND; 117 } else { 118 rc = LDAP_OTHER; 119 } 120 return rc; 121} 122 123const char * slap_sasl_getpolicy() 124{ 125 if ( authz_policy == (SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND) ) 126 return "all"; 127 else 128 return policy_txt[authz_policy]; 129} 130 131int slap_parse_user( struct berval *id, struct berval *user, 132 struct berval *realm, struct berval *mech ) 133{ 134 char u; 135 136 assert( id != NULL ); 137 assert( !BER_BVISNULL( id ) ); 138 assert( user != NULL ); 139 assert( realm != NULL ); 140 assert( mech != NULL ); 141 142 u = id->bv_val[ 0 ]; 143 144 if ( u != 'u' && u != 'U' ) { 145 /* called with something other than u: */ 146 return LDAP_PROTOCOL_ERROR; 147 } 148 149 /* uauthzid form: 150 * u[.mech[/realm]]:user 151 */ 152 153 user->bv_val = ber_bvchr( id, ':' ); 154 if ( BER_BVISNULL( user ) ) { 155 return LDAP_PROTOCOL_ERROR; 156 } 157 user->bv_val[ 0 ] = '\0'; 158 user->bv_val++; 159 user->bv_len = id->bv_len - ( user->bv_val - id->bv_val ); 160 161 mech->bv_val = ber_bvchr( id, '.' ); 162 if ( !BER_BVISNULL( mech ) ) { 163 mech->bv_val[ 0 ] = '\0'; 164 mech->bv_val++; 165 mech->bv_len = user->bv_val - mech->bv_val - 1; 166 167 realm->bv_val = ber_bvchr( mech, '/' ); 168 169 if ( !BER_BVISNULL( realm ) ) { 170 realm->bv_val[ 0 ] = '\0'; 171 realm->bv_val++; 172 mech->bv_len = realm->bv_val - mech->bv_val - 1; 173 realm->bv_len = user->bv_val - realm->bv_val - 1; 174 } 175 176 } else { 177 BER_BVZERO( realm ); 178 } 179 180 if ( id->bv_val[ 1 ] != '\0' ) { 181 return LDAP_PROTOCOL_ERROR; 182 } 183 184 if ( !BER_BVISNULL( mech ) ) { 185 assert( mech->bv_val == id->bv_val + 2 ); 186 187 AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 ); 188 mech->bv_val -= 2; 189 } 190 191 if ( !BER_BVISNULL( realm ) ) { 192 assert( realm->bv_val >= id->bv_val + 2 ); 193 194 AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 ); 195 realm->bv_val -= 2; 196 } 197 198 /* leave "u:" before user */ 199 user->bv_val -= 2; 200 user->bv_len += 2; 201 user->bv_val[ 0 ] = u; 202 user->bv_val[ 1 ] = ':'; 203 204 return LDAP_SUCCESS; 205} 206 207int 208authzValidate( 209 Syntax *syntax, 210 struct berval *in ) 211{ 212 struct berval bv; 213 int rc = LDAP_INVALID_SYNTAX; 214 LDAPURLDesc *ludp = NULL; 215 int scope = -1; 216 217 /* 218 * 1) <DN> 219 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>} 220 * 3) dn.regex:<pattern> 221 * 4) u[.mech[/realm]]:<ID> 222 * 5) group[/<groupClass>[/<memberAttr>]]:<DN> 223 * 6) <URL> 224 */ 225 226 assert( in != NULL ); 227 assert( !BER_BVISNULL( in ) ); 228 229 Debug( LDAP_DEBUG_TRACE, 230 "authzValidate: parsing %s\n", in->bv_val, 0, 0 ); 231 232 /* 233 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>} 234 * 3) dn.regex:<pattern> 235 * 236 * <DN> must pass DN normalization 237 */ 238 if ( !strncasecmp( in->bv_val, "dn", STRLENOF( "dn" ) ) ) { 239 bv.bv_val = in->bv_val + STRLENOF( "dn" ); 240 241 if ( bv.bv_val[ 0 ] == '.' ) { 242 bv.bv_val++; 243 244 if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) { 245 bv.bv_val += STRLENOF( "exact:" ); 246 scope = LDAP_X_SCOPE_EXACT; 247 248 } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) { 249 bv.bv_val += STRLENOF( "regex:" ); 250 scope = LDAP_X_SCOPE_REGEX; 251 252 } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) { 253 bv.bv_val += STRLENOF( "children:" ); 254 scope = LDAP_X_SCOPE_CHILDREN; 255 256 } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) { 257 bv.bv_val += STRLENOF( "subtree:" ); 258 scope = LDAP_X_SCOPE_SUBTREE; 259 260 } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) { 261 bv.bv_val += STRLENOF( "onelevel:" ); 262 scope = LDAP_X_SCOPE_ONELEVEL; 263 264 } else { 265 return LDAP_INVALID_SYNTAX; 266 } 267 268 } else { 269 if ( bv.bv_val[ 0 ] != ':' ) { 270 return LDAP_INVALID_SYNTAX; 271 } 272 scope = LDAP_X_SCOPE_EXACT; 273 bv.bv_val++; 274 } 275 276 bv.bv_val += strspn( bv.bv_val, " " ); 277 /* jump here in case no type specification was present 278 * and uri was not an URI... HEADS-UP: assuming EXACT */ 279is_dn: bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val ); 280 281 /* a single '*' means any DN without using regexes */ 282 if ( ber_bvccmp( &bv, '*' ) ) { 283 /* LDAP_X_SCOPE_USERS */ 284 return LDAP_SUCCESS; 285 } 286 287 switch ( scope ) { 288 case LDAP_X_SCOPE_EXACT: 289 case LDAP_X_SCOPE_CHILDREN: 290 case LDAP_X_SCOPE_SUBTREE: 291 case LDAP_X_SCOPE_ONELEVEL: 292 return dnValidate( NULL, &bv ); 293 294 case LDAP_X_SCOPE_REGEX: 295 return LDAP_SUCCESS; 296 } 297 298 return rc; 299 300 /* 301 * 4) u[.mech[/realm]]:<ID> 302 */ 303 } else if ( ( in->bv_val[ 0 ] == 'u' || in->bv_val[ 0 ] == 'U' ) 304 && ( in->bv_val[ 1 ] == ':' 305 || in->bv_val[ 1 ] == '/' 306 || in->bv_val[ 1 ] == '.' ) ) 307 { 308 char buf[ SLAP_LDAPDN_MAXLEN ]; 309 struct berval id, 310 user = BER_BVNULL, 311 realm = BER_BVNULL, 312 mech = BER_BVNULL; 313 314 if ( sizeof( buf ) <= in->bv_len ) { 315 return LDAP_INVALID_SYNTAX; 316 } 317 318 id.bv_len = in->bv_len; 319 id.bv_val = buf; 320 strncpy( buf, in->bv_val, sizeof( buf ) ); 321 322 rc = slap_parse_user( &id, &user, &realm, &mech ); 323 if ( rc != LDAP_SUCCESS ) { 324 return LDAP_INVALID_SYNTAX; 325 } 326 327 return rc; 328 329 /* 330 * 5) group[/groupClass[/memberAttr]]:<DN> 331 * 332 * <groupClass> defaults to "groupOfNames" 333 * <memberAttr> defaults to "member" 334 * 335 * <DN> must pass DN normalization 336 */ 337 } else if ( strncasecmp( in->bv_val, "group", STRLENOF( "group" ) ) == 0 ) 338 { 339 struct berval group_dn = BER_BVNULL, 340 group_oc = BER_BVNULL, 341 member_at = BER_BVNULL; 342 343 bv.bv_val = in->bv_val + STRLENOF( "group" ); 344 bv.bv_len = in->bv_len - STRLENOF( "group" ); 345 group_dn.bv_val = ber_bvchr( &bv, ':' ); 346 if ( group_dn.bv_val == NULL ) { 347 /* last chance: assume it's a(n exact) DN ... */ 348 bv.bv_val = in->bv_val; 349 scope = LDAP_X_SCOPE_EXACT; 350 goto is_dn; 351 } 352 353 /* 354 * FIXME: we assume that "member" and "groupOfNames" 355 * are present in schema... 356 */ 357 if ( bv.bv_val[ 0 ] == '/' ) { 358 group_oc.bv_val = &bv.bv_val[ 1 ]; 359 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val; 360 361 member_at.bv_val = ber_bvchr( &group_oc, '/' ); 362 if ( member_at.bv_val ) { 363 AttributeDescription *ad = NULL; 364 const char *text = NULL; 365 366 group_oc.bv_len = member_at.bv_val - group_oc.bv_val; 367 member_at.bv_val++; 368 member_at.bv_len = group_dn.bv_val - member_at.bv_val; 369 rc = slap_bv2ad( &member_at, &ad, &text ); 370 if ( rc != LDAP_SUCCESS ) { 371 return rc; 372 } 373 } 374 375 if ( oc_bvfind( &group_oc ) == NULL ) { 376 return LDAP_INVALID_SYNTAX; 377 } 378 } 379 380 group_dn.bv_val++; 381 group_dn.bv_len = in->bv_len - ( group_dn.bv_val - in->bv_val ); 382 383 rc = dnValidate( NULL, &group_dn ); 384 if ( rc != LDAP_SUCCESS ) { 385 return rc; 386 } 387 388 return rc; 389 } 390 391 /* 392 * ldap:///<base>??<scope>?<filter> 393 * <scope> ::= {base|one|subtree} 394 * 395 * <scope> defaults to "base" 396 * <base> must pass DN normalization 397 * <filter> must pass str2filter() 398 */ 399 rc = ldap_url_parse( in->bv_val, &ludp ); 400 switch ( rc ) { 401 case LDAP_URL_SUCCESS: 402 /* FIXME: the check is pedantic, but I think it's necessary, 403 * because people tend to use things like ldaps:// which 404 * gives the idea SSL is being used. Maybe we could 405 * accept ldapi:// as well, but the point is that we use 406 * an URL as an easy means to define bits of a search with 407 * little parsing. 408 */ 409 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) { 410 /* 411 * must be ldap:/// 412 */ 413 rc = LDAP_INVALID_SYNTAX; 414 goto done; 415 } 416 break; 417 418 case LDAP_URL_ERR_BADSCHEME: 419 /* 420 * last chance: assume it's a(n exact) DN ... 421 * 422 * NOTE: must pass DN normalization 423 */ 424 ldap_free_urldesc( ludp ); 425 bv.bv_val = in->bv_val; 426 scope = LDAP_X_SCOPE_EXACT; 427 goto is_dn; 428 429 default: 430 rc = LDAP_INVALID_SYNTAX; 431 goto done; 432 } 433 434 if ( ( ludp->lud_host && *ludp->lud_host ) 435 || ludp->lud_attrs || ludp->lud_exts ) 436 { 437 /* host part must be empty */ 438 /* attrs and extensions parts must be empty */ 439 rc = LDAP_INVALID_SYNTAX; 440 goto done; 441 } 442 443 /* Grab the filter */ 444 if ( ludp->lud_filter ) { 445 Filter *f = str2filter( ludp->lud_filter ); 446 if ( f == NULL ) { 447 rc = LDAP_INVALID_SYNTAX; 448 goto done; 449 } 450 filter_free( f ); 451 } 452 453 /* Grab the searchbase */ 454 assert( ludp->lud_dn != NULL ); 455 ber_str2bv( ludp->lud_dn, 0, 0, &bv ); 456 rc = dnValidate( NULL, &bv ); 457 458done: 459 ldap_free_urldesc( ludp ); 460 return( rc ); 461} 462 463static int 464authzPrettyNormal( 465 struct berval *val, 466 struct berval *normalized, 467 void *ctx, 468 int normalize ) 469{ 470 struct berval bv; 471 int rc = LDAP_INVALID_SYNTAX; 472 LDAPURLDesc *ludp = NULL; 473 char *lud_dn = NULL, 474 *lud_filter = NULL; 475 int scope = -1; 476 477 /* 478 * 1) <DN> 479 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>} 480 * 3) dn.regex:<pattern> 481 * 4) u[.mech[/realm]]:<ID> 482 * 5) group[/<groupClass>[/<memberAttr>]]:<DN> 483 * 6) <URL> 484 */ 485 486 assert( val != NULL ); 487 assert( !BER_BVISNULL( val ) ); 488 489 /* 490 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>} 491 * 3) dn.regex:<pattern> 492 * 493 * <DN> must pass DN normalization 494 */ 495 if ( !strncasecmp( val->bv_val, "dn", STRLENOF( "dn" ) ) ) { 496 struct berval out = BER_BVNULL, 497 prefix = BER_BVNULL; 498 char *ptr; 499 500 bv.bv_val = val->bv_val + STRLENOF( "dn" ); 501 502 if ( bv.bv_val[ 0 ] == '.' ) { 503 bv.bv_val++; 504 505 if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) { 506 bv.bv_val += STRLENOF( "exact:" ); 507 scope = LDAP_X_SCOPE_EXACT; 508 509 } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) { 510 bv.bv_val += STRLENOF( "regex:" ); 511 scope = LDAP_X_SCOPE_REGEX; 512 513 } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) { 514 bv.bv_val += STRLENOF( "children:" ); 515 scope = LDAP_X_SCOPE_CHILDREN; 516 517 } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) { 518 bv.bv_val += STRLENOF( "subtree:" ); 519 scope = LDAP_X_SCOPE_SUBTREE; 520 521 } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) { 522 bv.bv_val += STRLENOF( "onelevel:" ); 523 scope = LDAP_X_SCOPE_ONELEVEL; 524 525 } else { 526 return LDAP_INVALID_SYNTAX; 527 } 528 529 } else { 530 if ( bv.bv_val[ 0 ] != ':' ) { 531 return LDAP_INVALID_SYNTAX; 532 } 533 scope = LDAP_X_SCOPE_EXACT; 534 bv.bv_val++; 535 } 536 537 bv.bv_val += strspn( bv.bv_val, " " ); 538 /* jump here in case no type specification was present 539 * and uri was not an URI... HEADS-UP: assuming EXACT */ 540is_dn: bv.bv_len = val->bv_len - ( bv.bv_val - val->bv_val ); 541 542 /* a single '*' means any DN without using regexes */ 543 if ( ber_bvccmp( &bv, '*' ) ) { 544 ber_str2bv_x( "dn:*", STRLENOF( "dn:*" ), 1, normalized, ctx ); 545 return LDAP_SUCCESS; 546 } 547 548 switch ( scope ) { 549 case LDAP_X_SCOPE_EXACT: 550 case LDAP_X_SCOPE_CHILDREN: 551 case LDAP_X_SCOPE_SUBTREE: 552 case LDAP_X_SCOPE_ONELEVEL: 553 if ( normalize ) { 554 rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx ); 555 } else { 556 rc = dnPretty( NULL, &bv, &out, ctx ); 557 } 558 if( rc != LDAP_SUCCESS ) { 559 return LDAP_INVALID_SYNTAX; 560 } 561 break; 562 563 case LDAP_X_SCOPE_REGEX: 564 normalized->bv_len = STRLENOF( "dn.regex:" ) + bv.bv_len; 565 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx ); 566 ptr = lutil_strcopy( normalized->bv_val, "dn.regex:" ); 567 ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len ); 568 ptr[ 0 ] = '\0'; 569 return LDAP_SUCCESS; 570 571 default: 572 return LDAP_INVALID_SYNTAX; 573 } 574 575 /* prepare prefix */ 576 switch ( scope ) { 577 case LDAP_X_SCOPE_EXACT: 578 BER_BVSTR( &prefix, "dn:" ); 579 break; 580 581 case LDAP_X_SCOPE_CHILDREN: 582 BER_BVSTR( &prefix, "dn.children:" ); 583 break; 584 585 case LDAP_X_SCOPE_SUBTREE: 586 BER_BVSTR( &prefix, "dn.subtree:" ); 587 break; 588 589 case LDAP_X_SCOPE_ONELEVEL: 590 BER_BVSTR( &prefix, "dn.onelevel:" ); 591 break; 592 593 default: 594 assert( 0 ); 595 break; 596 } 597 598 normalized->bv_len = prefix.bv_len + out.bv_len; 599 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx ); 600 601 ptr = lutil_strcopy( normalized->bv_val, prefix.bv_val ); 602 ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len ); 603 ptr[ 0 ] = '\0'; 604 ber_memfree_x( out.bv_val, ctx ); 605 606 return LDAP_SUCCESS; 607 608 /* 609 * 4) u[.mech[/realm]]:<ID> 610 */ 611 } else if ( ( val->bv_val[ 0 ] == 'u' || val->bv_val[ 0 ] == 'U' ) 612 && ( val->bv_val[ 1 ] == ':' 613 || val->bv_val[ 1 ] == '/' 614 || val->bv_val[ 1 ] == '.' ) ) 615 { 616 char buf[ SLAP_LDAPDN_MAXLEN ]; 617 struct berval id, 618 user = BER_BVNULL, 619 realm = BER_BVNULL, 620 mech = BER_BVNULL; 621 622 if ( sizeof( buf ) <= val->bv_len ) { 623 return LDAP_INVALID_SYNTAX; 624 } 625 626 id.bv_len = val->bv_len; 627 id.bv_val = buf; 628 strncpy( buf, val->bv_val, sizeof( buf ) ); 629 630 rc = slap_parse_user( &id, &user, &realm, &mech ); 631 if ( rc != LDAP_SUCCESS ) { 632 return LDAP_INVALID_SYNTAX; 633 } 634 635 ber_dupbv_x( normalized, val, ctx ); 636 637 return rc; 638 639 /* 640 * 5) group[/groupClass[/memberAttr]]:<DN> 641 * 642 * <groupClass> defaults to "groupOfNames" 643 * <memberAttr> defaults to "member" 644 * 645 * <DN> must pass DN normalization 646 */ 647 } else if ( strncasecmp( val->bv_val, "group", STRLENOF( "group" ) ) == 0 ) 648 { 649 struct berval group_dn = BER_BVNULL, 650 group_oc = BER_BVNULL, 651 member_at = BER_BVNULL, 652 out = BER_BVNULL; 653 char *ptr; 654 655 bv.bv_val = val->bv_val + STRLENOF( "group" ); 656 bv.bv_len = val->bv_len - STRLENOF( "group" ); 657 group_dn.bv_val = ber_bvchr( &bv, ':' ); 658 if ( group_dn.bv_val == NULL ) { 659 /* last chance: assume it's a(n exact) DN ... */ 660 bv.bv_val = val->bv_val; 661 scope = LDAP_X_SCOPE_EXACT; 662 goto is_dn; 663 } 664 665 /* 666 * FIXME: we assume that "member" and "groupOfNames" 667 * are present in schema... 668 */ 669 if ( bv.bv_val[ 0 ] == '/' ) { 670 ObjectClass *oc = NULL; 671 672 group_oc.bv_val = &bv.bv_val[ 1 ]; 673 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val; 674 675 member_at.bv_val = ber_bvchr( &group_oc, '/' ); 676 if ( member_at.bv_val ) { 677 AttributeDescription *ad = NULL; 678 const char *text = NULL; 679 680 group_oc.bv_len = member_at.bv_val - group_oc.bv_val; 681 member_at.bv_val++; 682 member_at.bv_len = group_dn.bv_val - member_at.bv_val; 683 rc = slap_bv2ad( &member_at, &ad, &text ); 684 if ( rc != LDAP_SUCCESS ) { 685 return rc; 686 } 687 688 member_at = ad->ad_cname; 689 690 } 691 692 oc = oc_bvfind( &group_oc ); 693 if ( oc == NULL ) { 694 return LDAP_INVALID_SYNTAX; 695 } 696 697 group_oc = oc->soc_cname; 698 } 699 700 group_dn.bv_val++; 701 group_dn.bv_len = val->bv_len - ( group_dn.bv_val - val->bv_val ); 702 703 if ( normalize ) { 704 rc = dnNormalize( 0, NULL, NULL, &group_dn, &out, ctx ); 705 } else { 706 rc = dnPretty( NULL, &group_dn, &out, ctx ); 707 } 708 if ( rc != LDAP_SUCCESS ) { 709 return rc; 710 } 711 712 normalized->bv_len = STRLENOF( "group" ":" ) + out.bv_len; 713 if ( !BER_BVISNULL( &group_oc ) ) { 714 normalized->bv_len += STRLENOF( "/" ) + group_oc.bv_len; 715 if ( !BER_BVISNULL( &member_at ) ) { 716 normalized->bv_len += STRLENOF( "/" ) + member_at.bv_len; 717 } 718 } 719 720 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx ); 721 ptr = lutil_strcopy( normalized->bv_val, "group" ); 722 if ( !BER_BVISNULL( &group_oc ) ) { 723 ptr[ 0 ] = '/'; 724 ptr++; 725 ptr = lutil_strncopy( ptr, group_oc.bv_val, group_oc.bv_len ); 726 if ( !BER_BVISNULL( &member_at ) ) { 727 ptr[ 0 ] = '/'; 728 ptr++; 729 ptr = lutil_strncopy( ptr, member_at.bv_val, member_at.bv_len ); 730 } 731 } 732 ptr[ 0 ] = ':'; 733 ptr++; 734 ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len ); 735 ptr[ 0 ] = '\0'; 736 ber_memfree_x( out.bv_val, ctx ); 737 738 return rc; 739 } 740 741 /* 742 * ldap:///<base>??<scope>?<filter> 743 * <scope> ::= {base|one|subtree} 744 * 745 * <scope> defaults to "base" 746 * <base> must pass DN normalization 747 * <filter> must pass str2filter() 748 */ 749 rc = ldap_url_parse( val->bv_val, &ludp ); 750 switch ( rc ) { 751 case LDAP_URL_SUCCESS: 752 /* FIXME: the check is pedantic, but I think it's necessary, 753 * because people tend to use things like ldaps:// which 754 * gives the idea SSL is being used. Maybe we could 755 * accept ldapi:// as well, but the point is that we use 756 * an URL as an easy means to define bits of a search with 757 * little parsing. 758 */ 759 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) { 760 /* 761 * must be ldap:/// 762 */ 763 rc = LDAP_INVALID_SYNTAX; 764 goto done; 765 } 766 767 AC_MEMCPY( ludp->lud_scheme, "ldap", STRLENOF( "ldap" ) ); 768 break; 769 770 case LDAP_URL_ERR_BADSCHEME: 771 /* 772 * last chance: assume it's a(n exact) DN ... 773 * 774 * NOTE: must pass DN normalization 775 */ 776 ldap_free_urldesc( ludp ); 777 bv.bv_val = val->bv_val; 778 scope = LDAP_X_SCOPE_EXACT; 779 goto is_dn; 780 781 default: 782 rc = LDAP_INVALID_SYNTAX; 783 goto done; 784 } 785 786 if ( ( ludp->lud_host && *ludp->lud_host ) 787 || ludp->lud_attrs || ludp->lud_exts ) 788 { 789 /* host part must be empty */ 790 /* attrs and extensions parts must be empty */ 791 rc = LDAP_INVALID_SYNTAX; 792 goto done; 793 } 794 795 /* Grab the filter */ 796 if ( ludp->lud_filter ) { 797 struct berval filterstr; 798 Filter *f; 799 800 lud_filter = ludp->lud_filter; 801 802 f = str2filter( lud_filter ); 803 if ( f == NULL ) { 804 rc = LDAP_INVALID_SYNTAX; 805 goto done; 806 } 807 filter2bv( f, &filterstr ); 808 filter_free( f ); 809 if ( BER_BVISNULL( &filterstr ) ) { 810 rc = LDAP_INVALID_SYNTAX; 811 goto done; 812 } 813 814 ludp->lud_filter = filterstr.bv_val; 815 } 816 817 /* Grab the searchbase */ 818 assert( ludp->lud_dn != NULL ); 819 if ( ludp->lud_dn ) { 820 struct berval out = BER_BVNULL; 821 822 lud_dn = ludp->lud_dn; 823 824 ber_str2bv( lud_dn, 0, 0, &bv ); 825 if ( normalize ) { 826 rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx ); 827 } else { 828 rc = dnPretty( NULL, &bv, &out, ctx ); 829 } 830 831 if ( rc != LDAP_SUCCESS ) { 832 goto done; 833 } 834 835 ludp->lud_dn = out.bv_val; 836 } 837 838 ludp->lud_port = 0; 839 normalized->bv_val = ldap_url_desc2str( ludp ); 840 if ( normalized->bv_val ) { 841 normalized->bv_len = strlen( normalized->bv_val ); 842 843 } else { 844 rc = LDAP_INVALID_SYNTAX; 845 } 846 847done: 848 if ( lud_filter ) { 849 if ( ludp->lud_filter != lud_filter ) { 850 ber_memfree( ludp->lud_filter ); 851 } 852 ludp->lud_filter = lud_filter; 853 } 854 855 if ( lud_dn ) { 856 if ( ludp->lud_dn != lud_dn ) { 857 ber_memfree( ludp->lud_dn ); 858 } 859 ludp->lud_dn = lud_dn; 860 } 861 862 ldap_free_urldesc( ludp ); 863 864 return( rc ); 865} 866 867int 868authzNormalize( 869 slap_mask_t usage, 870 Syntax *syntax, 871 MatchingRule *mr, 872 struct berval *val, 873 struct berval *normalized, 874 void *ctx ) 875{ 876 int rc; 877 878 Debug( LDAP_DEBUG_TRACE, ">>> authzNormalize: <%s>\n", 879 val->bv_val, 0, 0 ); 880 881 rc = authzPrettyNormal( val, normalized, ctx, 1 ); 882 883 Debug( LDAP_DEBUG_TRACE, "<<< authzNormalize: <%s> (%d)\n", 884 normalized->bv_val, rc, 0 ); 885 886 return rc; 887} 888 889int 890authzPretty( 891 Syntax *syntax, 892 struct berval *val, 893 struct berval *out, 894 void *ctx) 895{ 896 int rc; 897 898 Debug( LDAP_DEBUG_TRACE, ">>> authzPretty: <%s>\n", 899 val->bv_val, 0, 0 ); 900 901 rc = authzPrettyNormal( val, out, ctx, 0 ); 902 903 Debug( LDAP_DEBUG_TRACE, "<<< authzPretty: <%s> (%d)\n", 904 out->bv_val, rc, 0 ); 905 906 return rc; 907} 908 909 910static int 911slap_parseURI( 912 Operation *op, 913 struct berval *uri, 914 struct berval *base, 915 struct berval *nbase, 916 int *scope, 917 Filter **filter, 918 struct berval *fstr, 919 int normalize ) 920{ 921 struct berval bv; 922 int rc; 923 LDAPURLDesc *ludp; 924 925 struct berval idx; 926 927 assert( uri != NULL && !BER_BVISNULL( uri ) ); 928 BER_BVZERO( base ); 929 BER_BVZERO( nbase ); 930 BER_BVZERO( fstr ); 931 *scope = -1; 932 *filter = NULL; 933 934 Debug( LDAP_DEBUG_TRACE, 935 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 ); 936 937 rc = LDAP_PROTOCOL_ERROR; 938 939 idx = *uri; 940 if ( idx.bv_val[ 0 ] == '{' ) { 941 char *ptr; 942 943 ptr = ber_bvchr( &idx, '}' ) + 1; 944 945 assert( ptr != (void *)1 ); 946 947 idx.bv_len -= ptr - idx.bv_val; 948 idx.bv_val = ptr; 949 uri = &idx; 950 } 951 952 /* 953 * dn[.<dnstyle>]:<dnpattern> 954 * <dnstyle> ::= {exact|regex|children|subtree|onelevel} 955 * 956 * <dnstyle> defaults to "exact" 957 * if <dnstyle> is not "regex", <dnpattern> must pass DN normalization 958 */ 959 if ( !strncasecmp( uri->bv_val, "dn", STRLENOF( "dn" ) ) ) { 960 bv.bv_val = uri->bv_val + STRLENOF( "dn" ); 961 962 if ( bv.bv_val[ 0 ] == '.' ) { 963 bv.bv_val++; 964 965 if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) { 966 bv.bv_val += STRLENOF( "exact:" ); 967 *scope = LDAP_X_SCOPE_EXACT; 968 969 } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) { 970 bv.bv_val += STRLENOF( "regex:" ); 971 *scope = LDAP_X_SCOPE_REGEX; 972 973 } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) { 974 bv.bv_val += STRLENOF( "children:" ); 975 *scope = LDAP_X_SCOPE_CHILDREN; 976 977 } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) { 978 bv.bv_val += STRLENOF( "subtree:" ); 979 *scope = LDAP_X_SCOPE_SUBTREE; 980 981 } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) { 982 bv.bv_val += STRLENOF( "onelevel:" ); 983 *scope = LDAP_X_SCOPE_ONELEVEL; 984 985 } else { 986 return LDAP_PROTOCOL_ERROR; 987 } 988 989 } else { 990 if ( bv.bv_val[ 0 ] != ':' ) { 991 return LDAP_PROTOCOL_ERROR; 992 } 993 *scope = LDAP_X_SCOPE_EXACT; 994 bv.bv_val++; 995 } 996 997 bv.bv_val += strspn( bv.bv_val, " " ); 998 /* jump here in case no type specification was present 999 * and uri was not an URI... HEADS-UP: assuming EXACT */ 1000is_dn: bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val); 1001 1002 /* a single '*' means any DN without using regexes */ 1003 if ( ber_bvccmp( &bv, '*' ) ) { 1004 *scope = LDAP_X_SCOPE_USERS; 1005 } 1006 1007 switch ( *scope ) { 1008 case LDAP_X_SCOPE_EXACT: 1009 case LDAP_X_SCOPE_CHILDREN: 1010 case LDAP_X_SCOPE_SUBTREE: 1011 case LDAP_X_SCOPE_ONELEVEL: 1012 if ( normalize ) { 1013 rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx ); 1014 if( rc != LDAP_SUCCESS ) { 1015 *scope = -1; 1016 } 1017 } else { 1018 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx ); 1019 rc = LDAP_SUCCESS; 1020 } 1021 break; 1022 1023 case LDAP_X_SCOPE_REGEX: 1024 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx ); 1025 1026 case LDAP_X_SCOPE_USERS: 1027 rc = LDAP_SUCCESS; 1028 break; 1029 1030 default: 1031 *scope = -1; 1032 break; 1033 } 1034 1035 return rc; 1036 1037 /* 1038 * u:<uid> 1039 */ 1040 } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' ) 1041 && ( uri->bv_val[ 1 ] == ':' 1042 || uri->bv_val[ 1 ] == '/' 1043 || uri->bv_val[ 1 ] == '.' ) ) 1044 { 1045 Connection c = *op->o_conn; 1046 char buf[ SLAP_LDAPDN_MAXLEN ]; 1047 struct berval id, 1048 user = BER_BVNULL, 1049 realm = BER_BVNULL, 1050 mech = BER_BVNULL; 1051 1052 if ( sizeof( buf ) <= uri->bv_len ) { 1053 return LDAP_INVALID_SYNTAX; 1054 } 1055 1056 id.bv_len = uri->bv_len; 1057 id.bv_val = buf; 1058 strncpy( buf, uri->bv_val, sizeof( buf ) ); 1059 1060 rc = slap_parse_user( &id, &user, &realm, &mech ); 1061 if ( rc != LDAP_SUCCESS ) { 1062 return rc; 1063 } 1064 1065 if ( !BER_BVISNULL( &mech ) ) { 1066 c.c_sasl_bind_mech = mech; 1067 } else { 1068 BER_BVSTR( &c.c_sasl_bind_mech, "AUTHZ" ); 1069 } 1070 1071 rc = slap_sasl_getdn( &c, op, &user, 1072 realm.bv_val, nbase, SLAP_GETDN_AUTHZID ); 1073 1074 if ( rc == LDAP_SUCCESS ) { 1075 *scope = LDAP_X_SCOPE_EXACT; 1076 } 1077 1078 return rc; 1079 1080 /* 1081 * group[/<groupoc>[/<groupat>]]:<groupdn> 1082 * 1083 * groupoc defaults to "groupOfNames" 1084 * groupat defaults to "member" 1085 * 1086 * <groupdn> must pass DN normalization 1087 */ 1088 } else if ( strncasecmp( uri->bv_val, "group", STRLENOF( "group" ) ) == 0 ) 1089 { 1090 struct berval group_dn = BER_BVNULL, 1091 group_oc = BER_BVNULL, 1092 member_at = BER_BVNULL; 1093 char *tmp; 1094 1095 bv.bv_val = uri->bv_val + STRLENOF( "group" ); 1096 bv.bv_len = uri->bv_len - STRLENOF( "group" ); 1097 group_dn.bv_val = ber_bvchr( &bv, ':' ); 1098 if ( group_dn.bv_val == NULL ) { 1099 /* last chance: assume it's a(n exact) DN ... */ 1100 bv.bv_val = uri->bv_val; 1101 *scope = LDAP_X_SCOPE_EXACT; 1102 goto is_dn; 1103 } 1104 1105 if ( bv.bv_val[ 0 ] == '/' ) { 1106 group_oc.bv_val = &bv.bv_val[ 1 ]; 1107 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val; 1108 1109 member_at.bv_val = ber_bvchr( &group_oc, '/' ); 1110 if ( member_at.bv_val ) { 1111 group_oc.bv_len = member_at.bv_val - group_oc.bv_val; 1112 member_at.bv_val++; 1113 member_at.bv_len = group_dn.bv_val - member_at.bv_val; 1114 1115 } else { 1116 BER_BVSTR( &member_at, SLAPD_GROUP_ATTR ); 1117 } 1118 1119 } else { 1120 BER_BVSTR( &group_oc, SLAPD_GROUP_CLASS ); 1121 BER_BVSTR( &member_at, SLAPD_GROUP_ATTR ); 1122 } 1123 group_dn.bv_val++; 1124 group_dn.bv_len = uri->bv_len - ( group_dn.bv_val - uri->bv_val ); 1125 1126 if ( normalize ) { 1127 rc = dnNormalize( 0, NULL, NULL, &group_dn, nbase, op->o_tmpmemctx ); 1128 if ( rc != LDAP_SUCCESS ) { 1129 *scope = -1; 1130 return rc; 1131 } 1132 } else { 1133 ber_dupbv_x( nbase, &group_dn, op->o_tmpmemctx ); 1134 rc = LDAP_SUCCESS; 1135 } 1136 *scope = LDAP_X_SCOPE_GROUP; 1137 1138 /* FIXME: caller needs to add value of member attribute 1139 * and close brackets twice */ 1140 fstr->bv_len = STRLENOF( "(&(objectClass=)(=" /* )) */ ) 1141 + group_oc.bv_len + member_at.bv_len; 1142 fstr->bv_val = ch_malloc( fstr->bv_len + 1 ); 1143 1144 tmp = lutil_strncopy( fstr->bv_val, "(&(objectClass=" /* )) */ , 1145 STRLENOF( "(&(objectClass=" /* )) */ ) ); 1146 tmp = lutil_strncopy( tmp, group_oc.bv_val, group_oc.bv_len ); 1147 tmp = lutil_strncopy( tmp, /* ( */ ")(" /* ) */ , 1148 STRLENOF( /* ( */ ")(" /* ) */ ) ); 1149 tmp = lutil_strncopy( tmp, member_at.bv_val, member_at.bv_len ); 1150 tmp = lutil_strncopy( tmp, "=", STRLENOF( "=" ) ); 1151 1152 return rc; 1153 } 1154 1155 /* 1156 * ldap:///<base>??<scope>?<filter> 1157 * <scope> ::= {base|one|subtree} 1158 * 1159 * <scope> defaults to "base" 1160 * <base> must pass DN normalization 1161 * <filter> must pass str2filter() 1162 */ 1163 rc = ldap_url_parse( uri->bv_val, &ludp ); 1164 switch ( rc ) { 1165 case LDAP_URL_SUCCESS: 1166 /* FIXME: the check is pedantic, but I think it's necessary, 1167 * because people tend to use things like ldaps:// which 1168 * gives the idea SSL is being used. Maybe we could 1169 * accept ldapi:// as well, but the point is that we use 1170 * an URL as an easy means to define bits of a search with 1171 * little parsing. 1172 */ 1173 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) { 1174 /* 1175 * must be ldap:/// 1176 */ 1177 rc = LDAP_PROTOCOL_ERROR; 1178 goto done; 1179 } 1180 break; 1181 1182 case LDAP_URL_ERR_BADSCHEME: 1183 /* 1184 * last chance: assume it's a(n exact) DN ... 1185 * 1186 * NOTE: must pass DN normalization 1187 */ 1188 ldap_free_urldesc( ludp ); 1189 bv.bv_val = uri->bv_val; 1190 *scope = LDAP_X_SCOPE_EXACT; 1191 goto is_dn; 1192 1193 default: 1194 rc = LDAP_PROTOCOL_ERROR; 1195 goto done; 1196 } 1197 1198 if ( ( ludp->lud_host && *ludp->lud_host ) 1199 || ludp->lud_attrs || ludp->lud_exts ) 1200 { 1201 /* host part must be empty */ 1202 /* attrs and extensions parts must be empty */ 1203 rc = LDAP_PROTOCOL_ERROR; 1204 goto done; 1205 } 1206 1207 /* Grab the scope */ 1208 *scope = ludp->lud_scope; 1209 1210 /* Grab the filter */ 1211 if ( ludp->lud_filter ) { 1212 *filter = str2filter_x( op, ludp->lud_filter ); 1213 if ( *filter == NULL ) { 1214 rc = LDAP_PROTOCOL_ERROR; 1215 goto done; 1216 } 1217 ber_str2bv( ludp->lud_filter, 0, 0, fstr ); 1218 } 1219 1220 /* Grab the searchbase */ 1221 ber_str2bv( ludp->lud_dn, 0, 0, base ); 1222 if ( normalize ) { 1223 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx ); 1224 } else { 1225 ber_dupbv_x( nbase, base, op->o_tmpmemctx ); 1226 rc = LDAP_SUCCESS; 1227 } 1228 1229done: 1230 if( rc != LDAP_SUCCESS ) { 1231 if( *filter ) filter_free_x( op, *filter, 1 ); 1232 BER_BVZERO( base ); 1233 BER_BVZERO( fstr ); 1234 } else { 1235 /* Don't free these, return them to caller */ 1236 ludp->lud_filter = NULL; 1237 ludp->lud_dn = NULL; 1238 } 1239 1240 ldap_free_urldesc( ludp ); 1241 return( rc ); 1242} 1243 1244#ifndef SLAP_AUTH_REWRITE 1245static int slap_sasl_rx_off(char *rep, int *off) 1246{ 1247 const char *c; 1248 int n; 1249 1250 /* Precompile replace pattern. Find the $<n> placeholders */ 1251 off[0] = -2; 1252 n = 1; 1253 for ( c = rep; *c; c++ ) { 1254 if ( *c == '\\' && c[1] ) { 1255 c++; 1256 continue; 1257 } 1258 if ( *c == '$' ) { 1259 if ( n == SASLREGEX_REPLACE ) { 1260 Debug( LDAP_DEBUG_ANY, 1261 "SASL replace pattern %s has too many $n " 1262 "placeholders (max %d)\n", 1263 rep, SASLREGEX_REPLACE, 0 ); 1264 1265 return( LDAP_OTHER ); 1266 } 1267 off[n] = c - rep; 1268 n++; 1269 } 1270 } 1271 1272 /* Final placeholder, after the last $n */ 1273 off[n] = c - rep; 1274 n++; 1275 off[n] = -1; 1276 return( LDAP_SUCCESS ); 1277} 1278#endif /* ! SLAP_AUTH_REWRITE */ 1279 1280#ifdef SLAP_AUTH_REWRITE 1281int slap_sasl_rewrite_config( 1282 const char *fname, 1283 int lineno, 1284 int argc, 1285 char **argv 1286) 1287{ 1288 int rc; 1289 char *savearg0; 1290 1291 /* init at first call */ 1292 if ( sasl_rwinfo == NULL ) { 1293 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT ); 1294 } 1295 1296 /* strip "authid-" prefix for parsing */ 1297 savearg0 = argv[0]; 1298 argv[0] += STRLENOF( "authid-" ); 1299 rc = rewrite_parse( sasl_rwinfo, fname, lineno, argc, argv ); 1300 argv[0] = savearg0; 1301 1302 return rc; 1303} 1304 1305static int 1306slap_sasl_rewrite_destroy( void ) 1307{ 1308 if ( sasl_rwinfo ) { 1309 rewrite_info_delete( &sasl_rwinfo ); 1310 sasl_rwinfo = NULL; 1311 } 1312 1313 return 0; 1314} 1315 1316int slap_sasl_regexp_rewrite_config( 1317 const char *fname, 1318 int lineno, 1319 const char *match, 1320 const char *replace, 1321 const char *context ) 1322{ 1323 int rc; 1324 char *argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL }; 1325 1326 /* init at first call */ 1327 if ( sasl_rwinfo == NULL ) { 1328 char *argvEngine[] = { "rewriteEngine", "on", NULL }; 1329 char *argvContext[] = { "rewriteContext", NULL, NULL }; 1330 1331 /* initialize rewrite engine */ 1332 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT ); 1333 1334 /* switch on rewrite engine */ 1335 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine ); 1336 if (rc != LDAP_SUCCESS) { 1337 return rc; 1338 } 1339 1340 /* create generic authid context */ 1341 argvContext[1] = AUTHID_CONTEXT; 1342 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvContext ); 1343 if (rc != LDAP_SUCCESS) { 1344 return rc; 1345 } 1346 } 1347 1348 argvRule[1] = (char *)match; 1349 argvRule[2] = (char *)replace; 1350 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule ); 1351 1352 return rc; 1353} 1354#endif /* SLAP_AUTH_REWRITE */ 1355 1356int slap_sasl_regexp_config( const char *match, const char *replace ) 1357{ 1358 int rc; 1359 SaslRegexp_t *reg; 1360 1361 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp, 1362 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) ); 1363 1364 reg = &SaslRegexp[nSaslRegexp]; 1365 1366#ifdef SLAP_AUTH_REWRITE 1367 rc = slap_sasl_regexp_rewrite_config( "sasl-regexp", 0, 1368 match, replace, AUTHID_CONTEXT ); 1369#else /* ! SLAP_AUTH_REWRITE */ 1370 1371 /* Precompile matching pattern */ 1372 rc = regcomp( ®->sr_workspace, match, REG_EXTENDED|REG_ICASE ); 1373 if ( rc ) { 1374 Debug( LDAP_DEBUG_ANY, 1375 "SASL match pattern %s could not be compiled by regexp engine\n", 1376 match, 0, 0 ); 1377 1378#ifdef ENABLE_REWRITE 1379 /* Dummy block to force symbol references in librewrite */ 1380 if ( slapMode == ( SLAP_SERVER_MODE|SLAP_TOOL_MODE )) { 1381 rewrite_info_init( 0 ); 1382 } 1383#endif 1384 return( LDAP_OTHER ); 1385 } 1386 1387 rc = slap_sasl_rx_off( replace, reg->sr_offset ); 1388#endif /* ! SLAP_AUTH_REWRITE */ 1389 if ( rc == LDAP_SUCCESS ) { 1390 reg->sr_match = ch_strdup( match ); 1391 reg->sr_replace = ch_strdup( replace ); 1392 1393 nSaslRegexp++; 1394 } 1395 1396 return rc; 1397} 1398 1399void 1400slap_sasl_regexp_destroy( void ) 1401{ 1402 if ( SaslRegexp ) { 1403 int n; 1404 1405 for ( n = 0; n < nSaslRegexp; n++ ) { 1406 ch_free( SaslRegexp[ n ].sr_match ); 1407 ch_free( SaslRegexp[ n ].sr_replace ); 1408#ifndef SLAP_AUTH_REWRITE 1409 regfree( &SaslRegexp[ n ].sr_workspace ); 1410#endif /* SLAP_AUTH_REWRITE */ 1411 } 1412 1413 ch_free( SaslRegexp ); 1414 } 1415 1416#ifdef SLAP_AUTH_REWRITE 1417 slap_sasl_rewrite_destroy(); 1418#endif /* SLAP_AUTH_REWRITE */ 1419} 1420 1421void slap_sasl_regexp_unparse( BerVarray *out ) 1422{ 1423 int i; 1424 BerVarray bva = NULL; 1425 char ibuf[32], *ptr; 1426 struct berval idx; 1427 1428 if ( !nSaslRegexp ) return; 1429 1430 idx.bv_val = ibuf; 1431 bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) ); 1432 BER_BVZERO(bva+nSaslRegexp); 1433 for ( i=0; i<nSaslRegexp; i++ ) { 1434 idx.bv_len = sprintf( idx.bv_val, "{%d}", i); 1435 bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) + 1436 strlen( SaslRegexp[i].sr_replace ) + 5; 1437 bva[i].bv_val = ch_malloc( bva[i].bv_len+1 ); 1438 ptr = lutil_strcopy( bva[i].bv_val, ibuf ); 1439 *ptr++ = '"'; 1440 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match ); 1441 ptr = lutil_strcopy( ptr, "\" \"" ); 1442 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace ); 1443 *ptr++ = '"'; 1444 *ptr = '\0'; 1445 } 1446 *out = bva; 1447} 1448 1449#ifndef SLAP_AUTH_REWRITE 1450/* Perform replacement on regexp matches */ 1451static void slap_sasl_rx_exp( 1452 const char *rep, 1453 const int *off, 1454 regmatch_t *str, 1455 const char *saslname, 1456 struct berval *out, 1457 void *ctx ) 1458{ 1459 int i, n, len, insert; 1460 1461 /* Get the total length of the final URI */ 1462 1463 n=1; 1464 len = 0; 1465 while( off[n] >= 0 ) { 1466 /* Len of next section from replacement string (x,y,z above) */ 1467 len += off[n] - off[n-1] - 2; 1468 if( off[n+1] < 0) 1469 break; 1470 1471 /* Len of string from saslname that matched next $i (b,d above) */ 1472 i = rep[ off[n] + 1 ] - '0'; 1473 len += str[i].rm_eo - str[i].rm_so; 1474 n++; 1475 } 1476 out->bv_val = slap_sl_malloc( len + 1, ctx ); 1477 out->bv_len = len; 1478 1479 /* Fill in URI with replace string, replacing $i as we go */ 1480 n=1; 1481 insert = 0; 1482 while( off[n] >= 0) { 1483 /* Paste in next section from replacement string (x,y,z above) */ 1484 len = off[n] - off[n-1] - 2; 1485 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len); 1486 insert += len; 1487 if( off[n+1] < 0) 1488 break; 1489 1490 /* Paste in string from saslname that matched next $i (b,d above) */ 1491 i = rep[ off[n] + 1 ] - '0'; 1492 len = str[i].rm_eo - str[i].rm_so; 1493 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len ); 1494 insert += len; 1495 1496 n++; 1497 } 1498 1499 out->bv_val[insert] = '\0'; 1500} 1501#endif /* ! SLAP_AUTH_REWRITE */ 1502 1503/* Take the passed in SASL name and attempt to convert it into an 1504 LDAP URI to find the matching LDAP entry, using the pattern matching 1505 strings given in the saslregexp config file directive(s) */ 1506 1507static int slap_authz_regexp( struct berval *in, struct berval *out, 1508 int flags, void *ctx ) 1509{ 1510#ifdef SLAP_AUTH_REWRITE 1511 const char *context = AUTHID_CONTEXT; 1512 1513 if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) { 1514 return 0; 1515 } 1516 1517 /* FIXME: if aware of authc/authz mapping, 1518 * we could use different contexts ... */ 1519 switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL, 1520 &out->bv_val ) ) 1521 { 1522 case REWRITE_REGEXEC_OK: 1523 if ( !BER_BVISNULL( out ) ) { 1524 char *val = out->bv_val; 1525 ber_str2bv_x( val, 0, 1, out, ctx ); 1526 if ( val != in->bv_val ) { 1527 free( val ); 1528 } 1529 } else { 1530 ber_dupbv_x( out, in, ctx ); 1531 } 1532 Debug( LDAP_DEBUG_ARGS, 1533 "[rw] %s: \"%s\" -> \"%s\"\n", 1534 context, in->bv_val, out->bv_val ); 1535 return 1; 1536 1537 case REWRITE_REGEXEC_UNWILLING: 1538 case REWRITE_REGEXEC_ERR: 1539 default: 1540 return 0; 1541 } 1542 1543#else /* ! SLAP_AUTH_REWRITE */ 1544 char *saslname = in->bv_val; 1545 SaslRegexp_t *reg; 1546 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */ 1547 int i; 1548 1549 memset( out, 0, sizeof( *out ) ); 1550 1551 Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n", 1552 saslname, 0, 0 ); 1553 1554 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) { 1555 return( 0 ); 1556 } 1557 1558 /* Match the normalized SASL name to the saslregexp patterns */ 1559 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) { 1560 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE, 1561 sr_strings, 0) == 0 ) 1562 break; 1563 } 1564 1565 if( i >= nSaslRegexp ) return( 0 ); 1566 1567 /* 1568 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the 1569 * replace pattern of the form "x$1y$2z". The returned string needs 1570 * to replace the $1,$2 with the strings that matched (b.*) and (d.*) 1571 */ 1572 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset, 1573 sr_strings, saslname, out, ctx ); 1574 1575 Debug( LDAP_DEBUG_TRACE, 1576 "slap_authz_regexp: converted SASL name to %s\n", 1577 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 ); 1578 1579 return( 1 ); 1580#endif /* ! SLAP_AUTH_REWRITE */ 1581} 1582 1583/* This callback actually does some work...*/ 1584static int sasl_sc_sasl2dn( Operation *op, SlapReply *rs ) 1585{ 1586 struct berval *ndn = op->o_callback->sc_private; 1587 1588 if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS; 1589 1590 /* We only want to be called once */ 1591 if ( !BER_BVISNULL( ndn ) ) { 1592 op->o_tmpfree( ndn->bv_val, op->o_tmpmemctx ); 1593 BER_BVZERO( ndn ); 1594 1595 Debug( LDAP_DEBUG_TRACE, 1596 "%s: slap_sc_sasl2dn: search DN returned more than 1 entry\n", 1597 op->o_log_prefix, 0, 0 ); 1598 return LDAP_UNAVAILABLE; /* short-circuit the search */ 1599 } 1600 1601 ber_dupbv_x( ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx ); 1602 return LDAP_SUCCESS; 1603} 1604 1605 1606typedef struct smatch_info { 1607 struct berval *dn; 1608 int match; 1609} smatch_info; 1610 1611static int sasl_sc_smatch( Operation *o, SlapReply *rs ) 1612{ 1613 smatch_info *sm = o->o_callback->sc_private; 1614 1615 if (rs->sr_type != REP_SEARCH) return 0; 1616 1617 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) { 1618 sm->match = 1; 1619 return LDAP_UNAVAILABLE; /* short-circuit the search */ 1620 } 1621 1622 return 0; 1623} 1624 1625int 1626slap_sasl_matches( Operation *op, BerVarray rules, 1627 struct berval *assertDN, struct berval *authc ) 1628{ 1629 int rc = LDAP_INAPPROPRIATE_AUTH; 1630 1631 if ( rules != NULL ) { 1632 int i; 1633 1634 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) { 1635 rc = slap_sasl_match( op, &rules[i], assertDN, authc ); 1636 if ( rc == LDAP_SUCCESS ) break; 1637 } 1638 } 1639 1640 return rc; 1641} 1642 1643/* 1644 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base 1645 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise, 1646 * the rule must be used as an internal search for entries. If that search 1647 * returns the *assertDN entry, the match is successful. 1648 * 1649 * The assertDN should not have the dn: prefix 1650 */ 1651 1652static int 1653slap_sasl_match( Operation *opx, struct berval *rule, 1654 struct berval *assertDN, struct berval *authc ) 1655{ 1656 int rc; 1657 regex_t reg; 1658 smatch_info sm; 1659 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL }; 1660 Operation op = {0}; 1661 SlapReply rs = {REP_RESULT}; 1662 struct berval base = BER_BVNULL; 1663 1664 sm.dn = assertDN; 1665 sm.match = 0; 1666 cb.sc_private = &sm; 1667 1668 Debug( LDAP_DEBUG_TRACE, 1669 "===>slap_sasl_match: comparing DN %s to rule %s\n", 1670 assertDN->bv_len ? assertDN->bv_val : "(null)", rule->bv_val, 0 ); 1671 1672 /* NOTE: don't normalize rule if authz syntax is enabled */ 1673 rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn, 1674 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 0 ); 1675 1676 if( rc != LDAP_SUCCESS ) goto CONCLUDED; 1677 1678 switch ( op.ors_scope ) { 1679 case LDAP_X_SCOPE_EXACT: 1680exact_match: 1681 if ( dn_match( &op.o_req_ndn, assertDN ) ) { 1682 rc = LDAP_SUCCESS; 1683 } else { 1684 rc = LDAP_INAPPROPRIATE_AUTH; 1685 } 1686 goto CONCLUDED; 1687 1688 case LDAP_X_SCOPE_CHILDREN: 1689 case LDAP_X_SCOPE_SUBTREE: 1690 case LDAP_X_SCOPE_ONELEVEL: 1691 { 1692 int d = assertDN->bv_len - op.o_req_ndn.bv_len; 1693 1694 rc = LDAP_INAPPROPRIATE_AUTH; 1695 1696 if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) { 1697 goto exact_match; 1698 1699 } else if ( d > 0 ) { 1700 struct berval bv; 1701 1702 /* leave room for at least one char of attributeType, 1703 * one for '=' and one for ',' */ 1704 if ( d < (int) STRLENOF( "x=,") ) { 1705 goto CONCLUDED; 1706 } 1707 1708 bv.bv_len = op.o_req_ndn.bv_len; 1709 bv.bv_val = assertDN->bv_val + d; 1710 1711 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) { 1712 switch ( op.ors_scope ) { 1713 case LDAP_X_SCOPE_SUBTREE: 1714 case LDAP_X_SCOPE_CHILDREN: 1715 rc = LDAP_SUCCESS; 1716 break; 1717 1718 case LDAP_X_SCOPE_ONELEVEL: 1719 { 1720 struct berval pdn; 1721 1722 dnParent( assertDN, &pdn ); 1723 /* the common portion of the DN 1724 * already matches, so only check 1725 * if parent DN of assertedDN 1726 * is all the pattern */ 1727 if ( pdn.bv_len == op.o_req_ndn.bv_len ) { 1728 rc = LDAP_SUCCESS; 1729 } 1730 break; 1731 } 1732 default: 1733 /* at present, impossible */ 1734 assert( 0 ); 1735 } 1736 } 1737 } 1738 goto CONCLUDED; 1739 } 1740 1741 case LDAP_X_SCOPE_REGEX: 1742 rc = regcomp(®, op.o_req_ndn.bv_val, 1743 REG_EXTENDED|REG_ICASE|REG_NOSUB); 1744 if ( rc == 0 ) { 1745 rc = regexec(®, assertDN->bv_val, 0, NULL, 0); 1746 regfree( ® ); 1747 } 1748 if ( rc == 0 ) { 1749 rc = LDAP_SUCCESS; 1750 } else { 1751 rc = LDAP_INAPPROPRIATE_AUTH; 1752 } 1753 goto CONCLUDED; 1754 1755 case LDAP_X_SCOPE_GROUP: { 1756 char *tmp; 1757 1758 /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>=" 1759 * we need to append the <assertDN> so that the <group_dn> is searched 1760 * with scope "base", and the filter ensures that <assertDN> is 1761 * member of the group */ 1762 tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len + 1763 assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 ); 1764 if ( tmp == NULL ) { 1765 rc = LDAP_NO_MEMORY; 1766 goto CONCLUDED; 1767 } 1768 op.ors_filterstr.bv_val = tmp; 1769 1770 tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val ); 1771 tmp = lutil_strcopy( tmp, /*"(("*/ "))" ); 1772 1773 /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */ 1774 op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val ); 1775 if ( op.ors_filter == NULL ) { 1776 rc = LDAP_PROTOCOL_ERROR; 1777 goto CONCLUDED; 1778 } 1779 op.ors_scope = LDAP_SCOPE_BASE; 1780 1781 /* hijack match DN: use that of the group instead of the assertDN; 1782 * assertDN is now in the filter */ 1783 sm.dn = &op.o_req_ndn; 1784 1785 /* do the search */ 1786 break; 1787 } 1788 1789 case LDAP_X_SCOPE_USERS: 1790 if ( !BER_BVISEMPTY( assertDN ) ) { 1791 rc = LDAP_SUCCESS; 1792 } else { 1793 rc = LDAP_INAPPROPRIATE_AUTH; 1794 } 1795 goto CONCLUDED; 1796 1797 default: 1798 break; 1799 } 1800 1801 /* Must run an internal search. */ 1802 if ( op.ors_filter == NULL ) { 1803 rc = LDAP_FILTER_ERROR; 1804 goto CONCLUDED; 1805 } 1806 1807 Debug( LDAP_DEBUG_TRACE, 1808 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n", 1809 op.o_req_ndn.bv_val, op.ors_scope, 0 ); 1810 1811 op.o_bd = select_backend( &op.o_req_ndn, 1 ); 1812 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) { 1813 rc = LDAP_INAPPROPRIATE_AUTH; 1814 goto CONCLUDED; 1815 } 1816 1817 op.o_hdr = opx->o_hdr; 1818 op.o_tag = LDAP_REQ_SEARCH; 1819 op.o_ndn = *authc; 1820 op.o_callback = &cb; 1821 slap_op_time( &op.o_time, &op.o_tincr ); 1822 op.o_do_not_cache = 1; 1823 op.o_is_auth_check = 1; 1824 /* use req_ndn as req_dn instead of non-pretty base of uri */ 1825 if( !BER_BVISNULL( &base ) ) { 1826 ch_free( base.bv_val ); 1827 /* just in case... */ 1828 BER_BVZERO( &base ); 1829 } 1830 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx ); 1831 op.ors_deref = LDAP_DEREF_NEVER; 1832 op.ors_slimit = 1; 1833 op.ors_tlimit = SLAP_NO_LIMIT; 1834 op.ors_attrs = slap_anlist_no_attrs; 1835 op.ors_attrsonly = 1; 1836 1837 op.o_bd->be_search( &op, &rs ); 1838 1839 if (sm.match) { 1840 rc = LDAP_SUCCESS; 1841 } else { 1842 rc = LDAP_INAPPROPRIATE_AUTH; 1843 } 1844 1845CONCLUDED: 1846 if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx ); 1847 if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx ); 1848 if( op.ors_filter ) filter_free_x( opx, op.ors_filter, 1 ); 1849 if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val ); 1850 1851 Debug( LDAP_DEBUG_TRACE, 1852 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0); 1853 1854 return( rc ); 1855} 1856 1857 1858/* 1859 * This function answers the question, "Can this ID authorize to that ID?", 1860 * based on authorization rules. The rules are stored in the *searchDN, in the 1861 * attribute named by *attr. If any of those rules map to the *assertDN, the 1862 * authorization is approved. 1863 * 1864 * The DNs should not have the dn: prefix 1865 */ 1866static int 1867slap_sasl_check_authz( Operation *op, 1868 struct berval *searchDN, 1869 struct berval *assertDN, 1870 AttributeDescription *ad, 1871 struct berval *authc ) 1872{ 1873 int rc, 1874 do_not_cache = op->o_do_not_cache; 1875 BerVarray vals = NULL; 1876 1877 Debug( LDAP_DEBUG_TRACE, 1878 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n", 1879 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val); 1880 1881 /* ITS#4760: don't cache group access */ 1882 op->o_do_not_cache = 1; 1883 rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH ); 1884 op->o_do_not_cache = do_not_cache; 1885 if( rc != LDAP_SUCCESS ) goto COMPLETE; 1886 1887 /* Check if the *assertDN matches any *vals */ 1888 rc = slap_sasl_matches( op, vals, assertDN, authc ); 1889 1890COMPLETE: 1891 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx ); 1892 1893 Debug( LDAP_DEBUG_TRACE, 1894 "<==slap_sasl_check_authz: %s check returning %d\n", 1895 ad->ad_cname.bv_val, rc, 0); 1896 1897 return( rc ); 1898} 1899 1900/* 1901 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH") 1902 * return the LDAP DN to which it matches. The SASL regexp rules in the config 1903 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a 1904 * search with scope=base), just return the URI (or its searchbase). Otherwise 1905 * an internal search must be done, and if that search returns exactly one 1906 * entry, return the DN of that one entry. 1907 */ 1908void 1909slap_sasl2dn( 1910 Operation *opx, 1911 struct berval *saslname, 1912 struct berval *sasldn, 1913 int flags ) 1914{ 1915 int rc; 1916 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL }; 1917 Operation op = {0}; 1918 SlapReply rs = {REP_RESULT}; 1919 struct berval regout = BER_BVNULL; 1920 struct berval base = BER_BVNULL; 1921 1922 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: " 1923 "converting SASL name %s to a DN\n", 1924 saslname->bv_val, 0,0 ); 1925 1926 BER_BVZERO( sasldn ); 1927 cb.sc_private = sasldn; 1928 1929 /* Convert the SASL name into a minimal URI */ 1930 if( !slap_authz_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) { 1931 goto FINISHED; 1932 } 1933 1934 /* NOTE: always normalize regout because it results 1935 * from string submatch expansion */ 1936 rc = slap_parseURI( opx, ®out, &base, &op.o_req_ndn, 1937 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 ); 1938 if ( !BER_BVISNULL( ®out ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx ); 1939 if ( rc != LDAP_SUCCESS ) { 1940 goto FINISHED; 1941 } 1942 1943 /* Must do an internal search */ 1944 op.o_bd = select_backend( &op.o_req_ndn, 1 ); 1945 1946 switch ( op.ors_scope ) { 1947 case LDAP_X_SCOPE_EXACT: 1948 *sasldn = op.o_req_ndn; 1949 BER_BVZERO( &op.o_req_ndn ); 1950 /* intentionally continue to next case */ 1951 1952 case LDAP_X_SCOPE_REGEX: 1953 case LDAP_X_SCOPE_SUBTREE: 1954 case LDAP_X_SCOPE_CHILDREN: 1955 case LDAP_X_SCOPE_ONELEVEL: 1956 case LDAP_X_SCOPE_GROUP: 1957 case LDAP_X_SCOPE_USERS: 1958 /* correctly parsed, but illegal */ 1959 goto FINISHED; 1960 1961 case LDAP_SCOPE_BASE: 1962 case LDAP_SCOPE_ONELEVEL: 1963 case LDAP_SCOPE_SUBTREE: 1964 case LDAP_SCOPE_SUBORDINATE: 1965 /* do a search */ 1966 break; 1967 1968 default: 1969 /* catch unhandled cases (there shouldn't be) */ 1970 assert( 0 ); 1971 } 1972 1973 Debug( LDAP_DEBUG_TRACE, 1974 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n", 1975 op.o_req_ndn.bv_val, op.ors_scope, 0 ); 1976 1977 if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) { 1978 goto FINISHED; 1979 } 1980 1981 /* Must run an internal search. */ 1982 if ( op.ors_filter == NULL ) { 1983 rc = LDAP_FILTER_ERROR; 1984 goto FINISHED; 1985 } 1986 1987 op.o_hdr = opx->o_hdr; 1988 op.o_tag = LDAP_REQ_SEARCH; 1989 op.o_ndn = opx->o_conn->c_ndn; 1990 op.o_callback = &cb; 1991 slap_op_time( &op.o_time, &op.o_tincr ); 1992 op.o_do_not_cache = 1; 1993 op.o_is_auth_check = 1; 1994 op.ors_deref = LDAP_DEREF_NEVER; 1995 op.ors_slimit = 1; 1996 op.ors_tlimit = SLAP_NO_LIMIT; 1997 op.ors_attrs = slap_anlist_no_attrs; 1998 op.ors_attrsonly = 1; 1999 /* use req_ndn as req_dn instead of non-pretty base of uri */ 2000 if( !BER_BVISNULL( &base ) ) { 2001 ch_free( base.bv_val ); 2002 /* just in case... */ 2003 BER_BVZERO( &base ); 2004 } 2005 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx ); 2006 2007 op.o_bd->be_search( &op, &rs ); 2008 2009FINISHED: 2010 if( !BER_BVISEMPTY( sasldn ) ) { 2011 opx->o_conn->c_authz_backend = op.o_bd; 2012 } 2013 if( !BER_BVISNULL( &op.o_req_dn ) ) { 2014 slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx ); 2015 } 2016 if( !BER_BVISNULL( &op.o_req_ndn ) ) { 2017 slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx ); 2018 } 2019 if( op.ors_filter ) { 2020 filter_free_x( opx, op.ors_filter, 1 ); 2021 } 2022 if( !BER_BVISNULL( &op.ors_filterstr ) ) { 2023 ch_free( op.ors_filterstr.bv_val ); 2024 } 2025 2026 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n", 2027 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 ); 2028 2029 return; 2030} 2031 2032 2033/* Check if a bind can SASL authorize to another identity. 2034 * The DNs should not have the dn: prefix 2035 */ 2036 2037int slap_sasl_authorized( Operation *op, 2038 struct berval *authcDN, struct berval *authzDN ) 2039{ 2040 int rc = LDAP_INAPPROPRIATE_AUTH; 2041 2042 /* User binding as anonymous */ 2043 if ( !authzDN || !authzDN->bv_len || !authzDN->bv_val ) { 2044 rc = LDAP_SUCCESS; 2045 goto DONE; 2046 } 2047 2048 /* User is anonymous */ 2049 if ( !authcDN || !authcDN->bv_len || !authcDN->bv_val ) { 2050 goto DONE; 2051 } 2052 2053 Debug( LDAP_DEBUG_TRACE, 2054 "==>slap_sasl_authorized: can %s become %s?\n", 2055 authcDN->bv_len ? authcDN->bv_val : "(null)", 2056 authzDN->bv_len ? authzDN->bv_val : "(null)", 0 ); 2057 2058 /* If person is authorizing to self, succeed */ 2059 if ( dn_match( authcDN, authzDN ) ) { 2060 rc = LDAP_SUCCESS; 2061 goto DONE; 2062 } 2063 2064 /* Allow the manager to authorize as any DN. */ 2065 if( op->o_conn->c_authz_backend && 2066 be_isroot_dn( op->o_conn->c_authz_backend, authcDN )) 2067 { 2068 rc = LDAP_SUCCESS; 2069 goto DONE; 2070 } 2071 2072 /* Check source rules */ 2073 if( authz_policy & SASL_AUTHZ_TO ) { 2074 rc = slap_sasl_check_authz( op, authcDN, authzDN, 2075 slap_schema.si_ad_saslAuthzTo, authcDN ); 2076 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) { 2077 goto DONE; 2078 } 2079 } 2080 2081 /* Check destination rules */ 2082 if( authz_policy & SASL_AUTHZ_FROM ) { 2083 rc = slap_sasl_check_authz( op, authzDN, authcDN, 2084 slap_schema.si_ad_saslAuthzFrom, authcDN ); 2085 if( rc == LDAP_SUCCESS ) { 2086 goto DONE; 2087 } 2088 } 2089 2090 rc = LDAP_INAPPROPRIATE_AUTH; 2091 2092DONE: 2093 2094 Debug( LDAP_DEBUG_TRACE, 2095 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 ); 2096 2097 return( rc ); 2098} 2099