dsalib_dn.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  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. #if defined( XP_WIN32 )
  39. #include <windows.h>
  40. #else
  41. #include <sys/types.h>
  42. #endif
  43. #include <string.h>
  44. #include "dsalib.h"
  45. #include "portable.h"
  46. #include <stdlib.h>
  47. #define DNSEPARATOR(c) (c == ',' || c == ';')
  48. #define SEPARATOR(c) (c == ',' || c == ';' || c == '+')
  49. #define SPACE(c) (c == ' ' || c == '\n')
  50. #define NEEDSESCAPE(c) (c == '\\' || c == '"')
  51. #define B4TYPE 0
  52. #define INTYPE 1
  53. #define B4EQUAL 2
  54. #define B4VALUE 3
  55. #define INVALUE 4
  56. #define INQUOTEDVALUE 5
  57. #define B4SEPARATOR 6
  58. DS_EXPORT_SYMBOL char*
  59. dn_normalize( char *dn )
  60. {
  61. char *d, *s;
  62. int state, gotesc;
  63. /* Debug( LDAP_DEBUG_TRACE, "=> dn_normalize \"%s\"\n", dn, 0, 0 ); */
  64. gotesc = 0;
  65. state = B4TYPE;
  66. for ( d = s = dn; *s; s++ ) {
  67. switch ( state ) {
  68. case B4TYPE:
  69. if ( ! SPACE( *s ) ) {
  70. state = INTYPE;
  71. *d++ = *s;
  72. }
  73. break;
  74. case INTYPE:
  75. if ( *s == '=' ) {
  76. state = B4VALUE;
  77. *d++ = *s;
  78. } else if ( SPACE( *s ) ) {
  79. state = B4EQUAL;
  80. } else {
  81. *d++ = *s;
  82. }
  83. break;
  84. case B4EQUAL:
  85. if ( *s == '=' ) {
  86. state = B4VALUE;
  87. *d++ = *s;
  88. } else if ( ! SPACE( *s ) ) {
  89. /* not a valid dn - but what can we do here? */
  90. *d++ = *s;
  91. }
  92. break;
  93. case B4VALUE:
  94. if ( *s == '"' ) {
  95. state = INQUOTEDVALUE;
  96. *d++ = *s;
  97. } else if ( ! SPACE( *s ) ) {
  98. state = INVALUE;
  99. *d++ = *s;
  100. }
  101. break;
  102. case INVALUE:
  103. if ( !gotesc && SEPARATOR( *s ) ) {
  104. while ( SPACE( *(d - 1) ) )
  105. d--;
  106. state = B4TYPE;
  107. if ( *s == '+' ) {
  108. *d++ = *s;
  109. } else {
  110. *d++ = ',';
  111. }
  112. } else if ( gotesc && !NEEDSESCAPE( *s ) &&
  113. !SEPARATOR( *s ) ) {
  114. *--d = *s;
  115. d++;
  116. } else {
  117. *d++ = *s;
  118. }
  119. break;
  120. case INQUOTEDVALUE:
  121. if ( !gotesc && *s == '"' ) {
  122. state = B4SEPARATOR;
  123. *d++ = *s;
  124. } else if ( gotesc && !NEEDSESCAPE( *s ) ) {
  125. *--d = *s;
  126. d++;
  127. } else {
  128. *d++ = *s;
  129. }
  130. break;
  131. case B4SEPARATOR:
  132. if ( SEPARATOR( *s ) ) {
  133. state = B4TYPE;
  134. if ( *s == '+' ) {
  135. *d++ = *s;
  136. } else {
  137. *d++ = ',';
  138. }
  139. }
  140. break;
  141. default:
  142. break;
  143. }
  144. if ( *s == '\\' ) {
  145. gotesc = 1;
  146. } else {
  147. gotesc = 0;
  148. }
  149. }
  150. *d = '\0';
  151. /* Debug( LDAP_DEBUG_TRACE, "<= dn_normalize \"%s\"\n", dn, 0, 0 ); */
  152. return( dn );
  153. }
  154. DS_EXPORT_SYMBOL char*
  155. ds_dn_expand (char* dn)
  156. {
  157. char* edn;
  158. size_t i = 0;
  159. char* s;
  160. int state = B4TYPE;
  161. int gotesc = 0;
  162. if (dn == NULL) return NULL;
  163. edn = strdup (dn);
  164. if (edn == NULL) return NULL;
  165. for (s = dn; *s != '\0'; ++s, ++i) {
  166. switch (state) {
  167. case B4TYPE:
  168. if ( ! SPACE (*s)) {
  169. state = INTYPE;
  170. }
  171. break;
  172. case INTYPE:
  173. if (*s == '=') {
  174. state = B4VALUE;
  175. } else if (SPACE (*s)) {
  176. state = B4EQUAL;
  177. }
  178. break;
  179. case B4EQUAL:
  180. if (*s == '=') {
  181. state = B4VALUE;
  182. }
  183. break;
  184. case B4VALUE:
  185. if (*s == '"') {
  186. state = INQUOTEDVALUE;
  187. } else if ( ! SPACE (*s)) {
  188. state = INVALUE;
  189. }
  190. break;
  191. case INQUOTEDVALUE:
  192. if (gotesc) {
  193. if ( ! NEEDSESCAPE (*s)) {
  194. --i;
  195. memmove (edn+i, edn+i+1, strlen (edn+i));
  196. }
  197. } else {
  198. if (*s == '"') {
  199. state = B4SEPARATOR;
  200. }
  201. }
  202. break;
  203. case INVALUE:
  204. if (gotesc) {
  205. if ( ! NEEDSESCAPE (*s) && ! SEPARATOR (*s)) {
  206. --i;
  207. memmove (edn+i, edn+i+1, strlen (edn+i));
  208. }
  209. break;
  210. }
  211. case B4SEPARATOR:
  212. if (SEPARATOR (*s)) {
  213. state = B4TYPE;
  214. if ( ! SPACE (s[1])) {
  215. /* insert a space following edn[i] */
  216. edn = (char*) realloc (edn, strlen (edn)+2);
  217. ++i;
  218. memmove (edn+i+1, edn+i, strlen (edn+i)+1);
  219. edn[i] = ' ';
  220. }
  221. }
  222. break;
  223. default:
  224. break;
  225. }
  226. gotesc = (*s == '\\');
  227. }
  228. return edn;
  229. }
  230. int
  231. hexchar2int( char c )
  232. {
  233. if ( '0' <= c && c <= '9' ) {
  234. return( c - '0' );
  235. }
  236. if ( 'a' <= c && c <= 'f' ) {
  237. return( c - 'a' + 10 );
  238. }
  239. if ( 'A' <= c && c <= 'F' ) {
  240. return( c - 'A' + 10 );
  241. }
  242. return( -1 );
  243. }
  244. /*
  245. * substr_dn_normalize - map a DN to a canonical form.
  246. * The DN is read from *dn through *(end-1) and normalized in place.
  247. * The new end is returned; that is, the canonical form is in
  248. * *dn through *(the_return_value-1).
  249. */
  250. /* The goals of this function are:
  251. * 1. be compatible with previous implementations. Especially, enable
  252. * a server running this code to find database index keys that were
  253. * computed by Directory Server 3.0 with a prior version of this code.
  254. * 2. Normalize in place; that is, avoid allocating memory to contain
  255. * the canonical form.
  256. * 3. eliminate insignificant differences; that is, any two DNs are
  257. * not significantly different if and only if their canonical forms
  258. * are identical (ignoring upper/lower case).
  259. * 4. handle a DN in the syntax defined by RFC 2253.
  260. * 5. handle a DN in the syntax defined by RFC 1779.
  261. *
  262. * Goals 3 through 5 are not entirely achieved by this implementation,
  263. * because it can't be done without violating goal 1. Specifically,
  264. * DNs like cn="a,b" and cn=a\,b are not mapped to the same canonical form,
  265. * although they're not significantly different. Likewise for any pair
  266. * of DNs that differ only in their choice of quoting convention.
  267. * A previous version of this code changed all DNs to the most compact
  268. * quoting convention, but that violated goal 1, since Directory Server
  269. * 3.0 did not.
  270. *
  271. * Also, this implementation handles the \xx convention of RFC 2253 and
  272. * consequently violates RFC 1779, according to which this type of quoting
  273. * would be interpreted as a sequence of 2 numerals (not a single byte).
  274. */
  275. DS_EXPORT_SYMBOL char *
  276. dn_normalize_convert( char *dn )
  277. {
  278. /* \xx is changed to \c.
  279. \c is changed to c, unless this would change its meaning.
  280. All values that contain 2 or more separators are "enquoted";
  281. all other values are not enquoted.
  282. */
  283. char *value, *value_separator;
  284. char *d, *s;
  285. char *end;
  286. int gotesc = 0;
  287. int state = B4TYPE;
  288. if (NULL == dn)
  289. return dn;
  290. end = dn + strlen(dn);
  291. for ( d = s = dn; s != end; s++ ) {
  292. switch ( state ) {
  293. case B4TYPE:
  294. if ( ! SPACE( *s ) ) {
  295. state = INTYPE;
  296. *d++ = *s;
  297. }
  298. break;
  299. case INTYPE:
  300. if ( *s == '=' ) {
  301. state = B4VALUE;
  302. *d++ = *s;
  303. } else if ( SPACE( *s ) ) {
  304. state = B4EQUAL;
  305. } else {
  306. *d++ = *s;
  307. }
  308. break;
  309. case B4EQUAL:
  310. if ( *s == '=' ) {
  311. state = B4VALUE;
  312. *d++ = *s;
  313. } else if ( ! SPACE( *s ) ) {
  314. /* not a valid dn - but what can we do here? */
  315. *d++ = *s;
  316. }
  317. break;
  318. case B4VALUE:
  319. if ( *s == '"' || ! SPACE( *s ) ) {
  320. value_separator = NULL;
  321. value = d;
  322. state = ( *s == '"' ) ? INQUOTEDVALUE : INVALUE;
  323. *d++ = *s;
  324. }
  325. break;
  326. case INVALUE:
  327. if ( gotesc ) {
  328. if ( SEPARATOR( *s ) ) {
  329. if ( value_separator ) value_separator = dn;
  330. else value_separator = d;
  331. } else if ( ! NEEDSESCAPE( *s ) ) {
  332. --d; /* eliminate the \ */
  333. }
  334. } else if ( SEPARATOR( *s ) ) {
  335. while ( SPACE( *(d - 1) ) )
  336. d--;
  337. if ( value_separator == dn ) { /* 2 or more separators */
  338. /* convert to quoted value: */
  339. auto char *L = NULL;
  340. auto char *R;
  341. for ( R = value; (R = strchr( R, '\\' )) && (R < d); L = ++R ) {
  342. if ( SEPARATOR( R[1] )) {
  343. if ( L == NULL ) {
  344. auto const size_t len = R - value;
  345. if ( len > 0 ) memmove( value+1, value, len );
  346. *value = '"'; /* opening quote */
  347. value = R + 1;
  348. } else {
  349. auto const size_t len = R - L;
  350. if ( len > 0 ) {
  351. memmove( value, L, len );
  352. value += len;
  353. }
  354. --d;
  355. }
  356. }
  357. }
  358. memmove( value, L, d - L + 1 );
  359. *d++ = '"'; /* closing quote */
  360. }
  361. state = B4TYPE;
  362. *d++ = (*s == '+') ? '+' : ',';
  363. break;
  364. }
  365. *d++ = *s;
  366. break;
  367. case INQUOTEDVALUE:
  368. if ( gotesc ) {
  369. if ( ! NEEDSESCAPE( *s ) ) {
  370. --d; /* eliminate the \ */
  371. }
  372. } else if ( *s == '"' ) {
  373. state = B4SEPARATOR;
  374. if ( value_separator == dn /* 2 or more separators */
  375. || SPACE( value[1] ) || SPACE( d[-1] ) ) {
  376. *d++ = *s;
  377. } else {
  378. /* convert to non-quoted value: */
  379. if ( value_separator == NULL ) { /* no separators */
  380. memmove ( value, value+1, (d-value)-1 );
  381. --d;
  382. } else { /* 1 separator */
  383. memmove ( value, value+1, (value_separator-value)-1 );
  384. *(value_separator - 1) = '\\';
  385. }
  386. }
  387. break;
  388. }
  389. if ( SEPARATOR( *s )) {
  390. if ( value_separator ) value_separator = dn;
  391. else value_separator = d;
  392. }
  393. *d++ = *s;
  394. break;
  395. case B4SEPARATOR:
  396. if ( SEPARATOR( *s ) ) {
  397. state = B4TYPE;
  398. *d++ = (*s == '+') ? '+' : ',';
  399. }
  400. break;
  401. default:
  402. /* LDAPDebug( LDAP_DEBUG_ANY,
  403. "slapi_dn_normalize - unknown state %d\n", state, 0, 0 );*/
  404. break;
  405. }
  406. if ( *s != '\\' ) {
  407. gotesc = 0;
  408. } else {
  409. gotesc = 1;
  410. if ( s+2 < end ) {
  411. auto int n = hexchar2int( s[1] );
  412. if ( n >= 0 ) {
  413. auto int n2 = hexchar2int( s[2] );
  414. if ( n2 >= 0 ) {
  415. n = (n << 4) + n2;
  416. if (n == 0) { /* don't change \00 */
  417. *d++ = *++s;
  418. *d++ = *++s;
  419. gotesc = 0;
  420. } else { /* change \xx to a single char */
  421. ++s;
  422. *(unsigned char*)(s+1) = n;
  423. }
  424. }
  425. }
  426. }
  427. }
  428. }
  429. /* Trim trailing spaces */
  430. while ( d != dn && *(d - 1) == ' ' ) d--;
  431. *d = 0;
  432. return( dn );
  433. }
  434. /* if dn contains an unescaped quote return true */
  435. DS_EXPORT_SYMBOL int
  436. ds_dn_uses_LDAPv2_quoting(const char *dn)
  437. {
  438. const char ESC = '\\';
  439. const char Q = '"';
  440. int ret = 0;
  441. const char *p = 0;
  442. /* check dn for a even number (incl. 0) of ESC followed by Q */
  443. if (!dn)
  444. return ret;
  445. p = strchr(dn, Q);
  446. if (p)
  447. {
  448. int nESC = 0;
  449. for (--p; (p >= dn) && (*p == ESC); --p)
  450. ++nESC;
  451. if (!(nESC % 2))
  452. ret = 1;
  453. }
  454. return ret;
  455. }