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