diff --git a/dlls/adsldp/Makefile.in b/dlls/adsldp/Makefile.in index 07f79a9993b..a99917a16ad 100644 --- a/dlls/adsldp/Makefile.in +++ b/dlls/adsldp/Makefile.in @@ -6,7 +6,8 @@ EXTRADLLFLAGS = -mno-cygwin C_SRCS = \ adsldp.c \ - ldap.c + ldap.c \ + schema.c IDL_SRCS = \ adsldp.idl diff --git a/dlls/adsldp/adsldp.c b/dlls/adsldp/adsldp.c index b88867bec1b..d3feb89a053 100644 --- a/dlls/adsldp/adsldp.c +++ b/dlls/adsldp/adsldp.c @@ -390,6 +390,8 @@ typedef struct ULONG port; ULONG attrs_count, attrs_count_allocated; struct ldap_attribute *attrs; + struct attribute_type *at; + ULONG at_count; struct { ADS_SCOPEENUM scope; @@ -479,6 +481,7 @@ static ULONG WINAPI ldapns_Release(IADs *iface) SysFreeString(ldap->host); SysFreeString(ldap->object); free_attributes(ldap); + free_attribute_types(ldap->at, ldap->at_count); heap_free(ldap); } @@ -937,7 +940,8 @@ static HRESULT WINAPI openobj_OpenDSObject(IADsOpenDSObject *iface, BSTR path, B IADs *ads; LDAP *ld = NULL; HRESULT hr; - ULONG err; + ULONG err, at_count = 0; + struct attribute_type *at = NULL; TRACE("%p,%s,%s,%p,%08x,%p\n", iface, debugstr_w(path), debugstr_w(user), password, flags, obj); @@ -1035,6 +1039,8 @@ static HRESULT WINAPI openobj_OpenDSObject(IADsOpenDSObject *iface, BSTR path, B goto fail; } } + + at = load_schema(ld, &at_count); } hr = LDAPNamespace_create(&IID_IADs, (void **)&ads); @@ -1045,6 +1051,8 @@ static HRESULT WINAPI openobj_OpenDSObject(IADsOpenDSObject *iface, BSTR path, B ldap->host = host; ldap->port = port; ldap->object = object; + ldap->at = at; + ldap->at_count = at_count; hr = IADs_QueryInterface(ads, &IID_IDispatch, (void **)obj); IADs_Release(ads); return hr; @@ -1306,28 +1314,58 @@ static HRESULT WINAPI search_GetNextColumnName(IDirectorySearch *iface, ADS_SEAR return S_ADS_NOMORE_COLUMNS; } -static HRESULT add_column_values(ADS_SEARCH_COLUMN *col, struct berval **values, DWORD count) +static HRESULT add_column_values(LDAP_namespace *ldap, ADS_SEARCH_COLUMN *col, + const WCHAR *name, struct berval **values, DWORD count) { + ADSTYPEENUM type; DWORD i; + type = get_schema_type(name, ldap->at, ldap->at_count); + col->pADsValues = heap_alloc(count * sizeof(col->pADsValues[0])); if (!col->pADsValues) return E_OUTOFMEMORY; for (i = 0; i < count; i++) { - DWORD outlen; - TRACE("=> %s\n", debugstr_an(values[i]->bv_val, values[i]->bv_len)); - col->pADsValues[i].u.CaseIgnoreString = strnAtoW(values[i]->bv_val, values[i]->bv_len, &outlen); - if (!col->pADsValues[i].u.CaseIgnoreString) + switch (type) { - heap_free(col->pADsValues); - return E_OUTOFMEMORY; + default: + FIXME("no special handling for type %d\n", type); + /* fall through */ + case ADSTYPE_DN_STRING: + case ADSTYPE_CASE_EXACT_STRING: + case ADSTYPE_CASE_IGNORE_STRING: + case ADSTYPE_PRINTABLE_STRING: + case ADSTYPE_NT_SECURITY_DESCRIPTOR: + { + DWORD outlen; + TRACE("=> %s\n", debugstr_an(values[i]->bv_val, values[i]->bv_len)); + col->pADsValues[i].u.CaseIgnoreString = strnUtoW(values[i]->bv_val, values[i]->bv_len, &outlen); + if (!col->pADsValues[i].u.CaseIgnoreString) + { + heap_free(col->pADsValues); + return E_OUTOFMEMORY; + } + break; + } + + case ADSTYPE_INTEGER: + col->pADsValues[i].u.Integer = strtol(values[i]->bv_val, NULL, 10); + TRACE("%s => %d\n", debugstr_an(values[i]->bv_val, values[i]->bv_len), col->pADsValues[i].u.Integer); + break; + + case ADSTYPE_OCTET_STRING: + TRACE("=> %s\n", debugstr_an(values[i]->bv_val, values[i]->bv_len)); + col->pADsValues[i].u.OctetString.dwLength = values[i]->bv_len; + col->pADsValues[i].u.OctetString.lpValue = (BYTE *)values[i]->bv_val; + break; } } - col->dwADsType = ADSTYPE_CASE_IGNORE_STRING; + col->dwADsType = type; col->dwNumValues = count; + col->pszAttrName = strdupW(name); return S_OK; } @@ -1388,10 +1426,8 @@ exit: count = ldap_count_values_len(values); - hr = add_column_values(col, values, count); + hr = add_column_values(ldap, col, name, values, count); ldap_value_free_len(values); - if (hr == S_OK) - col->pszAttrName = strdupW(name); return hr; } @@ -1451,6 +1487,8 @@ static HRESULT LDAPNamespace_create(REFIID riid, void **obj) ldap->attrs_count_allocated = 0; ldap->attrs = NULL; ldap->search.scope = ADS_SCOPE_SUBTREE; + ldap->at = NULL; + ldap->at_count = 0; hr = IADs_QueryInterface(&ldap->IADs_iface, riid, obj); IADs_Release(&ldap->IADs_iface); diff --git a/dlls/adsldp/adsldp_private.h b/dlls/adsldp/adsldp_private.h index 149d52eb63d..bf24de23850 100644 --- a/dlls/adsldp/adsldp_private.h +++ b/dlls/adsldp/adsldp_private.h @@ -29,16 +29,16 @@ static inline WCHAR *strdupW(const WCHAR *src) return dst; } -static inline LPWSTR strnAtoW( LPCSTR str, DWORD inlen, DWORD *outlen ) +static inline LPWSTR strnUtoW( LPCSTR str, DWORD inlen, DWORD *outlen ) { LPWSTR ret = NULL; *outlen = 0; if (str) { - DWORD len = MultiByteToWideChar( CP_ACP, 0, str, inlen, NULL, 0 ); + DWORD len = MultiByteToWideChar( CP_UTF8, 0, str, inlen, NULL, 0 ); if ((ret = heap_alloc( (len + 1) * sizeof(WCHAR) ))) { - MultiByteToWideChar( CP_ACP, 0, str, inlen, ret, len ); + MultiByteToWideChar( CP_UTF8, 0, str, inlen, ret, len ); ret[len] = 0; *outlen = len; } @@ -46,6 +46,17 @@ static inline LPWSTR strnAtoW( LPCSTR str, DWORD inlen, DWORD *outlen ) return ret; } +struct attribute_type +{ + WCHAR *oid; + WCHAR *name; + WCHAR *syntax; + int single_value; +}; + DWORD map_ldap_error(DWORD) DECLSPEC_HIDDEN; +struct attribute_type *load_schema(LDAP *ld, ULONG *) DECLSPEC_HIDDEN; +ADSTYPEENUM get_schema_type(const WCHAR *, const struct attribute_type *, ULONG) DECLSPEC_HIDDEN; +void free_attribute_types(struct attribute_type *, ULONG) DECLSPEC_HIDDEN; #endif diff --git a/dlls/adsldp/schema.c b/dlls/adsldp/schema.c new file mode 100644 index 00000000000..fa03d63edca --- /dev/null +++ b/dlls/adsldp/schema.c @@ -0,0 +1,269 @@ +/* + * Copyright 2020 Dmitry Timoshkov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "iads.h" +#include "winldap.h" + +#include "adsldp_private.h" + +#include "wine/heap.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(adsldp); + +static const struct attribute_type *find_schema_type(const WCHAR *name, const struct attribute_type *at, ULONG count) +{ + ULONG i; + + for (i = 0; i < count; i++) + if (at[i].name && !wcsicmp(at[i].name, name)) return &at[i]; + + return NULL; +} + +/* RFC 4517 */ +ADSTYPEENUM get_schema_type(const WCHAR *name, const struct attribute_type *at, ULONG at_count) +{ + const struct attribute_type *type; + + type = find_schema_type(name, at, at_count); + if (!type || !type->syntax) return ADSTYPE_CASE_IGNORE_STRING; + + if (!wcscmp(type->syntax, L"1.3.6.1.4.1.1466.115.121.1.12")) + return ADSTYPE_DN_STRING; + if (!wcscmp(type->syntax, L"1.3.6.1.4.1.1466.115.121.1.15")) + return ADSTYPE_NT_SECURITY_DESCRIPTOR; + if (!wcscmp(type->syntax, L"1.3.6.1.4.1.1466.115.121.1.27")) + return ADSTYPE_INTEGER; + if (!wcscmp(type->syntax, L"1.3.6.1.4.1.1466.115.121.1.40")) + return ADSTYPE_OCTET_STRING; + + FIXME("not handled type syntax %s for %s\n", debugstr_w(type->syntax), debugstr_w(name)); + return ADSTYPE_CASE_IGNORE_STRING; +} + +static void free_attribute_type(struct attribute_type *at) +{ + heap_free(at->oid); + heap_free(at->name); + heap_free(at->syntax); +} + +void free_attribute_types(struct attribute_type *at, ULONG count) +{ + ULONG i; + + for (i = 0; i < count; i++) + free_attribute_type(&at[i]); + + heap_free(at); +} + +static BOOL is_space(WCHAR c) +{ + return c == ' ' || c == '\t' || c == '\r' || c == '\n'; +} + +static WCHAR *parse_oid(WCHAR **str) +{ + WCHAR *oid, *p = *str, *end; + int count; + + while (is_space(*p)) p++; + + if (*p == '\'') + { + p++; + end = wcschr(p, '\''); + if (!end) return NULL; + } + else + { + end = p; + while (!is_space(*end)) end++; + } + + count = end - p; + oid = heap_alloc((count + 1) * sizeof(WCHAR)); + if (!oid) return NULL; + + memcpy(oid, p, count * sizeof(WCHAR)); + oid[count] = 0; + + *str = end + 1; + + return oid; +} + +static WCHAR *parse_name(WCHAR **str) +{ + WCHAR *name, *p = *str, *end; + int count; + + while (is_space(*p)) p++; + + if (*p != '\'') + { + FIXME("not suported NAME start at %s\n", debugstr_w(p)); + return NULL; + } + + p++; + end = wcschr(p, '\''); + if (!end) return NULL; + + count = end - p; + name = heap_alloc((count + 1) * sizeof(WCHAR)); + if (!name) return NULL; + + memcpy(name, p, count * sizeof(WCHAR)); + name[count] = 0; + + *str = end + 1; + + return name; +} + +static BOOL parse_attribute_type(WCHAR *str, struct attribute_type *at) +{ + WCHAR *p = str; + + at->oid = NULL; + at->name = NULL; + at->syntax = NULL; + at->single_value = 0; + + while (is_space(*p)) p++; + if (*p++ != '(') return FALSE; + + at->oid = parse_oid(&p); + if (!at->oid) return FALSE; + + while (*p) + { + while (is_space(*p)) p++; + if (*p == ')') break; + + if (!wcsnicmp(p, L"NAME", 4)) + { + p += 4; + at->name = parse_name(&p); + } + else if (!wcsnicmp(p, L"SYNTAX", 6)) + { + p += 6; + at->syntax = parse_oid(&p); + } + else if (!wcsnicmp(p, L"SINGLE-VALUE", 12)) + { + p += 12; + at->single_value = 1; + } + else if (!wcsnicmp(p, L"NO-USER-MODIFICATION", 20)) + { + p += 20; + } + else + { + FIXME("not supported token at %s\n", debugstr_w(p)); + free_attribute_type(at); + return FALSE; + } + } + + return TRUE; +} + +struct attribute_type *load_schema(LDAP *ld, ULONG *at_count) +{ + WCHAR *subschema[] = { (WCHAR *)L"subschemaSubentry", NULL }; + WCHAR *attribute_types[] = { (WCHAR *)L"attributeTypes", NULL }; + ULONG err, count; + LDAPMessage *res, *entry; + WCHAR **schema = NULL; + struct attribute_type *at = NULL; + + *at_count = 0; + + err = ldap_search_sW(ld, NULL, LDAP_SCOPE_BASE, (WCHAR *)L"(objectClass=*)", subschema, FALSE, &res); + if (err != LDAP_SUCCESS) + { + TRACE("ldap_search_sW error %#x\n", err); + return NULL; + } + + entry = ldap_first_entry(ld, res); + if (entry) + schema = ldap_get_valuesW(ld, entry, subschema[0]); + + ldap_msgfree(res); + if (!schema) return NULL; + + err = ldap_search_sW(ld, schema[0], LDAP_SCOPE_BASE, (WCHAR *)L"(objectClass=*)", attribute_types, FALSE, &res); + if (err != LDAP_SUCCESS) + { + TRACE("ldap_search_sW error %#x\n", err); + ldap_value_freeW(schema); + return NULL; + } + + count = 0; + + entry = ldap_first_entry(ld, res); + if (entry) + { + WCHAR **types; + + types = ldap_get_valuesW(ld, entry, attribute_types[0]); + if (types) + { + ULONG i, total = ldap_count_valuesW(types); + + at = heap_alloc(total * sizeof(*at)); + if (!at) goto exit; + + for (i = 0; i < total; i++) + { + TRACE("%s\n", debugstr_w(types[i])); + + if (!parse_attribute_type(types[i], &at[count])) + { + WARN("parse_attribute_type failed\n"); + continue; + } + + TRACE("oid %s, name %s, syntax %s, single-value %d\n", debugstr_w(at[i].oid), + debugstr_w(at[i].name), debugstr_w(at[i].syntax), at[i].single_value); + + count++; + } + + ldap_value_freeW(types); + } + } + +exit: + ldap_msgfree(res); + if (at) *at_count = count; + + return at; +}