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 <ldb_handlers.h>
38 struct entryuuid_private
{
39 struct ldb_context
*ldb
;
40 struct ldb_dn
**base_dns
;
43 static struct ldb_val
encode_guid(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
46 NTSTATUS status
= GUID_from_data_blob(val
, &guid
);
47 struct ldb_val out
= data_blob(NULL
, 0);
49 if (!NT_STATUS_IS_OK(status
)) {
52 status
= GUID_to_ndr_blob(&guid
, ctx
, &out
);
53 if (!NT_STATUS_IS_OK(status
)) {
54 return data_blob(NULL
, 0);
60 static struct ldb_val
guid_always_string(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
62 struct ldb_val out
= data_blob(NULL
, 0);
64 NTSTATUS status
= GUID_from_data_blob(val
, &guid
);
65 if (!NT_STATUS_IS_OK(status
)) {
68 return data_blob_string_const(GUID_string(ctx
, &guid
));
71 static struct ldb_val
encode_ns_guid(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
74 NTSTATUS status
= NS_GUID_from_string((char *)val
->data
, &guid
);
75 struct ldb_val out
= data_blob(NULL
, 0);
77 if (!NT_STATUS_IS_OK(status
)) {
80 status
= GUID_to_ndr_blob(&guid
, ctx
, &out
);
81 if (!NT_STATUS_IS_OK(status
)) {
82 return data_blob(NULL
, 0);
88 static struct ldb_val
guid_ns_string(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
90 struct ldb_val out
= data_blob(NULL
, 0);
92 NTSTATUS status
= GUID_from_data_blob(val
, &guid
);
93 if (!NT_STATUS_IS_OK(status
)) {
96 return data_blob_string_const(NS_GUID_string(ctx
, &guid
));
99 /* The backend holds binary sids, so just copy them back */
100 static struct ldb_val
val_copy(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
102 struct ldb_val out
= data_blob(NULL
, 0);
103 out
= ldb_val_dup(ctx
, val
);
108 /* Ensure we always convert sids into binary, so the backend doesn't have to know about both forms */
109 static struct ldb_val
sid_always_binary(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
111 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
112 struct ldb_val out
= data_blob(NULL
, 0);
113 const struct ldb_schema_attribute
*a
= ldb_schema_attribute_by_name(ldb
, "objectSid");
115 if (a
->syntax
->canonicalise_fn(ldb
, ctx
, val
, &out
) != LDB_SUCCESS
) {
116 return data_blob(NULL
, 0);
122 /* Ensure we always convert sids into string, so the backend doesn't have to know about both forms */
123 static struct ldb_val
sid_always_string(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
125 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
126 struct ldb_val out
= data_blob(NULL
, 0);
128 if (ldif_comparision_objectSid_isString(val
)) {
129 if (ldb_handler_copy(ldb
, ctx
, val
, &out
) != LDB_SUCCESS
) {
130 return data_blob(NULL
, 0);
134 if (ldif_write_objectSid(ldb
, ctx
, val
, &out
) != LDB_SUCCESS
) {
135 return data_blob(NULL
, 0);
141 /* Ensure we always convert objectCategory into a DN */
142 static struct ldb_val
objectCategory_always_dn(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
144 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
146 struct ldb_val out
= data_blob(NULL
, 0);
147 const struct ldb_schema_attribute
*a
= ldb_schema_attribute_by_name(ldb
, "objectCategory");
149 dn
= ldb_dn_from_ldb_val(ctx
, ldb
, val
);
150 if (ldb_dn_validate(dn
)) {
152 return val_copy(module
, ctx
, val
);
156 if (a
->syntax
->canonicalise_fn(ldb
, ctx
, val
, &out
) != LDB_SUCCESS
) {
157 return data_blob(NULL
, 0);
163 static struct ldb_val
normalise_to_signed32(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
166 /* We've to use "strtoll" here to have the intended overflows.
167 * Otherwise we may get "LONG_MAX" and the conversion is wrong. */
168 int32_t i
= (int32_t) strtoll((char *)val
->data
, NULL
, 0);
169 out
= data_blob_string_const(talloc_asprintf(ctx
, "%d", i
));
173 static struct ldb_val
usn_to_entryCSN(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
176 unsigned long long usn
= strtoull((const char *)val
->data
, NULL
, 10);
177 time_t t
= (usn
>> 24);
178 out
= data_blob_string_const(talloc_asprintf(ctx
, "%s#%06x#00#000000", ldb_timestring(ctx
, t
), (unsigned int)(usn
& 0xFFFFFF)));
182 static unsigned long long entryCSN_to_usn_int(TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
184 char *entryCSN
= talloc_strndup(ctx
, (const char *)val
->data
, val
->length
);
187 unsigned long long usn
;
192 p
= strchr(entryCSN
, '#');
207 usn
= strtol(mod_per_sec
, NULL
, 16);
209 t
= ldb_string_to_time(entryCSN
);
211 usn
= usn
| ((unsigned long long)t
<<24);
215 static struct ldb_val
entryCSN_to_usn(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
218 unsigned long long usn
= entryCSN_to_usn_int(ctx
, val
);
219 out
= data_blob_string_const(talloc_asprintf(ctx
, "%lld", usn
));
223 static struct ldb_val
usn_to_timestamp(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
226 unsigned long long usn
= strtoull((const char *)val
->data
, NULL
, 10);
227 time_t t
= (usn
>> 24);
228 out
= data_blob_string_const(ldb_timestring(ctx
, t
));
232 static struct ldb_val
timestamp_to_usn(struct ldb_module
*module
, TALLOC_CTX
*ctx
, const struct ldb_val
*val
)
236 unsigned long long usn
;
238 ldb_val_to_time(val
, &t
);
240 usn
= ((unsigned long long)t
<<24);
242 out
= data_blob_string_const(talloc_asprintf(ctx
, "%lld", usn
));
247 static const struct ldb_map_attribute entryuuid_attributes
[] =
251 .local_name
= "objectGUID",
252 .type
= LDB_MAP_CONVERT
,
255 .remote_name
= "entryUUID",
256 .convert_local
= guid_always_string
,
257 .convert_remote
= encode_guid
,
263 .local_name
= "invocationId",
264 .type
= LDB_MAP_CONVERT
,
267 .remote_name
= "invocationId",
268 .convert_local
= guid_always_string
,
269 .convert_remote
= encode_guid
,
275 .local_name
= "objectSid",
276 .type
= LDB_MAP_CONVERT
,
279 .remote_name
= "objectSid",
280 .convert_local
= sid_always_binary
,
281 .convert_remote
= val_copy
,
285 /* securityIdentifier */
287 .local_name
= "securityIdentifier",
288 .type
= LDB_MAP_CONVERT
,
291 .remote_name
= "securityIdentifier",
292 .convert_local
= sid_always_binary
,
293 .convert_remote
= val_copy
,
298 .local_name
= "name",
299 .type
= LDB_MAP_RENAME
,
302 .remote_name
= "rdnValue"
307 .local_name
= "whenCreated",
308 .type
= LDB_MAP_RENAME
,
311 .remote_name
= "createTimestamp"
316 .local_name
= "whenChanged",
317 .type
= LDB_MAP_RENAME
,
320 .remote_name
= "modifyTimestamp"
325 .local_name
= "objectClasses",
326 .type
= LDB_MAP_RENAME
,
329 .remote_name
= "samba4ObjectClasses"
334 .local_name
= "dITContentRules",
335 .type
= LDB_MAP_RENAME
,
338 .remote_name
= "samba4DITContentRules"
343 .local_name
= "attributeTypes",
344 .type
= LDB_MAP_RENAME
,
347 .remote_name
= "samba4AttributeTypes"
352 .local_name
= "objectCategory",
353 .type
= LDB_MAP_CONVERT
,
356 .remote_name
= "objectCategory",
357 .convert_local
= objectCategory_always_dn
,
358 .convert_remote
= val_copy
,
363 .local_name
= "distinguishedName",
364 .type
= LDB_MAP_RENAME
,
367 .remote_name
= "entryDN"
372 .local_name
= "primaryGroupID",
373 .type
= LDB_MAP_CONVERT
,
376 .remote_name
= "primaryGroupID",
377 .convert_local
= normalise_to_signed32
,
378 .convert_remote
= val_copy
,
383 .local_name
= "groupType",
384 .type
= LDB_MAP_CONVERT
,
387 .remote_name
= "groupType",
388 .convert_local
= normalise_to_signed32
,
389 .convert_remote
= val_copy
,
394 .local_name
= "userAccountControl",
395 .type
= LDB_MAP_CONVERT
,
398 .remote_name
= "userAccountControl",
399 .convert_local
= normalise_to_signed32
,
400 .convert_remote
= val_copy
,
405 .local_name
= "sAMAccountType",
406 .type
= LDB_MAP_CONVERT
,
409 .remote_name
= "sAMAccountType",
410 .convert_local
= normalise_to_signed32
,
411 .convert_remote
= val_copy
,
416 .local_name
= "systemFlags",
417 .type
= LDB_MAP_CONVERT
,
420 .remote_name
= "systemFlags",
421 .convert_local
= normalise_to_signed32
,
422 .convert_remote
= val_copy
,
427 .local_name
= "usnChanged",
428 .type
= LDB_MAP_CONVERT
,
431 .remote_name
= "entryCSN",
432 .convert_local
= usn_to_entryCSN
,
433 .convert_remote
= entryCSN_to_usn
438 .local_name
= "usnCreated",
439 .type
= LDB_MAP_CONVERT
,
442 .remote_name
= "createTimestamp",
443 .convert_local
= usn_to_timestamp
,
444 .convert_remote
= timestamp_to_usn
,
450 .type
= LDB_MAP_KEEP
,
457 /* This objectClass conflicts with builtin classes on OpenLDAP */
458 const struct ldb_map_objectclass entryuuid_objectclasses
[] =
461 .local_name
= "subSchema",
462 .remote_name
= "samba4SubSchema"
469 /* These things do not show up in wildcard searches in OpenLDAP, but
470 * we need them to show up in the AD-like view */
471 static const char * const entryuuid_wildcard_attributes
[] = {
481 static const struct ldb_map_attribute nsuniqueid_attributes
[] =
485 .local_name
= "objectGUID",
486 .type
= LDB_MAP_CONVERT
,
489 .remote_name
= "nsuniqueid",
490 .convert_local
= guid_ns_string
,
491 .convert_remote
= encode_ns_guid
,
497 .local_name
= "objectSid",
498 .type
= LDB_MAP_CONVERT
,
501 .remote_name
= "sambaSID",
502 .convert_local
= sid_always_string
,
503 .convert_remote
= sid_always_binary
,
507 /* securityIdentifier */
509 .local_name
= "securityIdentifier",
510 .type
= LDB_MAP_CONVERT
,
513 .remote_name
= "securityIdentifier",
514 .convert_local
= sid_always_binary
,
515 .convert_remote
= val_copy
,
520 .local_name
= "whenCreated",
521 .type
= LDB_MAP_RENAME
,
524 .remote_name
= "createTimestamp"
529 .local_name
= "whenChanged",
530 .type
= LDB_MAP_RENAME
,
533 .remote_name
= "modifyTimestamp"
538 .local_name
= "objectCategory",
539 .type
= LDB_MAP_CONVERT
,
542 .remote_name
= "objectCategory",
543 .convert_local
= objectCategory_always_dn
,
544 .convert_remote
= val_copy
,
549 .local_name
= "distinguishedName",
550 .type
= LDB_MAP_RENAME
,
553 .remote_name
= "entryDN"
558 .local_name
= "primaryGroupID",
559 .type
= LDB_MAP_CONVERT
,
562 .remote_name
= "primaryGroupID",
563 .convert_local
= normalise_to_signed32
,
564 .convert_remote
= val_copy
,
569 .local_name
= "groupType",
570 .type
= LDB_MAP_CONVERT
,
573 .remote_name
= "sambaGroupType",
574 .convert_local
= normalise_to_signed32
,
575 .convert_remote
= val_copy
,
580 .local_name
= "userAccountControl",
581 .type
= LDB_MAP_CONVERT
,
584 .remote_name
= "userAccountControl",
585 .convert_local
= normalise_to_signed32
,
586 .convert_remote
= val_copy
,
591 .local_name
= "sAMAccountType",
592 .type
= LDB_MAP_CONVERT
,
595 .remote_name
= "sAMAccountType",
596 .convert_local
= normalise_to_signed32
,
597 .convert_remote
= val_copy
,
602 .local_name
= "systemFlags",
603 .type
= LDB_MAP_CONVERT
,
606 .remote_name
= "systemFlags",
607 .convert_local
= normalise_to_signed32
,
608 .convert_remote
= val_copy
,
613 .local_name
= "usnChanged",
614 .type
= LDB_MAP_CONVERT
,
617 .remote_name
= "modifyTimestamp",
618 .convert_local
= usn_to_timestamp
,
619 .convert_remote
= timestamp_to_usn
,
624 .local_name
= "usnCreated",
625 .type
= LDB_MAP_CONVERT
,
628 .remote_name
= "createTimestamp",
629 .convert_local
= usn_to_timestamp
,
630 .convert_remote
= timestamp_to_usn
,
635 .local_name
= "pwdLastSet",
636 .type
= LDB_MAP_RENAME
,
639 .remote_name
= "sambaPwdLastSet"
644 .local_name
= "lastLogon",
645 .type
= LDB_MAP_RENAME
,
648 .remote_name
= "sambaLogonTime"
653 .local_name
= "lastLogoff",
654 .type
= LDB_MAP_RENAME
,
657 .remote_name
= "sambaLogoffTime"
662 .local_name
= "badPwdCount",
663 .type
= LDB_MAP_RENAME
,
666 .remote_name
= "sambaBadPasswordCount"
671 .local_name
= "logonHours",
672 .type
= LDB_MAP_RENAME
,
675 .remote_name
= "sambaLogonHours"
680 .local_name
= "homeDrive",
681 .type
= LDB_MAP_RENAME
,
684 .remote_name
= "sambaHomeDrive"
689 .local_name
= "scriptPath",
690 .type
= LDB_MAP_RENAME
,
693 .remote_name
= "sambaLogonScript"
698 .local_name
= "profilePath",
699 .type
= LDB_MAP_RENAME
,
702 .remote_name
= "sambaProfilePath"
707 .local_name
= "userWorkstations",
708 .type
= LDB_MAP_RENAME
,
711 .remote_name
= "sambaUserWorkstations"
716 .local_name
= "homeDirectory",
717 .type
= LDB_MAP_RENAME
,
720 .remote_name
= "sambaHomePath"
725 .local_name
= "nextRid",
726 .type
= LDB_MAP_RENAME
,
729 .remote_name
= "sambaNextRid"
734 .local_name
= "privilegeDisplayName",
735 .type
= LDB_MAP_RENAME
,
738 .remote_name
= "sambaPrivName"
744 .type
= LDB_MAP_KEEP
,
751 /* This objectClass conflicts with builtin classes on FDS */
752 const struct ldb_map_objectclass nsuniqueid_objectclasses
[] =
759 /* These things do not show up in wildcard searches in OpenLDAP, but
760 * we need them to show up in the AD-like view */
761 static const char * const nsuniqueid_wildcard_attributes
[] = {
770 /* the context init function */
771 static int entryuuid_init(struct ldb_module
*module
)
774 ret
= ldb_map_init(module
, entryuuid_attributes
, entryuuid_objectclasses
, entryuuid_wildcard_attributes
, "samba4Top", NULL
);
775 if (ret
!= LDB_SUCCESS
)
778 return ldb_next_init(module
);
781 /* the context init function */
782 static int nsuniqueid_init(struct ldb_module
*module
)
785 ret
= ldb_map_init(module
, nsuniqueid_attributes
, nsuniqueid_objectclasses
, nsuniqueid_wildcard_attributes
, "extensibleObject", NULL
);
786 if (ret
!= LDB_SUCCESS
)
789 return ldb_next_init(module
);
792 static int get_seq_callback(struct ldb_request
*req
,
793 struct ldb_reply
*ares
)
795 unsigned long long *seq
= (unsigned long long *)req
->context
;
798 return ldb_request_done(req
, LDB_ERR_OPERATIONS_ERROR
);
800 if (ares
->error
!= LDB_SUCCESS
) {
801 return ldb_request_done(req
, ares
->error
);
804 if (ares
->type
== LDB_REPLY_ENTRY
) {
805 struct ldb_message_element
*el
= ldb_msg_find_element(ares
->message
, "contextCSN");
807 *seq
= entryCSN_to_usn_int(ares
, &el
->values
[0]);
811 if (ares
->type
== LDB_REPLY_DONE
) {
812 return ldb_request_done(req
, LDB_SUCCESS
);
819 static int entryuuid_sequence_number(struct ldb_module
*module
, struct ldb_request
*req
)
821 struct ldb_context
*ldb
;
823 struct map_private
*map_private
;
824 struct entryuuid_private
*entryuuid_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 entryuuid_private
= talloc_get_type(map_private
->caller_private
, struct entryuuid_private
);
847 /* All this to get the DN of the parition, so we can search the right thing */
848 partition_ctrl
= ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
);
849 if (!partition_ctrl
) {
850 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
851 "entryuuid_sequence_number: no current partition control found!");
852 return LDB_ERR_PROTOCOL_ERROR
;
855 partition
= talloc_get_type(partition_ctrl
->data
,
856 struct dsdb_control_current_partition
);
857 if ((partition
== NULL
) || (partition
->version
!= DSDB_CONTROL_CURRENT_PARTITION_VERSION
)) {
858 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
859 "entryuuid_sequence_number: current partition control with wrong data!");
860 return LDB_ERR_PROTOCOL_ERROR
;
863 ret
= ldb_build_search_req(&search_req
, ldb
, req
,
864 partition
->dn
, LDB_SCOPE_BASE
,
865 NULL
, contextCSN_attr
, NULL
,
866 &seq_num
, get_seq_callback
,
868 LDB_REQ_SET_LOCATION(search_req
);
869 if (ret
!= LDB_SUCCESS
) {
873 ret
= ldb_next_request(module
, search_req
);
875 if (ret
== LDB_SUCCESS
) {
876 ret
= ldb_wait(search_req
->handle
, LDB_WAIT_ALL
);
879 talloc_free(search_req
);
880 if (ret
!= LDB_SUCCESS
) {
884 ext
= talloc_zero(req
, struct ldb_extended
);
888 seqr
= talloc_zero(req
, struct ldb_seqnum_result
);
893 ext
->oid
= LDB_EXTENDED_SEQUENCE_NUMBER
;
897 case LDB_SEQ_HIGHEST_SEQ
:
898 seqr
->seq_num
= seq_num
;
901 seqr
->seq_num
= seq_num
;
904 case LDB_SEQ_HIGHEST_TIMESTAMP
:
905 return ldb_module_error(module
, LDB_ERR_OPERATIONS_ERROR
, "LDB_SEQ_HIGHEST_TIMESTAMP not supported");
909 seqr
->flags
|= LDB_SEQ_GLOBAL_SEQUENCE
;
911 /* send request done */
912 return ldb_module_done(req
, NULL
, ext
, LDB_SUCCESS
);
915 static int entryuuid_extended(struct ldb_module
*module
, struct ldb_request
*req
)
917 if (strcmp(req
->op
.extended
.oid
, LDB_EXTENDED_SEQUENCE_NUMBER
) == 0) {
918 return entryuuid_sequence_number(module
, req
);
921 return ldb_next_request(module
, req
);
924 static const struct ldb_module_ops ldb_entryuuid_module_ops
= {
926 .init_context
= entryuuid_init
,
927 .extended
= entryuuid_extended
,
931 static const struct ldb_module_ops ldb_nsuniqueid_module_ops
= {
932 .name
= "nsuniqueid",
933 .init_context
= nsuniqueid_init
,
934 .extended
= entryuuid_extended
,
939 initialise the module
941 _PUBLIC_
int ldb_simple_ldap_map_module_init(const char *version
)
944 LDB_MODULE_CHECK_VERSION(version
);
945 ret
= ldb_register_module(&ldb_entryuuid_module_ops
);
946 if (ret
!= LDB_SUCCESS
) {
949 ret
= ldb_register_module(&ldb_nsuniqueid_module_ops
);
950 if (ret
!= LDB_SUCCESS
) {