]> git.sur5r.net Git - openldap/blob - servers/slapd/dn.c
Update validation of printable-like syntaxes
[openldap] / servers / slapd / dn.c
1 /* dn.c - routines for dealing with distinguished names */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11
12 #include <ac/ctype.h>
13 #include <ac/socket.h>
14 #include <ac/string.h>
15 #include <ac/time.h>
16
17 #include "ldap_pvt.h"
18
19 #include "slap.h"
20
21 #define B4LEADTYPE              0
22 #define B4TYPE                  1
23 #define INOIDTYPE               2
24 #define INKEYTYPE               3
25 #define B4EQUAL                 4
26 #define B4VALUE                 5
27 #define INVALUE                 6
28 #define INQUOTEDVALUE   7
29 #define B4SEPARATOR             8
30
31 #define UTF8DN 1
32
33 #ifdef UTF8DN
34 /*
35  * Upper cases a UTF8 character. We cheat a bit, we only change to upper
36  * if the byte length is preserved. Should be replaced.
37  */
38
39 int
40 UTF8touppercheat( unsigned char *p )
41 {
42         ldap_unicode_t u;
43         int len;
44
45         len = LDAP_UTF8_CHARLEN( p );
46         if ( !len ) {
47                 return 1;
48         }
49
50         u = ldap_utf8_to_ucs4( p );
51         if ( u == LDAP_UCS4_INVALID ) {
52                 return 1;
53         }
54
55         u = uctoupper ( u );
56         if ( u < 0 ) {
57                 return 1;
58         }
59
60         if ( u < 0x80 ) {
61                 if ( len == 1 ) {
62                         p[0] = u;
63                 } else {
64                         return 1;
65                 }
66         } else if ( u < 0x800 ) {
67                 if ( len == 2 ) {
68                         p[0] = 0xc0 | ( u >> 6 );
69                         p[1] = 0x80 | ( u & 0x3f );
70                 } else {
71                         return 1;
72                 }
73         } else if ( u < 0x10000 ) {
74                 if ( len == 3 ) {
75                         p[0] = 0xe0 | ( u >> 12 );
76                         p[1] = 0x80 | ( (u >> 6) & 0x3f );
77                         p[2] = 0x80 | ( u & 0x3f );
78                 } else {
79                         return 1;
80                 }
81         } else if ( u < 0x200000 ) {
82                 if ( len == 4 ) {
83                         p[0] = 0xf0 | ( u >> 18 );
84                         p[1] = 0x80 | ( (u >> 12) & 0x3f );
85                         p[2] = 0x80 | ( (u >> 6) & 0x3f );
86                         p[3] = 0x80 | ( u & 0x3f );
87                 } else {
88                         return 1;
89                 }             
90         } else if ( u < 0x4000000 ) {
91                 if ( len == 5 ) {
92                         p[0] = 0xf8 | ( u >> 24 );
93                         p[1] = 0x80 | ( (u >> 18) & 0x3f );
94                         p[2] = 0x80 | ( (u >> 12) & 0x3f );
95                         p[3] = 0x80 | ( (u >> 6) & 0x3f );
96                         p[4] = 0x80 | ( u & 0x3f );
97                 } else {
98                         return 1;
99                 }
100         } else if ( len == 6 ) {
101                 /* u < 0x80000000 */
102                 p[0] = 0xfc | ( u >> 30 );
103                 p[1] = 0x80 | ( (u >> 24) & 0x3f );
104                 p[2] = 0x80 | ( (u >> 18) & 0x3f );
105                 p[3] = 0x80 | ( (u >> 12) & 0x3f );
106                 p[4] = 0x80 | ( (u >> 6) & 0x3f );
107                 p[5] = 0x80 | ( u & 0x3f );
108         } else {
109                 return 1;
110         }
111
112         return len;
113 }
114 #endif
115
116 /*
117  * dn_validate - validate and compress dn.  the dn is
118  * compressed in place are returned if valid.
119  */
120
121 char *
122 dn_validate( char *dn_in )
123 {
124         char    *d, *s;
125         int     state, gotesc;
126         char    *dn = dn_in;
127
128         gotesc = 0;
129         state = B4LEADTYPE;
130         for ( d = s = dn; *s; s++ ) {
131                 switch ( state ) {
132                 case B4LEADTYPE:
133                 case B4TYPE:
134                         if ( OID_LEADCHAR(*s) ) {
135                                 state = INOIDTYPE;
136                                 *d++ = *s;
137                         } else if ( ATTR_LEADCHAR(*s) ) {
138                                 state = INKEYTYPE;
139                                 *d++ = *s;
140                         } else if ( ! ASCII_SPACE( *s ) ) {
141                                 dn = NULL;
142                                 state = INKEYTYPE;
143                                 *d++ = *s;
144                         }
145                         break;
146
147                 case INOIDTYPE:
148                         if ( OID_CHAR(*s) ) {
149                                 *d++ = *s;
150                         } else if ( *s == '=' ) {
151                                 state = B4VALUE;
152                                 *d++ = *s;
153                         } else if ( ASCII_SPACE( *s ) ) {
154                                 state = B4EQUAL;
155                         } else {
156                                 dn = NULL;
157                                 *d++ = *s;
158                         }
159                         break;
160
161                 case INKEYTYPE:
162                         if ( ATTR_CHAR(*s) ) {
163                                 *d++ = *s;
164                         } else if ( *s == '=' ) {
165                                 state = B4VALUE;
166                                 *d++ = *s;
167                         } else if ( ASCII_SPACE( *s ) ) {
168                                 state = B4EQUAL;
169                         } else {
170                                 dn = NULL;
171                                 *d++ = *s;
172                         }
173                         break;
174
175                 case B4EQUAL:
176                         if ( *s == '=' ) {
177                                 state = B4VALUE;
178                                 *d++ = *s;
179                         } else if ( ! ASCII_SPACE( *s ) ) {
180                                 /* not a valid dn - but what can we do here? */
181                                 *d++ = *s;
182                                 dn = NULL;
183                         }
184                         break;
185
186                 case B4VALUE:
187                         if ( *s == '"' ) {
188                                 state = INQUOTEDVALUE;
189                                 *d++ = *s;
190                         } else if ( ! ASCII_SPACE( *s ) ) { 
191                                 state = INVALUE;
192                                 *d++ = *s;
193                         }
194                         break;
195
196                 case INVALUE:
197                         if ( !gotesc && RDN_SEPARATOR( *s ) ) {
198                                 while ( ASCII_SPACE( *(d - 1) ) )
199                                         d--;
200                                 state = B4TYPE;
201                                 if ( *s == '+' ) {
202                                         *d++ = *s;
203                                 } else {
204                                         *d++ = ',';
205                                 }
206                         } else if ( gotesc && !RDN_NEEDSESCAPE( *s ) &&
207                             !RDN_SEPARATOR( *s ) ) {
208                                 *--d = *s;
209                                 d++;
210                         } else if( !ASCII_SPACE( *s ) || !ASCII_SPACE( *(d - 1) ) ) {
211                                 *d++ = *s;
212                         }
213                         break;
214
215                 case INQUOTEDVALUE:
216                         if ( !gotesc && *s == '"' ) {
217                                 state = B4SEPARATOR;
218                                 *d++ = *s;
219                         } else if ( gotesc && !RDN_NEEDSESCAPE( *s ) ) {
220                                 *--d = *s;
221                                 d++;
222                         } else if( !ASCII_SPACE( *s ) || !ASCII_SPACE( *(d - 1) ) ) {
223                                 *d++ = *s;
224                         }
225                         break;
226
227                 case B4SEPARATOR:
228                         if ( RDN_SEPARATOR( *s ) ) {
229                                 state = B4TYPE;
230                                 *d++ = *s;
231                         } else if ( !ASCII_SPACE( *s ) ) {
232                                 dn = NULL;
233                         }
234                         break;
235
236                 default:
237                         dn = NULL;
238                         Debug( LDAP_DEBUG_ANY,
239                             "dn_validate - unknown state %d\n", state, 0, 0 );
240                         break;
241                 }
242
243                 if ( *s == '\\' ) {
244                         gotesc = 1;
245                 } else {
246                         gotesc = 0;
247                 }
248         }
249
250         /* trim trailing spaces */
251         while( d > dn_in && ASCII_SPACE( *(d-1) ) ) {
252                 --d;
253         }
254         *d = '\0';
255
256         if( gotesc ) {
257                 /* shouldn't be left in escape */
258                 dn = NULL;
259         }
260
261         /* check end state */
262         switch( state ) {
263         case B4LEADTYPE:        /* looking for first type */
264         case B4SEPARATOR:       /* looking for separator */
265         case INVALUE:           /* inside value */
266                 break;
267         default:
268                 dn = NULL;
269         }
270
271         return( dn );
272 }
273
274 /*
275  * dn_normalize - put dn into a canonical form suitable for storing
276  * in a hash database.  this involves normalizing the case as well as
277  * the format.  the dn is normalized in place as well as returned if valid.
278  */
279
280 char *
281 dn_normalize( char *dn )
282 {
283         /* upper case it */
284 #ifndef UTF8DN
285         ldap_pvt_str2upper( dn );
286 #else
287         /* enabling this might require reindexing */
288         char *p;
289
290         p = dn;
291         while ( *p ) {
292                 /* optimizing */
293                 if ( LDAP_UTF8_ISASCII(p) ) {
294                         *p = TOUPPER( (unsigned char) *p );
295                         p++;
296                 } else {
297                         p += UTF8touppercheat( p );
298                 }
299         }
300 #endif
301         /* validate and compress dn */
302         dn = dn_validate( dn );
303
304         return( dn );
305 }
306
307 /*
308  * dn_parent - return a copy of the dn of dn's parent
309  */
310
311 char *
312 dn_parent(
313     Backend     *be,
314     const char  *dn
315 )
316 {
317         const char      *s;
318         int     inquote;
319
320         if( dn == NULL ) {
321                 return NULL;
322         }
323
324         while(*dn != '\0' && ASCII_SPACE(*dn)) {
325                 dn++;
326         }
327
328         if( *dn == '\0' ) {
329                 return NULL;
330         }
331
332         if ( be != NULL && be_issuffix( be, dn ) ) {
333                 return NULL;
334         }
335
336         /*
337          * assume it is an X.500-style name, which looks like
338          * foo=bar,sha=baz,...
339          */
340
341         inquote = 0;
342         for ( s = dn; *s; s++ ) {
343                 if ( *s == '\\' ) {
344                         if ( *(s + 1) ) {
345                                 s++;
346                         }
347                         continue;
348                 }
349                 if ( inquote ) {
350                         if ( *s == '"' ) {
351                                 inquote = 0;
352                         }
353                 } else {
354                         if ( *s == '"' ) {
355                                 inquote = 1;
356                         } else if ( DN_SEPARATOR( *s ) ) {
357                                 return ch_strdup( &s[1] );
358                         }
359                 }
360         }
361
362         return ch_strdup( "" );
363 }
364
365 char * dn_rdn( 
366     Backend     *be,
367     const char  *dn_in )
368 {
369         char    *dn, *s;
370         int     inquote;
371
372         if( dn_in == NULL ) {
373                 return NULL;
374         }
375
376         while(*dn_in && ASCII_SPACE(*dn_in)) {
377                 dn_in++;
378         }
379
380         if( *dn_in == '\0' ) {
381                 return( NULL );
382         }
383
384         if ( be != NULL && be_issuffix( be, dn_in ) ) {
385                 return( NULL );
386         }
387
388         dn = ch_strdup( dn_in );
389
390         inquote = 0;
391
392         for ( s = dn; *s; s++ ) {
393                 if ( *s == '\\' ) {
394                         if ( *(s + 1) ) {
395                                 s++;
396                         }
397                         continue;
398                 }
399                 if ( inquote ) {
400                         if ( *s == '"' ) {
401                                 inquote = 0;
402                         }
403                 } else {
404                         if ( *s == '"' ) {
405                                 inquote = 1;
406                         } else if ( DN_SEPARATOR( *s ) ) {
407                                 *s = '\0';
408                                 return( dn );
409                         }
410                 }
411         }
412
413         return( dn );
414 }
415
416
417 /*
418  * return a charray of all subtrees to which the DN resides in
419  */
420 char **dn_subtree(
421         Backend *be,
422     const char  *dn )
423 {
424         char *child, *parent;
425         char **subtree = NULL;
426         
427         child = ch_strdup( dn );
428
429         do {
430                 charray_add( &subtree, child );
431
432                 parent = dn_parent( be, child );
433
434                 free( child );
435
436                 child = parent;
437         } while ( child != NULL );
438
439         return subtree;
440 }
441
442
443 /*
444  * dn_issuffix - tells whether suffix is a suffix of dn.  both dn
445  * and suffix must be normalized.
446  */
447
448 int
449 dn_issuffix(
450     const char  *dn,
451     const char  *suffix
452 )
453 {
454         int     dnlen, suffixlen;
455
456         if ( dn == NULL ) {
457                 return( 0 );
458         }
459
460         suffixlen = strlen( suffix );
461         dnlen = strlen( dn );
462
463         if ( suffixlen > dnlen ) {
464                 return( 0 );
465         }
466
467         return( strcmp( dn + dnlen - suffixlen, suffix ) == 0 );
468 }
469
470 /*
471  * get_next_substring(), rdn_attr_type(), rdn_attr_value(), and
472  * build_new_dn().
473  * 
474  * Copyright 1999, Juan C. Gomez, All rights reserved.
475  * This software is not subject to any license of Silicon Graphics 
476  * Inc. or Purdue University.
477  *
478  * Redistribution and use in source and binary forms are permitted
479  * without restriction or fee of any kind as long as this notice
480  * is preserved.
481  *
482  */
483
484 /* get_next_substring:
485  *
486  * Gets next substring in s, using d (or the end of the string '\0') as a 
487  * string delimiter, and places it in a duplicated memory space. Leading 
488  * spaces are ignored. String s **must** be null-terminated.
489  */ 
490
491 static char * 
492 get_next_substring( const char * s, char d )
493 {
494
495         char    *str, *r;
496
497         r = str = ch_malloc( strlen(s) + 1 );
498
499         /* Skip leading spaces */
500         
501         while ( *s && ASCII_SPACE(*s) ) {
502                 s++;
503         }
504         
505         /* Copy word */
506
507         while ( *s && (*s != d) ) {
508
509                 /* Don't stop when you see trailing spaces may be a multi-word
510                 * string, i.e. name=John Doe!
511                 */
512
513                 *str++ = *s++;
514             
515         }
516         
517         *str = '\0';
518         
519         return r;
520         
521 }
522
523
524 /* rdn_attr_type:
525  *
526  * Given a string (i.e. an rdn) of the form:
527  *       "attribute_type = attribute_value"
528  * this function returns the type of an attribute, that is the 
529  * string "attribute_type" which is placed in newly allocated 
530  * memory. The returned string will be null-terminated.
531  */
532
533 char * rdn_attr_type( const char * s )
534 {
535         return get_next_substring( s, '=' );
536 }
537
538
539 /* rdn_attr_value:
540  *
541  * Given a string (i.e. an rdn) of the form:
542  *       "attribute_type = attribute_value"
543  * this function returns "attribute_type" which is placed in newly allocated 
544  * memory. The returned string will be null-terminated and may contain 
545  * spaces (i.e. "John Doe\0").
546  */
547
548 char * 
549 rdn_attr_value( const char * rdn )
550 {
551
552         const char      *str;
553
554         if ( (str = strchr( rdn, '=' )) != NULL ) {
555                 return get_next_substring(++str, '\0');
556         }
557
558         return NULL;
559
560 }
561
562
563 int rdn_validate( const char * rdn )
564 {
565         /* just a simple check for now */
566         return strchr( rdn, '=' ) != NULL;
567 }
568
569
570 /* build_new_dn:
571  *
572  * Used by ldbm/bdb2_back_modrdn to create the new dn of entries being
573  * renamed.
574  *
575  * new_dn = parent (p_dn)  + separator(s) + rdn (newrdn) + null.
576  */
577
578 void
579 build_new_dn( char ** new_dn,
580         const char *e_dn,
581         const char * p_dn,
582         const char * newrdn )
583 {
584
585     if ( p_dn == NULL ) {
586         *new_dn = ch_strdup( newrdn );
587         return;
588     }
589     
590     *new_dn = (char *) ch_malloc( strlen( p_dn ) + strlen( newrdn ) + 3 );
591
592         strcpy( *new_dn, newrdn );
593         strcat( *new_dn, "," );
594         strcat( *new_dn, p_dn );
595 }