1/* $NetBSD: saslauthz.c,v 1.3 2021/08/14 16:14:58 christos Exp $ */ 2 3/* $OpenLDAP$ */ 4/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1998-2021 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.3 2021/08/14 16:14:58 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 75typedef struct sasl_regexp { 76 char *sr_match; /* regexp match pattern */ 77 char *sr_replace; /* regexp replace pattern */ 78} SaslRegexp_t; 79 80static int nSaslRegexp = 0; 81static SaslRegexp_t *SaslRegexp = NULL; 82 83#include "rewrite.h" 84struct rewrite_info *sasl_rwinfo = NULL; 85#define AUTHID_CONTEXT "authid" 86static 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 94static const char *policy_txt[] = { 95 "none", "from", "to", "any" 96}; 97 98static int authz_policy = SASL_AUTHZ_NONE; 99 100static int 101slap_sasl_match( Operation *opx, struct berval *rule, 102 struct berval *assertDN, struct berval *authc ); 103 104int 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 124const 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 132int 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 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 ); 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 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 464done: 465 ldap_free_urldesc( ludp ); 466 return( rc ); 467} 468 469static int 470authzPrettyNormal( 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 */ 547is_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 856done: 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 876int 877authzNormalize( 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 898int 899authzPretty( 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 919static int 920slap_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 */ 1009is_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 1238done: 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 1256static 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 1285static 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 1306static void 1307slap_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 1338static int 1339slap_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 1349int 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 1412int 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 ber_memfree( authz_rewrites[ i ].bv_val ); 1432 for ( i = valx; !BER_BVISNULL( &authz_rewrites[ i + 1 ] ); i++ ) 1433 { 1434 authz_rewrites[ i ] = authz_rewrites[ i + 1 ]; 1435 } 1436 BER_BVZERO( &authz_rewrites[ i ] ); 1437 1438 slap_sasl_rewrite_destroy(); 1439 1440 for ( i = 0; !BER_BVISNULL( &authz_rewrites[ i ] ); i++ ) 1441 { 1442 rc = slap_sasl_rewrite_config_bv( "slapd", 0, authz_rewrites[ i ] ); 1443 assert( rc == 0 ); 1444 } 1445 1446 return rc; 1447} 1448 1449int slap_sasl_rewrite_unparse( BerVarray *bva ) { 1450 if ( authz_rewrites ) { 1451 return slap_bv_x_ordered_unparse( authz_rewrites, bva ); 1452 } 1453 return 0; 1454} 1455 1456static int 1457slap_sasl_regexp_rewrite_config( 1458 struct rewrite_info **rwinfo, 1459 const char *fname, 1460 int lineno, 1461 const char *match, 1462 const char *replace, 1463 const char *context ) 1464{ 1465 int rc; 1466 char *argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL }; 1467 struct rewrite_info *rw = *rwinfo; 1468 1469 /* init at first call */ 1470 if ( rw == NULL ) { 1471 char *argvEngine[] = { "rewriteEngine", "on", NULL }; 1472 char *argvContext[] = { "rewriteContext", NULL, NULL }; 1473 1474 /* initialize rewrite engine */ 1475 rw = rewrite_info_init( REWRITE_MODE_USE_DEFAULT ); 1476 1477 /* switch on rewrite engine */ 1478 rc = rewrite_parse( rw, fname, lineno, 2, argvEngine ); 1479 if (rc != LDAP_SUCCESS) { 1480 goto out; 1481 } 1482 1483 /* create generic authid context */ 1484 argvContext[1] = AUTHID_CONTEXT; 1485 rc = rewrite_parse( rw, fname, lineno, 2, argvContext ); 1486 if (rc != LDAP_SUCCESS) { 1487 goto out; 1488 } 1489 } 1490 1491 argvRule[1] = (char *)match; 1492 argvRule[2] = (char *)replace; 1493 rc = rewrite_parse( rw, fname, lineno, 4, argvRule ); 1494out: 1495 if (rc == LDAP_SUCCESS) { 1496 *rwinfo = rw; 1497 } else { 1498 rewrite_info_delete( &rw ); 1499 } 1500 1501 return rc; 1502} 1503 1504int slap_sasl_regexp_config( const char *match, const char *replace, int valx ) 1505{ 1506 int i, rc; 1507 SaslRegexp_t sr; 1508 struct rewrite_info *rw = NULL; 1509 1510 if ( valx < 0 || valx > nSaslRegexp ) 1511 valx = nSaslRegexp; 1512 1513 for ( i = 0; i < valx; i++) { 1514 rc = slap_sasl_regexp_rewrite_config( &rw, "sasl-regexp", 0, 1515 SaslRegexp[i].sr_match, 1516 SaslRegexp[i].sr_replace, 1517 AUTHID_CONTEXT); 1518 assert( rc == 0 ); 1519 } 1520 1521 rc = slap_sasl_regexp_rewrite_config( &rw, "sasl-regexp", 0, 1522 match, replace, AUTHID_CONTEXT ); 1523 1524 if ( rc == LDAP_SUCCESS ) { 1525 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp, 1526 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) ); 1527 1528 for ( i = nSaslRegexp; i > valx; i-- ) { 1529 SaslRegexp[i] = SaslRegexp[i - 1]; 1530 } 1531 1532 SaslRegexp[i] = sr; 1533 SaslRegexp[i].sr_match = ch_strdup( match ); 1534 SaslRegexp[i].sr_replace = ch_strdup( replace ); 1535 1536 nSaslRegexp++; 1537 1538 for ( i = valx + 1; i < nSaslRegexp; i++ ) { 1539 rc = slap_sasl_regexp_rewrite_config( &rw, "sasl-regexp", 0, 1540 SaslRegexp[i].sr_match, 1541 SaslRegexp[i].sr_replace, 1542 AUTHID_CONTEXT); 1543 assert( rc == 0 ); 1544 } 1545 1546 slap_sasl_rewrite_destroy(); 1547 sasl_rwinfo = rw; 1548 } else if ( rw ) { 1549 rewrite_info_delete( &rw ); 1550 } 1551 1552 return rc; 1553} 1554 1555static void 1556slap_sasl_regexp_destroy_one( int n ) 1557{ 1558 ch_free( SaslRegexp[ n ].sr_match ); 1559 ch_free( SaslRegexp[ n ].sr_replace ); 1560} 1561 1562void 1563slap_sasl_regexp_destroy( void ) 1564{ 1565 if ( SaslRegexp ) { 1566 int n; 1567 1568 for ( n = 0; n < nSaslRegexp; n++ ) { 1569 slap_sasl_regexp_destroy_one( n ); 1570 } 1571 1572 ch_free( SaslRegexp ); 1573 SaslRegexp = NULL; 1574 nSaslRegexp = 0; 1575 } 1576 1577 slap_sasl_rewrite_destroy(); 1578} 1579 1580int slap_sasl_regexp_delete( int valx ) 1581{ 1582 int rc = 0; 1583 1584 if ( valx >= nSaslRegexp ) { 1585 rc = 1; 1586 } else if ( valx < 0 || nSaslRegexp == 1 ) { 1587 slap_sasl_regexp_destroy(); 1588 } else { 1589 int i; 1590 1591 slap_sasl_regexp_destroy_one( valx ); 1592 nSaslRegexp--; 1593 1594 for ( i = valx; i < nSaslRegexp; i++ ) { 1595 SaslRegexp[ i ] = SaslRegexp[ i + 1 ]; 1596 } 1597 1598 slap_sasl_rewrite_destroy(); 1599 for ( i = 0; i < nSaslRegexp; i++ ) { 1600 rc = slap_sasl_regexp_rewrite_config( &sasl_rwinfo, "sasl-regexp", 0, 1601 SaslRegexp[ i ].sr_match, 1602 SaslRegexp[ i ].sr_replace, 1603 AUTHID_CONTEXT ); 1604 assert( rc == 0 ); 1605 } 1606 } 1607 1608 return rc; 1609} 1610 1611void slap_sasl_regexp_unparse( BerVarray *out ) 1612{ 1613 int i; 1614 BerVarray bva = NULL; 1615 char ibuf[32], *ptr; 1616 struct berval idx; 1617 1618 if ( !nSaslRegexp ) return; 1619 1620 idx.bv_val = ibuf; 1621 bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) ); 1622 BER_BVZERO(bva+nSaslRegexp); 1623 for ( i=0; i<nSaslRegexp; i++ ) { 1624 idx.bv_len = sprintf( idx.bv_val, "{%d}", i); 1625 bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) + 1626 strlen( SaslRegexp[i].sr_replace ) + 5; 1627 bva[i].bv_val = ch_malloc( bva[i].bv_len+1 ); 1628 ptr = lutil_strcopy( bva[i].bv_val, ibuf ); 1629 *ptr++ = '"'; 1630 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match ); 1631 ptr = lutil_strcopy( ptr, "\" \"" ); 1632 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace ); 1633 *ptr++ = '"'; 1634 *ptr = '\0'; 1635 } 1636 *out = bva; 1637} 1638 1639/* Take the passed in SASL name and attempt to convert it into an 1640 LDAP URI to find the matching LDAP entry, using the pattern matching 1641 strings given in the saslregexp config file directive(s) */ 1642 1643static int slap_authz_regexp( struct berval *in, struct berval *out, 1644 int flags, void *ctx ) 1645{ 1646 const char *context = AUTHID_CONTEXT; 1647 1648 if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) { 1649 return 0; 1650 } 1651 1652 /* FIXME: if aware of authc/authz mapping, 1653 * we could use different contexts ... */ 1654 switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL, 1655 &out->bv_val ) ) 1656 { 1657 case REWRITE_REGEXEC_OK: 1658 if ( !BER_BVISNULL( out ) ) { 1659 char *val = out->bv_val; 1660 ber_str2bv_x( val, 0, 1, out, ctx ); 1661 if ( val != in->bv_val ) { 1662 free( val ); 1663 } 1664 } else { 1665 ber_dupbv_x( out, in, ctx ); 1666 } 1667 Debug( LDAP_DEBUG_ARGS, 1668 "[rw] %s: \"%s\" -> \"%s\"\n", 1669 context, in->bv_val, out->bv_val ); 1670 return 1; 1671 1672 case REWRITE_REGEXEC_UNWILLING: 1673 case REWRITE_REGEXEC_ERR: 1674 default: 1675 return 0; 1676 } 1677 1678} 1679 1680/* This callback actually does some work...*/ 1681static int sasl_sc_sasl2dn( Operation *op, SlapReply *rs ) 1682{ 1683 struct berval *ndn = op->o_callback->sc_private; 1684 1685 if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS; 1686 1687 /* We only want to be called once */ 1688 if ( !BER_BVISNULL( ndn ) ) { 1689 op->o_tmpfree( ndn->bv_val, op->o_tmpmemctx ); 1690 BER_BVZERO( ndn ); 1691 1692 Debug( LDAP_DEBUG_TRACE, 1693 "%s: slap_sc_sasl2dn: search DN returned more than 1 entry\n", 1694 op->o_log_prefix ); 1695 return LDAP_UNAVAILABLE; /* short-circuit the search */ 1696 } 1697 1698 ber_dupbv_x( ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx ); 1699 return LDAP_SUCCESS; 1700} 1701 1702 1703typedef struct smatch_info { 1704 struct berval *dn; 1705 int match; 1706} smatch_info; 1707 1708static int sasl_sc_smatch( Operation *o, SlapReply *rs ) 1709{ 1710 smatch_info *sm = o->o_callback->sc_private; 1711 1712 if (rs->sr_type != REP_SEARCH) return 0; 1713 1714 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) { 1715 sm->match = 1; 1716 return LDAP_UNAVAILABLE; /* short-circuit the search */ 1717 } 1718 1719 return 0; 1720} 1721 1722int 1723slap_sasl_matches( Operation *op, BerVarray rules, 1724 struct berval *assertDN, struct berval *authc ) 1725{ 1726 int rc = LDAP_INAPPROPRIATE_AUTH; 1727 1728 if ( rules != NULL ) { 1729 int i; 1730 1731 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) { 1732 rc = slap_sasl_match( op, &rules[i], assertDN, authc ); 1733 if ( rc == LDAP_SUCCESS ) break; 1734 } 1735 } 1736 1737 return rc; 1738} 1739 1740/* 1741 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base 1742 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise, 1743 * the rule must be used as an internal search for entries. If that search 1744 * returns the *assertDN entry, the match is successful. 1745 * 1746 * The assertDN should not have the dn: prefix 1747 */ 1748 1749static int 1750slap_sasl_match( Operation *opx, struct berval *rule, 1751 struct berval *assertDN, struct berval *authc ) 1752{ 1753 int rc; 1754 regex_t reg; 1755 smatch_info sm; 1756 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL }; 1757 Operation op = {0}; 1758 SlapReply rs = {REP_RESULT}; 1759 struct berval base = BER_BVNULL; 1760 1761 sm.dn = assertDN; 1762 sm.match = 0; 1763 cb.sc_private = &sm; 1764 1765 Debug( LDAP_DEBUG_TRACE, 1766 "===>slap_sasl_match: comparing DN %s to rule %s\n", 1767 assertDN->bv_len ? assertDN->bv_val : "(null)", rule->bv_val ); 1768 1769 /* NOTE: don't normalize rule if authz syntax is enabled */ 1770 rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn, 1771 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 0 ); 1772 1773 if( rc != LDAP_SUCCESS ) goto CONCLUDED; 1774 1775 switch ( op.ors_scope ) { 1776 case LDAP_X_SCOPE_EXACT: 1777exact_match: 1778 if ( dn_match( &op.o_req_ndn, assertDN ) ) { 1779 rc = LDAP_SUCCESS; 1780 } else { 1781 rc = LDAP_INAPPROPRIATE_AUTH; 1782 } 1783 goto CONCLUDED; 1784 1785 case LDAP_X_SCOPE_CHILDREN: 1786 case LDAP_X_SCOPE_SUBTREE: 1787 case LDAP_X_SCOPE_ONELEVEL: 1788 { 1789 int d = assertDN->bv_len - op.o_req_ndn.bv_len; 1790 1791 rc = LDAP_INAPPROPRIATE_AUTH; 1792 1793 if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) { 1794 goto exact_match; 1795 1796 } else if ( d > 0 ) { 1797 struct berval bv; 1798 1799 /* leave room for at least one char of attributeType, 1800 * one for '=' and one for ',' */ 1801 if ( d < (int) STRLENOF( "x=,") ) { 1802 goto CONCLUDED; 1803 } 1804 1805 bv.bv_len = op.o_req_ndn.bv_len; 1806 bv.bv_val = assertDN->bv_val + d; 1807 1808 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) { 1809 switch ( op.ors_scope ) { 1810 case LDAP_X_SCOPE_SUBTREE: 1811 case LDAP_X_SCOPE_CHILDREN: 1812 rc = LDAP_SUCCESS; 1813 break; 1814 1815 case LDAP_X_SCOPE_ONELEVEL: 1816 { 1817 struct berval pdn; 1818 1819 dnParent( assertDN, &pdn ); 1820 /* the common portion of the DN 1821 * already matches, so only check 1822 * if parent DN of assertedDN 1823 * is all the pattern */ 1824 if ( pdn.bv_len == op.o_req_ndn.bv_len ) { 1825 rc = LDAP_SUCCESS; 1826 } 1827 break; 1828 } 1829 default: 1830 /* at present, impossible */ 1831 assert( 0 ); 1832 } 1833 } 1834 } 1835 goto CONCLUDED; 1836 } 1837 1838 case LDAP_X_SCOPE_REGEX: 1839 rc = regcomp(®, op.o_req_ndn.bv_val, 1840 REG_EXTENDED|REG_ICASE|REG_NOSUB); 1841 if ( rc == 0 ) { 1842 rc = regexec(®, assertDN->bv_val, 0, NULL, 0); 1843 regfree( ® ); 1844 } 1845 if ( rc == 0 ) { 1846 rc = LDAP_SUCCESS; 1847 } else { 1848 rc = LDAP_INAPPROPRIATE_AUTH; 1849 } 1850 goto CONCLUDED; 1851 1852 case LDAP_X_SCOPE_GROUP: { 1853 char *tmp; 1854 1855 /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>=" 1856 * we need to append the <assertDN> so that the <group_dn> is searched 1857 * with scope "base", and the filter ensures that <assertDN> is 1858 * member of the group */ 1859 tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len + 1860 assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 ); 1861 if ( tmp == NULL ) { 1862 rc = LDAP_NO_MEMORY; 1863 goto CONCLUDED; 1864 } 1865 op.ors_filterstr.bv_val = tmp; 1866 1867 tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val ); 1868 tmp = lutil_strcopy( tmp, /*"(("*/ "))" ); 1869 1870 /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */ 1871 op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val ); 1872 if ( op.ors_filter == NULL ) { 1873 rc = LDAP_PROTOCOL_ERROR; 1874 goto CONCLUDED; 1875 } 1876 op.ors_scope = LDAP_SCOPE_BASE; 1877 1878 /* hijack match DN: use that of the group instead of the assertDN; 1879 * assertDN is now in the filter */ 1880 sm.dn = &op.o_req_ndn; 1881 1882 /* do the search */ 1883 break; 1884 } 1885 1886 case LDAP_X_SCOPE_USERS: 1887 if ( !BER_BVISEMPTY( assertDN ) ) { 1888 rc = LDAP_SUCCESS; 1889 } else { 1890 rc = LDAP_INAPPROPRIATE_AUTH; 1891 } 1892 goto CONCLUDED; 1893 1894 default: 1895 break; 1896 } 1897 1898 /* Must run an internal search. */ 1899 if ( op.ors_filter == NULL ) { 1900 rc = LDAP_FILTER_ERROR; 1901 goto CONCLUDED; 1902 } 1903 1904 Debug( LDAP_DEBUG_TRACE, 1905 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n", 1906 op.o_req_ndn.bv_val, op.ors_scope ); 1907 1908 op.o_bd = select_backend( &op.o_req_ndn, 1 ); 1909 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) { 1910 rc = LDAP_INAPPROPRIATE_AUTH; 1911 goto CONCLUDED; 1912 } 1913 1914 op.o_hdr = opx->o_hdr; 1915 op.o_tag = LDAP_REQ_SEARCH; 1916 op.o_ndn = *authc; 1917 op.o_callback = &cb; 1918 slap_op_time( &op.o_time, &op.o_tincr ); 1919 op.o_do_not_cache = 1; 1920 op.o_is_auth_check = 1; 1921 /* use req_ndn as req_dn instead of non-pretty base of uri */ 1922 if( !BER_BVISNULL( &base ) ) { 1923 ch_free( base.bv_val ); 1924 /* just in case... */ 1925 BER_BVZERO( &base ); 1926 } 1927 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx ); 1928 op.ors_deref = LDAP_DEREF_NEVER; 1929 op.ors_slimit = 1; 1930 op.ors_tlimit = SLAP_NO_LIMIT; 1931 op.ors_attrs = slap_anlist_no_attrs; 1932 op.ors_attrsonly = 1; 1933 1934 op.o_bd->be_search( &op, &rs ); 1935 1936 if (sm.match) { 1937 rc = LDAP_SUCCESS; 1938 } else { 1939 rc = LDAP_INAPPROPRIATE_AUTH; 1940 } 1941 1942CONCLUDED: 1943 if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx ); 1944 if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx ); 1945 if( op.ors_filter ) filter_free_x( opx, op.ors_filter, 1 ); 1946 if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val ); 1947 1948 Debug( LDAP_DEBUG_TRACE, 1949 "<===slap_sasl_match: comparison returned %d\n", rc ); 1950 1951 return( rc ); 1952} 1953 1954 1955/* 1956 * This function answers the question, "Can this ID authorize to that ID?", 1957 * based on authorization rules. The rules are stored in the *searchDN, in the 1958 * attribute named by *attr. If any of those rules map to the *assertDN, the 1959 * authorization is approved. 1960 * 1961 * The DNs should not have the dn: prefix 1962 */ 1963static int 1964slap_sasl_check_authz( Operation *op, 1965 struct berval *searchDN, 1966 struct berval *assertDN, 1967 AttributeDescription *ad, 1968 struct berval *authc ) 1969{ 1970 int rc, 1971 do_not_cache = op->o_do_not_cache; 1972 BerVarray vals = NULL; 1973 1974 Debug( LDAP_DEBUG_TRACE, 1975 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n", 1976 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val); 1977 1978 /* ITS#4760: don't cache group access */ 1979 op->o_do_not_cache = 1; 1980 rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH ); 1981 op->o_do_not_cache = do_not_cache; 1982 if( rc != LDAP_SUCCESS ) goto COMPLETE; 1983 1984 /* Check if the *assertDN matches any *vals */ 1985 rc = slap_sasl_matches( op, vals, assertDN, authc ); 1986 1987COMPLETE: 1988 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx ); 1989 1990 Debug( LDAP_DEBUG_TRACE, 1991 "<==slap_sasl_check_authz: %s check returning %d\n", 1992 ad->ad_cname.bv_val, rc ); 1993 1994 return( rc ); 1995} 1996 1997/* 1998 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH") 1999 * return the LDAP DN to which it matches. The SASL regexp rules in the config 2000 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a 2001 * search with scope=base), just return the URI (or its searchbase). Otherwise 2002 * an internal search must be done, and if that search returns exactly one 2003 * entry, return the DN of that one entry. 2004 */ 2005void 2006slap_sasl2dn( 2007 Operation *opx, 2008 struct berval *saslname, 2009 struct berval *sasldn, 2010 int flags ) 2011{ 2012 int rc; 2013 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL }; 2014 Operation op = {0}; 2015 SlapReply rs = {REP_RESULT}; 2016 struct berval regout = BER_BVNULL; 2017 struct berval base = BER_BVNULL; 2018 2019 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: " 2020 "converting SASL name %s to a DN\n", 2021 saslname->bv_val ); 2022 2023 BER_BVZERO( sasldn ); 2024 cb.sc_private = sasldn; 2025 2026 /* Convert the SASL name into a minimal URI */ 2027 if( !slap_authz_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) { 2028 goto FINISHED; 2029 } 2030 2031 /* NOTE: always normalize regout because it results 2032 * from string submatch expansion */ 2033 rc = slap_parseURI( opx, ®out, &base, &op.o_req_ndn, 2034 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 ); 2035 if ( !BER_BVISNULL( ®out ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx ); 2036 if ( rc != LDAP_SUCCESS ) { 2037 goto FINISHED; 2038 } 2039 2040 /* Must do an internal search */ 2041 op.o_bd = select_backend( &op.o_req_ndn, 1 ); 2042 2043 switch ( op.ors_scope ) { 2044 case LDAP_X_SCOPE_EXACT: 2045 *sasldn = op.o_req_ndn; 2046 BER_BVZERO( &op.o_req_ndn ); 2047 /* intentionally continue to next case */ 2048 2049 case LDAP_X_SCOPE_REGEX: 2050 case LDAP_X_SCOPE_SUBTREE: 2051 case LDAP_X_SCOPE_CHILDREN: 2052 case LDAP_X_SCOPE_ONELEVEL: 2053 case LDAP_X_SCOPE_GROUP: 2054 case LDAP_X_SCOPE_USERS: 2055 /* correctly parsed, but illegal */ 2056 goto FINISHED; 2057 2058 case LDAP_SCOPE_BASE: 2059 case LDAP_SCOPE_ONELEVEL: 2060 case LDAP_SCOPE_SUBTREE: 2061 case LDAP_SCOPE_SUBORDINATE: 2062 /* do a search */ 2063 break; 2064 2065 default: 2066 /* catch unhandled cases (there shouldn't be) */ 2067 assert( 0 ); 2068 } 2069 2070 Debug( LDAP_DEBUG_TRACE, 2071 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n", 2072 op.o_req_ndn.bv_val, op.ors_scope ); 2073 2074 if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) { 2075 goto FINISHED; 2076 } 2077 2078 /* Must run an internal search. */ 2079 if ( op.ors_filter == NULL ) { 2080 rc = LDAP_FILTER_ERROR; 2081 goto FINISHED; 2082 } 2083 2084 op.o_hdr = opx->o_hdr; 2085 op.o_tag = LDAP_REQ_SEARCH; 2086 op.o_ndn = opx->o_conn->c_ndn; 2087 op.o_callback = &cb; 2088 slap_op_time( &op.o_time, &op.o_tincr ); 2089 op.o_do_not_cache = 1; 2090 op.o_is_auth_check = 1; 2091 op.ors_deref = LDAP_DEREF_NEVER; 2092 op.ors_slimit = 1; 2093 op.ors_tlimit = SLAP_NO_LIMIT; 2094 op.ors_attrs = slap_anlist_no_attrs; 2095 op.ors_attrsonly = 1; 2096 /* use req_ndn as req_dn instead of non-pretty base of uri */ 2097 if( !BER_BVISNULL( &base ) ) { 2098 ch_free( base.bv_val ); 2099 /* just in case... */ 2100 BER_BVZERO( &base ); 2101 } 2102 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx ); 2103 2104 op.o_bd->be_search( &op, &rs ); 2105 2106FINISHED: 2107 if( opx == opx->o_conn->c_sasl_bindop && !BER_BVISEMPTY( sasldn ) ) { 2108 opx->o_conn->c_authz_backend = op.o_bd; 2109 } 2110 if( !BER_BVISNULL( &op.o_req_dn ) ) { 2111 slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx ); 2112 } 2113 if( !BER_BVISNULL( &op.o_req_ndn ) ) { 2114 slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx ); 2115 } 2116 if( op.ors_filter ) { 2117 filter_free_x( opx, op.ors_filter, 1 ); 2118 } 2119 if( !BER_BVISNULL( &op.ors_filterstr ) ) { 2120 ch_free( op.ors_filterstr.bv_val ); 2121 } 2122 2123 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n", 2124 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>" ); 2125 2126 return; 2127} 2128 2129 2130/* Check if a bind can SASL authorize to another identity. 2131 * The DNs should not have the dn: prefix 2132 */ 2133 2134int slap_sasl_authorized( Operation *op, 2135 struct berval *authcDN, struct berval *authzDN ) 2136{ 2137 int rc = LDAP_INAPPROPRIATE_AUTH; 2138 2139 /* User binding as anonymous */ 2140 if ( !authzDN || !authzDN->bv_len || !authzDN->bv_val ) { 2141 rc = LDAP_SUCCESS; 2142 goto DONE; 2143 } 2144 2145 /* User is anonymous */ 2146 if ( !authcDN || !authcDN->bv_len || !authcDN->bv_val ) { 2147 goto DONE; 2148 } 2149 2150 Debug( LDAP_DEBUG_TRACE, 2151 "==>slap_sasl_authorized: can %s become %s?\n", 2152 authcDN->bv_len ? authcDN->bv_val : "(null)", 2153 authzDN->bv_len ? authzDN->bv_val : "(null)" ); 2154 2155 /* If person is authorizing to self, succeed */ 2156 if ( dn_match( authcDN, authzDN ) ) { 2157 rc = LDAP_SUCCESS; 2158 goto DONE; 2159 } 2160 2161 /* Allow the manager to authorize as any DN in its own DBs. */ 2162 { 2163 Backend *zbe = select_backend( authzDN, 1 ); 2164 if ( zbe && be_isroot_dn( zbe, authcDN )) { 2165 rc = LDAP_SUCCESS; 2166 goto DONE; 2167 } 2168 } 2169 2170 /* Check source rules */ 2171 if( authz_policy & SASL_AUTHZ_TO ) { 2172 rc = slap_sasl_check_authz( op, authcDN, authzDN, 2173 slap_schema.si_ad_saslAuthzTo, authcDN ); 2174 if(( rc == LDAP_SUCCESS ) ^ (( authz_policy & SASL_AUTHZ_AND) != 0)) { 2175 if( rc != LDAP_SUCCESS ) 2176 rc = LDAP_INAPPROPRIATE_AUTH; 2177 goto DONE; 2178 } 2179 } 2180 2181 /* Check destination rules */ 2182 if( authz_policy & SASL_AUTHZ_FROM ) { 2183 rc = slap_sasl_check_authz( op, authzDN, authcDN, 2184 slap_schema.si_ad_saslAuthzFrom, authcDN ); 2185 if( rc == LDAP_SUCCESS ) { 2186 goto DONE; 2187 } 2188 } 2189 2190 rc = LDAP_INAPPROPRIATE_AUTH; 2191 2192DONE: 2193 2194 Debug( LDAP_DEBUG_TRACE, 2195 "<== slap_sasl_authorized: return %d\n", rc ); 2196 2197 return( rc ); 2198} 2199