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