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