xref: /illumos-gate/usr/src/lib/libldap5/sources/ldap/common/getdn.c (revision bdb9230ac765cb7af3fc1f4119caf2c5720dceb3)
1 /*
2  * Copyright (c) 2001 by Sun Microsystems, Inc.
3  * All rights reserved.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * The contents of this file are subject to the Netscape Public
10  * License Version 1.1 (the "License"); you may not use this file
11  * except in compliance with the License. You may obtain a copy of
12  * the License at http://www.mozilla.org/NPL/
13  *
14  * Software distributed under the License is distributed on an "AS
15  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
16  * implied. See the License for the specific language governing
17  * rights and limitations under the License.
18  *
19  * The Original Code is Mozilla Communicator client code, released
20  * March 31, 1998.
21  *
22  * The Initial Developer of the Original Code is Netscape
23  * Communications Corporation. Portions created by Netscape are
24  * Copyright (C) 1998-1999 Netscape Communications Corporation. All
25  * Rights Reserved.
26  *
27  * Contributor(s):
28  */
29 /*
30  *  Copyright (c) 1994 Regents of the University of Michigan.
31  *  All rights reserved.
32  */
33 /*
34  *  getdn.c
35  */
36 
37 #if 0
38 #ifndef lint
39 static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
40 #endif
41 #endif
42 
43 #include "ldap-int.h"
44 
45 char *
46 LDAP_CALL
47 ldap_get_dn( LDAP *ld, LDAPMessage *entry )
48 {
49 	char			*dn;
50 	struct berelement	tmp;
51 
52 	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_get_dn\n", 0, 0, 0 );
53 
54 	if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
55 		return( NULL );		/* punt */
56 	}
57 
58 	if ( !NSLDAPI_VALID_LDAPMESSAGE_ENTRY_POINTER( entry )) {
59 		LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
60 		return( NULL );
61 	}
62 
63 	tmp = *entry->lm_ber;	/* struct copy */
64 	if ( ber_scanf( &tmp, "{a", &dn ) == LBER_ERROR ) {
65 		LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL );
66 		return( NULL );
67 	}
68 
69 	return( dn );
70 }
71 
72 char *
73 LDAP_CALL
74 ldap_dn2ufn( const char *dn )
75 {
76 	char	*p, *ufn, *r;
77 	size_t	plen;
78 	int	state;
79 
80 	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_dn2ufn\n", 0, 0, 0 );
81 
82 	if ( dn == NULL ) {
83 		dn = "";
84 	}
85 
86 	if ( ldap_is_dns_dn( dn ) || ( p = strchr( dn, '=' )) == NULL )
87 		return( nsldapi_strdup( (char *)dn ));
88 
89 	ufn = nsldapi_strdup( ++p );
90 
91 #define INQUOTE		1
92 #define OUTQUOTE	2
93 	state = OUTQUOTE;
94 	for ( p = ufn, r = ufn; *p; p += plen ) {
95 	    plen = 1;
96 		switch ( *p ) {
97 		case '\\':
98 			if ( *++p == '\0' )
99 				plen=0;
100 			else {
101 				*r++ = '\\';
102 				r += (plen = LDAP_UTF8COPY(r,p));
103 			}
104 			break;
105 		case '"':
106 			if ( state == INQUOTE )
107 				state = OUTQUOTE;
108 			else
109 				state = INQUOTE;
110 			*r++ = *p;
111 			break;
112 		case ';':
113 		case ',':
114 			if ( state == OUTQUOTE )
115 				*r++ = ',';
116 			else
117 				*r++ = *p;
118 			break;
119 		case '=':
120 			if ( state == INQUOTE )
121 				*r++ = *p;
122 			else {
123 				char	*rsave = r;
124 				LDAP_UTF8DEC(r);
125 				*rsave = '\0';
126 				while ( !ldap_utf8isspace( r ) && *r != ';'
127 				    && *r != ',' && r > ufn )
128 					LDAP_UTF8DEC(r);
129 				LDAP_UTF8INC(r);
130 
131 				if ( strcasecmp( r, "c" )
132 				    && strcasecmp( r, "o" )
133 				    && strcasecmp( r, "ou" )
134 				    && strcasecmp( r, "st" )
135 				    && strcasecmp( r, "l" )
136 				    && strcasecmp( r, "dc" )
137 				    && strcasecmp( r, "uid" )
138 				    && strcasecmp( r, "cn" ) ) {
139 					r = rsave;
140 					*r++ = '=';
141 				}
142 			}
143 			break;
144 		default:
145 			r += (plen = LDAP_UTF8COPY(r,p));
146 			break;
147 		}
148 	}
149 	*r = '\0';
150 
151 	return( ufn );
152 }
153 
154 char **
155 LDAP_CALL
156 ldap_explode_dns( const char *dn )
157 {
158 	int	ncomps, maxcomps;
159 	char	*s, *cpydn;
160 	char	**rdns;
161 #ifdef HAVE_STRTOK_R	/* defined in portable.h */
162 	char	*lasts;
163 #endif
164 
165 	if ( dn == NULL ) {
166 		dn = "";
167 	}
168 
169 	if ( (rdns = (char **)NSLDAPI_MALLOC( 8 * sizeof(char *) )) == NULL ) {
170 		return( NULL );
171 	}
172 
173 	maxcomps = 8;
174 	ncomps = 0;
175 	cpydn = nsldapi_strdup( (char *)dn );
176 	for ( s = STRTOK( cpydn, "@.", &lasts ); s != NULL;
177 	    s = STRTOK( NULL, "@.", &lasts ) ) {
178 		if ( ncomps == maxcomps ) {
179 			maxcomps *= 2;
180 			if ( (rdns = (char **)NSLDAPI_REALLOC( rdns, maxcomps *
181 			    sizeof(char *) )) == NULL ) {
182 				NSLDAPI_FREE( cpydn );
183 				return( NULL );
184 			}
185 		}
186 		rdns[ncomps++] = nsldapi_strdup( s );
187 	}
188 	rdns[ncomps] = NULL;
189 	NSLDAPI_FREE( cpydn );
190 
191 	return( rdns );
192 }
193 
194 #define LDAP_DN		1
195 #define LDAP_RDN	2
196 
197 static char **
198 ldap_explode( const char *dn, const int notypes, const int nametype )
199 {
200 	char	*p, *q, *rdnstart, **rdns = NULL;
201 	size_t	plen = 0;
202 	int	state, count = 0, endquote, len, goteq;
203 
204 	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_explode\n", 0, 0, 0 );
205 
206 	if ( dn == NULL ) {
207 		dn = "";
208 	}
209 
210 #if 0
211 	if ( ldap_is_dns_dn( dn ) ) {
212 		return( ldap_explode_dns( dn ) );
213 	}
214 #endif
215 
216 	while ( ldap_utf8isspace( (char *)dn )) { /* ignore leading spaces */
217 		++dn;
218 	}
219 
220 	p = rdnstart = (char *) dn;
221 	state = OUTQUOTE;
222 	goteq = 0;
223 
224 	do {
225 		p += plen;
226 		plen = 1;
227 		switch ( *p ) {
228 		case '\\':
229 			if ( *++p == '\0' )
230 				p--;
231 			else
232 				plen = LDAP_UTF8LEN(p);
233 			break;
234 		case '"':
235 			if ( state == INQUOTE )
236 				state = OUTQUOTE;
237 			else
238 				state = INQUOTE;
239 			break;
240 		case '+': if ( nametype != LDAP_RDN ) break;
241 		case ';':
242 		case ',':
243 		case '\0':
244 			if ( state == OUTQUOTE ) {
245 				/*
246 				 * semicolon and comma are not valid RDN
247 				 * separators.
248 				 */
249 				if ( nametype == LDAP_RDN &&
250 					( *p == ';' || *p == ',' || !goteq)) {
251 					ldap_charray_free( rdns );
252 					return NULL;
253 				}
254 				if ( (*p == ',' || *p == ';') && !goteq ) {
255                                    /* If we get here, we have a case similar
256 				    * to <attr>=<value>,<string>,<attr>=<value>
257 				    * This is not a valid dn */
258 				    ldap_charray_free( rdns );
259 				    return NULL;
260 				}
261 				goteq = 0;
262 				++count;
263 				if ( rdns == NULL ) {
264 					if (( rdns = (char **)NSLDAPI_MALLOC( 8
265 						 * sizeof( char *))) == NULL )
266 						return( NULL );
267 				} else if ( count >= 8 ) {
268 					if (( rdns = (char **)NSLDAPI_REALLOC(
269 					    rdns, (count+1) *
270 					    sizeof( char *))) == NULL )
271 						return( NULL );
272 				}
273 				rdns[ count ] = NULL;
274 				endquote = 0;
275 				if ( notypes ) {
276 					for ( q = rdnstart;
277 					    q < p && *q != '='; ++q ) {
278 						;
279 					}
280 					if ( q < p ) { /* *q == '=' */
281 						rdnstart = ++q;
282 					}
283 					if ( *rdnstart == '"' ) {
284 						++rdnstart;
285 					}
286 
287 					if ( *(p-1) == '"' ) {
288 						endquote = 1;
289 						--p;
290 					}
291 				}
292 
293 				len = p - rdnstart;
294 				if (( rdns[ count-1 ] = (char *)NSLDAPI_CALLOC(
295 				    1, len + 1 )) != NULL ) {
296 				    	SAFEMEMCPY( rdns[ count-1 ], rdnstart,
297 					    len );
298 					if ( !endquote ) {
299 						/* trim trailing spaces */
300 						while ( len > 0 &&
301 						    ldap_utf8isspace(
302 						    &rdns[count-1][len-1] )) {
303 							--len;
304 						}
305 					}
306 					rdns[ count-1 ][ len ] = '\0';
307 				}
308 
309 				/*
310 				 *  Don't forget to increment 'p' back to where
311 				 *  it should be.  If we don't, then we will
312 				 *  never get past an "end quote."
313 				 */
314 				if ( endquote == 1 )
315 					p++;
316 
317 				rdnstart = *p ? p + 1 : p;
318 				while ( ldap_utf8isspace( rdnstart ))
319 					++rdnstart;
320 			}
321 			break;
322 		case '=':
323 			if ( state == OUTQUOTE ) {
324 				goteq = 1;
325 			}
326 			/* FALL */
327 		default:
328 			plen = LDAP_UTF8LEN(p);
329 			break;
330 		}
331 	} while ( *p );
332 
333 	return( rdns );
334 }
335 
336 char **
337 LDAP_CALL
338 ldap_explode_dn( const char *dn, const int notypes )
339 {
340 	return( ldap_explode( dn, notypes, LDAP_DN ) );
341 }
342 
343 char **
344 LDAP_CALL
345 ldap_explode_rdn( const char *rdn, const int notypes )
346 {
347 	return( ldap_explode( rdn, notypes, LDAP_RDN ) );
348 }
349 
350 int
351 LDAP_CALL
352 ldap_is_dns_dn( const char *dn )
353 {
354 	return( dn != NULL && dn[ 0 ] != '\0' && strchr( dn, '=' ) == NULL &&
355 	    strchr( dn, ',' ) == NULL );
356 }
357 
358 #ifdef _SOLARIS_SDK
359 
360 /*
361  * Convert a DNS domain name into an X.500 distinguished name.
362  * For example, "sales.wiz.com" -> "dc=sales,dc=wiz,dc=com"
363  *
364  * If an error is encountered zero is returned, otherwise a string
365  * distinguished name and the number of nameparts is returned.
366  * The caller should free the returned string if it is non-zero.
367  */
368 
369 char *
370 ldap_dns_to_dn(
371         char    *dns_name,
372         int     *nameparts
373 )
374 {
375         size_t  dns_len;
376         char    *dn = 0;
377         char    *cp;
378 
379         /* check for NULL string, empty name and name ending in '.' */
380         if (dns_name && (dns_len = strlen(dns_name)) &&
381             (dns_name[dns_len - 1] != '.')) {
382                 if (dn = (char *)malloc(dns_len * 3 + 1)) {
383                         *nameparts = 0;
384                         cp = dn;
385                         while (*dns_name) {
386                                 *cp++ = 'd';
387                                 *cp++ = 'c';
388                                 *cp++ = '=';
389 
390                                 while (*dns_name && (*dns_name != '.')) {
391                                         *cp++ = *dns_name++;
392                                 }
393                                 if (*dns_name == '.') {
394                                         dns_name++;
395                                         *cp++ = ',';
396                                 }
397                                 (*nameparts)++;
398                         }
399                         *cp = '\0';
400                 }
401         }
402         return (dn);
403 }
404 
405 #endif
406 
407