/* * Break apart the pieces of an LDAP URL. * Syntax: * ldap://<hostname>:<port>/<base_dn>?<attributes>?<scope>?<filter>?<ext> * * <hostname> already known from 'conn->host.name'. * <port> already known from 'conn->remote_port'. * extract the rest from 'conn->data->state.path+1'. All fields are optional. * e.g. * ldap://<hostname>:<port>/?<attributes>?<scope>?<filter> * yields ludp->lud_dn = "". * * Defined in RFC4516 section 2. */ static int _ldap_url_parse2 (const struct connectdata *conn, LDAPURLDesc *ludp) { int rc = LDAP_SUCCESS; char *path; char *p; char *q; size_t i; if(!conn->data || !conn->data->state.path || conn->data->state.path[0] != '/' || !checkprefix("LDAP", conn->data->change.url)) return LDAP_INVALID_SYNTAX; ludp->lud_scope = LDAP_SCOPE_BASE; ludp->lud_port = conn->remote_port; ludp->lud_host = conn->host.name; /* Duplicate the path */ p = path = strdup(conn->data->state.path + 1); if(!path) return LDAP_NO_MEMORY; /* Parse the DN (Distinguished Name) */ q = strchr(p, '?'); if(q) *q++ = '\0'; if(*p) { char *dn = p; char *unescaped; LDAP_TRACE (("DN '%s'\n", dn)); /* Unescape the DN */ unescaped = curl_easy_unescape(conn->data, dn, 0, NULL); if(!unescaped) { rc = LDAP_NO_MEMORY; goto quit; } #if defined(USE_WIN32_LDAP) /* Convert the unescaped string to a tchar */ ludp->lud_dn = Curl_convert_UTF8_to_tchar(unescaped); /* Free the unescaped string as we are done with it */ Curl_unicodefree(unescaped); if(!ludp->lud_dn) { rc = LDAP_NO_MEMORY; goto quit; } #else ludp->lud_dn = unescaped; #endif } p = q; if(!p) goto quit; /* Parse the attributes. skip "??" */ q = strchr(p, '?'); if(q) *q++ = '\0'; if(*p) { char **attributes; size_t count = 0; /* Split the string into an array of attributes */ if(!split_str(p, &attributes, &count)) { rc = LDAP_NO_MEMORY; goto quit; } /* Allocate our array (+1 for the NULL entry) */ #if defined(USE_WIN32_LDAP) ludp->lud_attrs = calloc(count + 1, sizeof(TCHAR *)); #else ludp->lud_attrs = calloc(count + 1, sizeof(char *)); #endif if(!ludp->lud_attrs) { free(attributes); rc = LDAP_NO_MEMORY; goto quit; } for(i = 0; i < count; i++) { char *unescaped; LDAP_TRACE (("attr[%d] '%s'\n", i, attributes[i])); /* Unescape the attribute */ unescaped = curl_easy_unescape(conn->data, attributes[i], 0, NULL); if(!unescaped) { free(attributes); rc = LDAP_NO_MEMORY; goto quit; } #if defined(USE_WIN32_LDAP) /* Convert the unescaped string to a tchar */ ludp->lud_attrs[i] = Curl_convert_UTF8_to_tchar(unescaped); /* Free the unescaped string as we are done with it */ Curl_unicodefree(unescaped); if(!ludp->lud_attrs[i]) { free(attributes); rc = LDAP_NO_MEMORY; goto quit; } #else ludp->lud_attrs[i] = unescaped; #endif ludp->lud_attrs_dups++; } free(attributes); } p = q; if(!p) goto quit; /* Parse the scope. skip "??" */ q = strchr(p, '?'); if(q) *q++ = '\0'; if(*p) { ludp->lud_scope = str2scope(p); if(ludp->lud_scope == -1) { rc = LDAP_INVALID_SYNTAX; goto quit; } LDAP_TRACE (("scope %d\n", ludp->lud_scope)); } p = q; if(!p) goto quit; /* Parse the filter */ q = strchr(p, '?'); if(q) *q++ = '\0'; if(*p) { char *filter = p; char *unescaped; LDAP_TRACE (("filter '%s'\n", filter)); /* Unescape the filter */ unescaped = curl_easy_unescape(conn->data, filter, 0, NULL); if(!unescaped) { rc = LDAP_NO_MEMORY; goto quit; } #if defined(USE_WIN32_LDAP) /* Convert the unescaped string to a tchar */ ludp->lud_filter = Curl_convert_UTF8_to_tchar(unescaped); /* Free the unescaped string as we are done with it */ Curl_unicodefree(unescaped); if(!ludp->lud_filter) { rc = LDAP_NO_MEMORY; goto quit; } #else ludp->lud_filter = unescaped; #endif } p = q; if(p && !*p) { rc = LDAP_INVALID_SYNTAX; goto quit; } quit: free(path); return rc; }
/* * Break apart the pieces of an LDAP URL. * Syntax: * ldap://<hostname>:<port>/<base_dn>?<attributes>?<scope>?<filter>?<ext> * * <hostname> already known from 'conn->host.name'. * <port> already known from 'conn->remote_port'. * extract the rest from 'conn->data->state.path+1'. All fields are optional. * e.g. * ldap://<hostname>:<port>/?<attributes>?<scope>?<filter> * yields ludp->lud_dn = "". * * Defined in RFC4516 section 2. */ static int _ldap_url_parse2 (const struct connectdata *conn, LDAPURLDesc *ludp) { char *p, *q; int i; if(!conn->data || !conn->data->state.path || conn->data->state.path[0] != '/' || !checkprefix("LDAP", conn->data->change.url)) return LDAP_INVALID_SYNTAX; ludp->lud_scope = LDAP_SCOPE_BASE; ludp->lud_port = conn->remote_port; ludp->lud_host = conn->host.name; /* parse DN (Distinguished Name). */ ludp->lud_dn = strdup(conn->data->state.path+1); if(!ludp->lud_dn) return LDAP_NO_MEMORY; p = strchr(ludp->lud_dn, '?'); LDAP_TRACE (("DN '%.*s'\n", p ? (size_t)(p-ludp->lud_dn) : strlen(ludp->lud_dn), ludp->lud_dn)); if(!p) goto success; *p++ = '\0'; /* parse attributes. skip "??". */ q = strchr(p, '?'); if(q) *q++ = '\0'; if(*p && *p != '?') { ludp->lud_attrs = split_str(p); if(!ludp->lud_attrs) return LDAP_NO_MEMORY; for(i = 0; ludp->lud_attrs[i]; i++) LDAP_TRACE (("attr[%d] '%s'\n", i, ludp->lud_attrs[i])); } p = q; if(!p) goto success; /* parse scope. skip "??" */ q = strchr(p, '?'); if(q) *q++ = '\0'; if(*p && *p != '?') { ludp->lud_scope = str2scope(p); if(ludp->lud_scope == -1) { return LDAP_INVALID_SYNTAX; } LDAP_TRACE (("scope %d\n", ludp->lud_scope)); } p = q; if(!p) goto success; /* parse filter */ q = strchr(p, '?'); if(q) *q++ = '\0'; if(!*p) { return LDAP_INVALID_SYNTAX; } ludp->lud_filter = p; LDAP_TRACE (("filter '%s'\n", ludp->lud_filter)); success: if(!unescape_elements(conn->data, ludp)) return LDAP_NO_MEMORY; return LDAP_SUCCESS; }
/** * Parse the URL provided into an apr_ldap_url_desc_t object. * * APR_SUCCESS is returned on success, APR_EGENERAL on failure. * The LDAP result code and reason string is returned in the * apr_ldap_err_t structure. */ APU_DECLARE(int) apr_ldap_url_parse_ext(apr_pool_t *pool, const char *url_in, apr_ldap_url_desc_t **ludpp, apr_ldap_err_t **result_err) { apr_ldap_url_desc_t *ludp; char *p, *q, *r; int i, enclosed; const char *scheme = NULL; const char *url_tmp; char *url; apr_ldap_err_t *result = (apr_ldap_err_t *)apr_pcalloc(pool, sizeof(apr_ldap_err_t)); *result_err = result; /* sanity check our parameters */ if( url_in == NULL || ludpp == NULL ) { result->reason = "Either the LDAP URL, or the URL structure was NULL. Oops."; result->rc = APR_LDAP_URL_ERR_PARAM; return APR_EGENERAL; } *ludpp = NULL; /* pessimistic */ url_tmp = skip_url_prefix( url_in, &enclosed, &scheme ); if ( url_tmp == NULL ) { result->reason = "The scheme was not recognised as a valid LDAP URL scheme."; result->rc = APR_LDAP_URL_ERR_BADSCHEME; return APR_EGENERAL; } /* make working copy of the remainder of the URL */ url = (char *)apr_pstrdup(pool, url_tmp); if ( url == NULL ) { result->reason = "Out of memory parsing LDAP URL."; result->rc = APR_LDAP_URL_ERR_MEM; return APR_EGENERAL; } if ( enclosed ) { p = &url[strlen(url)-1]; if( *p != '>' ) { result->reason = "Bad enclosure error while parsing LDAP URL."; result->rc = APR_LDAP_URL_ERR_BADENCLOSURE; return APR_EGENERAL; } *p = '\0'; } /* allocate return struct */ ludp = (apr_ldap_url_desc_t *)apr_pcalloc(pool, sizeof(apr_ldap_url_desc_t)); if ( ludp == NULL ) { result->reason = "Out of memory parsing LDAP URL."; result->rc = APR_LDAP_URL_ERR_MEM; return APR_EGENERAL; } ludp->lud_next = NULL; ludp->lud_host = NULL; ludp->lud_port = LDAP_PORT; ludp->lud_dn = NULL; ludp->lud_attrs = NULL; ludp->lud_filter = NULL; ludp->lud_scope = -1; ludp->lud_filter = NULL; ludp->lud_exts = NULL; ludp->lud_scheme = (char *)apr_pstrdup(pool, scheme); if ( ludp->lud_scheme == NULL ) { result->reason = "Out of memory parsing LDAP URL."; result->rc = APR_LDAP_URL_ERR_MEM; return APR_EGENERAL; } if( strcasecmp( ludp->lud_scheme, "ldaps" ) == 0 ) { ludp->lud_port = LDAPS_PORT; } /* scan forward for '/' that marks end of hostport and begin. of dn */ p = strchr( url, '/' ); if( p != NULL ) { /* terminate hostport; point to start of dn */ *p++ = '\0'; } /* IPv6 syntax with [ip address]:port */ if ( *url == '[' ) { r = strchr( url, ']' ); if ( r == NULL ) { result->reason = "Bad LDAP URL while parsing IPV6 syntax."; result->rc = APR_LDAP_URL_ERR_BADURL; return APR_EGENERAL; } *r++ = '\0'; q = strrchr( r, ':' ); } else { q = strrchr( url, ':' ); } if ( q != NULL ) { apr_ldap_pvt_hex_unescape( ++q ); if( *q == '\0' ) { result->reason = "Bad LDAP URL while parsing."; result->rc = APR_LDAP_URL_ERR_BADURL; return APR_EGENERAL; } ludp->lud_port = atoi( q ); } apr_ldap_pvt_hex_unescape( url ); /* If [ip address]:port syntax, url is [ip and we skip the [ */ ludp->lud_host = (char *)apr_pstrdup(pool, url + ( *url == '[' )); if( ludp->lud_host == NULL ) { result->reason = "Out of memory parsing LDAP URL."; result->rc = APR_LDAP_URL_ERR_MEM; return APR_EGENERAL; } /* * Kludge. ldap://111.222.333.444:389??cn=abc,o=company * * On early Novell releases, search references/referrals were returned * in this format, i.e., the dn was kind of in the scope position, * but the required slash is missing. The whole thing is illegal syntax, * but we need to account for it. Fortunately it can't be confused with * anything real. */ if( (p == NULL) && (q != NULL) && ((q = strchr( q, '?')) != NULL)) { q++; /* ? immediately followed by question */ if( *q == '?') { q++; if( *q != '\0' ) { /* parse dn part */ apr_ldap_pvt_hex_unescape( q ); ludp->lud_dn = (char *)apr_pstrdup(pool, q); } else { ludp->lud_dn = (char *)apr_pstrdup(pool, ""); } if( ludp->lud_dn == NULL ) { result->reason = "Out of memory parsing LDAP URL."; result->rc = APR_LDAP_URL_ERR_MEM; return APR_EGENERAL; } } } if( p == NULL ) { *ludpp = ludp; return APR_SUCCESS; } /* scan forward for '?' that may marks end of dn */ q = strchr( p, '?' ); if( q != NULL ) { /* terminate dn part */ *q++ = '\0'; } if( *p != '\0' ) { /* parse dn part */ apr_ldap_pvt_hex_unescape( p ); ludp->lud_dn = (char *)apr_pstrdup(pool, p); } else { ludp->lud_dn = (char *)apr_pstrdup(pool, ""); } if( ludp->lud_dn == NULL ) { result->reason = "Out of memory parsing LDAP URL."; result->rc = APR_LDAP_URL_ERR_MEM; return APR_EGENERAL; } if( q == NULL ) { /* no more */ *ludpp = ludp; return APR_SUCCESS; } /* scan forward for '?' that may marks end of attributes */ p = q; q = strchr( p, '?' ); if( q != NULL ) { /* terminate attributes part */ *q++ = '\0'; } if( *p != '\0' ) { /* parse attributes */ apr_ldap_pvt_hex_unescape( p ); ludp->lud_attrs = apr_ldap_str2charray(pool, p, ","); if( ludp->lud_attrs == NULL ) { result->reason = "Bad attributes encountered while parsing LDAP URL."; result->rc = APR_LDAP_URL_ERR_BADATTRS; return APR_EGENERAL; } } if ( q == NULL ) { /* no more */ *ludpp = ludp; return APR_SUCCESS; } /* scan forward for '?' that may marks end of scope */ p = q; q = strchr( p, '?' ); if( q != NULL ) { /* terminate the scope part */ *q++ = '\0'; } if( *p != '\0' ) { /* parse the scope */ apr_ldap_pvt_hex_unescape( p ); ludp->lud_scope = str2scope( p ); if( ludp->lud_scope == -1 ) { result->reason = "Bad scope encountered while parsing LDAP URL."; result->rc = APR_LDAP_URL_ERR_BADSCOPE; return APR_EGENERAL; } } if ( q == NULL ) { /* no more */ *ludpp = ludp; return APR_SUCCESS; } /* scan forward for '?' that may marks end of filter */ p = q; q = strchr( p, '?' ); if( q != NULL ) { /* terminate the filter part */ *q++ = '\0'; } if( *p != '\0' ) { /* parse the filter */ apr_ldap_pvt_hex_unescape( p ); if( ! *p ) { /* missing filter */ result->reason = "Bad filter encountered while parsing LDAP URL."; result->rc = APR_LDAP_URL_ERR_BADFILTER; return APR_EGENERAL; } ludp->lud_filter = (char *)apr_pstrdup(pool, p); if( ludp->lud_filter == NULL ) { result->reason = "Out of memory parsing LDAP URL."; result->rc = APR_LDAP_URL_ERR_MEM; return APR_EGENERAL; } } if ( q == NULL ) { /* no more */ *ludpp = ludp; return APR_SUCCESS; } /* scan forward for '?' that may marks end of extensions */ p = q; q = strchr( p, '?' ); if( q != NULL ) { /* extra '?' */ result->reason = "Bad URL encountered while parsing LDAP URL."; result->rc = APR_LDAP_URL_ERR_BADURL; return APR_EGENERAL; } /* parse the extensions */ ludp->lud_exts = apr_ldap_str2charray(pool, p, ","); if( ludp->lud_exts == NULL ) { result->reason = "Bad extensions encountered while parsing LDAP URL."; result->rc = APR_LDAP_URL_ERR_BADEXTS; return APR_EGENERAL; } for( i=0; ludp->lud_exts[i] != NULL; i++ ) { apr_ldap_pvt_hex_unescape( ludp->lud_exts[i] ); if( *ludp->lud_exts[i] == '!' ) { /* count the number of critical extensions */ ludp->lud_crit_exts++; } } if( i == 0 ) { /* must have 1 or more */ result->reason = "Bad extensions encountered while parsing LDAP URL."; result->rc = APR_LDAP_URL_ERR_BADEXTS; return APR_EGENERAL; } /* no more */ *ludpp = ludp; return APR_SUCCESS; }