referral.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. /** BEGIN COPYRIGHT BLOCK
  2. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  3. * Copyright (C) 2005 Red Hat, Inc.
  4. * All rights reserved.
  5. *
  6. * License: GPL (version 3 or any later version).
  7. * See LICENSE for details.
  8. * END COPYRIGHT BLOCK **/
  9. #ifdef HAVE_CONFIG_H
  10. # include <config.h>
  11. #endif
  12. /*
  13. * referrals.c - LDAP referral-related routines.
  14. * a) manage in-memory copiedfrom and copyingfrom referrals.
  15. * b) function to adjust smart referrals to match operation parameters.
  16. */
  17. #include <stdio.h>
  18. #include "slap.h"
  19. /* Forward Decls */
  20. static void adjust_referral_basedn( char **urlp, const Slapi_DN *refcontainerdn, char *opdn_norm, int isreference );
  21. static int dn_is_below( const char *dn_norm, const char *ancestor_norm );
  22. static Ref_Array *g_get_global_referrals(void);
  23. static void ref_free (Ref **goner);
  24. static Ref_Array global_referrals;
  25. #define SLAPD_DEFAULT_REFARRAY_SIZE 10
  26. /*
  27. * Function: g_get_global_referrals
  28. *
  29. * Returns: nothing
  30. *
  31. * Description: Fills the global_referrals referral array.
  32. * The size is set to "size". The mutex is created.
  33. * The array is calloc'd.
  34. */
  35. static Ref_Array *
  36. g_get_global_referrals(void)
  37. {
  38. if (global_referrals.ra_rwlock == NULL)
  39. {
  40. /* Make a new lock */
  41. global_referrals.ra_rwlock = slapi_new_rwlock();
  42. if (global_referrals.ra_rwlock == NULL) {
  43. LDAPDebug( LDAP_DEBUG_ANY,
  44. "ref_array_init: new lock creation failed\n", 0, 0, 0);
  45. exit (-1);
  46. }
  47. /* Initialize all the fields. */
  48. global_referrals.ra_size = SLAPD_DEFAULT_REFARRAY_SIZE;
  49. global_referrals.ra_nextindex = 0;
  50. global_referrals.ra_readcount = 0;
  51. global_referrals.ra_refs = (Ref **) slapi_ch_calloc(SLAPD_DEFAULT_REFARRAY_SIZE, sizeof( Ref * ));
  52. }
  53. return( &global_referrals );
  54. }
  55. /*
  56. * Function: ref_free
  57. *
  58. * Returns: nothing
  59. *
  60. * Description: frees up "goner"
  61. *
  62. * Author: RJP
  63. *
  64. */
  65. static void
  66. ref_free (Ref **goner)
  67. {
  68. slapi_ch_free((void**) &((*goner)->ref_dn));
  69. ber_bvfree( (*goner)->ref_referral );
  70. slapi_ch_free((void**) goner);
  71. }
  72. /*
  73. * Function: referrals_free
  74. *
  75. * Returns: nothing
  76. *
  77. * Description: frees up everything.
  78. *
  79. * Author: RJP
  80. *
  81. */
  82. void
  83. referrals_free (void)
  84. {
  85. int walker;
  86. Ref_Array *grefs = NULL;
  87. grefs = g_get_global_referrals();
  88. GR_LOCK_WRITE();
  89. /* Walk down the array, deleting each referral */
  90. for (walker = 0; walker < grefs->ra_nextindex; walker++){
  91. ref_free (&grefs->ra_refs[walker]);
  92. }
  93. slapi_ch_free ((void **) &grefs->ra_refs);
  94. GR_UNLOCK_WRITE();
  95. slapi_destroy_rwlock( grefs->ra_rwlock );
  96. }
  97. /*
  98. * ref_adjust() -- adjust referrals based on operation-specific data.
  99. * The general idea is for us (the server) to be smart so LDAP clients
  100. * can be as dumb as possible.
  101. *
  102. * XXXmcs: We always duplicate the referrals even if no adjustments need to
  103. * be made. It would be more efficient but more complicated to avoid making
  104. * a copy if we don't need to change anything. If that were done, the
  105. * interface to this function would need to change since the caller would
  106. * need to know whether the returned memory needed to be freed or not.
  107. *
  108. * Parameters:
  109. * pb is the pblock for the operation we are working on.
  110. * urls is a list of referrals.
  111. * refsdn is the Slapi_DN of the entry where the referral is stored.
  112. * is_reference is true if a searchResultReference is being returned.
  113. *
  114. * Returns:
  115. * a (possibly modified) copy of the urls. It should be freed by
  116. * calling ber_bvecfree().
  117. *
  118. *
  119. * First we strip off any labels from the referral URLs. For example, a
  120. * referral like this:
  121. *
  122. * ldap://directory.airius.com/ou=people,o=airius.com Ref to people tree
  123. *
  124. * is changed to:
  125. *
  126. * ldap://directory.airius.com/ou=people,o=airius.com
  127. *
  128. * Next, if the referral includes a baseDN we potentially modify it or strip
  129. * it off entirely. If the referral doesn't include a baseDN and we're
  130. * processing a continuation reference, we add a baseDN.
  131. *
  132. * Finally, if we are processing a continuation reference that resulted from
  133. * a one-level search, we append "??base" to the referral so the client will
  134. * use the correct scope when chasing the reference.
  135. */
  136. struct berval **
  137. ref_adjust( Slapi_PBlock *pb, struct berval **urls, const Slapi_DN *refsdn,
  138. int is_reference )
  139. {
  140. int i, len, scope;
  141. Slapi_DN *sdn = NULL;
  142. char *p, *opdn_norm;
  143. struct berval **urlscopy;
  144. Operation *op;
  145. if ( NULL == urls || NULL == urls[0] ) {
  146. return( NULL );
  147. }
  148. PR_ASSERT( pb != NULL );
  149. /*
  150. * grab the operation target DN and operation structure.
  151. * if the operation is a search, get the scope as well.
  152. */
  153. if ( slapi_pblock_get( pb, SLAPI_TARGET_SDN, &sdn ) != 0 || sdn == NULL ||
  154. slapi_pblock_get( pb, SLAPI_OPERATION, &op ) != 0 || op == NULL ||
  155. ( operation_get_type(op) == SLAPI_OPERATION_SEARCH && slapi_pblock_get( pb,
  156. SLAPI_SEARCH_SCOPE, &scope ) != 0 )) {
  157. LDAPDebug0Args( LDAP_DEBUG_ANY, "ref_adjust: referrals suppressed "
  158. "(could not get target DN, operation, "
  159. "or scope from pblock)\n" );
  160. return( NULL );
  161. }
  162. /*
  163. * normalize the DNs we plan to compare with others later.
  164. */
  165. opdn_norm = slapi_ch_strdup( slapi_sdn_get_dn(sdn) );
  166. /*
  167. * count referrals and duplicate the array
  168. */
  169. for ( i = 0; urls[i] != NULL; ++i ) {
  170. ;
  171. }
  172. urlscopy = (struct berval **) slapi_ch_malloc(( i + 1 ) *
  173. sizeof( struct berval * ));
  174. for ( i = 0; urls[i] != NULL; ++i ) {
  175. /*
  176. * duplicate the URL, stripping off the label if there is one and
  177. * leaving extra room for "??base" in case we need to append that.
  178. */
  179. urlscopy[i] = (struct berval *)slapi_ch_malloc(
  180. sizeof( struct berval ));
  181. if (( p = strchr( urls[i]->bv_val, ' ' )) == NULL ) {
  182. len = strlen( urls[i]->bv_val );
  183. } else {
  184. len = p - urls[i]->bv_val;
  185. }
  186. urlscopy[i]->bv_val = (char *)slapi_ch_malloc( len + 7 );
  187. memcpy( urlscopy[i]->bv_val, urls[i]->bv_val, len );
  188. urlscopy[i]->bv_val[len] = '\0';
  189. /*
  190. * adjust the baseDN as needed and set the length
  191. */
  192. adjust_referral_basedn( &urlscopy[i]->bv_val, refsdn,
  193. opdn_norm, is_reference );
  194. urlscopy[i]->bv_len = strlen( urlscopy[i]->bv_val );
  195. /*
  196. * if we are dealing with a continuation reference that resulted
  197. * from a one-level search, add a scope of base to the URL.
  198. */
  199. if ( is_reference && operation_get_type(op) == SLAPI_OPERATION_SEARCH &&
  200. scope == LDAP_SCOPE_ONELEVEL ) {
  201. strcat( urlscopy[i]->bv_val, "??base" );
  202. urlscopy[i]->bv_len += 6;
  203. }
  204. }
  205. urlscopy[i] = NULL; /* NULL terminate the new referrals array */
  206. /*
  207. * log what we did (for debugging purposes)
  208. */
  209. if ( LDAPDebugLevelIsSet( LDAP_DEBUG_ARGS )) {
  210. for ( i = 0; urlscopy[i] != NULL; ++i ) {
  211. LDAPDebug( LDAP_DEBUG_ARGS, "ref_adjust: \"%s\" -> \"%s\"\n",
  212. urls[i]->bv_val, urlscopy[i]->bv_val, 0 );
  213. }
  214. }
  215. /*
  216. * done -- clean up and return the new referrals array
  217. */
  218. slapi_ch_free( (void **)&opdn_norm );
  219. return( urlscopy );
  220. }
  221. /*
  222. * adjust_referral_basedn() -- pull the referral apart and modify the
  223. * baseDN contained in the referral if appropriate not.
  224. * If the referral does not have baseDN and we're not processing a
  225. * continuation reference, we do not need to do anything. If we're
  226. * processing a continuation reference, we need to add a baseDN.
  227. *
  228. * If it does have one, there are two special cases we deal with:
  229. *
  230. * 1) The referral's baseDN is an ancestor of the operation's baseDN,
  231. * which means that the LDAP client already has a more specific
  232. * baseDN than is contained in the referral. If so, we strip off
  233. * the baseDN so the client will re-use its operation DN when
  234. * chasing the referral.
  235. *
  236. * 2) The referral's baseDN is not an ancestor of the operation's
  237. * baseDN and the DN of the entry that contains the referral is
  238. * an ancestor of the operation DN. If so, we remove the portion
  239. * of the operation DN that matches the DN of the entry containing
  240. * the referral and prepend what's left to the referral base DN.
  241. *
  242. * We assume that the baseDN is the right-most piece of the URL, i.e., there
  243. * is no attribute list, scope, or options after the baseDN.
  244. *
  245. * The code also assumes that the baseDN doesn't contain any '/' character.
  246. * This later assumption NEED to be removed and code changed appropriately.
  247. */
  248. static void
  249. adjust_referral_basedn( char **urlp, const Slapi_DN *refsdn,
  250. char *opdn_norm, int isreference )
  251. {
  252. LDAPURLDesc *ludp = NULL;
  253. char *p, *refdn_norm;
  254. int rc = 0;
  255. int secure = 0;
  256. PR_ASSERT( urlp != NULL );
  257. PR_ASSERT( *urlp != NULL );
  258. PR_ASSERT( refsdn != NULL );
  259. PR_ASSERT( opdn_norm != NULL );
  260. if (opdn_norm == NULL){
  261. /* Be safe but this should never ever happen */
  262. return;
  263. }
  264. rc = slapi_ldap_url_parse( *urlp, &ludp, 0, &secure );
  265. if (rc != 0)
  266. {
  267. /* Nothing to do, just return */
  268. /* log bogus url? */
  269. return;
  270. }
  271. if (ludp && (ludp->lud_dn != NULL) && (ludp->lud_dn[0])) {
  272. refdn_norm = slapi_dn_normalize( slapi_ch_strdup( ludp->lud_dn ));
  273. if ( dn_is_below( opdn_norm, refdn_norm )) {
  274. /*
  275. * Strip off the baseDN.
  276. */
  277. if (( p = strrchr( *urlp, '/' )) != NULL ) {
  278. *p = '\0';
  279. }
  280. } else if ( dn_is_below( opdn_norm, slapi_sdn_get_ndn(refsdn) )) {
  281. int cur_len, add_len;
  282. /*
  283. * Prepend the portion of the operation DN that does not match
  284. * the ref container DN to the referral baseDN.
  285. */
  286. add_len = strlen( opdn_norm ) - slapi_sdn_get_ndn_len( refsdn );
  287. cur_len = strlen( *urlp );
  288. /* + 7 because we keep extra space in case we add ??base */
  289. *urlp = slapi_ch_realloc( *urlp, cur_len + add_len + 7 );
  290. if (( p = strrchr( *urlp, '/' )) != NULL ) {
  291. ++p;
  292. memmove( p + add_len, p, strlen( p ) + 1 );
  293. memmove( p, opdn_norm, add_len );
  294. }
  295. } /* else: leave the original referral baseDN intact. */
  296. slapi_ch_free( (void **)&refdn_norm );
  297. } else if (isreference) {
  298. int cur_len, add_len;
  299. /* If ludp->lud_dn == NULL and isreference : Add the DN of the ref entry*/
  300. if ( dn_is_below( opdn_norm, slapi_sdn_get_ndn(refsdn) )){
  301. add_len = strlen(opdn_norm);
  302. p = opdn_norm;
  303. } else {
  304. add_len = slapi_sdn_get_ndn_len(refsdn);
  305. p = (char *)slapi_sdn_get_ndn(refsdn);
  306. }
  307. cur_len = strlen( *urlp );
  308. /* + 8 because we keep extra space in case we add a / and/or ??base */
  309. *urlp = slapi_ch_realloc( *urlp, cur_len + add_len + 8 );
  310. if ((*urlp)[cur_len - 1] != '/'){
  311. /* The URL is not ending with a /, let's add one */
  312. strcat(*urlp, "/");
  313. }
  314. strcat(*urlp, p);
  315. }
  316. if ( ludp != NULL ) {
  317. ldap_free_urldesc( ludp );
  318. }
  319. return;
  320. }
  321. /*
  322. * dn_is_below(): return non-zero if "dn_norm" is below "ancestor_norm" in
  323. * the DIT and return zero if not.
  324. */
  325. static int
  326. dn_is_below( const char *dn_norm, const char *ancestor_norm )
  327. {
  328. PR_ASSERT( dn_norm != NULL );
  329. PR_ASSERT( ancestor_norm != NULL );
  330. if ( 0 == strcasecmp( dn_norm, ancestor_norm )) {
  331. return( 0 ); /* same DN --> not an ancestor relationship */
  332. }
  333. return( slapi_dn_issuffix( dn_norm, ancestor_norm ));
  334. }
  335. /*
  336. * This function is useful to discover the source of data and provide
  337. * this as a referral. It is also useful if someone simply wants
  338. * to know if a dn is mastered somewhere else.
  339. *
  340. * For a given dn, traverse the referral list and look for the copiedFrom
  341. * attribute. If such an attribute is found get the server hostname
  342. * and port in the form "ldap://hostname:port".
  343. * Else return NULL.
  344. *
  345. * ORC extension: if orc == 1, this function will search for copyingFrom
  346. * which will refer searches and compares on trees that are half-baked.
  347. *
  348. * ORC extension: if cf_refs is NULL, then global_referrals is consulted,
  349. * otherwise, cf_refs is consulted.
  350. */
  351. struct berval **
  352. get_data_source(Slapi_PBlock *pb, const Slapi_DN *sdn, int orc, void *cfrp)
  353. {
  354. int walker;
  355. struct berval **bvp;
  356. struct berval *bv;
  357. int found_it;
  358. Ref_Array *grefs = NULL;
  359. Ref_Array *the_refs = NULL;
  360. Ref_Array *cf_refs = (Ref_Array *)cfrp;
  361. /* If no Ref_Array is given, use global_referrals */
  362. if (cf_refs == NULL) {
  363. grefs = g_get_global_referrals();
  364. the_refs = grefs;
  365. GR_LOCK_READ();
  366. } else {
  367. the_refs = cf_refs;
  368. }
  369. /* optimization: if orc is 1 (a read), then check the readcount*/
  370. if (orc && the_refs->ra_readcount == 0) {
  371. if (cf_refs == NULL) {
  372. GR_UNLOCK_READ();
  373. }
  374. return (NULL);
  375. }
  376. bvp = NULL;
  377. bv = NULL;
  378. found_it = 0;
  379. /* Walk down the array, testing each dn to make see if it's a parent of "dn" */
  380. for (walker = 0; walker < the_refs->ra_nextindex; walker++){
  381. if ( slapi_dn_issuffix(slapi_sdn_get_ndn(sdn), the_refs->ra_refs[walker]->ref_dn)) {
  382. found_it = 1;
  383. break;
  384. }
  385. }
  386. /* no referral, so return NULL */
  387. if (!found_it) {
  388. if (cf_refs == NULL) {
  389. GR_UNLOCK_READ();
  390. }
  391. return (NULL);
  392. }
  393. /*
  394. * Gotta make sure we're returning the right one. If orc is 1, then
  395. * only return a read referral. if orc is 0, then only return a
  396. * write referral.
  397. */
  398. if (orc && the_refs->ra_refs[walker]->ref_reads != 1) {
  399. if (cf_refs == NULL) {
  400. GR_UNLOCK_READ();
  401. }
  402. return (NULL);
  403. }
  404. if (!orc && the_refs->ra_refs[walker]->ref_writes != 1) {
  405. if (cf_refs == NULL) {
  406. GR_UNLOCK_READ();
  407. }
  408. return (NULL);
  409. }
  410. /* Fix for 310968 --- return an SSL referral to an SSL client */
  411. if ( 0 != ( pb->pb_conn->c_flags & CONN_FLAG_SSL )) {
  412. /* SSL connection */
  413. char * old_referral_string = NULL;
  414. char * new_referral_string = NULL;
  415. char *p = NULL;
  416. /* Get the basic referral */
  417. bv = slapi_ch_bvdup(the_refs->ra_refs[walker]->ref_referral);
  418. old_referral_string = bv->bv_val;
  419. /* Re-write it to replace ldap with ldaps, and remove the port information */
  420. /* The original string will look like this: ldap://host:port */
  421. /* We need to make it look like this: ldaps://host */
  422. /* Potentially the ":port" part might be missing from the original */
  423. new_referral_string = slapi_ch_smprintf("%s%s" , LDAPS_URL_PREFIX, old_referral_string + strlen(LDAP_URL_PREFIX) );
  424. /* Go looking for the port */
  425. p = new_referral_string + (strlen(LDAPS_URL_PREFIX) + 1);
  426. while (*p != '\0' && *p != ':') p++;
  427. if (':' == *p) {
  428. /* It had a port, zap it */
  429. *p = '\0';
  430. }
  431. /* Fix the bv to point to this new string */
  432. bv->bv_val = new_referral_string;
  433. /* Fix its length */
  434. bv->bv_len = strlen(bv->bv_val);
  435. /* Free the copy we made of the original */
  436. slapi_ch_free((void**)&old_referral_string);
  437. } else {
  438. /* regular connection */
  439. bv = (struct berval *) slapi_ch_bvdup(the_refs->ra_refs[walker]->ref_referral);
  440. }
  441. /* Package it up and send that puppy. */
  442. bvp = (struct berval **) slapi_ch_malloc( 2 * sizeof(struct berval *) );
  443. bvp[0] = bv;
  444. bvp[1] = NULL;
  445. if (cf_refs == NULL) {
  446. GR_UNLOCK_READ();
  447. }
  448. return(bvp);
  449. }