From 3cf9a521fa43183c657a5dca507dec3a4379e383 Mon Sep 17 00:00:00 2001 From: Mark Reynolds Date: Wed, 1 Aug 2012 12:40:31 -0400 Subject: [PATCH] Ticket 328 - make sure all internal search filters are properly escaped Bug Description: Not all internal search fitlers are properly escaped. Fix Description: Created a new sprintf type function, and a filter value escape function. The sprintf function(slapi_filter_sprintf) varies from sprintf as you pass a description argument before the value you want to escape/normalize: ESC_NEXT_VAL - escape the next argument NORM_NEXT_VAL - normalize the next argument ESC_AND_NORM_NEXT_VAL - escape and normalize the next argument In order to correctly normalize the value, the function also parses out the attribute so we can get the syntax plugin normalization function. As a side effect you must add an additional string format character for the description arg(%s): sprintf(buf,"cn=%s", value); escaped_val = slapi_filter_sprintf("cn=%s%s", ESC_NEXT_VAL, value); Also did some code cleanup. https://fedorahosted.org/389/ticket/328 reviewed by: Noriko(Thanks!) --- ldap/servers/plugins/acl/acllas.c | 17 +- ldap/servers/plugins/memberof/memberof.c | 24 ++- ldap/servers/plugins/referint/referint.c | 5 +- ldap/servers/plugins/replication/urp.c | 3 +- .../plugins/replication/windows_protocol_util.c | 90 +++---- ldap/servers/slapd/attr.c | 60 ++-- ldap/servers/slapd/filter.c | 51 ++-- ldap/servers/slapd/ldaputil.c | 145 ++++++----- ldap/servers/slapd/libslapd.def | 4 +- ldap/servers/slapd/main.c | 8 +- ldap/servers/slapd/mapping_tree.c | 14 +- ldap/servers/slapd/plugin.c | 2 +- ldap/servers/slapd/slapi-plugin.h | 15 + ldap/servers/slapd/slapi-private.h | 1 - ldap/servers/slapd/util.c | 278 +++++++++++++++++++- 15 files changed, 501 insertions(+), 216 deletions(-) diff --git a/ldap/servers/plugins/acl/acllas.c b/ldap/servers/plugins/acl/acllas.c index cc556c4..5bcb482 100644 --- a/ldap/servers/plugins/acl/acllas.c +++ b/ldap/servers/plugins/acl/acllas.c @@ -2896,7 +2896,6 @@ acllas__eval_memberGroupDnAttr (char *attrName, Slapi_Entry *e, ** client is a member of. */ if (enumerate_groups) { - char filter_str[BUFSIZ]; char *attrs[3]; struct eval_info info = {0}; char *curMemberDn; @@ -2934,23 +2933,15 @@ acllas__eval_memberGroupDnAttr (char *attrName, Slapi_Entry *e, curMemberDn = n_clientdn; while (!Done) { - char *filter_str_ptr = &filter_str[0]; - char *new_filter_str = NULL; - int lenf = strlen(curMemberDn)<<1; - - if (lenf > (BUFSIZ - 28)) { /* 28 for "(|(uniquemember=%s)(member=%s))" */ - new_filter_str = slapi_ch_malloc(lenf + 28); - filter_str_ptr = new_filter_str; - } + char *filter_str_ptr; /* ** Search the db for groups that the client is a member of. ** Once found cache it. cache only unique groups. */ tt = info.lu_idx; - sprintf (filter_str_ptr,"(|(uniquemember=%s)(member=%s))", - curMemberDn, curMemberDn); - + filter_str_ptr = slapi_filter_sprintf("(|(uniquemember=%s%s)(member=%s%s))", + ESC_AND_NORM_NEXT_VAL, curMemberDn, ESC_AND_NORM_NEXT_VAL ,curMemberDn); /* Use new search internal API */ { Slapi_PBlock *aPb = slapi_pblock_new (); @@ -2976,7 +2967,7 @@ acllas__eval_memberGroupDnAttr (char *attrName, Slapi_Entry *e, slapi_pblock_destroy (aPb); } - if (new_filter_str) slapi_ch_free((void **) &new_filter_str); + slapi_ch_free_string(&filter_str_ptr); if (tt == info.lu_idx) { slapi_log_error( SLAPI_LOG_ACL, plugin_name, "currDn:(%s) \n\tNO MEMBER ADDED\n", diff --git a/ldap/servers/plugins/memberof/memberof.c b/ldap/servers/plugins/memberof/memberof.c index a2fdbd9..a69dd16 100644 --- a/ldap/servers/plugins/memberof/memberof.c +++ b/ldap/servers/plugins/memberof/memberof.c @@ -520,12 +520,14 @@ int memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn, Slapi_PBlock *search_pb = NULL; Slapi_DN *base_sdn = NULL; Slapi_Backend *be = NULL; + char *escaped_filter_val; char *filter_str = NULL; char *cookie = NULL; int all_backends = memberof_config_get_all_backends(); int types_name_len = 0; int num_types = 0; int dn_len = slapi_sdn_get_ndn_len(sdn); + int free_it = 0; int rc = 0; int i = 0; @@ -537,7 +539,15 @@ int memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn, types_name_len += strlen(types[num_types]); } - /* Build the search filter. */ + /* Escape the dn, and build the search filter. */ + escaped_filter_val = slapi_escape_filter_value((char *)slapi_sdn_get_dn(sdn), dn_len); + if(escaped_filter_val){ + dn_len = strlen(escaped_filter_val); + free_it = 1; + } else { + escaped_filter_val = (char *)slapi_sdn_get_dn(sdn); + } + if (num_types > 1) { int bytes_out = 0; @@ -553,7 +563,7 @@ int memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn, for (i = 0; types[i]; i++) { bytes_out += snprintf(filter_str + bytes_out, filter_str_len - bytes_out, - "(%s=%s)", types[i], slapi_sdn_get_ndn(sdn)); + "(%s=%s)", types[i], escaped_filter_val); } /* Add end of filter. */ @@ -561,10 +571,12 @@ int memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn, } else if (num_types == 1) { - filter_str = - slapi_ch_smprintf("(%s=%s)", types[0], slapi_sdn_get_ndn(sdn)); + filter_str = slapi_ch_smprintf("(%s=%s)", types[0], escaped_filter_val); } + if(free_it) + slapi_ch_free_string(&escaped_filter_val); + if(filter_str == NULL){ return rc; } @@ -1131,12 +1143,12 @@ memberof_modop_one_replace_r(Slapi_PBlock *pb, MemberOfConfig *config, Slapi_PBlock *search_pb = slapi_pblock_new(); Slapi_DN *base_sdn = 0; Slapi_Backend *be = 0; - char *filter_str = 0; + char *filter_str; char *cookie = NULL; int n_entries = 0; int all_backends = config->allBackends; - filter_str = slapi_ch_smprintf("(%s=%s)", config->memberof_attr, op_to); + filter_str = slapi_filter_sprintf("(%s=%s%s)", config->memberof_attr, ESC_NEXT_VAL, op_to); be = slapi_get_first_backend(&cookie); while(be){ /* diff --git a/ldap/servers/plugins/referint/referint.c b/ldap/servers/plugins/referint/referint.c index 3342634..0d381fe 100644 --- a/ldap/servers/plugins/referint/referint.c +++ b/ldap/servers/plugins/referint/referint.c @@ -684,7 +684,6 @@ update_integrity(char **argv, Slapi_DN *origSDN, char *attrName = NULL; char *filter = NULL; char *attrs[2]; - size_t len = slapi_sdn_get_ndn_len(origSDN); int search_result; int nval = 0; int i, j; @@ -709,9 +708,7 @@ update_integrity(char **argv, Slapi_DN *origSDN, search_base = slapi_sdn_get_dn( sdn ); for(i = 3; argv[i] != NULL; i++){ - char buf[BUFSIZ]; - filter = slapi_ch_smprintf("(%s=*%s)", argv[i], - escape_filter_value(origDN, len, buf)); + filter = slapi_filter_sprintf("(%s=*%s%s)", argv[i], ESC_NEXT_VAL, origDN); if ( filter ) { /* Need only the current attribute and its subtypes */ attrs[0] = argv[i]; diff --git a/ldap/servers/plugins/replication/urp.c b/ldap/servers/plugins/replication/urp.c index 1d8799a..7608382 100644 --- a/ldap/servers/plugins/replication/urp.c +++ b/ldap/servers/plugins/replication/urp.c @@ -1082,7 +1082,8 @@ urp_get_min_naming_conflict_entry ( Slapi_PBlock *pb, char *sessionid, CSN *opcs slapi_log_error ( SLAPI_LOG_REPL, sessionid, "Enter urp_get_min_naming_conflict_entry for %s\n", basedn); - filter = PR_smprintf("(%s=%s %s)", ATTR_NSDS5_REPLCONFLICT, REASON_ANNOTATE_DN, basedn); + filter = slapi_filter_sprintf("(%s=%s %s%s)", ATTR_NSDS5_REPLCONFLICT, REASON_ANNOTATE_DN, + ESC_NEXT_VAL, basedn); /* server_ctrls will be freed when newpb is destroyed */ server_ctrls = (LDAPControl **)slapi_ch_calloc (2, sizeof (LDAPControl *)); diff --git a/ldap/servers/plugins/replication/windows_protocol_util.c b/ldap/servers/plugins/replication/windows_protocol_util.c index 3970353..fb10fb0 100644 --- a/ldap/servers/plugins/replication/windows_protocol_util.c +++ b/ldap/servers/plugins/replication/windows_protocol_util.c @@ -2913,14 +2913,9 @@ find_entry_by_attr_value_remote(const char *attribute, const char *value, Slapi_ char *filter = NULL; const char *searchbase = NULL; Slapi_Entry *found_entry = NULL; - char *filter_escaped_value = NULL; - size_t vallen = 0; - vallen = value ? strlen(value) : 0; - filter_escaped_value = slapi_ch_calloc(sizeof(char), vallen*3+1); /* should not have to escape attribute names */ - filter = PR_smprintf("(%s=%s)",attribute,escape_filter_value(value, vallen, filter_escaped_value)); - slapi_ch_free_string(&filter_escaped_value); + filter = slapi_filter_sprintf("(%s=%s%s)",attribute, ESC_NEXT_VAL, value); searchbase = slapi_sdn_get_dn(windows_private_get_windows_subtree(prp->agmt)); cres = windows_search_entry(prp->conn, (char*)searchbase, filter, &found_entry); if (cres) @@ -2935,7 +2930,7 @@ find_entry_by_attr_value_remote(const char *attribute, const char *value, Slapi_ } if (filter) { - PR_smprintf_free(filter); + slapi_ch_free_string(&filter); filter = NULL; } return retval; @@ -3035,33 +3030,28 @@ find_entry_by_attr_value(const char *attribute, const char *value, Slapi_Entry * { Slapi_PBlock *pb = slapi_pblock_new(); Slapi_Entry **entries = NULL, **ep = NULL; - Slapi_Entry *entry_found = NULL; + Slapi_Entry *entry_found = NULL; + LDAPControl **server_controls = NULL; + const char *subtree_dn = NULL; + char *subtree_dn_copy = NULL; + char **attrs = NULL; char *query = NULL; - int found_or_not = ENTRY_NOTFOUND; - int rval = 0; - const char *subtree_dn = NULL; - int not_unique = 0; - char *subtree_dn_copy = NULL; - int scope = LDAP_SCOPE_SUBTREE; - char **attrs = NULL; - LDAPControl **server_controls = NULL; - char *filter_escaped_value = NULL; - size_t vallen = 0; + int found_or_not = ENTRY_NOTFOUND; + int scope = LDAP_SCOPE_SUBTREE; + int not_unique = 0; + int rval = 0; if (pb == NULL) goto done; - vallen = value ? strlen(value) : 0; - filter_escaped_value = slapi_ch_calloc(sizeof(char), vallen*3+1); /* should not have to escape attribute names */ - query = slapi_ch_smprintf("(%s=%s)", attribute, escape_filter_value(value, vallen, filter_escaped_value)); - slapi_ch_free_string(&filter_escaped_value); + query = slapi_filter_sprintf("(%s=%s%s)", attribute, ESC_NEXT_VAL, value); if (query == NULL) - goto done; + goto done; - subtree_dn = slapi_sdn_get_dn(windows_private_get_directory_subtree(ra)); - subtree_dn_copy = slapi_ch_strdup(subtree_dn); + subtree_dn = slapi_sdn_get_dn(windows_private_get_directory_subtree(ra)); + subtree_dn_copy = slapi_ch_strdup(subtree_dn); winsync_plugin_call_pre_ds_search_entry_cb(ra, NULL, &subtree_dn_copy, &scope, &query, &attrs, &server_controls); @@ -3079,35 +3069,35 @@ find_entry_by_attr_value(const char *attribute, const char *value, Slapi_Entry * slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rval); if (rval != LDAP_SUCCESS) - { - goto done; - } + { + goto done; + } slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); if ((entries == NULL) || (entries[0] == NULL)) - { - goto done; - } - entry_found = entries[0]; - for (ep = entries; *ep; ep++) { - if (not_unique) - { - found_or_not = ENTRY_NOT_UNIQUE; - } - not_unique = 1; - } + { + goto done; + } + entry_found = entries[0]; + for (ep = entries; *ep; ep++) { + if (not_unique) + { + found_or_not = ENTRY_NOT_UNIQUE; + } + not_unique = 1; + } done: - if (entry_found && (found_or_not != ENTRY_NOT_UNIQUE)) - { - found_or_not = 0; - *e = slapi_entry_dup(entry_found); - } - if (pb) - { - slapi_free_search_results_internal(pb); - slapi_pblock_destroy(pb); - } - return found_or_not; + if (entry_found && (found_or_not != ENTRY_NOT_UNIQUE)) + { + found_or_not = 0; + *e = slapi_entry_dup(entry_found); + } + if (pb) + { + slapi_free_search_results_internal(pb); + slapi_pblock_destroy(pb); + } + return found_or_not; } static int diff --git a/ldap/servers/slapd/attr.c b/ldap/servers/slapd/attr.c index 7fb613c..73892bf 100644 --- a/ldap/servers/slapd/attr.c +++ b/ldap/servers/slapd/attr.c @@ -574,36 +574,36 @@ slapi_attr_value_cmp( const Slapi_Attr *a, const struct berval *v1, const struct int slapi_attr_value_cmp_ext(const Slapi_Attr *a, Slapi_Value *v1, Slapi_Value *v2) { - int retVal; - const struct berval *bv2 = slapi_value_get_berval(v2); - - if ( a->a_flags & SLAPI_ATTR_FLAG_CMP_BITBYBIT ) - { - const struct berval *bv1 = slapi_value_get_berval(v1); - return slapi_attr_value_cmp(a, bv1, bv2); - } - else - { - Slapi_Attr a2; - struct ava ava; - Slapi_Value *cvals[2]; - unsigned long v2_flags = v2->v_flags; - - a2 = *a; - cvals[0] = v1; - cvals[1] = NULL; - a2.a_present_values.va = cvals; /* JCM - PUKE */ + int retVal; + const struct berval *bv2 = slapi_value_get_berval(v2); - ava.ava_type = a->a_type; - ava.ava_value = *bv2; - if (v2_flags) { - ava.ava_private = &v2_flags; - } else { - ava.ava_private = NULL; - } - retVal = plugin_call_syntax_filter_ava(&a2, LDAP_FILTER_EQUALITY, &ava); - } - return retVal; + if ( a->a_flags & SLAPI_ATTR_FLAG_CMP_BITBYBIT ) + { + const struct berval *bv1 = slapi_value_get_berval(v1); + return slapi_attr_value_cmp(a, bv1, bv2); + } + else + { + Slapi_Attr a2; + struct ava ava; + Slapi_Value *cvals[2]; + unsigned long v2_flags = v2->v_flags; + + a2 = *a; + cvals[0] = v1; + cvals[1] = NULL; + a2.a_present_values.va = cvals; /* JCM - PUKE */ + + ava.ava_type = a->a_type; + ava.ava_value = *bv2; + if (v2_flags) { + ava.ava_private = &v2_flags; + } else { + ava.ava_private = NULL; + } + retVal = plugin_call_syntax_filter_ava(&a2, LDAP_FILTER_EQUALITY, &ava); + } + return retVal; } /* diff --git a/ldap/servers/slapd/filter.c b/ldap/servers/slapd/filter.c index ae87e5c..65e9de4 100644 --- a/ldap/servers/slapd/filter.c +++ b/ldap/servers/slapd/filter.c @@ -124,11 +124,11 @@ get_filter( Connection *conn, BerElement *ber, int scope, } -#define FILTER_EQ_FMT "(%s=%s)" -#define FILTER_GE_FMT "(%s>=%s)" -#define FILTER_LE_FMT "(%s<=%s)" -#define FILTER_APROX_FMT "(%s~=%s)" -#define FILTER_EXTENDED_FMT "(%s%s%s%s:=%s)" +#define FILTER_EQ_FMT "(%s=%s%s)" +#define FILTER_GE_FMT "(%s>=%s%s)" +#define FILTER_LE_FMT "(%s<=%s%s)" +#define FILTER_APROX_FMT "(%s~=%s%s)" +#define FILTER_EXTENDED_FMT "(%s%s%s%s:=%s%s)" #define FILTER_EQ_LEN 4 #define FILTER_GE_LEN 5 #define FILTER_LE_LEN 5 @@ -140,16 +140,14 @@ get_filter( Connection *conn, BerElement *ber, int scope, static char * filter_escape_filter_value_extended(struct slapi_filter *f) { - char ebuf[BUFSIZ], *ptr; - const char *estr; + char *ptr; - estr = escape_filter_value( f->f_mr_value.bv_val, f->f_mr_value.bv_len, ebuf ); - ptr = slapi_ch_smprintf(FILTER_EXTENDED_FMT, + ptr = slapi_filter_sprintf(FILTER_EXTENDED_FMT, f->f_mr_type ? f->f_mr_type : "", f->f_mr_dnAttrs ? ":dn" : "", f->f_mr_oid ? ":" : "", f->f_mr_oid ? f->f_mr_oid : "", - estr ); + ESC_NEXT_VAL, f->f_mr_value.bv_val); return ptr; } @@ -158,12 +156,11 @@ filter_escape_filter_value_extended(struct slapi_filter *f) static char * filter_escape_filter_value(struct slapi_filter *f, const char *fmt, size_t len) { - char ebuf[BUFSIZ], *ptr; - const char *estr; + char *ptr; + + filter_compute_hash(f); + ptr = slapi_filter_sprintf(fmt, f->f_avtype, ESC_NEXT_VAL, f->f_avvalue.bv_val ); - estr = escape_filter_value( f->f_avvalue.bv_val, f->f_avvalue.bv_len, ebuf ); - filter_compute_hash(f); - ptr = slapi_ch_smprintf(fmt, f->f_avtype, estr ); return ptr; } @@ -474,8 +471,7 @@ get_substring_filter( { ber_tag_t tag, rc; ber_len_t len = -1; - char *val, *last, *type = NULL; - char ebuf[BUFSIZ]; + char *val, *eval, *last, *type = NULL; LDAPDebug( LDAP_DEBUG_FILTER, "=> get_substring_filter\n", 0, 0, 0 ); @@ -516,8 +512,11 @@ get_substring_filter( return( LDAP_PROTOCOL_ERROR ); } f->f_sub_initial = val; - /* jcm: Had to cast away a const */ - val = (char*)escape_filter_value( val, -1, ebuf ); + eval = (char*)slapi_escape_filter_value( val, -1); + if(eval){ + slapi_ch_free_string(&val); + val = eval; + } *fstr = slapi_ch_realloc( *fstr, strlen( *fstr ) + strlen( val ) + 1 ); strcat( *fstr, val ); @@ -526,8 +525,11 @@ get_substring_filter( case LDAP_SUBSTRING_ANY: LDAPDebug( LDAP_DEBUG_FILTER, " ANY\n", 0, 0, 0 ); charray_add( &f->f_sub_any, val ); - /* jcm: Had to cast away a const */ - val = (char*)escape_filter_value( val, -1, ebuf ); + eval = (char*)slapi_escape_filter_value( val, -1); + if(eval){ + slapi_ch_free_string(&val); + val = eval; + } *fstr = slapi_ch_realloc( *fstr, strlen( *fstr ) + strlen( val ) + 2 ); strcat( *fstr, "*" ); @@ -540,8 +542,11 @@ get_substring_filter( return( LDAP_PROTOCOL_ERROR ); } f->f_sub_final = val; - /* jcm: Had to cast away a const */ - val = (char*)escape_filter_value( val, -1, ebuf ); + eval = (char*)slapi_escape_filter_value( val, -1); + if(eval){ + slapi_ch_free_string(&val); + val = eval; + } *fstr = slapi_ch_realloc( *fstr, strlen( *fstr ) + strlen( val ) + 2 ); strcat( *fstr, "*" ); diff --git a/ldap/servers/slapd/ldaputil.c b/ldap/servers/slapd/ldaputil.c index aaca1af..7d77644 100644 --- a/ldap/servers/slapd/ldaputil.c +++ b/ldap/servers/slapd/ldaputil.c @@ -304,53 +304,56 @@ slapi_ldap_url_parse(const char *url, LDAPURLDesc **ludpp, int require_dn, int * replace all but the last colon with %3A Go to the 3rd '/' or to the end of the string (convert only the host:port part) */ if (url) { - char *p = strstr(url, "://"); - if (p) { - int foundspace = 0; - int coloncount = 0; - char *lastcolon = NULL; - p += 3; - for (; *p && (*p != '/'); p++) { - if (*p == ' ') { - foundspace = 1; - } - if (*p == ':') { - coloncount++; - lastcolon = p; - } - } - if (foundspace) { - char *src = NULL, *dest = NULL; - /* have to convert url */ - /* len * 3 is way too much, but acceptable */ - urlescaped = slapi_ch_calloc(strlen(url) * 3, sizeof(char)); - dest = urlescaped; - /* copy the scheme */ - src = strstr(url, "://"); - src += 3; - memcpy(dest, url, src-url); - dest += (src-url); - /* we have to convert all spaces to %20 - we have to convert - all colons except the last one to %3A */ - for (; *src; ++src) { - if (src < p) { - if (*src == ' ') { - memcpy(dest, "%20", 3); - dest += 3; - } else if ((coloncount > 1) && (*src == ':') && (src != lastcolon)) { - memcpy(dest, "%3A", 3); - dest += 3; - } else { - *dest++ = *src; - } - } else { - *dest++ = *src; - } - } - *dest = '\0'; - url_to_use = urlescaped; - } - } + char *p = strstr(url, "://"); + if (p) { + int foundspace = 0; + int coloncount = 0; + char *lastcolon = NULL; + + p += 3; + for (; *p && (*p != '/'); p++) { + if (*p == ' ') { + foundspace = 1; + } + if (*p == ':') { + coloncount++; + lastcolon = p; + } + } + if (foundspace) { + char *src = NULL, *dest = NULL; + + /* have to convert url, len * 3 is way too much, but acceptable */ + urlescaped = slapi_ch_calloc(strlen(url) * 3, sizeof(char)); + dest = urlescaped; + /* copy the scheme */ + src = strstr(url, "://"); + src += 3; + memcpy(dest, url, src-url); + dest += (src-url); + /* + * we have to convert all spaces to %20 - we have to convert + * all colons except the last one to %3A + */ + for (; *src; ++src) { + if (src < p) { + if (*src == ' ') { + memcpy(dest, "%20", 3); + dest += 3; + } else if ((coloncount > 1) && (*src == ':') && (src != lastcolon)) { + memcpy(dest, "%3A", 3); + dest += 3; + } else { + *dest++ = *src; + } + } else { + *dest++ = *src; + } + } + *dest = '\0'; + url_to_use = urlescaped; + } + } } #endif @@ -388,30 +391,30 @@ slapi_ldap_url_parse(const char *url, LDAPURLDesc **ludpp, int require_dn, int * #if defined(USE_OPENLDAP) if (urlescaped && (*ludpp) && (*ludpp)->lud_host) { - /* have to unescape lud_host - can unescape in place */ - char *p = strstr((*ludpp)->lud_host, "://"); - if (p) { - char *dest = NULL; - p += 3; - dest = p; - /* up to the first '/', unescape the host */ - for (; *p && (*p != '/'); p++) { - if (!strncmp(p, "%20", 3)) { - *dest++ = ' '; - p += 2; - } else if (!strncmp(p, "%3A", 3)) { - *dest++ = ':'; - p += 2; - } else { - *dest++ = *p; - } - } - /* just copy the remainder of the host, if any */ - while (*p) { - *dest++ = *p++; - } - *dest = '\0'; - } + /* have to unescape lud_host - can unescape in place */ + char *p = strstr((*ludpp)->lud_host, "://"); + if (p) { + char *dest = NULL; + p += 3; + dest = p; + /* up to the first '/', unescape the host */ + for (; *p && (*p != '/'); p++) { + if (!strncmp(p, "%20", 3)) { + *dest++ = ' '; + p += 2; + } else if (!strncmp(p, "%3A", 3)) { + *dest++ = ':'; + p += 2; + } else { + *dest++ = *p; + } + } + /* just copy the remainder of the host, if any */ + while (*p) { + *dest++ = *p++; + } + *dest = '\0'; + } } slapi_ch_free_string(&urlescaped); #endif diff --git a/ldap/servers/slapd/libslapd.def b/ldap/servers/slapd/libslapd.def index c5fd242..70a2993 100644 --- a/ldap/servers/slapd/libslapd.def +++ b/ldap/servers/slapd/libslapd.def @@ -1097,8 +1097,8 @@ EXPORTS slapi_mtn_get_dn @1098 dl_add_index @1099 dl_replace @1100 - send_referrals_from_entry @1101 - escape_filter_value @1102 + send_referrals_from_entry @1101 + slapi_escape_filter_value @1102 slapd_pk11_destroyContext @1103 secoid_destroyAlgorithmID @1104 op_shared_is_allowed_attr @1105 diff --git a/ldap/servers/slapd/main.c b/ldap/servers/slapd/main.c index 7b15249..d60913f 100644 --- a/ldap/servers/slapd/main.c +++ b/ldap/servers/slapd/main.c @@ -1929,7 +1929,8 @@ lookup_instance_name_by_suffix(char *suffix, goto done; if (isexact) { - query = slapi_ch_smprintf("(&(objectclass=nsmappingtree)(|(cn=\"%s\")(cn=%s)))", suffix, suffix); + query = slapi_filter_sprintf("(&(objectclass=nsmappingtree)(|(cn=\"%s%s\")(cn=%s%s)))", + ESC_NEXT_VAL, suffix, ESC_NEXT_VAL, suffix); if (query == NULL) goto done; @@ -1951,7 +1952,8 @@ lookup_instance_name_by_suffix(char *suffix, } else { char *suffixp = suffix; while (NULL != suffixp && strlen(suffixp) > 0) { - query = slapi_ch_smprintf("(&(objectclass=nsmappingtree)(|(cn=*%s\")(cn=*%s)))", suffixp, suffixp); + query = slapi_filter_sprintf("(&(objectclass=nsmappingtree)(|(cn=*%s%s\")(cn=*%s%s)))", + ESC_NEXT_VAL, suffixp, ESC_NEXT_VAL, suffixp); if (query == NULL) goto done; /* Note: This DN is no need to be normalized. */ @@ -2036,7 +2038,7 @@ static struct slapdplugin *lookup_plugin_by_instance_name(const char *name) if (pb == NULL) return NULL; - query = slapi_ch_smprintf("(&(cn=%s)(objectclass=nsBackendInstance))", name); + query = slapi_filter_sprintf("(&(cn=%s%s)(objectclass=nsBackendInstance))", ESC_AND_NORM_NEXT_VAL, name); if (query == NULL) { slapi_pblock_destroy(pb); return NULL; diff --git a/ldap/servers/slapd/mapping_tree.c b/ldap/servers/slapd/mapping_tree.c index e8e414f..dad5940 100644 --- a/ldap/servers/slapd/mapping_tree.c +++ b/ldap/servers/slapd/mapping_tree.c @@ -908,9 +908,9 @@ static int mapping_tree_node_get_children(mapping_tree_node *target, int is_root) { Slapi_PBlock *pb; - char * filter = NULL; - int res; Slapi_Entry **entries = NULL; + char *filter = NULL; + int res; int x; int result = 0; @@ -923,11 +923,11 @@ mapping_tree_node_get_children(mapping_tree_node *target, int is_root) filter = slapi_ch_smprintf("(&(objectclass=nsMappingTree)(!(%s=*)))", MAPPING_TREE_PARENT_ATTRIBUTE); } else { - filter = slapi_ch_smprintf("(&(objectclass=nsMappingTree)(|(%s=\"%s\")(%s=%s)))", - MAPPING_TREE_PARENT_ATTRIBUTE, - slapi_sdn_get_dn(target->mtn_subtree), - MAPPING_TREE_PARENT_ATTRIBUTE, - slapi_sdn_get_dn(target->mtn_subtree)); + const char *filter_value = slapi_sdn_get_dn(target->mtn_subtree); + + filter = slapi_filter_sprintf("(&(objectclass=nsMappingTree)(|(%s=\"%s%s\")(%s=%s%s)))", + MAPPING_TREE_PARENT_ATTRIBUTE, ESC_NEXT_VAL, filter_value, + MAPPING_TREE_PARENT_ATTRIBUTE, ESC_NEXT_VAL, filter_value ); } slapi_search_internal_set_pb(pb, MAPPING_TREE_BASE_DN, LDAP_SCOPE_ONELEVEL, diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c index 436cc02..f90bd0d 100644 --- a/ldap/servers/slapd/plugin.c +++ b/ldap/servers/slapd/plugin.c @@ -3024,7 +3024,7 @@ plugin_enabled(const char *plugin_name, void *identity) char *filter = NULL; int rc = 0; /* disabled, by default */ - filter = slapi_ch_smprintf("cn=%s", plugin_name); + filter = slapi_filter_sprintf("cn=%s%s", ESC_NEXT_VAL, plugin_name); search_pb = slapi_pblock_new(); slapi_search_internal_set_pb(search_pb, PLUGIN_BASE_DN, LDAP_SCOPE_ONELEVEL, filter, NULL, 0, NULL, NULL, identity, 0); diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h index 192d663..3fd8aed 100644 --- a/ldap/servers/slapd/slapi-plugin.h +++ b/ldap/servers/slapd/slapi-plugin.h @@ -3593,6 +3593,21 @@ void slapi_rand_array(void *randx, size_t len); */ int slapi_rand(); +/** + * Escape special characters in a search filter value + * + * \param filter_str A ldap search filter value (cn=VALUE_TO_BE_ESCAPED) + * \param len length of the search filter value. -1 indicates the string is null terminated + * \param buf the buffer to store the escaped filter value string + * \return the escaped filter value string + */ +char* slapi_escape_filter_value(char* filter_str, int len); + +#define ESC_NEXT_VAL "__ESC__NEXT__VAL__" +#define NORM_NEXT_VAL "__NORM__NEXT__VAL__" +#define ESC_AND_NORM_NEXT_VAL "__ESC__AND__NORM__NEXT__VAL__" + +char* slapi_filter_sprintf(const char *fmt, ...); /* * attribute routines diff --git a/ldap/servers/slapd/slapi-private.h b/ldap/servers/slapd/slapi-private.h index 68c2329..a57b453 100644 --- a/ldap/servers/slapd/slapi-private.h +++ b/ldap/servers/slapd/slapi-private.h @@ -1143,7 +1143,6 @@ char* slapd_get_tmp_dir( void ); #include /* GGOODREPL - For BUFSIZ, below, gak */ const char* escape_string (const char* str, char buf[BUFSIZ]); const char* escape_string_with_punctuation(const char* str, char buf[BUFSIZ]); -const char* escape_filter_value(const char* str, int len, char buf[BUFSIZ]); void strcpy_unescape_value( char *d, const char *s ); char *slapi_berval_get_string_copy(const struct berval *bval); diff --git a/ldap/servers/slapd/util.c b/ldap/servers/slapd/util.c index 48a452f..c3f3593 100644 --- a/ldap/servers/slapd/util.c +++ b/ldap/servers/slapd/util.c @@ -72,6 +72,10 @@ #define _CSEP '/' #endif +/* slapi_filter_sprintf macros */ +#define ATTRSIZE 256 /* size allowed for an attr name */ +#define FILTER_BUF 128 /* initial buffer size for attr value */ +#define BUF_INCR 16 /* the amount to increase the FILTER_BUF once it fills up */ static int special_np(unsigned char c) { @@ -95,6 +99,7 @@ static int special_np_and_punct(unsigned char c) return UTIL_ESCAPE_NONE; } +#ifndef USE_OPENLDAP static int special_filter(unsigned char c) { /* @@ -110,6 +115,27 @@ static int special_filter(unsigned char c) c == '\\' || c == '"') ? UTIL_ESCAPE_HEX : UTIL_ESCAPE_NONE; } +#endif + +/* + * Used by filter_stuff_func to help extract an attribute so we know + * how to normalize the value. + */ +static int +special_attr_char(unsigned char c) +{ + return (c < 32 || + c > 126 || + c == '*' || + c == '|' || + c == '&' || + c == '!' || + c == '(' || + c == ')' || + c == '\\' || + c == '=' || + c == '"'); +} static const char* do_escape_string ( @@ -180,7 +206,7 @@ do_escape_string ( *bufNext = '\0'; return buf; } - } + } return str; } @@ -206,10 +232,254 @@ escape_string_with_punctuation(const char* str, char buf[BUFSIZ]) return do_escape_string(str,-1,buf,special_np_and_punct); } -const char* -escape_filter_value(const char* str, int len, char buf[BUFSIZ]) +#define ESCAPE_FILTER 1 +#define NORM_FILTER 2 + +struct filter_ctx { + char *buf; + char attr[ATTRSIZE]; + int attr_position; + int attr_found; + int buf_size; + int buf_len; + int next_arg_needs_esc_norm; + int skip_escape; +}; + +/* + * This function is called by slapi_filter_sprintf to escape/normalize certain values + */ +static PRIntn +filter_stuff_func(void *arg, const char *val, PRUint32 slen) { - return do_escape_string(str,len,buf,special_filter); + struct filter_ctx *ctx = (struct filter_ctx *)arg; + struct berval escaped_filter; + struct berval raw_filter; + char *buf = (char *)val; + int extra_space; + int filter_len = slen; + int free_buf = 0; + + /* look at val - if val is one of our special keywords, and make a note of it for the next pass */ + if (strcmp(val, ESC_NEXT_VAL) == 0){ + ctx->next_arg_needs_esc_norm |= ESCAPE_FILTER; + return 0; + } + if (strcmp(val, NORM_NEXT_VAL) == 0){ + ctx->next_arg_needs_esc_norm |= NORM_FILTER; + return 0; + } + if (strcmp(val, ESC_AND_NORM_NEXT_VAL) == 0){ + ctx->next_arg_needs_esc_norm = NORM_FILTER | ESCAPE_FILTER; + return 0; + } + /* + * Start collecting the attribute name so we can use the correct + * syntax normalization func. + */ + if(ctx->attr_found == 0 && ctx->attr_position < (ATTRSIZE - 1)){ + if(ctx->attr[0] == '\0'){ + if(strstr(val,"=")){ + /* we have an attr we need to record */ + if(!special_attr_char(val[0])){ + memcpy(ctx->attr, val, 1); + ctx->attr_position++; + } + } else { + /* + * We have passed in an attribute as a arg - so we can just set the + * attr with val. The next pass should be '=', otherwise we will + * reset it. + */ + memcpy(ctx->attr, val, slen); + ctx->attr_position = slen; + } + } else { + if(val[0] == '='){ /* hit the end of the attribute name */ + ctx->attr_found = 1; + } else { + if(special_attr_char(val[0])){ + /* this is not an attribute, we should not be collecting this, reset everything */ + memset(ctx->attr, '\0', ATTRSIZE); + ctx->attr_position = 0; + } else { + memcpy(ctx->attr + ctx->attr_position, val, 1); + ctx->attr_position++; + } + } + } + } + + if (ctx->next_arg_needs_esc_norm && !ctx->skip_escape){ + /* + * Normalize the filter value first + */ + if(ctx->next_arg_needs_esc_norm & NORM_FILTER){ + char *norm_val = NULL; + + if(ctx->attr_found){ + slapi_attr_value_normalize(NULL, NULL, ctx->attr , buf, 1, &norm_val ); + if(norm_val){ + buf = norm_val; + filter_len = strlen(buf); + } + } + } + /* + * Escape the filter value + */ + if(ctx->next_arg_needs_esc_norm & ESCAPE_FILTER){ +#if defined (USE_OPENLDAP) + raw_filter.bv_val = (char *)buf; + raw_filter.bv_len = filter_len; + if(ldap_bv2escaped_filter_value(&raw_filter, &escaped_filter) != 0){ + LDAPDebug(LDAP_DEBUG_TRACE, "slapi_filter_sprintf: failed to escape filter value(%s)\n",val,0,0); + ctx->next_arg_needs_esc_norm = 0; + return -1; + } else { + filter_len = escaped_filter.bv_len; + buf = escaped_filter.bv_val; + } +#else + buf = slapi_ch_calloc(sizeof(char), filter_len*3 + 1); + free_buf = 1; + if(do_escape_string(val, filter_len, buf, special_filter) == NULL){ + LDAPDebug(LDAP_DEBUG_TRACE, "slapi_filter_sprintf: failed to escape filter value(%s)\n",val,0,0); + ctx->next_arg_needs_esc_norm = 0; + slapi_ch_free_string(&buf); + return -1; + } else { + filter_len = strlen(buf); + } +#endif + } + + /* + * Now add the new value to the buffer, and allocate more memory if needed + */ + if (ctx->buf_size + filter_len >= ctx->buf_len){ + /* increase buffer for this filter */ + extra_space = (ctx->buf_len + filter_len + BUF_INCR); + slapi_ch_realloc(ctx->buf, sizeof(char) * extra_space); + ctx->buf_len = extra_space; + } + + /* append the escaped value */ + memcpy(ctx->buf + ctx->buf_size, buf, filter_len); + ctx->buf_size += filter_len; + + /* done with the value, reset everything */ + ctx->next_arg_needs_esc_norm = 0; + ctx->attr_found = 0; + ctx->attr_position = 0; + memset(ctx->attr, '\0', ATTRSIZE); + if(free_buf){ + slapi_ch_free_string(&buf); + } + + return filter_len; + } else { /* process arg as is */ + /* check if we have enough room in our buffer */ + if (ctx->buf_size + slen >= ctx->buf_len){ + /* increase buffer for this filter */ + extra_space = (ctx->buf_len + slen + BUF_INCR); + ctx->buf = slapi_ch_realloc((char *)ctx->buf, sizeof(char) * extra_space); + ctx->buf_len = extra_space; + } + memcpy(ctx->buf + ctx->buf_size, buf, slen); + ctx->buf_size += slen; + + return slen; + } +} + +/* + * This is basically like slapi_ch_smprintf() except it can handle special + * keywords that will cause the next value to be escaped and/or normalized. + * + * ESC_NEXT_VAL - escape the next value + * NORM_NEXT_VAL - normalize the next value + * ESC_AND_NORM_NEXT_VAL - escape and normalize the next value + * + * Example: + * + * slapi_filter_sprintf("cn=%s%s", ESC_NEXT_VAL, value); + * slapi_filter_sprintf("(|(cn=%s%s)(sn=%s%s))", ESC_NEXT_VAL, value, NORM_NEXT_VAL, value); + * + * Note: you need a string format specifier(%s) for each keyword + */ +char* +slapi_filter_sprintf(const char *fmt, ...) +{ + struct filter_ctx ctx; + va_list args; + char *buf; + int rc; + + buf = slapi_ch_calloc(sizeof(char), FILTER_BUF + 1); + ctx.buf = buf; + memset(ctx.attr,'\0', ATTRSIZE); + ctx.attr_position = 0; + ctx.attr_found = 0; + ctx.buf_len = FILTER_BUF; + ctx.buf_size = 0; + ctx.next_arg_needs_esc_norm = 0; + ctx.skip_escape = 0; + + va_start(args, fmt); + rc = PR_vsxprintf(filter_stuff_func, &ctx, fmt, args); + if(rc == -1){ + /* transformation failed, just return non-normalized/escaped string */ + ctx.skip_escape = 1; + PR_vsxprintf(filter_stuff_func, &ctx, fmt, args); + } + va_end(args); + + return buf; +} + +/* + * escape special characters in values used in search filters + * + * caller must free the returned value + */ +char* +slapi_escape_filter_value(char* filter_str, int len) +{ + struct berval *escaped_filter = NULL; + struct berval raw_filter; + int filter_len; + + /* + * Check the length for special cases + */ + if(len == -1){ + /* filter str is null terminated */ + filter_len = strlen(filter_str); + } else if (len == 0){ + /* return the filter as is */ + return slapi_ch_strdup(filter_str); + } else { + /* the len is the length */ + filter_len = len; + } +#if defined (USE_OPENLDAP) + /* + * Construct the berval and escape it + */ + raw_filter.bv_val = filter_str; + raw_filter.bv_len = filter_len; + if(ldap_bv2escaped_filter_value(&raw_filter, escaped_filter) != 0){ + LDAPDebug(LDAP_DEBUG_TRACE, "slapi_escape_filter_value: failed to escape filter value(%s)\n",filter_str,0,0); + return NULL; + } else { + return slapi_ch_strdup(escaped_filter->bv_val); + } +#else + char *buf = slapi_ch_calloc(sizeof(char), filter_len*3+1); + + return do_escape_string(filter_str, filter_len, buf, special_filter); +#endif } /* -- 1.7.1