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:
parent
dbe8d61c70
commit
5875a659c1
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue