referral.c 17 KB

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