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