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