2020-03-27 10:06:18 +01:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
2020-03-31 13:00:21 +02:00
|
|
|
static const struct attribute_type *find_schema_type_sorted(const WCHAR *name, const struct attribute_type *at, ULONG count)
|
2020-03-27 10:06:18 +01:00
|
|
|
{
|
2020-03-31 13:00:21 +02:00
|
|
|
int idx, min, max, res;
|
|
|
|
|
|
|
|
if (!count) return NULL;
|
|
|
|
|
|
|
|
min = 0;
|
|
|
|
max = count - 1;
|
|
|
|
|
|
|
|
while (min <= max)
|
|
|
|
{
|
|
|
|
idx = (min + max) / 2;
|
|
|
|
res = wcsicmp(name, at[idx].name);
|
|
|
|
if (!res) return &at[idx];
|
|
|
|
if (res > 0) min = idx + 1;
|
|
|
|
else max = idx - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const struct attribute_type *find_schema_type(const WCHAR *name, const struct attribute_type *at, ULONG single, ULONG multiple)
|
|
|
|
{
|
|
|
|
const struct attribute_type *found;
|
2020-03-30 09:57:47 +02:00
|
|
|
ULONG i, n, off;
|
2020-03-27 10:06:18 +01:00
|
|
|
|
2020-03-31 13:00:21 +02:00
|
|
|
/* Perform binary search within definitions with single name */
|
|
|
|
found = find_schema_type_sorted(name, at, single);
|
|
|
|
if (found) return found;
|
|
|
|
|
|
|
|
/* Perform linear search within definitions with multiple names */
|
|
|
|
at += single;
|
|
|
|
|
|
|
|
for (i = 0; i < multiple; i++)
|
2020-03-30 09:57:47 +02:00
|
|
|
{
|
|
|
|
off = 0;
|
|
|
|
|
|
|
|
for (n = 0; n < at[i].name_count; n++)
|
|
|
|
{
|
|
|
|
if (!wcsicmp(at[i].name + off, name)) return &at[i];
|
|
|
|
off += wcslen(at[i].name + off) + 1;
|
|
|
|
}
|
|
|
|
}
|
2020-03-27 10:06:18 +01:00
|
|
|
|
2020-03-31 13:00:21 +02:00
|
|
|
FIXME("%s not found\n", debugstr_w(name));
|
2020-03-27 10:06:18 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* RFC 4517 */
|
2020-03-31 13:00:21 +02:00
|
|
|
ADSTYPEENUM get_schema_type(const WCHAR *name, const struct attribute_type *at, ULONG single, ULONG multiple)
|
2020-03-27 10:06:18 +01:00
|
|
|
{
|
|
|
|
const struct attribute_type *type;
|
|
|
|
|
2020-03-31 13:00:21 +02:00
|
|
|
type = find_schema_type(name, at, single, multiple);
|
2020-03-27 10:06:18 +01:00
|
|
|
if (!type || !type->syntax) return ADSTYPE_CASE_IGNORE_STRING;
|
|
|
|
|
2020-03-30 09:58:01 +02:00
|
|
|
if (!wcscmp(type->syntax, L"1.3.6.1.4.1.1466.115.121.1.7"))
|
|
|
|
return ADSTYPE_BOOLEAN;
|
2020-03-27 10:06:18 +01:00
|
|
|
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"))
|
2020-03-30 09:57:58 +02:00
|
|
|
return ADSTYPE_CASE_IGNORE_STRING;
|
2020-04-07 12:14:03 +02:00
|
|
|
if (!wcscmp(type->syntax, L"1.3.6.1.4.1.1466.115.121.1.24"))
|
|
|
|
return ADSTYPE_UTC_TIME;
|
2020-04-10 09:34:53 +02:00
|
|
|
if (!wcscmp(type->syntax, L"1.3.6.1.4.1.1466.115.121.1.26"))
|
2020-04-13 12:07:08 +02:00
|
|
|
return ADSTYPE_CASE_IGNORE_STRING;
|
2020-03-27 10:06:18 +01:00
|
|
|
if (!wcscmp(type->syntax, L"1.3.6.1.4.1.1466.115.121.1.27"))
|
|
|
|
return ADSTYPE_INTEGER;
|
2020-03-30 09:57:49 +02:00
|
|
|
if (!wcscmp(type->syntax, L"1.3.6.1.4.1.1466.115.121.1.38"))
|
|
|
|
return ADSTYPE_CASE_IGNORE_STRING;
|
2020-03-27 10:06:18 +01:00
|
|
|
if (!wcscmp(type->syntax, L"1.3.6.1.4.1.1466.115.121.1.40"))
|
|
|
|
return ADSTYPE_OCTET_STRING;
|
2020-04-08 11:41:39 +02:00
|
|
|
if (!wcscmp(type->syntax, L"1.2.840.113556.1.4.903"))
|
|
|
|
return ADSTYPE_DN_WITH_BINARY;
|
2020-04-07 12:14:05 +02:00
|
|
|
if (!wcscmp(type->syntax, L"1.2.840.113556.1.4.906"))
|
|
|
|
return ADSTYPE_LARGE_INTEGER;
|
2020-04-02 11:17:22 +02:00
|
|
|
if (!wcscmp(type->syntax, L"1.2.840.113556.1.4.907"))
|
|
|
|
return ADSTYPE_NT_SECURITY_DESCRIPTOR;
|
2020-03-27 10:06:18 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-03-30 09:57:47 +02:00
|
|
|
static WCHAR *parse_name(WCHAR **str, ULONG *name_count)
|
2020-03-27 10:06:18 +01:00
|
|
|
{
|
|
|
|
WCHAR *name, *p = *str, *end;
|
|
|
|
int count;
|
|
|
|
|
2020-03-30 09:57:47 +02:00
|
|
|
*name_count = 0;
|
|
|
|
|
2020-03-27 10:06:18 +01:00
|
|
|
while (is_space(*p)) p++;
|
|
|
|
|
2020-03-30 09:57:47 +02:00
|
|
|
if (*p == '(')
|
|
|
|
{
|
|
|
|
int total_count = 0;
|
|
|
|
|
|
|
|
p++;
|
|
|
|
name = NULL;
|
|
|
|
|
|
|
|
while (*p)
|
|
|
|
{
|
|
|
|
WCHAR *tmp_name, *new_name;
|
|
|
|
ULONG dummy;
|
|
|
|
|
|
|
|
while (is_space(*p)) p++;
|
|
|
|
if (*p == ')')
|
|
|
|
{
|
|
|
|
*str = p + 1;
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp_name = parse_name(&p, &dummy);
|
|
|
|
if (!tmp_name) break;
|
|
|
|
|
|
|
|
TRACE("NAME[%u] %s\n", *name_count, debugstr_w(tmp_name));
|
|
|
|
|
|
|
|
count = wcslen(tmp_name);
|
|
|
|
|
|
|
|
if (!name)
|
|
|
|
new_name = heap_alloc((count + 1) * sizeof(WCHAR));
|
|
|
|
else
|
|
|
|
new_name = heap_realloc(name, (total_count + count + 1) * sizeof(WCHAR));
|
|
|
|
|
|
|
|
if (!new_name) break;
|
|
|
|
|
|
|
|
memcpy(new_name + total_count, tmp_name, (count + 1) * sizeof(WCHAR));
|
|
|
|
|
|
|
|
name = new_name;
|
|
|
|
heap_free(tmp_name);
|
|
|
|
total_count += count + 1;
|
|
|
|
|
|
|
|
*name_count += 1;
|
|
|
|
*str = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
*str = *p ? p + 1 : p;
|
|
|
|
|
|
|
|
heap_free(name);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-03-27 10:06:18 +01:00
|
|
|
if (*p != '\'')
|
|
|
|
{
|
2020-04-14 16:57:41 +02:00
|
|
|
FIXME("not supported NAME start at %s\n", debugstr_w(p));
|
2020-03-27 10:06:18 +01:00
|
|
|
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;
|
|
|
|
|
2020-03-30 09:57:47 +02:00
|
|
|
*name_count = 1;
|
|
|
|
|
2020-03-27 10:06:18 +01:00
|
|
|
*str = end + 1;
|
|
|
|
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2020-03-30 09:57:39 +02:00
|
|
|
static void skip_token(WCHAR **str)
|
|
|
|
{
|
|
|
|
WCHAR *p = *str;
|
|
|
|
|
|
|
|
while (is_space(*p)) p++;
|
|
|
|
|
|
|
|
if (*p == '\'')
|
|
|
|
{
|
|
|
|
p++;
|
|
|
|
while (*p && *p != '\'') p++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while (*p && !is_space(*p)) p++;
|
|
|
|
}
|
|
|
|
|
|
|
|
*str = *p ? p + 1 : p;
|
|
|
|
}
|
|
|
|
|
2020-03-27 10:06:18 +01:00
|
|
|
static BOOL parse_attribute_type(WCHAR *str, struct attribute_type *at)
|
|
|
|
{
|
2020-03-30 09:57:39 +02:00
|
|
|
static const WCHAR * const not_supported[] = { L"DESC", L"EQUALITY",
|
|
|
|
L"ORDERING", L"SUBSTR", L"SUP", L"USAGE", L"X-ORDERED" };
|
|
|
|
int i;
|
2020-03-27 10:06:18 +01:00
|
|
|
WCHAR *p = str;
|
|
|
|
|
|
|
|
at->oid = NULL;
|
|
|
|
at->name = NULL;
|
2020-03-30 09:57:47 +02:00
|
|
|
at->name_count = 0;
|
2020-03-27 10:06:18 +01:00
|
|
|
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++;
|
2020-03-30 09:57:43 +02:00
|
|
|
if (*p == ')') return TRUE;
|
2020-03-27 10:06:18 +01:00
|
|
|
|
|
|
|
if (!wcsnicmp(p, L"NAME", 4))
|
|
|
|
{
|
|
|
|
p += 4;
|
2020-03-30 09:57:47 +02:00
|
|
|
at->name = parse_name(&p, &at->name_count);
|
2020-03-27 10:06:18 +01:00
|
|
|
}
|
|
|
|
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
|
|
|
|
{
|
2020-03-30 09:57:39 +02:00
|
|
|
BOOL recognized = FALSE;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(not_supported); i++)
|
|
|
|
{
|
|
|
|
if (!wcsnicmp(p, not_supported[i], wcslen(not_supported[i])))
|
|
|
|
{
|
|
|
|
p += wcslen(not_supported[i]);
|
|
|
|
skip_token(&p);
|
|
|
|
recognized = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!recognized)
|
|
|
|
{
|
|
|
|
FIXME("not supported token at %s\n", debugstr_w(p));
|
|
|
|
free_attribute_type(at);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2020-03-27 10:06:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-30 09:57:43 +02:00
|
|
|
WARN("attribute definition is not terminated\n");
|
|
|
|
|
|
|
|
free_attribute_type(at);
|
|
|
|
return FALSE;
|
2020-03-27 10:06:18 +01:00
|
|
|
}
|
|
|
|
|
2020-03-31 13:00:21 +02:00
|
|
|
static int __cdecl at_cmp(const void *a1, const void *a2)
|
|
|
|
{
|
|
|
|
const struct attribute_type *at1 = a1;
|
|
|
|
const struct attribute_type *at2 = a2;
|
|
|
|
|
|
|
|
if (at1->name_count == 1 && at2->name_count == 1)
|
|
|
|
return wcsicmp(at1->name, at2->name);
|
|
|
|
|
|
|
|
/* put definitions with multiple names at the end */
|
|
|
|
return at1->name_count - at2->name_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct attribute_type *load_schema(LDAP *ld, ULONG *at_single_count, ULONG *at_multiple_count)
|
2020-03-27 10:06:18 +01:00
|
|
|
{
|
|
|
|
WCHAR *subschema[] = { (WCHAR *)L"subschemaSubentry", NULL };
|
|
|
|
WCHAR *attribute_types[] = { (WCHAR *)L"attributeTypes", NULL };
|
2020-03-31 13:00:21 +02:00
|
|
|
ULONG err, count, multiple_count;
|
2020-03-27 10:06:18 +01:00
|
|
|
LDAPMessage *res, *entry;
|
|
|
|
WCHAR **schema = NULL;
|
|
|
|
struct attribute_type *at = NULL;
|
|
|
|
|
2020-03-31 13:00:21 +02:00
|
|
|
*at_single_count = 0;
|
|
|
|
*at_multiple_count = 0;
|
2020-03-27 10:06:18 +01:00
|
|
|
|
|
|
|
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;
|
2020-03-31 13:00:21 +02:00
|
|
|
multiple_count = 0;
|
2020-03-27 10:06:18 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-03-31 13:00:15 +02:00
|
|
|
if (!at[count].name)
|
|
|
|
{
|
|
|
|
free_attribute_type(&at[count]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-03-30 09:57:47 +02:00
|
|
|
TRACE("oid %s, name %s, name_count %u, syntax %s, single-value %d\n", debugstr_w(at[count].oid),
|
|
|
|
debugstr_w(at[count].name), at[count].name_count, debugstr_w(at[count].syntax), at[count].single_value);
|
2020-03-27 10:06:18 +01:00
|
|
|
|
2020-03-31 13:00:21 +02:00
|
|
|
if (at[count].name_count > 1)
|
|
|
|
multiple_count++;
|
|
|
|
|
2020-03-27 10:06:18 +01:00
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
ldap_value_freeW(types);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
exit:
|
2020-04-17 11:12:13 +02:00
|
|
|
ldap_value_freeW(schema);
|
2020-03-27 10:06:18 +01:00
|
|
|
ldap_msgfree(res);
|
2020-03-31 13:00:21 +02:00
|
|
|
if (at)
|
|
|
|
{
|
|
|
|
*at_single_count = count - multiple_count;
|
|
|
|
*at_multiple_count = multiple_count;
|
|
|
|
|
|
|
|
qsort(at, count, sizeof(at[0]), at_cmp);
|
|
|
|
}
|
2020-03-27 10:06:18 +01:00
|
|
|
|
|
|
|
return at;
|
|
|
|
}
|