adsldp: Add initial version of attribute schema parser.

Signed-off-by: Dmitry Timoshkov <dmitry@baikal.ru>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Dmitry Timoshkov 2020-03-27 17:06:18 +08:00 committed by Alexandre Julliard
parent dbe8d61c70
commit 5875a659c1
4 changed files with 335 additions and 16 deletions

View File

@ -6,7 +6,8 @@ EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \ C_SRCS = \
adsldp.c \ adsldp.c \
ldap.c ldap.c \
schema.c
IDL_SRCS = \ IDL_SRCS = \
adsldp.idl adsldp.idl

View File

@ -390,6 +390,8 @@ typedef struct
ULONG port; ULONG port;
ULONG attrs_count, attrs_count_allocated; ULONG attrs_count, attrs_count_allocated;
struct ldap_attribute *attrs; struct ldap_attribute *attrs;
struct attribute_type *at;
ULONG at_count;
struct struct
{ {
ADS_SCOPEENUM scope; ADS_SCOPEENUM scope;
@ -479,6 +481,7 @@ static ULONG WINAPI ldapns_Release(IADs *iface)
SysFreeString(ldap->host); SysFreeString(ldap->host);
SysFreeString(ldap->object); SysFreeString(ldap->object);
free_attributes(ldap); free_attributes(ldap);
free_attribute_types(ldap->at, ldap->at_count);
heap_free(ldap); heap_free(ldap);
} }
@ -937,7 +940,8 @@ static HRESULT WINAPI openobj_OpenDSObject(IADsOpenDSObject *iface, BSTR path, B
IADs *ads; IADs *ads;
LDAP *ld = NULL; LDAP *ld = NULL;
HRESULT hr; 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); 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; goto fail;
} }
} }
at = load_schema(ld, &at_count);
} }
hr = LDAPNamespace_create(&IID_IADs, (void **)&ads); 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->host = host;
ldap->port = port; ldap->port = port;
ldap->object = object; ldap->object = object;
ldap->at = at;
ldap->at_count = at_count;
hr = IADs_QueryInterface(ads, &IID_IDispatch, (void **)obj); hr = IADs_QueryInterface(ads, &IID_IDispatch, (void **)obj);
IADs_Release(ads); IADs_Release(ads);
return hr; return hr;
@ -1306,28 +1314,58 @@ static HRESULT WINAPI search_GetNextColumnName(IDirectorySearch *iface, ADS_SEAR
return S_ADS_NOMORE_COLUMNS; 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; DWORD i;
type = get_schema_type(name, ldap->at, ldap->at_count);
col->pADsValues = heap_alloc(count * sizeof(col->pADsValues[0])); col->pADsValues = heap_alloc(count * sizeof(col->pADsValues[0]));
if (!col->pADsValues) if (!col->pADsValues)
return E_OUTOFMEMORY; return E_OUTOFMEMORY;
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
DWORD outlen; switch (type)
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)
{ {
heap_free(col->pADsValues); default:
return E_OUTOFMEMORY; 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->dwNumValues = count;
col->pszAttrName = strdupW(name);
return S_OK; return S_OK;
} }
@ -1388,10 +1426,8 @@ exit:
count = ldap_count_values_len(values); 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); ldap_value_free_len(values);
if (hr == S_OK)
col->pszAttrName = strdupW(name);
return hr; return hr;
} }
@ -1451,6 +1487,8 @@ static HRESULT LDAPNamespace_create(REFIID riid, void **obj)
ldap->attrs_count_allocated = 0; ldap->attrs_count_allocated = 0;
ldap->attrs = NULL; ldap->attrs = NULL;
ldap->search.scope = ADS_SCOPE_SUBTREE; ldap->search.scope = ADS_SCOPE_SUBTREE;
ldap->at = NULL;
ldap->at_count = 0;
hr = IADs_QueryInterface(&ldap->IADs_iface, riid, obj); hr = IADs_QueryInterface(&ldap->IADs_iface, riid, obj);
IADs_Release(&ldap->IADs_iface); IADs_Release(&ldap->IADs_iface);

View File

@ -29,16 +29,16 @@ static inline WCHAR *strdupW(const WCHAR *src)
return dst; 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; LPWSTR ret = NULL;
*outlen = 0; *outlen = 0;
if (str) 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) ))) 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; ret[len] = 0;
*outlen = len; *outlen = len;
} }
@ -46,6 +46,17 @@ static inline LPWSTR strnAtoW( LPCSTR str, DWORD inlen, DWORD *outlen )
return ret; return ret;
} }
struct attribute_type
{
WCHAR *oid;
WCHAR *name;
WCHAR *syntax;
int single_value;
};
DWORD map_ldap_error(DWORD) DECLSPEC_HIDDEN; 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 #endif

269
dlls/adsldp/schema.c Normal file
View File

@ -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 <stdarg.h>
#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;
}