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