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