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