From c2b134cc3b67d48961226cbfac6ea3a2fc7cc1a6 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Sat, 1 Feb 2003 05:20:11 +0000 Subject: Always escape ldap filter strings. Escaping code was from pam_ldap, but I'm to blame for the realloc() stuff. Plus a couple of minor updates to libads. Andrew Bartlett (This used to be commit 34b2e558a4b3cfd753339bb228a9799e27ed8170) --- source3/lib/ldap_escape.c | 90 +++++++++++++++++++++++++++++++++++++++++ source3/libads/ads_ldap.c | 12 +++++- source3/libads/ldap.c | 28 +++++++++---- source3/libads/ldap_user.c | 9 ++++- source3/nsswitch/winbindd_ads.c | 9 ++++- source3/passdb/pdb_ldap.c | 22 ++++++++-- source3/utils/net_ads.c | 8 +++- 7 files changed, 163 insertions(+), 15 deletions(-) create mode 100644 source3/lib/ldap_escape.c diff --git a/source3/lib/ldap_escape.c b/source3/lib/ldap_escape.c new file mode 100644 index 0000000000..9e88b4999c --- /dev/null +++ b/source3/lib/ldap_escape.c @@ -0,0 +1,90 @@ +/* + Unix SMB/CIFS implementation. + ldap filter argument escaping + + Copyright (C) 1998, 1999, 2000 Luke Howard , + Copyright (C) 2003 Andrew Bartlett + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/** + * Escape a parameter to an LDAP filter string, so they cannot contain + * embeded ( ) * or \ chars which may cause it not to parse correctly. + * + * @param s The input string + * + * @return A string allocated with malloc(), containing the escaped string, + * and to be free()ed by the caller. + **/ + +char *escape_ldap_string_alloc(const char *s) +{ + size_t len = strlen(s)+1; + char *output = malloc(len); + char *output_tmp; + const char *sub; + int i = 0; + char *p = output; + + while (*s) + { + switch (*s) + { + case '*': + sub = "\\2a"; + break; + case '(': + sub = "\\28"; + break; + case ')': + sub = "\\29"; + break; + case '\\': + sub = "\\5c"; + break; + default: + sub = NULL; + break; + } + + if (sub) { + len = len + 3; + output_tmp = realloc(output, len); + if (!output_tmp) { + SAFE_FREE(output); + return NULL; + } + output = output_tmp; + + p = &output[i]; + strncpy (p, sub, 3); + p += 3; + i += 3; + + } else { + *p = *s; + p++; + i++; + } + s++; + } + + *p = '\0'; + return output; +} diff --git a/source3/libads/ads_ldap.c b/source3/libads/ads_ldap.c index 05b016539e..97f12de0f7 100644 --- a/source3/libads/ads_ldap.c +++ b/source3/libads/ads_ldap.c @@ -37,9 +37,16 @@ NTSTATUS ads_name_to_sid(ADS_STRUCT *ads, char *exp; uint32 t; NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + char *escaped_name = escape_ldap_string_alloc(name); + char *escaped_realm = escape_ldap_string_alloc(ads->config.realm); + + if (!escaped_name || !escaped_realm) { + status = NT_STATUS_NO_MEMORY; + goto done; + } if (asprintf(&exp, "(|(sAMAccountName=%s)(userPrincipalName=%s@%s))", - name, name, ads->config.realm) == -1) { + escaped_name, escaped_name, escaped_realm) == -1) { DEBUG(1,("ads_name_to_sid: asprintf failed!\n")); status = NT_STATUS_NO_MEMORY; goto done; @@ -77,6 +84,9 @@ NTSTATUS ads_name_to_sid(ADS_STRUCT *ads, done: if (res) ads_msgfree(ads, res); + SAFE_FREE(escaped_name); + SAFE_FREE(escaped_realm); + return status; } diff --git a/source3/libads/ldap.c b/source3/libads/ldap.c index 0a95e019bf..603f17c994 100644 --- a/source3/libads/ldap.c +++ b/source3/libads/ldap.c @@ -974,7 +974,7 @@ ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods) /* make sure the end of the list is NULL */ mods[i] = NULL; - ret = ldap_add_s(ads->ld, utf8_dn ? utf8_dn : new_dn, mods); + ret = ldap_add_s(ads->ld, utf8_dn, mods); SAFE_FREE(utf8_dn); return ADS_ERROR(ret); } @@ -994,7 +994,7 @@ ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn) return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } - ret = ldap_delete(ads->ld, utf8_dn ? utf8_dn : del_dn); + ret = ldap_delete(ads->ld, utf8_dn); return ADS_ERROR(ret); } @@ -1029,8 +1029,8 @@ static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname, ADS_MODLIST mods; const char *objectClass[] = {"top", "person", "organizationalPerson", "user", "computer", NULL}; - const char *servicePrincipalName[3] = {NULL, NULL, NULL}; - char *psp; + const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL}; + char *psp, *psp2; unsigned acct_control; if (!(ctx = talloc_init("machine_account"))) @@ -1051,10 +1051,16 @@ static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname, ads->config.bind_path); servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname); psp = talloc_asprintf(ctx, "HOST/%s.%s", - hostname, - ads->config.realm); + hostname, + ads->config.realm); strlower(&psp[5]); servicePrincipalName[1] = psp; + servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", hostname); + psp2 = talloc_asprintf(ctx, "CIFS/%s.%s", + hostname, + ads->config.realm); + strlower(&psp2[5]); + servicePrincipalName[3] = psp2; free(ou_str); if (!new_dn) @@ -1405,6 +1411,7 @@ ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn) size_t sd_size = 0; struct berval bval = {0, NULL}; prs_struct ps_wire; + char *escaped_hostname = escape_ldap_string_alloc(hostname); LDAPMessage *res = 0; LDAPMessage *msg = 0; @@ -1420,11 +1427,18 @@ ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn) ret = ADS_ERROR(LDAP_SUCCESS); - if (asprintf(&exp, "(samAccountName=%s$)", hostname) == -1) { + if (!escaped_hostname) { + return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + } + + if (asprintf(&exp, "(samAccountName=%s$)", escaped_hostname) == -1) { DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n")); + SAFE_FREE(escaped_hostname); return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } + SAFE_FREE(escaped_hostname); + ret = ads_search(ads, (void *) &res, exp, attrs); if (!ADS_ERR_OK(ret)) return ret; diff --git a/source3/libads/ldap_user.c b/source3/libads/ldap_user.c index 2e38e7a00d..7efe5338f3 100644 --- a/source3/libads/ldap_user.c +++ b/source3/libads/ldap_user.c @@ -30,10 +30,15 @@ ADS_STATUS ads_find_user_acct(ADS_STRUCT *ads, void **res, const char *user) ADS_STATUS status; char *exp; const char *attrs[] = {"*", NULL}; + char *escaped_user = escape_ldap_string_alloc(user); + if (!escaped_user) { + return ADS_ERROR(LDAP_NO_MEMORY); + } - asprintf(&exp, "(samAccountName=%s)", user); + asprintf(&exp, "(samAccountName=%s)", escaped_user); status = ads_search(ads, res, exp, attrs); - free(exp); + SAFE_FREE(exp); + SAFE_FREE(escaped_user); return status; } diff --git a/source3/nsswitch/winbindd_ads.c b/source3/nsswitch/winbindd_ads.c index 261c2f2237..7cea4aa716 100644 --- a/source3/nsswitch/winbindd_ads.c +++ b/source3/nsswitch/winbindd_ads.c @@ -346,10 +346,17 @@ static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, ADS_STATUS rc; uint32 atype; DOM_SID sid; + char *escaped_dn = escape_ldap_string_alloc(dn); + + if (!escaped_dn) { + return False; + } asprintf(&exp, "(distinguishedName=%s)", dn); rc = ads_search_retry(ads, &res, exp, attrs); - free(exp); + SAFE_FREE(exp); + SAFE_FREE(escaped_dn); + if (!ADS_ERR_OK(rc)) { goto failed; } diff --git a/source3/passdb/pdb_ldap.c b/source3/passdb/pdb_ldap.c index e98a2cf04f..6f46201d8d 100644 --- a/source3/passdb/pdb_ldap.c +++ b/source3/passdb/pdb_ldap.c @@ -666,7 +666,12 @@ static int ldapsam_search_one_user_by_name (struct ldapsam_privates *ldap_state, LDAPMessage ** result) { pstring filter; - + char *escape_user = escape_ldap_string_alloc(user); + + if (!escape_user) { + return LDAP_NO_MEMORY; + } + /* * in the filter expression, replace %u with the real name * so in ldap filter, %u MUST exist :-) @@ -677,7 +682,10 @@ static int ldapsam_search_one_user_by_name (struct ldapsam_privates *ldap_state, * have to use this here because $ is filtered out * in pstring_sub */ - all_string_sub(filter, "%u", user, sizeof(pstring)); + + + all_string_sub(filter, "%u", escape_user, sizeof(pstring)); + SAFE_FREE(escape_user); return ldapsam_search_one_user(ldap_state, filter, result); } @@ -691,6 +699,7 @@ static int ldapsam_search_one_user_by_uid(struct ldapsam_privates *ldap_state, { struct passwd *user; pstring filter; + char *escape_user; /* Get the username from the system and look that up in the LDAP */ @@ -701,9 +710,16 @@ static int ldapsam_search_one_user_by_uid(struct ldapsam_privates *ldap_state, pstrcpy(filter, lp_ldap_filter()); - all_string_sub(filter, "%u", user->pw_name, sizeof(pstring)); + escape_user = escape_ldap_string_alloc(user->pw_name); + if (!escape_user) { + passwd_free(&user); + return LDAP_NO_MEMORY; + } + + all_string_sub(filter, "%u", escape_user, sizeof(pstring)); passwd_free(&user); + SAFE_FREE(escape_user); return ldapsam_search_one_user(ldap_state, filter, result); } diff --git a/source3/utils/net_ads.c b/source3/utils/net_ads.c index 29abc33fdf..867252c95f 100644 --- a/source3/utils/net_ads.c +++ b/source3/utils/net_ads.c @@ -308,12 +308,18 @@ static int ads_user_info(int argc, const char **argv) const char *attrs[] = {"memberOf", NULL}; char *searchstring=NULL; char **grouplist; + char *escaped_user = escape_ldap_string_alloc(argv[0]); if (argc < 1) return net_ads_user_usage(argc, argv); if (!(ads = ads_startup())) return -1; - asprintf(&searchstring, "(sAMAccountName=%s)", argv[0]); + if (!escaped_user) { + d_printf("ads_user_info: failed to escape user %s\n", argv[0]); + return -1; + } + + asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user); rc = ads_search(ads, &res, searchstring, attrs); safe_free(searchstring); -- cgit