str2filter.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  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. /* slapi_str2filter.c - parse an rfc 1588 string filter */
  39. #include <stdio.h>
  40. #include <string.h>
  41. #include <sys/types.h>
  42. #ifndef _WIN32
  43. #include <sys/socket.h>
  44. #endif
  45. #include "slap.h"
  46. static struct slapi_filter *str2list();
  47. static int str2subvals();
  48. struct slapi_filter *
  49. slapi_str2filter( char *str )
  50. {
  51. struct slapi_filter *f = NULL;
  52. char *end;
  53. LDAPDebug( LDAP_DEBUG_FILTER, "slapi_str2filter \"%s\"\n", str, 0, 0 );
  54. if ( str == NULL || *str == '\0' ) {
  55. return( NULL );
  56. }
  57. switch ( *str ) {
  58. case '(':
  59. if ( (end = slapi_find_matching_paren( str )) == NULL ) {
  60. slapi_filter_free( f, 1 );
  61. return( NULL );
  62. }
  63. *end = '\0';
  64. str++;
  65. switch ( *str ) {
  66. case '&':
  67. LDAPDebug( LDAP_DEBUG_FILTER, "slapi_str2filter: AND\n",
  68. 0, 0, 0 );
  69. str++;
  70. f = str2list( str, LDAP_FILTER_AND );
  71. break;
  72. case '|':
  73. LDAPDebug( LDAP_DEBUG_FILTER, "put_filter: OR\n",
  74. 0, 0, 0 );
  75. str++;
  76. f = str2list( str, LDAP_FILTER_OR );
  77. break;
  78. case '!':
  79. LDAPDebug( LDAP_DEBUG_FILTER, "put_filter: NOT\n",
  80. 0, 0, 0 );
  81. str++;
  82. f = str2list( str, LDAP_FILTER_NOT );
  83. break;
  84. default:
  85. LDAPDebug( LDAP_DEBUG_FILTER, "slapi_str2filter: simple\n",
  86. 0, 0, 0 );
  87. f = str2simple( str , 1 /* unescape_filter */);
  88. break;
  89. }
  90. *end = ')';
  91. break;
  92. default: /* assume it's a simple type=value filter */
  93. LDAPDebug( LDAP_DEBUG_FILTER, "slapi_str2filter: default\n", 0, 0,
  94. 0 );
  95. f = str2simple( str , 1 /* unescape_filter */);
  96. break;
  97. }
  98. return( f );
  99. }
  100. /*
  101. * Put a list of filters like this "(filter1)(filter2)..."
  102. */
  103. static struct slapi_filter *
  104. str2list( char *str, unsigned long ftype )
  105. {
  106. struct slapi_filter *f;
  107. struct slapi_filter **fp;
  108. char *next;
  109. char save;
  110. LDAPDebug( LDAP_DEBUG_FILTER, "str2list \"%s\"\n", str, 0, 0 );
  111. f = (struct slapi_filter *) slapi_ch_calloc( 1, sizeof(struct slapi_filter) );
  112. f->f_choice = ftype;
  113. fp = &f->f_list;
  114. while ( *str ) {
  115. while ( *str && ldap_utf8isspace( str ) )
  116. LDAP_UTF8INC( str );
  117. if ( *str == '\0' )
  118. break;
  119. if ( (next = slapi_find_matching_paren( str )) == NULL ) {
  120. slapi_filter_free( f, 1 );
  121. return( NULL );
  122. }
  123. save = *++next;
  124. *next = '\0';
  125. /* now we have "(filter)" with str pointing to it */
  126. if ( (*fp = slapi_str2filter( str )) == NULL ) {
  127. slapi_filter_free( f, 1 );
  128. *next = save;
  129. return( NULL );
  130. }
  131. *next = save;
  132. str = next;
  133. f->f_flags |= ((*fp)->f_flags & SLAPI_FILTER_LDAPSUBENTRY);
  134. f->f_flags |= ((*fp)->f_flags & SLAPI_FILTER_TOMBSTONE);
  135. fp = &(*fp)->f_next;
  136. }
  137. *fp = NULL;
  138. filter_compute_hash(f);
  139. return( f );
  140. }
  141. static char*
  142. str_find_star (char* s)
  143. /* Like strchr(s, '*'), except ignore "\*" */
  144. {
  145. char* r;
  146. if (s == NULL) return s;
  147. r = strchr (s, '*');
  148. if (r != s) while (r != NULL && r[-1] == '\\') {
  149. r = strchr (r+1, '*');
  150. }
  151. return r;
  152. }
  153. /*
  154. * unescape a string into another buffer -- note that an unescaped ldap
  155. * string may contain nulls if 'binary' is set! this is sort of a mix
  156. * between the LDAP SDK version and terry hayes' version. optimally it
  157. * would be nice if the LDAP SDK exported something like this.
  158. *
  159. * if 'binary' is set, "\00" is allowed, otherwise it's not.
  160. *
  161. * returns: 0 on error, 1 on success
  162. * (*outlen) is the actual length of the unescaped string
  163. */
  164. static int
  165. filt_unescape_str(const char *instr, char *outstr, size_t outsize, size_t* outlen, int binary)
  166. {
  167. const char *inp;
  168. char *outp;
  169. int ival;
  170. *outlen = 0;
  171. if (!outstr) return -1;
  172. for (inp = instr, outp = outstr; *inp; inp++)
  173. {
  174. if (! outsize)
  175. return 0; /* fail */
  176. if (*inp == '\\')
  177. {
  178. if (! *(++inp))
  179. return 0; /* fail */
  180. if (((ival = hexchar2int(inp[0])) < 0) || (hexchar2int(inp[1]) < 0))
  181. {
  182. /* LDAPv2 (RFC1960) escape sequence */
  183. *outp++ = *inp;
  184. (*outlen)++, outsize--;
  185. }
  186. else
  187. {
  188. /* LDAPv3 hex escape sequence */
  189. if (! *(++inp))
  190. return 0; /* fail */
  191. *outp = (ival << 4) | hexchar2int(*inp);
  192. if ((!binary) && (!*outp))
  193. return 0; /* fail: "\00" not allowed unless it's binary */
  194. outp++;
  195. (*outlen)++, outsize--;
  196. }
  197. }
  198. else
  199. {
  200. *outp++ = *inp;
  201. (*outlen)++, outsize--;
  202. }
  203. }
  204. return 1; /* ok */
  205. }
  206. /*
  207. * The caller unescapes it if unescape_filter == 0.
  208. */
  209. struct slapi_filter *
  210. str2simple( char *str , int unescape_filter)
  211. {
  212. struct slapi_filter *f;
  213. char *s;
  214. char *value, savechar;
  215. LDAPDebug( LDAP_DEBUG_FILTER, "str2simple \"%s\"\n", str, 0, 0 );
  216. PR_ASSERT(str);
  217. if ( (s = strchr( str, '=' )) == NULL ) {
  218. return( NULL );
  219. }
  220. value = s;
  221. LDAP_UTF8INC(value);
  222. LDAP_UTF8DEC(s);
  223. f = (struct slapi_filter *) slapi_ch_calloc( 1, sizeof(struct slapi_filter) );
  224. switch ( *s ) {
  225. case '<':
  226. f->f_choice = LDAP_FILTER_LE;
  227. break;
  228. case '>':
  229. f->f_choice = LDAP_FILTER_GE;
  230. break;
  231. case '~':
  232. f->f_choice = LDAP_FILTER_APPROX;
  233. break;
  234. default:
  235. LDAP_UTF8INC(s);
  236. if ( str_find_star( value ) == NULL ) {
  237. f->f_choice = LDAP_FILTER_EQUALITY;
  238. } else if ( strcmp( value, "*" ) == 0 ) {
  239. f->f_choice = LDAP_FILTER_PRESENT;
  240. } else {
  241. f->f_choice = LDAP_FILTER_SUBSTRINGS;
  242. savechar = *s;
  243. *s = 0;
  244. f->f_sub_type = slapi_ch_strdup( str );
  245. *s = savechar;
  246. if ( str2subvals( value, f , unescape_filter) != 0 ) {
  247. slapi_filter_free( f, 1 );
  248. return( NULL );
  249. }
  250. filter_compute_hash(f);
  251. return( f );
  252. }
  253. break;
  254. }
  255. if ( f->f_choice == LDAP_FILTER_PRESENT ) {
  256. savechar = *s;
  257. *s = 0;
  258. f->f_type = slapi_ch_strdup( str );
  259. *s = savechar;
  260. } else if ( unescape_filter ) {
  261. int r;
  262. char *unqstr;
  263. size_t len = strlen(value), len2;
  264. /* dup attr */
  265. savechar = *s;
  266. *s = 0;
  267. f->f_avtype = slapi_ch_strdup( str );
  268. *s = savechar;
  269. /* dup value */
  270. savechar = value[len];
  271. value[len] = 0;
  272. unqstr = slapi_ch_calloc( 1, len+1);
  273. r= filt_unescape_str(value, unqstr, len, &len2, 1);
  274. value[len] = savechar;
  275. if (!r) {
  276. slapi_filter_free(f, 1);
  277. return NULL;
  278. }
  279. f->f_avvalue.bv_val = unqstr;
  280. f->f_avvalue.bv_len = len2;
  281. if((f->f_choice == LDAP_FILTER_EQUALITY) &&
  282. (0 == strncasecmp (str,"objectclass",strlen("objectclass")))) {
  283. if (0 == strcasecmp (unqstr,"ldapsubentry"))
  284. f->f_flags |= SLAPI_FILTER_LDAPSUBENTRY;
  285. if (0 == strcasecmp (unqstr,SLAPI_ATTR_VALUE_TOMBSTONE))
  286. f->f_flags |= SLAPI_FILTER_TOMBSTONE;
  287. }
  288. } if ( !unescape_filter ) {
  289. f->f_avtype = slapi_ch_strdup( str );
  290. f->f_avvalue.bv_val = slapi_ch_strdup ( value );
  291. f->f_avvalue.bv_len = strlen ( f->f_avvalue.bv_val );
  292. }
  293. filter_compute_hash(f);
  294. return( f );
  295. }
  296. static int
  297. str2subvals( char *val, struct slapi_filter *f, int unescape_filter )
  298. {
  299. char *nextstar, *unqval;
  300. int gotstar;
  301. size_t len, outlen;
  302. LDAPDebug( LDAP_DEBUG_FILTER, "str2subvals \"%s\"\n", val, 0, 0 );
  303. gotstar = 0;
  304. while ( val != NULL && *val ) {
  305. if ( (nextstar = str_find_star( val )) != NULL )
  306. *nextstar = '\0';
  307. if ( unescape_filter ) {
  308. len = strlen(val);
  309. unqval = slapi_ch_malloc(len+1);
  310. if (!filt_unescape_str(val, unqval, len, &outlen, 0)) {
  311. slapi_ch_free((void **)&unqval);
  312. return -1;
  313. }
  314. unqval[outlen]= '\0';
  315. } else {
  316. unqval = slapi_ch_strdup ( val );
  317. }
  318. if (unqval && unqval[0]) {
  319. if (gotstar == 0) {
  320. f->f_sub_initial = unqval;
  321. } else if ( nextstar == NULL ) {
  322. f->f_sub_final = unqval;
  323. } else {
  324. charray_add( &f->f_sub_any, unqval );
  325. }
  326. } else {
  327. slapi_ch_free((void **)&unqval);
  328. }
  329. gotstar = 1;
  330. if ( nextstar != NULL )
  331. *nextstar++ = '*';
  332. val = nextstar;
  333. }
  334. return( 0 );
  335. }
  336. /*
  337. * find_matching_paren - return a pointer to the right paren in s matching
  338. * the left paren to which *s currently points
  339. */
  340. char *
  341. slapi_find_matching_paren( const char *s )
  342. {
  343. int balance, escape;
  344. balance = 0;
  345. escape = 0;
  346. for ( ; *s; s++ ) {
  347. if ( escape == 0 ) {
  348. if ( *s == '(' )
  349. balance++;
  350. else if ( *s == ')' )
  351. balance--;
  352. }
  353. if ( balance == 0 ) {
  354. return( (char *)s );
  355. }
  356. if ( *s == '\\' && ! escape )
  357. escape = 1;
  358. else
  359. escape = 0;
  360. }
  361. return( NULL );
  362. }