1/* referral.c - muck with referrals */ 2/* $OpenLDAP$ */ 3/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 1998-2011 The OpenLDAP Foundation. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted only as authorized by the OpenLDAP 10 * Public License. 11 * 12 * A copy of this license is available in the file LICENSE in the 13 * top-level directory of the distribution or, alternatively, at 14 * <http://www.OpenLDAP.org/license.html>. 15 */ 16 17#include "portable.h" 18 19#include <stdio.h> 20 21#include <ac/socket.h> 22#include <ac/errno.h> 23#include <ac/string.h> 24#include <ac/ctype.h> 25#include <ac/time.h> 26#include <ac/unistd.h> 27 28#include "slap.h" 29 30/* 31 * This routine generates the DN appropriate to return in 32 * an LDAP referral. 33 */ 34static char * referral_dn_muck( 35 const char * refDN, 36 struct berval * baseDN, 37 struct berval * targetDN ) 38{ 39 int rc; 40 struct berval bvin; 41 struct berval nrefDN = BER_BVNULL; 42 struct berval nbaseDN = BER_BVNULL; 43 struct berval ntargetDN = BER_BVNULL; 44 45 if( !baseDN ) { 46 /* no base, return target */ 47 return targetDN ? ch_strdup( targetDN->bv_val ) : NULL; 48 } 49 50 if( refDN ) { 51 bvin.bv_val = (char *)refDN; 52 bvin.bv_len = strlen( refDN ); 53 54 rc = dnPretty( NULL, &bvin, &nrefDN, NULL ); 55 if( rc != LDAP_SUCCESS ) { 56 /* Invalid refDN */ 57 return NULL; 58 } 59 } 60 61 if( !targetDN ) { 62 /* continuation reference 63 * if refDN present return refDN 64 * else return baseDN 65 */ 66 return nrefDN.bv_len ? nrefDN.bv_val : ch_strdup( baseDN->bv_val ); 67 } 68 69 rc = dnPretty( NULL, targetDN, &ntargetDN, NULL ); 70 if( rc != LDAP_SUCCESS ) { 71 /* Invalid targetDN */ 72 ch_free( nrefDN.bv_val ); 73 return NULL; 74 } 75 76 if( nrefDN.bv_len ) { 77 rc = dnPretty( NULL, baseDN, &nbaseDN, NULL ); 78 if( rc != LDAP_SUCCESS ) { 79 /* Invalid baseDN */ 80 ch_free( nrefDN.bv_val ); 81 ch_free( ntargetDN.bv_val ); 82 return NULL; 83 } 84 85 if( dn_match( &nbaseDN, &nrefDN ) ) { 86 ch_free( nrefDN.bv_val ); 87 ch_free( nbaseDN.bv_val ); 88 return ntargetDN.bv_val; 89 } 90 91 { 92 struct berval muck; 93 94 if( ntargetDN.bv_len < nbaseDN.bv_len ) { 95 ch_free( nrefDN.bv_val ); 96 ch_free( nbaseDN.bv_val ); 97 return ntargetDN.bv_val; 98 } 99 100 rc = strcasecmp( 101 &ntargetDN.bv_val[ntargetDN.bv_len-nbaseDN.bv_len], 102 nbaseDN.bv_val ); 103 if( rc ) { 104 /* target not subordinate to base */ 105 ch_free( nrefDN.bv_val ); 106 ch_free( nbaseDN.bv_val ); 107 return ntargetDN.bv_val; 108 } 109 110 muck.bv_len = ntargetDN.bv_len + nrefDN.bv_len - nbaseDN.bv_len; 111 muck.bv_val = ch_malloc( muck.bv_len + 1 ); 112 113 strncpy( muck.bv_val, ntargetDN.bv_val, 114 ntargetDN.bv_len-nbaseDN.bv_len ); 115 strcpy( &muck.bv_val[ntargetDN.bv_len-nbaseDN.bv_len], 116 nrefDN.bv_val ); 117 118 ch_free( nrefDN.bv_val ); 119 ch_free( nbaseDN.bv_val ); 120 ch_free( ntargetDN.bv_val ); 121 122 return muck.bv_val; 123 } 124 } 125 126 ch_free( nrefDN.bv_val ); 127 return ntargetDN.bv_val; 128} 129 130 131/* validate URL for global referral use 132 * LDAP URLs must not have: 133 * DN, attrs, scope, nor filter 134 * Any non-LDAP URL is okay 135 * 136 * XXYYZ: should return an error string 137 */ 138int validate_global_referral( const char *url ) 139{ 140 int rc; 141 LDAPURLDesc *lurl; 142 143 rc = ldap_url_parse_ext( url, &lurl, LDAP_PVT_URL_PARSE_NONE ); 144 145 switch( rc ) { 146 case LDAP_URL_SUCCESS: 147 break; 148 149 case LDAP_URL_ERR_BADSCHEME: 150 /* not LDAP hence valid */ 151 Debug( LDAP_DEBUG_CONFIG, "referral \"%s\": not LDAP.\n", url, 0, 0 ); 152 return 0; 153 154 default: 155 /* other error, bail */ 156 Debug( LDAP_DEBUG_ANY, 157 "referral: invalid URL (%s): %s (%d)\n", 158 url, "" /* ldap_url_error2str(rc) */, rc ); 159 return 1; 160 } 161 162 rc = 0; 163 164 if( lurl->lud_dn && *lurl->lud_dn ) { 165 Debug( LDAP_DEBUG_ANY, 166 "referral: URL (%s): contains DN\n", 167 url, 0, 0 ); 168 rc = 1; 169 170 } else if( lurl->lud_attrs ) { 171 Debug( LDAP_DEBUG_ANY, 172 "referral: URL (%s): requests attributes\n", 173 url, 0, 0 ); 174 rc = 1; 175 176 } else if( lurl->lud_scope != LDAP_SCOPE_DEFAULT ) { 177 Debug( LDAP_DEBUG_ANY, 178 "referral: URL (%s): contains explicit scope\n", 179 url, 0, 0 ); 180 rc = 1; 181 182 } else if( lurl->lud_filter ) { 183 Debug( LDAP_DEBUG_ANY, 184 "referral: URL (%s): contains explicit filter\n", 185 url, 0, 0 ); 186 rc = 1; 187 } 188 189 ldap_free_urldesc( lurl ); 190 return rc; 191} 192 193BerVarray referral_rewrite( 194 BerVarray in, 195 struct berval *base, 196 struct berval *target, 197 int scope ) 198{ 199 int i; 200 BerVarray refs; 201 struct berval *iv, *jv; 202 203 if ( in == NULL ) { 204 return NULL; 205 } 206 207 for ( i = 0; !BER_BVISNULL( &in[i] ); i++ ) { 208 /* just count them */ 209 } 210 211 if ( i < 1 ) { 212 return NULL; 213 } 214 215 refs = ch_malloc( ( i + 1 ) * sizeof( struct berval ) ); 216 217 for ( iv = in, jv = refs; !BER_BVISNULL( iv ); iv++ ) { 218 LDAPURLDesc *url; 219 char *dn; 220 int rc; 221 222 rc = ldap_url_parse_ext( iv->bv_val, &url, LDAP_PVT_URL_PARSE_NONE ); 223 if ( rc == LDAP_URL_ERR_BADSCHEME ) { 224 ber_dupbv( jv++, iv ); 225 continue; 226 227 } else if ( rc != LDAP_URL_SUCCESS ) { 228 continue; 229 } 230 231 dn = url->lud_dn; 232 url->lud_dn = referral_dn_muck( ( dn && *dn ) ? dn : NULL, 233 base, target ); 234 ldap_memfree( dn ); 235 236 if ( url->lud_scope == LDAP_SCOPE_DEFAULT ) { 237 url->lud_scope = scope; 238 } 239 240 jv->bv_val = ldap_url_desc2str( url ); 241 if ( jv->bv_val != NULL ) { 242 jv->bv_len = strlen( jv->bv_val ); 243 244 } else { 245 ber_dupbv( jv, iv ); 246 } 247 jv++; 248 249 ldap_free_urldesc( url ); 250 } 251 252 if ( jv == refs ) { 253 ch_free( refs ); 254 refs = NULL; 255 256 } else { 257 BER_BVZERO( jv ); 258 } 259 260 return refs; 261} 262 263 264BerVarray get_entry_referrals( 265 Operation *op, 266 Entry *e ) 267{ 268 Attribute *attr; 269 BerVarray refs; 270 unsigned i; 271 struct berval *iv, *jv; 272 273 AttributeDescription *ad_ref = slap_schema.si_ad_ref; 274 275 attr = attr_find( e->e_attrs, ad_ref ); 276 277 if( attr == NULL ) return NULL; 278 279 for( i=0; attr->a_vals[i].bv_val != NULL; i++ ) { 280 /* count references */ 281 } 282 283 if( i < 1 ) return NULL; 284 285 refs = ch_malloc( (i + 1) * sizeof(struct berval)); 286 287 for( iv=attr->a_vals, jv=refs; iv->bv_val != NULL; iv++ ) { 288 unsigned k; 289 ber_dupbv( jv, iv ); 290 291 /* trim the label */ 292 for( k=0; k<jv->bv_len; k++ ) { 293 if( isspace( (unsigned char) jv->bv_val[k] ) ) { 294 jv->bv_val[k] = '\0'; 295 jv->bv_len = k; 296 break; 297 } 298 } 299 300 if( jv->bv_len > 0 ) { 301 jv++; 302 } else { 303 free( jv->bv_val ); 304 } 305 } 306 307 if( jv == refs ) { 308 free( refs ); 309 refs = NULL; 310 311 } else { 312 jv->bv_val = NULL; 313 } 314 315 /* we should check that a referral value exists... */ 316 return refs; 317} 318 319 320int get_alias_dn( 321 Entry *e, 322 struct berval *ndn, 323 int *err, 324 const char **text ) 325{ 326 Attribute *a; 327 AttributeDescription *aliasedObjectName 328 = slap_schema.si_ad_aliasedObjectName; 329 330 a = attr_find( e->e_attrs, aliasedObjectName ); 331 332 if( a == NULL ) { 333 /* 334 * there was an aliasedobjectname defined but no data. 335 */ 336 *err = LDAP_ALIAS_PROBLEM; 337 *text = "alias missing aliasedObjectName attribute"; 338 return -1; 339 } 340 341 /* 342 * aliasedObjectName should be SINGLE-VALUED with a single value. 343 */ 344 if ( a->a_vals[0].bv_val == NULL ) { 345 /* 346 * there was an aliasedobjectname defined but no data. 347 */ 348 *err = LDAP_ALIAS_PROBLEM; 349 *text = "alias missing aliasedObjectName value"; 350 return -1; 351 } 352 353 if( a->a_nvals[1].bv_val != NULL ) { 354 *err = LDAP_ALIAS_PROBLEM; 355 *text = "alias has multivalued aliasedObjectName"; 356 return -1; 357 } 358 359 *ndn = a->a_nvals[0]; 360 361 return 0; 362} 363 364