1 /*
2 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
7 *
8 * The contents of this file are subject to the Netscape Public License
9 * Version 1.0 (the "NPL"); you may not use this file except in
10 * compliance with the NPL. You may obtain a copy of the NPL at
11 * http://www.mozilla.org/NPL/
12 *
13 * Software distributed under the NPL is distributed on an "AS IS" basis,
14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
15 * for the specific language governing rights and limitations under the
16 * NPL.
17 *
18 * The Initial Developer of this code under the NPL is Netscape
19 * Communications Corporation. Portions created by Netscape are
20 * Copyright (C) 1998 Netscape Communications Corporation. All Rights
21 * Reserved.
22 */
23 /*
24 * Copyright (c) 1993 Regents of the University of Michigan.
25 * All rights reserved.
26 */
27 /*
28 * getfilter.c -- optional add-on to libldap
29 */
30
31 #if 0
32 #ifndef lint
33 static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of Michigan.\nAll rights reserved.\n";
34 #endif
35 #endif
36
37 #include "ldap-int.h"
38 #include "regex.h"
39 #include <stdio.h> /* sprintf */
40
41 static int break_into_words( char *str, char *delims, char ***wordsp );
42
43 #if !defined( macintosh ) && !defined( DOS )
44 extern char * LDAP_CALL re_comp();
45 extern int LDAP_CALL re_exec( char *lp );
46 #endif
47
48 #define FILT_MAX_LINE_LEN 1024
49
50 LDAPFiltDesc *
51 LDAP_CALL
ldap_init_getfilter(char * fname)52 ldap_init_getfilter( char *fname )
53 {
54 FILE *fp;
55 char *buf;
56 ssize_t rlen, len;
57 int eof;
58 LDAPFiltDesc *lfdp;
59
60 if (( fp = fopen( fname, "rF" )) == NULL ) {
61 return( NULL );
62 }
63
64 if ( fseek( fp, 0L, SEEK_END ) != 0 ) { /* move to end to get len */
65 fclose( fp );
66 return( NULL );
67 }
68
69 len = ftell( fp );
70
71 if ( fseek( fp, 0L, SEEK_SET ) != 0 ) { /* back to start of file */
72 fclose( fp );
73 return( NULL );
74 }
75
76 if (( buf = NSLDAPI_MALLOC( (size_t)len )) == NULL ) {
77 fclose( fp );
78 return( NULL );
79 }
80
81 rlen = fread( buf, 1, (size_t)len, fp );
82 eof = feof( fp );
83 fclose( fp );
84
85 if ( rlen != len && !eof ) { /* error: didn't get the whole file */
86 NSLDAPI_FREE( buf );
87 return( NULL );
88 }
89
90
91 lfdp = ldap_init_getfilter_buf( buf, rlen );
92 NSLDAPI_FREE( buf );
93
94 return( lfdp );
95 }
96
97
98 LDAPFiltDesc *
99 LDAP_CALL
ldap_init_getfilter_buf(char * buf,ssize_t buflen)100 ldap_init_getfilter_buf( char *buf, ssize_t buflen )
101 {
102 LDAPFiltDesc *lfdp;
103 LDAPFiltList *flp, *nextflp;
104 LDAPFiltInfo *fip, *nextfip;
105 char *tag, **tok;
106 int tokcnt, i;
107 long buffer_len = (long)buflen;
108
109 if ( (buf == NULL) || (buflen < 0) ||
110 ( lfdp = (LDAPFiltDesc *)NSLDAPI_CALLOC(1, sizeof( LDAPFiltDesc)))
111 == NULL ) {
112 return( NULL );
113 }
114
115 flp = nextflp = NULL;
116 fip = NULL;
117 tag = NULL;
118
119 while ( buflen > 0 && ( tokcnt = ldap_next_line_tokens( &buf,
120 &buffer_len, &tok )) > 0 ) {
121 switch( tokcnt ) {
122 case 1: /* tag line */
123 if ( tag != NULL ) {
124 NSLDAPI_FREE( tag );
125 }
126 tag = tok[ 0 ];
127 NSLDAPI_FREE( tok );
128 break;
129 case 4:
130 case 5: /* start of filter info. list */
131 if (( nextflp = (LDAPFiltList *)NSLDAPI_CALLOC( 1,
132 sizeof( LDAPFiltList ))) == NULL ) {
133 ldap_getfilter_free( lfdp );
134 return( NULL );
135 }
136 nextflp->lfl_tag = nsldapi_strdup( tag );
137 nextflp->lfl_pattern = tok[ 0 ];
138 if ( re_comp( nextflp->lfl_pattern ) != NULL ) {
139 char msg[256];
140 ldap_getfilter_free( lfdp );
141 sprintf( msg, dgettext(TEXT_DOMAIN,
142 "bad regular expresssion %s\n"),
143 nextflp->lfl_pattern );
144 ber_err_print( msg );
145 ldap_free_strarray( tok );
146 return( NULL );
147 }
148
149 nextflp->lfl_delims = tok[ 1 ];
150 nextflp->lfl_ilist = NULL;
151 nextflp->lfl_next = NULL;
152 if ( flp == NULL ) { /* first one */
153 lfdp->lfd_filtlist = nextflp;
154 } else {
155 flp->lfl_next = nextflp;
156 }
157 flp = nextflp;
158 fip = NULL;
159 for ( i = 2; i < 5; ++i ) {
160 tok[ i - 2 ] = tok[ i ];
161 }
162 /* fall through */
163
164 case 2:
165 case 3: /* filter, desc, and optional search scope */
166 if ( nextflp != NULL ) { /* add to info list */
167 if (( nextfip = (LDAPFiltInfo *)NSLDAPI_CALLOC( 1,
168 sizeof( LDAPFiltInfo ))) == NULL ) {
169 ldap_getfilter_free( lfdp );
170 ldap_free_strarray( tok );
171 return( NULL );
172 }
173 if ( fip == NULL ) { /* first one */
174 nextflp->lfl_ilist = nextfip;
175 } else {
176 fip->lfi_next = nextfip;
177 }
178 fip = nextfip;
179 nextfip->lfi_next = NULL;
180 nextfip->lfi_filter = tok[ 0 ];
181 nextfip->lfi_desc = tok[ 1 ];
182 if ( tok[ 2 ] != NULL ) {
183 if ( strcasecmp( tok[ 2 ], "subtree" ) == 0 ) {
184 nextfip->lfi_scope = LDAP_SCOPE_SUBTREE;
185 } else if ( strcasecmp( tok[ 2 ], "onelevel" ) == 0 ) {
186 nextfip->lfi_scope = LDAP_SCOPE_ONELEVEL;
187 } else if ( strcasecmp( tok[ 2 ], "base" ) == 0 ) {
188 nextfip->lfi_scope = LDAP_SCOPE_BASE;
189 } else {
190 ldap_free_strarray( tok );
191 ldap_getfilter_free( lfdp );
192 return( NULL );
193 }
194 NSLDAPI_FREE( tok[ 2 ] );
195 tok[ 2 ] = NULL;
196 } else {
197 nextfip->lfi_scope = LDAP_SCOPE_SUBTREE; /* default */
198 }
199 nextfip->lfi_isexact = ( strchr( tok[ 0 ], '*' ) == NULL &&
200 strchr( tok[ 0 ], '~' ) == NULL );
201 NSLDAPI_FREE( tok );
202 }
203 break;
204
205 default:
206 ldap_free_strarray( tok );
207 ldap_getfilter_free( lfdp );
208 return( NULL );
209 }
210 }
211
212 if ( tag != NULL ) {
213 NSLDAPI_FREE( tag );
214 }
215
216 return( lfdp );
217 }
218
219
220 int
221 LDAP_CALL
ldap_set_filter_additions(LDAPFiltDesc * lfdp,char * prefix,char * suffix)222 ldap_set_filter_additions( LDAPFiltDesc *lfdp, char *prefix, char *suffix )
223 {
224 if ( lfdp == NULL ) {
225 return( LDAP_PARAM_ERROR );
226 }
227
228 if ( lfdp->lfd_filtprefix != NULL ) {
229 NSLDAPI_FREE( lfdp->lfd_filtprefix );
230 }
231 lfdp->lfd_filtprefix = ( prefix == NULL ) ? NULL : nsldapi_strdup( prefix );
232
233 if ( lfdp->lfd_filtsuffix != NULL ) {
234 NSLDAPI_FREE( lfdp->lfd_filtsuffix );
235 }
236 lfdp->lfd_filtsuffix = ( suffix == NULL ) ? NULL : nsldapi_strdup( suffix );
237
238 return( LDAP_SUCCESS );
239 }
240
241
242 /*
243 * ldap_setfilteraffixes() is deprecated -- use ldap_set_filter_additions()
244 */
245 void
246 LDAP_CALL
ldap_setfilteraffixes(LDAPFiltDesc * lfdp,char * prefix,char * suffix)247 ldap_setfilteraffixes( LDAPFiltDesc *lfdp, char *prefix, char *suffix )
248 {
249 (void)ldap_set_filter_additions( lfdp, prefix, suffix );
250 }
251
252
253 LDAPFiltInfo *
254 LDAP_CALL
ldap_getfirstfilter(LDAPFiltDesc * lfdp,char * tagpat,char * value)255 ldap_getfirstfilter( LDAPFiltDesc *lfdp, char *tagpat, char *value )
256 {
257 LDAPFiltList *flp;
258
259 if ( lfdp == NULL || tagpat == NULL || value == NULL ) {
260 return( NULL ); /* punt */
261 }
262
263 if ( lfdp->lfd_curvalcopy != NULL ) {
264 NSLDAPI_FREE( lfdp->lfd_curvalcopy );
265 NSLDAPI_FREE( lfdp->lfd_curvalwords );
266 }
267
268 lfdp->lfd_curval = value;
269 lfdp->lfd_curfip = NULL;
270
271 for ( flp = lfdp->lfd_filtlist; flp != NULL; flp = flp->lfl_next ) {
272 if ( re_comp( tagpat ) == NULL && re_exec( flp->lfl_tag ) == 1
273 && re_comp( flp->lfl_pattern ) == NULL
274 && re_exec( lfdp->lfd_curval ) == 1 ) {
275 lfdp->lfd_curfip = flp->lfl_ilist;
276 break;
277 }
278 }
279
280 if ( lfdp->lfd_curfip == NULL ) {
281 return( NULL );
282 }
283
284 if (( lfdp->lfd_curvalcopy = nsldapi_strdup( value )) == NULL ) {
285 return( NULL );
286 }
287
288 if ( break_into_words( lfdp->lfd_curvalcopy, flp->lfl_delims,
289 &lfdp->lfd_curvalwords ) < 0 ) {
290 NSLDAPI_FREE( lfdp->lfd_curvalwords );
291 lfdp->lfd_curvalwords = NULL;
292 return( NULL );
293 }
294
295 return( ldap_getnextfilter( lfdp ));
296 }
297
298
299 LDAPFiltInfo *
300 LDAP_CALL
ldap_getnextfilter(LDAPFiltDesc * lfdp)301 ldap_getnextfilter( LDAPFiltDesc *lfdp )
302 {
303 LDAPFiltInfo *fip;
304
305 if ( lfdp == NULL || ( fip = lfdp->lfd_curfip ) == NULL ) {
306 return( NULL );
307 }
308
309 lfdp->lfd_curfip = fip->lfi_next;
310
311 ldap_build_filter( lfdp->lfd_filter, LDAP_FILT_MAXSIZ, fip->lfi_filter,
312 lfdp->lfd_filtprefix, lfdp->lfd_filtsuffix, NULL,
313 lfdp->lfd_curval, lfdp->lfd_curvalwords );
314 lfdp->lfd_retfi.lfi_filter = lfdp->lfd_filter;
315 lfdp->lfd_retfi.lfi_desc = fip->lfi_desc;
316 lfdp->lfd_retfi.lfi_scope = fip->lfi_scope;
317 lfdp->lfd_retfi.lfi_isexact = fip->lfi_isexact;
318
319 return( &lfdp->lfd_retfi );
320 }
321
322
323 static char*
filter_add_strn(char * f,char * flimit,char * v,size_t vlen)324 filter_add_strn( char *f, char *flimit, char *v, size_t vlen )
325 /* Copy v into f. If flimit is too small, return NULL;
326 * otherwise return (f + vlen).
327 */
328 {
329 auto size_t flen = flimit - f;
330 if ( vlen > flen ) { /* flimit is too small */
331 if ( flen > 0 ) SAFEMEMCPY( f, v, flen );
332 return NULL;
333 }
334 if ( vlen > 0 ) SAFEMEMCPY( f, v, vlen );
335 return f + vlen;
336 }
337
338 static char*
filter_add_value(char * f,char * flimit,char * v,int escape_all)339 filter_add_value( char *f, char *flimit, char *v, int escape_all )
340 /* Copy v into f, but with parentheses escaped. But only escape * and \
341 * if escape_all is non-zero so that either "*" or "\2a" can be used in
342 * v, with different meanings.
343 * If flimit is too small, return NULL; otherwise
344 * return (f + the number of bytes copied).
345 */
346 {
347 auto char x[4];
348 auto size_t slen;
349 while ( f && *v ) {
350 switch ( *v ) {
351 case '*':
352 if ( escape_all ) {
353 f = filter_add_strn( f, flimit, "\\2a", 3 );
354 v++;
355 } else {
356 if ( f < flimit ) {
357 *f++ = *v++;
358 } else {
359 f = NULL; /* overflow */
360 }
361 }
362 break;
363
364 case '(':
365 case ')':
366 sprintf( x, "\\%02x", (unsigned)*v );
367 f = filter_add_strn( f, flimit, x, 3 );
368 v++;
369 break;
370
371 case '\\':
372 if ( escape_all ) {
373 f = filter_add_strn( f, flimit, "\\5c", 3 );
374 v++;
375 } else {
376 slen = (ldap_utf8isxdigit( v+1 ) &&
377 ldap_utf8isxdigit( v+2 )) ? 3 : (v[1] ? 2 : 1);
378 f = filter_add_strn( f, flimit, v, slen );
379 v += slen;
380 }
381 break;
382
383 default:
384 if ( f < flimit ) {
385 *f++ = *v++;
386 } else {
387 f = NULL; /* overflow */
388 }
389 break;
390 }
391 }
392 return f;
393 }
394
395 int
396 LDAP_CALL
ldap_create_filter(char * filtbuf,unsigned long buflen,char * pattern,char * prefix,char * suffix,char * attr,char * value,char ** valwords)397 ldap_create_filter( char *filtbuf, unsigned long buflen, char *pattern,
398 char *prefix, char *suffix, char *attr, char *value, char **valwords )
399 {
400 char *p, *f, *flimit;
401 int i, wordcount, wordnum, endwordnum, escape_all;
402
403 /*
404 * there is some confusion on what to create for a filter if
405 * attr or value are null pointers. For now we just leave them
406 * as TO BE DEALT with
407 */
408
409 if ( filtbuf == NULL || buflen == 0 || pattern == NULL ){
410 return( LDAP_PARAM_ERROR );
411 }
412
413 if ( valwords == NULL ) {
414 wordcount = 0;
415 } else {
416 for ( wordcount = 0; valwords[ wordcount ] != NULL; ++wordcount ) {
417 ;
418 }
419 }
420
421 f = filtbuf;
422 flimit = filtbuf + buflen - 1;
423
424 if ( prefix != NULL ) {
425 f = filter_add_strn( f, flimit, prefix, strlen( prefix ));
426 }
427
428 for ( p = pattern; f != NULL && *p != '\0'; ++p ) {
429 if ( *p == '%' ) {
430 ++p;
431 if ( *p == 'v' || *p == 'e' ) {
432 escape_all = ( *p == 'e' );
433 if ( ldap_utf8isdigit( p+1 )) {
434 ++p;
435 wordnum = *p - '1';
436 if ( *(p+1) == '-' ) {
437 ++p;
438 if ( ldap_utf8isdigit( p+1 )) {
439 ++p;
440 endwordnum = *p - '1'; /* e.g., "%v2-4" */
441 if ( endwordnum > wordcount - 1 ) {
442 endwordnum = wordcount - 1;
443 }
444 } else {
445 endwordnum = wordcount - 1; /* e.g., "%v2-" */
446 }
447 } else {
448 endwordnum = wordnum; /* e.g., "%v2" */
449 }
450
451 if ( wordcount > 0 ) {
452 for ( i = wordnum; i <= endwordnum; ++i ) {
453 if ( i > wordnum ) { /* add blank btw words */
454 f = filter_add_strn( f, flimit, " ", 1 );
455 if ( f == NULL ) break;
456 }
457 f = filter_add_value( f, flimit, valwords[ i ],
458 escape_all );
459 if ( f == NULL ) break;
460 }
461 }
462 } else if ( *(p+1) == '$' ) {
463 ++p;
464 if ( wordcount > 0 ) {
465 wordnum = wordcount - 1;
466 f = filter_add_value( f, flimit,
467 valwords[ wordnum ], escape_all );
468 }
469 } else if ( value != NULL ) {
470 f = filter_add_value( f, flimit, value, escape_all );
471 }
472 } else if ( *p == 'a' && attr != NULL ) {
473 f = filter_add_strn( f, flimit, attr, strlen( attr ));
474 } else {
475 *f++ = *p;
476 }
477 } else {
478 *f++ = *p;
479 }
480 if ( f > flimit ) { /* overflow */
481 f = NULL;
482 }
483 }
484
485 if ( suffix != NULL && f != NULL) {
486 f = filter_add_strn( f, flimit, suffix, strlen( suffix ));
487 }
488
489 if ( f == NULL ) {
490 *flimit = '\0';
491 return( LDAP_SIZELIMIT_EXCEEDED );
492 }
493 *f = '\0';
494 return( LDAP_SUCCESS );
495 }
496
497
498 /*
499 * ldap_build_filter() is deprecated -- use ldap_create_filter() instead
500 */
501 void
502 LDAP_CALL
ldap_build_filter(char * filtbuf,size_t buflen,char * pattern,char * prefix,char * suffix,char * attr,char * value,char ** valwords)503 ldap_build_filter( char *filtbuf, size_t buflen, char *pattern,
504 char *prefix, char *suffix, char *attr, char *value, char **valwords )
505 {
506 (void)ldap_create_filter( filtbuf, buflen, pattern, prefix, suffix, attr,
507 value, valwords );
508 }
509
510
511 static int
break_into_words(char * str,char * delims,char *** wordsp)512 break_into_words( char *str, char *delims, char ***wordsp )
513 {
514 char *word, **words;
515 int count;
516 char *lasts;
517
518 if (( words = (char **)NSLDAPI_CALLOC( 1, sizeof( char * ))) == NULL ) {
519 return( -1 );
520 }
521 count = 0;
522 words[ count ] = NULL;
523
524 word = ldap_utf8strtok_r( str, delims, &lasts );
525 while ( word != NULL ) {
526 if (( words = (char **)NSLDAPI_REALLOC( words,
527 ( count + 2 ) * sizeof( char * ))) == NULL ) {
528 return( -1 );
529 }
530
531 words[ count ] = word;
532 words[ ++count ] = NULL;
533 word = ldap_utf8strtok_r( NULL, delims, &lasts );
534 }
535
536 *wordsp = words;
537 return( count );
538 }
539