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