4 LDAP semantics mapping module
6 Copyright (C) Jelmer Vernooij 2005
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 This module relies on ldb_map to do all the real work, but performs
25 some of the trivial mappings between AD semantics and that provided
26 by OpenLDAP and similar servers.
30 #include <ldb_module.h>
31 #include "ldb/ldb_map/ldb_map.h"
33 #include "librpc/gen_ndr/ndr_misc.h"
34 #include "librpc/ndr/libndr.h"
35 #include "dsdb/samdb/samdb.h"
36 #include "dsdb/common/util.h"
37 #include <ldb_handlers.h>
39 struct entryuuid_private
{
40 struct ldb_context
*ldb
;
41 struct ldb_dn
**base_dns
;
44 static struct ldb_val
encode_guid(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
47 NTSTATUS status
= GUID_from_data_blob(val
, &guid
);
48 struct ldb_val out
= data_blob(NULL
, 0);
50 if (!NT_STATUS_IS_OK(status
)) {
53 status
= GUID_to_ndr_blob(&guid
, ctx
, &out
);
54 if (!NT_STATUS_IS_OK(status
)) {
55 return data_blob(NULL
, 0);
61 static struct ldb_val
guid_always_string(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
63 struct ldb_val out
= data_blob(NULL
, 0);
65 NTSTATUS status
= GUID_from_data_blob(val
, &guid
);
66 if (!NT_STATUS_IS_OK(status
)) {
69 return data_blob_string_const(GUID_string(ctx
, &guid
));
72 static struct ldb_val
encode_ns_guid(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
75 NTSTATUS status
= NS_GUID_from_string((char *)val
->data
, &guid
);
76 struct ldb_val out
= data_blob(NULL
, 0);
78 if (!NT_STATUS_IS_OK(status
)) {
81 status
= GUID_to_ndr_blob(&guid
, ctx
, &out
);
82 if (!NT_STATUS_IS_OK(status
)) {
83 return data_blob(NULL
, 0);
89 static struct ldb_val
guid_ns_string(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
91 struct ldb_val out
= data_blob(NULL
, 0);
93 NTSTATUS status
= GUID_from_data_blob(val
, &guid
);
94 if (!NT_STATUS_IS_OK(status
)) {
97 return data_blob_string_const(NS_GUID_string(ctx
, &guid
));
100 /* The backend holds binary sids, so just copy them back */
101 static struct ldb_val
val_copy(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
103 struct ldb_val out
= data_blob(NULL
, 0);
104 out
= ldb_val_dup(ctx
, val
);
109 /* Ensure we always convert sids into binary, so the backend doesn't have to know about both forms */
110 static struct ldb_val
sid_always_binary(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
112 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
113 struct ldb_val out
= data_blob(NULL
, 0);
114 const struct ldb_schema_attribute
*a
= ldb_schema_attribute_by_name(ldb
, "objectSid");
116 if (a
->syntax
->canonicalise_fn(ldb
, ctx
, val
, &out
) != LDB_SUCCESS
) {
117 return data_blob(NULL
, 0);
123 /* Ensure we always convert sids into string, so the backend doesn't have to know about both forms */
124 static struct ldb_val
sid_always_string(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
126 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
127 struct ldb_val out
= data_blob(NULL
, 0);
129 if (ldif_comparision_objectSid_isString(val
)) {
130 if (ldb_handler_copy(ldb
, ctx
, val
, &out
) != LDB_SUCCESS
) {
131 return data_blob(NULL
, 0);
135 if (ldif_write_objectSid(ldb
, ctx
, val
, &out
) != LDB_SUCCESS
) {
136 return data_blob(NULL
, 0);
142 /* Ensure we always convert objectCategory into a DN */
143 static struct ldb_val
objectCategory_always_dn(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
145 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
147 struct ldb_val out
= data_blob(NULL
, 0);
148 const struct ldb_schema_attribute
*a
= ldb_schema_attribute_by_name(ldb
, "objectCategory");
150 dn
= ldb_dn_from_ldb_val(ctx
, ldb
, val
);
151 if (ldb_dn_validate(dn
)) {
153 return val_copy(module
, ctx
, val
);
157 if (a
->syntax
->canonicalise_fn(ldb
, ctx
, val
, &out
) != LDB_SUCCESS
) {
158 return data_blob(NULL
, 0);
164 static struct ldb_val
normalise_to_signed32(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
167 /* We've to use "strtoll" here to have the intended overflows.
168 * Otherwise we may get "LONG_MAX" and the conversion is wrong. */
169 int32_t i
= (int32_t) strtoll((char *)val
->data
, NULL
, 0);
170 out
= data_blob_string_const(talloc_asprintf(ctx
, "%d", i
));
174 static struct ldb_val
usn_to_entryCSN(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
177 unsigned long long usn
= strtoull((const char *)val
->data
, NULL
, 10);
178 time_t t
= (usn
>> 24);
179 out
= data_blob_string_const(talloc_asprintf(ctx
, "%s#%06x#00#000000", ldb_timestring(ctx
, t
), (unsigned int)(usn
& 0xFFFFFF)));
183 static unsigned long long entryCSN_to_usn_int(TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
185 char *entryCSN
= talloc_strndup(ctx
, (const char *)val
->data
, val
->length
);
188 unsigned long long usn
;
193 p
= strchr(entryCSN
, '#');
208 usn
= strtol(mod_per_sec
, NULL
, 16);
210 t
= ldb_string_to_time(entryCSN
);
212 usn
= usn
| ((unsigned long long)t
<<24);
216 static struct ldb_val
entryCSN_to_usn(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
219 unsigned long long usn
= entryCSN_to_usn_int(ctx
, val
);
220 out
= data_blob_string_const(talloc_asprintf(ctx
, "%lld", usn
));
224 static struct ldb_val
usn_to_timestamp(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
227 unsigned long long usn
= strtoull((const char *)val
->data
, NULL
, 10);
228 time_t t
= (usn
>> 24);
229 out
= data_blob_string_const(ldb_timestring(ctx
, t
));
233 static struct ldb_val
timestamp_to_usn(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
237 unsigned long long usn
;
239 ldb_val_to_time(val
, &t
);
241 usn
= ((unsigned long long)t
<<24);
243 out
= data_blob_string_const(talloc_asprintf(ctx
, "%lld", usn
));
248 static const struct ldb_map_attribute entryuuid_attributes
[] =
252 .local_name
= "objectGUID",
253 .type
= LDB_MAP_CONVERT
,
256 .remote_name
= "entryUUID",
257 .convert_local
= guid_always_string
,
258 .convert_remote
= encode_guid
,
264 .local_name
= "invocationId",
265 .type
= LDB_MAP_CONVERT
,
268 .remote_name
= "invocationId",
269 .convert_local
= guid_always_string
,
270 .convert_remote
= encode_guid
,
276 .local_name
= "objectSid",
277 .type
= LDB_MAP_CONVERT
,
280 .remote_name
= "objectSid",
281 .convert_local
= sid_always_binary
,
282 .convert_remote
= val_copy
,
286 /* securityIdentifier */
288 .local_name
= "securityIdentifier",
289 .type
= LDB_MAP_CONVERT
,
292 .remote_name
= "securityIdentifier",
293 .convert_local
= sid_always_binary
,
294 .convert_remote
= val_copy
,
299 .local_name
= "name",
300 .type
= LDB_MAP_RENAME
,
303 .remote_name
= "rdnValue"
308 .local_name
= "whenCreated",
309 .type
= LDB_MAP_RENAME
,
312 .remote_name
= "createTimestamp"
317 .local_name
= "whenChanged",
318 .type
= LDB_MAP_RENAME
,
321 .remote_name
= "modifyTimestamp"
326 .local_name
= "objectClasses",
327 .type
= LDB_MAP_RENAME
,
330 .remote_name
= "samba4ObjectClasses"
335 .local_name
= "dITContentRules",
336 .type
= LDB_MAP_RENAME
,
339 .remote_name
= "samba4DITContentRules"
344 .local_name
= "attributeTypes",
345 .type
= LDB_MAP_RENAME
,
348 .remote_name
= "samba4AttributeTypes"
353 .local_name
= "objectCategory",
354 .type
= LDB_MAP_CONVERT
,
357 .remote_name
= "objectCategory",
358 .convert_local
= objectCategory_always_dn
,
359 .convert_remote
= val_copy
,
364 .local_name
= "distinguishedName",
365 .type
= LDB_MAP_RENDROP
,
368 .remote_name
= "entryDN"
373 .local_name
= "primaryGroupID",
374 .type
= LDB_MAP_CONVERT
,
377 .remote_name
= "primaryGroupID",
378 .convert_local
= normalise_to_signed32
,
379 .convert_remote
= val_copy
,
384 .local_name
= "groupType",
385 .type
= LDB_MAP_CONVERT
,
388 .remote_name
= "groupType",
389 .convert_local
= normalise_to_signed32
,
390 .convert_remote
= val_copy
,
395 .local_name
= "userAccountControl",
396 .type
= LDB_MAP_CONVERT
,
399 .remote_name
= "userAccountControl",
400 .convert_local
= normalise_to_signed32
,
401 .convert_remote
= val_copy
,
406 .local_name
= "sAMAccountType",
407 .type
= LDB_MAP_CONVERT
,
410 .remote_name
= "sAMAccountType",
411 .convert_local
= normalise_to_signed32
,
412 .convert_remote
= val_copy
,
417 .local_name
= "systemFlags",
418 .type
= LDB_MAP_CONVERT
,
421 .remote_name
= "systemFlags",
422 .convert_local
= normalise_to_signed32
,
423 .convert_remote
= val_copy
,
428 .local_name
= "usnChanged",
429 .type
= LDB_MAP_CONVERT
,
432 .remote_name
= "entryCSN",
433 .convert_local
= usn_to_entryCSN
,
434 .convert_remote
= entryCSN_to_usn
439 .local_name
= "usnCreated",
440 .type
= LDB_MAP_CONVERT
,
443 .remote_name
= "createTimestamp",
444 .convert_local
= usn_to_timestamp
,
445 .convert_remote
= timestamp_to_usn
,
451 .type
= LDB_MAP_KEEP
,
458 /* This objectClass conflicts with builtin classes on OpenLDAP */
459 const struct ldb_map_objectclass entryuuid_objectclasses
[] =
462 .local_name
= "subSchema",
463 .remote_name
= "samba4SubSchema"
470 /* These things do not show up in wildcard searches in OpenLDAP, but
471 * we need them to show up in the AD-like view */
472 static const char * const entryuuid_wildcard_attributes
[] = {
482 static const struct ldb_map_attribute nsuniqueid_attributes
[] =
486 .local_name
= "objectGUID",
487 .type
= LDB_MAP_CONVERT
,
490 .remote_name
= "nsuniqueid",
491 .convert_local
= guid_ns_string
,
492 .convert_remote
= encode_ns_guid
,
498 .local_name
= "objectSid",
499 .type
= LDB_MAP_CONVERT
,
502 .remote_name
= "sambaSID",
503 .convert_local
= sid_always_string
,
504 .convert_remote
= sid_always_binary
,
508 /* securityIdentifier */
510 .local_name
= "securityIdentifier",
511 .type
= LDB_MAP_CONVERT
,
514 .remote_name
= "securityIdentifier",
515 .convert_local
= sid_always_binary
,
516 .convert_remote
= val_copy
,
521 .local_name
= "whenCreated",
522 .type
= LDB_MAP_RENAME
,
525 .remote_name
= "createTimestamp"
530 .local_name
= "whenChanged",
531 .type
= LDB_MAP_RENAME
,
534 .remote_name
= "modifyTimestamp"
539 .local_name
= "objectCategory",
540 .type
= LDB_MAP_CONVERT
,
543 .remote_name
= "objectCategory",
544 .convert_local
= objectCategory_always_dn
,
545 .convert_remote
= val_copy
,
550 .local_name
= "distinguishedName",
551 .type
= LDB_MAP_RENAME
,
554 .remote_name
= "entryDN"
559 .local_name
= "primaryGroupID",
560 .type
= LDB_MAP_CONVERT
,
563 .remote_name
= "primaryGroupID",
564 .convert_local
= normalise_to_signed32
,
565 .convert_remote
= val_copy
,
570 .local_name
= "groupType",
571 .type
= LDB_MAP_CONVERT
,
574 .remote_name
= "sambaGroupType",
575 .convert_local
= normalise_to_signed32
,
576 .convert_remote
= val_copy
,
581 .local_name
= "userAccountControl",
582 .type
= LDB_MAP_CONVERT
,
585 .remote_name
= "userAccountControl",
586 .convert_local
= normalise_to_signed32
,
587 .convert_remote
= val_copy
,
592 .local_name
= "sAMAccountType",
593 .type
= LDB_MAP_CONVERT
,
596 .remote_name
= "sAMAccountType",
597 .convert_local
= normalise_to_signed32
,
598 .convert_remote
= val_copy
,
603 .local_name
= "systemFlags",
604 .type
= LDB_MAP_CONVERT
,
607 .remote_name
= "systemFlags",
608 .convert_local
= normalise_to_signed32
,
609 .convert_remote
= val_copy
,
614 .local_name
= "usnChanged",
615 .type
= LDB_MAP_CONVERT
,
618 .remote_name
= "modifyTimestamp",
619 .convert_local
= usn_to_timestamp
,
620 .convert_remote
= timestamp_to_usn
,
625 .local_name
= "usnCreated",
626 .type
= LDB_MAP_CONVERT
,
629 .remote_name
= "createTimestamp",
630 .convert_local
= usn_to_timestamp
,
631 .convert_remote
= timestamp_to_usn
,
636 .local_name
= "pwdLastSet",
637 .type
= LDB_MAP_RENAME
,
640 .remote_name
= "sambaPwdLastSet"
645 .local_name
= "lastLogon",
646 .type
= LDB_MAP_RENAME
,
649 .remote_name
= "sambaLogonTime"
654 .local_name
= "lastLogoff",
655 .type
= LDB_MAP_RENAME
,
658 .remote_name
= "sambaLogoffTime"
663 .local_name
= "badPwdCount",
664 .type
= LDB_MAP_RENAME
,
667 .remote_name
= "sambaBadPasswordCount"
672 .local_name
= "logonHours",
673 .type
= LDB_MAP_RENAME
,
676 .remote_name
= "sambaLogonHours"
681 .local_name
= "homeDrive",
682 .type
= LDB_MAP_RENAME
,
685 .remote_name
= "sambaHomeDrive"
690 .local_name
= "scriptPath",
691 .type
= LDB_MAP_RENAME
,
694 .remote_name
= "sambaLogonScript"
699 .local_name
= "profilePath",
700 .type
= LDB_MAP_RENAME
,
703 .remote_name
= "sambaProfilePath"
708 .local_name
= "userWorkstations",
709 .type
= LDB_MAP_RENAME
,
712 .remote_name
= "sambaUserWorkstations"
717 .local_name
= "homeDirectory",
718 .type
= LDB_MAP_RENAME
,
721 .remote_name
= "sambaHomePath"
726 .local_name
= "nextRid",
727 .type
= LDB_MAP_RENAME
,
730 .remote_name
= "sambaNextRid"
735 .local_name
= "privilegeDisplayName",
736 .type
= LDB_MAP_RENAME
,
739 .remote_name
= "sambaPrivName"
745 .type
= LDB_MAP_KEEP
,
752 /* This objectClass conflicts with builtin classes on FDS */
753 const struct ldb_map_objectclass nsuniqueid_objectclasses
[] =
760 /* These things do not show up in wildcard searches in OpenLDAP, but
761 * we need them to show up in the AD-like view */
762 static const char * const nsuniqueid_wildcard_attributes
[] = {
771 /* the context init function */
772 static int entryuuid_init(struct ldb_module
*module
)
775 ret
= ldb_map_init(module
, entryuuid_attributes
, entryuuid_objectclasses
, entryuuid_wildcard_attributes
, "samba4Top", NULL
);
776 if (ret
!= LDB_SUCCESS
)
779 return ldb_next_init(module
);
782 /* the context init function */
783 static int nsuniqueid_init(struct ldb_module
*module
)
786 ret
= ldb_map_init(module
, nsuniqueid_attributes
, nsuniqueid_objectclasses
, nsuniqueid_wildcard_attributes
, "extensibleObject", NULL
);
787 if (ret
!= LDB_SUCCESS
)
790 return ldb_next_init(module
);
793 static int get_seq_callback(struct ldb_request
*req
,
794 struct ldb_reply
*ares
)
796 unsigned long long *seq
= (unsigned long long *)req
->context
;
799 return ldb_request_done(req
, LDB_ERR_OPERATIONS_ERROR
);
801 if (ares
->error
!= LDB_SUCCESS
) {
802 return ldb_request_done(req
, ares
->error
);
805 if (ares
->type
== LDB_REPLY_ENTRY
) {
806 struct ldb_message_element
*el
= ldb_msg_find_element(ares
->message
, "contextCSN");
808 *seq
= entryCSN_to_usn_int(ares
, &el
->values
[0]);
812 if (ares
->type
== LDB_REPLY_DONE
) {
813 return ldb_request_done(req
, LDB_SUCCESS
);
820 static int entryuuid_sequence_number(struct ldb_module
*module
, struct ldb_request
*req
)
822 struct ldb_context
*ldb
;
824 struct map_private
*map_private
;
825 unsigned long long seq_num
= 0;
826 struct ldb_request
*search_req
;
828 const struct ldb_control
*partition_ctrl
;
829 const struct dsdb_control_current_partition
*partition
;
831 static const char *contextCSN_attr
[] = {
835 struct ldb_seqnum_request
*seq
;
836 struct ldb_seqnum_result
*seqr
;
837 struct ldb_extended
*ext
;
839 ldb
= ldb_module_get_ctx(module
);
841 seq
= talloc_get_type(req
->op
.extended
.data
, struct ldb_seqnum_request
);
843 map_private
= talloc_get_type(ldb_module_get_private(module
), struct map_private
);
845 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
846 "private data is not of type struct map_private");
847 return LDB_ERR_PROTOCOL_ERROR
;
850 /* All this to get the DN of the parition, so we can search the right thing */
851 partition_ctrl
= ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
);
852 if (!partition_ctrl
) {
853 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
854 "entryuuid_sequence_number: no current partition control found!");
855 return LDB_ERR_PROTOCOL_ERROR
;
858 partition
= talloc_get_type(partition_ctrl
->data
,
859 struct dsdb_control_current_partition
);
860 if ((partition
== NULL
) || (partition
->version
!= DSDB_CONTROL_CURRENT_PARTITION_VERSION
)) {
861 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
862 "entryuuid_sequence_number: current partition control with wrong data!");
863 return LDB_ERR_PROTOCOL_ERROR
;
866 ret
= ldb_build_search_req(&search_req
, ldb
, req
,
867 partition
->dn
, LDB_SCOPE_BASE
,
868 NULL
, contextCSN_attr
, NULL
,
869 &seq_num
, get_seq_callback
,
871 LDB_REQ_SET_LOCATION(search_req
);
872 if (ret
!= LDB_SUCCESS
) {
876 ret
= ldb_next_request(module
, search_req
);
878 if (ret
== LDB_SUCCESS
) {
879 ret
= ldb_wait(search_req
->handle
, LDB_WAIT_ALL
);
882 talloc_free(search_req
);
883 if (ret
!= LDB_SUCCESS
) {
887 ext
= talloc_zero(req
, struct ldb_extended
);
891 seqr
= talloc_zero(req
, struct ldb_seqnum_result
);
896 ext
->oid
= LDB_EXTENDED_SEQUENCE_NUMBER
;
900 case LDB_SEQ_HIGHEST_SEQ
:
901 seqr
->seq_num
= seq_num
;
904 seqr
->seq_num
= seq_num
;
907 case LDB_SEQ_HIGHEST_TIMESTAMP
:
908 return ldb_module_error(module
, LDB_ERR_OPERATIONS_ERROR
, "LDB_SEQ_HIGHEST_TIMESTAMP not supported");
912 seqr
->flags
|= LDB_SEQ_GLOBAL_SEQUENCE
;
914 /* send request done */
915 return ldb_module_done(req
, NULL
, ext
, LDB_SUCCESS
);
918 static int entryuuid_extended(struct ldb_module
*module
, struct ldb_request
*req
)
920 if (strcmp(req
->op
.extended
.oid
, LDB_EXTENDED_SEQUENCE_NUMBER
) == 0) {
921 return entryuuid_sequence_number(module
, req
);
924 return ldb_next_request(module
, req
);
927 static const struct ldb_module_ops ldb_entryuuid_module_ops
= {
929 .init_context
= entryuuid_init
,
930 .extended
= entryuuid_extended
,
934 static const struct ldb_module_ops ldb_nsuniqueid_module_ops
= {
935 .name
= "nsuniqueid",
936 .init_context
= nsuniqueid_init
,
937 .extended
= entryuuid_extended
,
942 initialise the module
944 _PUBLIC_
int ldb_simple_ldap_map_module_init(const char *version
)
947 LDB_MODULE_CHECK_VERSION(version
);
948 ret
= ldb_register_module(&ldb_entryuuid_module_ops
);
949 if (ret
!= LDB_SUCCESS
) {
952 ret
= ldb_register_module(&ldb_nsuniqueid_module_ops
);
953 if (ret
!= LDB_SUCCESS
) {