diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libldap4/common/url.c | |
| download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libldap4/common/url.c')
| -rw-r--r-- | usr/src/lib/libldap4/common/url.c | 836 |
1 files changed, 836 insertions, 0 deletions
diff --git a/usr/src/lib/libldap4/common/url.c b/usr/src/lib/libldap4/common/url.c new file mode 100644 index 0000000000..dab3cfb258 --- /dev/null +++ b/usr/src/lib/libldap4/common/url.c @@ -0,0 +1,836 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Copyright (c) 1996 Regents of the University of Michigan. + * All rights reserved. + * + * LIBLDAP url.c -- LDAP URL related routines + * + * LDAP URLs look like this: + * l d a p : / / hostport / dn [ ? attributes [ ? scope [ ? filter [ ? extensions ] ] ] ] + * + * where: + * attributes is a comma separated list + * scope is one of these three strings: base one sub (default=base) + * filter is an string-represented filter as in RFC 1558 + * extensions is a comma separated list of extension + * and extension is like this: [ ! ] oid/x-oid [ = value ] + * + * e.g., ldap://ldap.itd.umich.edu/c=US?o,description?one?o=umich + * + * We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl> + */ + +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1996 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif + +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#ifdef MACOS +#include <stdlib.h> +#include "macos.h" +#endif /* MACOS */ + +#if defined( DOS ) || defined( _WIN32 ) +#include <stdlib.h> +#include <malloc.h> +#include "msdos.h" +#endif /* DOS || _WIN32 */ + +#if !defined(MACOS) && !defined(DOS) && !defined( _WIN32 ) +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#endif /* !MACOS && !DOS && !_WIN32 */ + +#include "lber.h" +#include "ldap.h" +#include "ldap-private.h" +#include "ldap-int.h" + + +#ifdef NEEDPROTOS +static int skip_url_prefix( char **urlp, int *enclosedp ); +static void hex_unescape( char *s ); +static int unhex( char c ); +#else /* NEEDPROTOS */ +static int skip_url_prefix(); +static void hex_unescape(); +static int unhex(); +#endif /* NEEDPROTOS */ + + +int +ldap_is_ldap_url( char *url ) +{ + int enclosed; + + return( url != NULL && skip_url_prefix( &url, &enclosed )); +} + + +static int +skip_url_prefix( char **urlp, int *enclosedp ) +{ +/* + * return non-zero if this looks like a LDAP URL; zero if not + * if non-zero returned, *urlp will be moved past "ldap://" part of URL + */ + if ( *urlp == NULL ) { + return( 0 ); + } + + /* skip leading '<' (if any) */ + if ( **urlp == '<' ) { + *enclosedp = 1; + ++*urlp; + } else { + *enclosedp = 0; + } + + /* skip leading "URL:" (if any) */ + if ( strlen( *urlp ) >= LDAP_URL_URLCOLON_LEN && strncasecmp( + *urlp, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) { + *urlp += LDAP_URL_URLCOLON_LEN; + } + + /* check for missing "ldap://" prefix */ + if ( strlen( *urlp ) < LDAP_URL_PREFIX_LEN || + strncasecmp( *urlp, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) != 0 ) { + return( 0 ); + } + + /* skip over "ldap://" prefix and return success */ + *urlp += LDAP_URL_PREFIX_LEN; + return( 1 ); +} + +int ldap_url_extension_parse( char *exts, LDAPURLExt *** lueppp) +{ + /* Pick apart the pieces of an LDAP URL Extensions */ + /* No copy of exts is made, LDAPURLExt's points to exts string */ + LDAPURLExt ** lues; + LDAPURLExt *luep; + int i = 0; + char *p = exts; + char *ptr, *ptr2; + + *lueppp = NULL; + + /* Count the number of , in extensions */ + while ( (p = strchr (p, ',')) != NULL){ + i++; + } + /* There are at most i+1 extensions */ + if ((lues = (LDAPURLExt **)calloc(i+2, sizeof(LDAPURLExt *))) == NULL){ + return (LDAP_URL_ERR_MEM); + } + + p = exts; + i = 0; + + while ( p ) { + if ((ptr = strchr(p, ',')) != NULL) + *ptr++ = '\0'; + else + ptr = NULL; + + if ((luep = (LDAPURLExt *)calloc(1, sizeof(LDAPURLExt))) == NULL){ + ldap_free_urlexts(lues); + return (LDAP_URL_ERR_MEM); + } + lues[i] = luep; + + if (*p == '!'){ + luep->lue_iscritical = 1; + p++; + } + luep->lue_type = p; + + if (( ptr2 = strchr(p, '=')) != NULL) { + *ptr2++ = '\0'; + luep->lue_value = ptr2; + hex_unescape(ptr2); + } + + i++; + p = ptr; + } + *lueppp = lues; + + return( 0 ); +} + + +int +ldap_url_parse( char *url, LDAPURLDesc **ludpp ) +{ +/* + * Pick apart the pieces of an LDAP URL. + */ + + LDAPURLDesc *ludp; + char *attrs = NULL; + char *p = NULL; + char *q = NULL; + char *x = NULL; + int enclosed, i, nattrs, errcode; + + Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 262, "ldap_url_parse(%s)\n"), url, 0, 0 ); + + *ludpp = NULL; /* pessimistic */ + + if ( !skip_url_prefix( &url, &enclosed )) { + return( LDAP_URL_ERR_NOTLDAP ); + } + + /* allocate return struct */ + if (( ludp = (LDAPURLDesc *)calloc( 1, sizeof( LDAPURLDesc ))) + == NULLLDAPURLDESC ) { + return( LDAP_URL_ERR_MEM ); + } + + ludp->lud_port = LDAP_PORT; + + /* make working copy of the remainder of the URL */ + if (( url = strdup( url )) == NULL ) { + ldap_free_urldesc( ludp ); + return( LDAP_URL_ERR_MEM ); + } + + if ( enclosed && *((p = url + strlen( url ) - 1)) == '>' ) { + *p = '\0'; + } + + /* set defaults */ + /* LP By default don't set them... Then we can check if they are present or not in URL */ + ludp->lud_scope = LDAP_SCOPE_UNKNOWN; + ludp->lud_filter = NULL; + + + /* lud_string is the only malloc'd string space we use */ + ludp->lud_string = url; + + /* scan forward for '/' that marks end of hostport and begin. of dn */ + if (( ludp->lud_dn = strchr( url, '/' )) != NULL ) { + *ludp->lud_dn++ = '\0'; + } + + /* terminate hostport; point to start of dn */ + + if (( p = strchr( url, ':' )) != NULL ) { + *p++ = '\0'; + ludp->lud_port = atoi( p ); + } + + if ( *url == '\0' ) { + ludp->lud_host = NULL; + } else { + ludp->lud_host = url; + hex_unescape( ludp->lud_host ); + } + + if (ludp->lud_dn != NULL){ + /* scan for '?' that marks end of dn and beginning of attributes */ + if (( attrs = strchr( ludp->lud_dn, '?' )) != NULL ) { + /* terminate dn; point to start of attrs. */ + *attrs++ = '\0'; + + /* scan for '?' that marks end of attrs and begin. of scope */ + if (( p = strchr( attrs, '?' )) != NULL ) { + /* + * terminate attrs; point to start of scope and scan for + * '?' that marks end of scope and begin. of filter + */ + *p++ = '\0'; + + if (( q = strchr( p, '?' )) != NULL ) { + /* terminate scope; point to start of filter */ + *q++ = '\0'; + + if (( x = strchr(q, '?')) != NULL ) { + /* terminate filter; point to start of extension */ + *x++ = '\0'; + + if (*x != '\0'){ + /* parse extensions */ + } + } + + if ( *q != '\0' ) { + ludp->lud_filter = q; + hex_unescape( ludp->lud_filter ); + } + } + + if ( strcasecmp( p, "one" ) == 0 ) { + ludp->lud_scope = LDAP_SCOPE_ONELEVEL; + } else if ( strcasecmp( p, "base" ) == 0 ) { + ludp->lud_scope = LDAP_SCOPE_BASE; + } else if ( strcasecmp( p, "sub" ) == 0 ) { + ludp->lud_scope = LDAP_SCOPE_SUBTREE; + } else if ( *p != '\0' ) { + ldap_free_urldesc( ludp ); + return( LDAP_URL_ERR_BADSCOPE ); + } + } + } + if ( *ludp->lud_dn == '\0' ) { + ludp->lud_dn = NULL; + } else { + hex_unescape( ludp->lud_dn ); + } + + /* + * if attrs list was included, turn it into a null-terminated array + */ + if ( attrs != NULL && *attrs != '\0' ) { + for ( nattrs = 1, p = attrs; *p != '\0'; ++p ) { + if ( *p == ',' ) { + ++nattrs; + } + } + + if (( ludp->lud_attrs = (char **)calloc( nattrs + 1, + sizeof( char * ))) == NULL ) { + ldap_free_urldesc( ludp ); + return( LDAP_URL_ERR_MEM ); + } + + for ( i = 0, p = attrs; i < nattrs; ++i ) { + ludp->lud_attrs[ i ] = p; + if (( p = strchr( p, ',' )) != NULL ) { + *p++ ='\0'; + } + hex_unescape( ludp->lud_attrs[ i ] ); + } + } + + if (x != NULL && *x != '\0'){ + if (errcode = ldap_url_extension_parse(x, &ludp->lud_extensions)){ + ldap_free_urldesc(ludp); + return ( errcode ); + } + } + } + + *ludpp = ludp; + + return( 0 ); +} + +void ldap_free_urlexts( LDAPURLExt ** lues) +{ + int i; + for (i = 0; lues[i] != NULL; i++){ + free(lues[i]); + } + free(lues); +} + + +void +ldap_free_urldesc( LDAPURLDesc *ludp ) +{ + if ( ludp != NULLLDAPURLDESC ) { + if ( ludp->lud_string != NULL ) { + free( ludp->lud_string ); + } + if ( ludp->lud_attrs != NULL ) { + free( ludp->lud_attrs ); + } + if (ludp->lud_extensions != NULL) { + ldap_free_urlexts(ludp->lud_extensions); + } + free( ludp ); + } +} + + + +int +ldap_url_search( LDAP *ld, char *url, int attrsonly ) +{ + int err; + LDAPURLDesc *ludp; + BerElement *ber; + LDAPServer *srv = NULL; + +#ifdef _REENTRANT + LOCK_LDAP(ld); +#endif + if ( ldap_url_parse( url, &ludp ) != 0 ) { + ld->ld_errno = LDAP_PARAM_ERROR; +#ifdef _REENTRANT + UNLOCK_LDAP(ld); +#endif + return( -1 ); + } + + if (( ber = ldap_build_search_req( ld, ludp->lud_dn, + ludp->lud_scope == LDAP_SCOPE_UNKNOWN ? LDAP_SCOPE_BASE : ludp->lud_scope, + ludp->lud_filter ? ludp->lud_filter : "(objectclass=*)", + ludp->lud_attrs, attrsonly, NULL, NULL, -1 )) == NULLBER ) { +#ifdef _REENTRANT + UNLOCK_LDAP(ld); +#endif + return( -1 ); + } + + err = 0; + + if ( ludp->lud_host != NULL || ludp->lud_port != 0 ) { + if (( srv = (LDAPServer *)calloc( 1, sizeof( LDAPServer ))) + == NULL || ( srv->lsrv_host = strdup( ludp->lud_host == + NULL ? ld->ld_defhost : ludp->lud_host )) == NULL ) { + if ( srv != NULL ) { + free( srv ); + } + ld->ld_errno = LDAP_NO_MEMORY; + err = -1; + } else { + if ( ludp->lud_port == 0 ) { + srv->lsrv_port = LDAP_PORT; + } else { + srv->lsrv_port = ludp->lud_port; + } + } + } + + if ( err != 0 ) { + ber_free( ber, 1 ); + } else { + err = send_server_request( ld, ber, ld->ld_msgid, NULL, srv, NULL, 1 ); + } + + ldap_free_urldesc( ludp ); + +#ifdef _REENTRANT + UNLOCK_LDAP(ld); +#endif + return( err ); +} + + +int +ldap_url_search_st( LDAP *ld, char *url, int attrsonly, + struct timeval *timeout, LDAPMessage **res ) +{ + int msgid; + int retcode = LDAP_SUCCESS; + + if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) { + return( ld->ld_errno ); + } + + if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 ) { + return( ld->ld_errno ); + } + + if ( ld->ld_errno == LDAP_TIMEOUT ) { + (void) ldap_abandon( ld, msgid ); + ld->ld_errno = LDAP_TIMEOUT; + return( ld->ld_errno ); + } + +#ifdef _REENTRANT + LOCK_LDAP(ld); +#endif + retcode = ldap_parse_result(ld, *res, &ld->ld_errno, &ld->ld_matched, &ld->ld_error, + &ld->ld_referrals, &ld->ld_ret_ctrls, 0); + if (retcode == LDAP_SUCCESS) + retcode = ld->ld_errno; +#ifdef _REENTRANT + UNLOCK_LDAP(ld); +#endif + + return (retcode); +} + + +int +ldap_url_search_s( LDAP *ld, char *url, int attrsonly, LDAPMessage **res ) +{ + int msgid; + int retcode = LDAP_SUCCESS; + + if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) { + return( ld->ld_errno ); + } + + if ( ldap_result( ld, msgid, 1, (struct timeval *)NULL, res ) == -1 ) { + return( ld->ld_errno ); + } + +#ifdef _REENTRANT + LOCK_LDAP(ld); +#endif + retcode = ldap_parse_result(ld, *res, &ld->ld_errno, &ld->ld_matched, &ld->ld_error, + &ld->ld_referrals, &ld->ld_ret_ctrls, 0); + if (retcode == LDAP_SUCCESS) + retcode = ld->ld_errno; +#ifdef _REENTRANT + UNLOCK_LDAP(ld); +#endif + + return (retcode); +} + + +static void +hex_unescape( char *s ) +{ +/* + * Remove URL hex escapes from s... done in place. The basic concept for + * this routine is borrowed from the WWW library HTUnEscape() routine. + */ + char *p; + + for ( p = s; *s != '\0'; ++s ) { + if ( *s == '%' ) { + if ( *++s != '\0' ) { + *p = unhex( *s ) << 4; + } + if ( *++s != '\0' ) { + *p++ += unhex( *s ); + } + } else { + *p++ = *s; + } + } + + *p = '\0'; +} + + +static int +unhex( char c ) +{ + return( c >= '0' && c <= '9' ? c - '0' + : c >= 'A' && c <= 'F' ? c - 'A' + 10 + : c - 'a' + 10 ); +} + + +/* + * Locate the LDAP URL associated with a DNS domain name. + * + * The supplied DNS domain name is converted into a distinguished + * name. The directory entry specified by that distinguished name + * is searched for a labeledURI attribute. If successful then the + * LDAP URL is returned. If unsuccessful then that entry's parent + * is searched and so on until the target distinguished name is + * reduced to only two nameparts. + * + * For example, if 'ny.eng.wiz.com' is the DNS domain then the + * following entries are searched until one succeeds: + * dc=ny,dc=eng,dc=wiz,dc=com + * dc=eng,dc=wiz,dc=com + * dc=wiz,dc=com + * + * If dns_name is NULL then the environment variable LOCALDOMAIN is used. + * If attrs is not NULL then it is appended to the URL's attribute list. + * If scope is not NULL then it overrides the URL's scope. + * If filter is not NULL then it is merged with the URL's filter. + * + * If an error is encountered then zero is returned, otherwise a string + * URL is returned. The caller should free the returned string if it is + * non-zero. + */ + +char * +ldap_dns_to_url( + LDAP *ld, + char *dns_name, + char *attrs, + char *scope, + char *filter +) +{ + char *dn; + char *url = 0; + char *url2 = 0; + LDAPURLDesc *urldesc; + char *cp; + char *cp2; + size_t attrs_len = 0; + size_t scope_len = 0; + size_t filter_len = 0; + int nameparts; + int no_attrs = 0; + int no_scope = 0; + + if (dns_name == 0) { + dns_name = (char *)getenv("LOCALDOMAIN"); + } + + if ((ld == NULL) || ((dn = ldap_dns_to_dn(dns_name, &nameparts)) == + NULL)) + return (0); + + if ((url = ldap_dn_to_url(ld, dn, nameparts)) == NULL) { + free(dn); + return (0); + } + free(dn); + + /* merge filter and/or scope and/or attributes with URL */ + if (attrs || scope || filter) { + + if (attrs) + attrs_len = strlen(attrs) + 2; /* for comma and NULL */ + + if (scope) + scope_len = strlen(scope) + 1; /* for NULL */ + + if (filter) + filter_len = strlen(filter) + 4; + /* for ampersand, parentheses and NULL */ + + if (ldap_is_ldap_url(url)) { + + if ((url2 = (char *)malloc(attrs_len + scope_len + + filter_len + strlen(url) + 1)) == NULL) { + return (0); + } + cp = url; + cp2 = url2; + + /* copy URL scheme, hostname, port number and DN */ + while (*cp && (*cp != '?')) { + *cp2++ = *cp++; + } + + /* handle URL attributes */ + + if (*cp == '?') { /* test first '?' */ + *cp2++ = *cp++; /* copy first '?' */ + + if (*cp == '?') { /* test second '?' */ + + /* insert supplied attributes */ + if (attrs) { + while (*attrs) { + *cp2++ = *attrs++; + } + } else { + no_attrs = 1; + } + + } else { + + /* copy URL attributes */ + while (*cp && (*cp != '?')) { + *cp2++ = *cp++; + } + + /* append supplied attributes */ + if (attrs) { + *cp2++ = ','; + while (*attrs) { + *cp2++ = *attrs++; + } + } + } + + } else { + /* append supplied attributes */ + if (attrs) { + *cp2++ = '?'; + while (*attrs) { + *cp2++ = *attrs++; + } + } else { + no_attrs = 1; + } + } + + /* handle URL scope */ + + if (*cp == '?') { /* test second '?' */ + *cp2++ = *cp++; /* copy second '?' */ + + if (*cp == '?') { /* test third '?' */ + + /* insert supplied scope */ + if (scope) { + while (*scope) { + *cp2++ = *scope++; + } + } else { + no_scope = 1; + } + + } else { + + if (scope) { + /* skip over URL scope */ + while (*cp && (*cp != '?')) { + *cp++; + } + /* insert supplied scope */ + while (*scope) { + *cp2++ = *scope++; + } + } else { + + /* copy URL scope */ + while (*cp && (*cp != '?')) { + *cp2++ = *cp++; + } + } + } + + } else { + /* append supplied scope */ + if (scope) { + if (no_attrs) { + *cp2++ = '?'; + } + *cp2++ = '?'; + while (*scope) { + *cp2++ = *scope++; + } + } else { + no_scope = 1; + } + } + + /* handle URL filter */ + + if (*cp == '?') { /* test third '?' */ + *cp2++ = *cp++; /* copy third '?' */ + + if (filter) { + + /* merge URL and supplied filters */ + + *cp2++ = '('; + *cp2++ = '&'; + /* copy URL filter */ + while (*cp) { + *cp2++ = *cp++; + } + /* append supplied filter */ + while (*filter) { + *cp2++ = *filter++; + } + *cp2++ = ')'; + } else { + + /* copy URL filter */ + while (*cp) { + *cp2++ = *cp++; + } + } + + } else { + /* append supplied filter */ + if (filter) { + if (no_scope) { + if (no_attrs) { + *cp2++ = '?'; + } + *cp2++ = '?'; + } + *cp2++ = '?'; + while (*filter) { + *cp2++ = *filter++; + } + } + } + + *cp2++ = '\0'; + free (url); + url = url2; + + } else { + return (0); /* not an LDAP URL */ + } + } + return (url); +} + + +/* + * Locate the LDAP URL associated with a distinguished name. + * + * The number of nameparts in the supplied distinguished name must be + * provided. The specified directory entry is searched for a labeledURI + * attribute. If successful then the LDAP URL is returned. If unsuccessful + * then that entry's parent is searched and so on until the target + * distinguished name is reduced to only two nameparts. + * + * For example, if 'l=ny,ou=eng,o=wiz,c=us' is the distinguished name + * then the following entries are searched until one succeeds: + * l=ny,ou=eng,o=wiz,c=us + * ou=eng,o=wiz,c=us + * o=wiz,c=us + * + * If an error is encountered then zero is returned, otherwise a string + * URL is returned. The caller should free the returned string if it is + * non-zero. + */ + +char * +ldap_dn_to_url( + LDAP *ld, + char *dn, + int nameparts +) +{ + char *next_dn = dn; + char *url = 0; + char *attrs[2] = {"labeledURI", 0}; + LDAPMessage *res, *e; + char **vals; + + /* + * Search for a URL in the named entry or its parent entry. + * Continue until only 2 nameparts remain. + */ + while (dn && (nameparts > 1) && (! url)) { + + /* search for the labeledURI attribute */ + if (ldap_search_s(ld, dn, LDAP_SCOPE_BASE, + "(objectClass=*)", attrs, 0, &res) == LDAP_SUCCESS) { + + /* locate the first entry returned */ + if ((e = ldap_first_entry(ld, res)) != NULL) { + + /* locate the labeledURI attribute */ + if ((vals = + ldap_get_values(ld, e, "labeledURI")) != + NULL) { + + /* copy the attribute value */ + if ((url = strdup((char *)vals[0])) != + NULL) { + ldap_value_free(vals); + } + } + } + /* free the search results */ + ldap_msgfree(res); + } + + if (! url) { + /* advance along the DN by one namepart */ + if (next_dn = strchr(dn, ',')) { + next_dn++; + dn = next_dn; + nameparts--; + } + } + } + + return (url); +} |
