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
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
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
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
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
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
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*
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*
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
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
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
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