2 * idmap_ad: map between Active Directory and RFC 2307 accounts
4 * Copyright (C) Volker Lendecke 2015
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
23 #include "tldap_gensec_bind.h"
24 #include "tldap_util.h"
26 #include "lib/param/param.h"
27 #include "utils/net.h"
28 #include "auth/gensec/gensec.h"
29 #include "librpc/gen_ndr/ndr_netlogon.h"
30 #include "libads/ldap_schema_oids.h"
31 #include "../libds/common/flags.h"
32 #include "libcli/ldap/ldap_ndr.h"
33 #include "libcli/security/dom_sid.h"
35 struct idmap_ad_schema_names
;
37 struct idmap_ad_context
{
38 struct idmap_domain
*dom
;
39 struct tldap_context
*ld
;
40 struct idmap_ad_schema_names
*schema
;
41 const char *default_nc
;
44 static char *get_schema_path(TALLOC_CTX
*mem_ctx
, struct tldap_context
*ld
)
46 struct tldap_message
*rootdse
;
48 rootdse
= tldap_rootdse(ld
);
49 if (rootdse
== NULL
) {
53 return tldap_talloc_single_attribute(rootdse
, "schemaNamingContext",
57 static char *get_default_nc(TALLOC_CTX
*mem_ctx
, struct tldap_context
*ld
)
59 struct tldap_message
*rootdse
;
61 rootdse
= tldap_rootdse(ld
);
62 if (rootdse
== NULL
) {
66 return tldap_talloc_single_attribute(rootdse
, "defaultNamingContext",
70 struct idmap_ad_schema_names
{
79 static TLDAPRC
get_attrnames_by_oids(struct tldap_context
*ld
,
81 const char *schema_path
,
87 const char *attrs
[] = { "lDAPDisplayName", "attributeId" };
90 struct tldap_message
**msgs
;
93 filter
= talloc_strdup(mem_ctx
, "(|");
95 return TLDAP_NO_MEMORY
;
98 for (i
=0; i
<num_oids
; i
++) {
99 filter
= talloc_asprintf_append_buffer(
100 filter
, "(attributeId=%s)", oids
[i
]);
101 if (filter
== NULL
) {
102 return TLDAP_NO_MEMORY
;
106 filter
= talloc_asprintf_append_buffer(filter
, ")");
107 if (filter
== NULL
) {
108 return TLDAP_NO_MEMORY
;
111 rc
= tldap_search(ld
, schema_path
, TLDAP_SCOPE_SUB
, filter
,
112 attrs
, ARRAY_SIZE(attrs
), 0, NULL
, 0, NULL
, 0,
113 0, 0, 0, mem_ctx
, &msgs
);;
115 if (!TLDAP_RC_IS_SUCCESS(rc
)) {
119 for (i
=0; i
<num_oids
; i
++) {
123 num_msgs
= talloc_array_length(msgs
);
125 for (i
=0; i
<num_msgs
; i
++) {
126 struct tldap_message
*msg
= msgs
[i
];
130 if (tldap_msg_type(msg
) != TLDAP_RES_SEARCH_ENTRY
) {
131 /* Could be a TLDAP_RES_SEARCH_REFERENCE */
135 oid
= tldap_talloc_single_attribute(
136 msg
, "attributeId", msg
);
141 for (j
=0; j
<num_oids
; j
++) {
142 if (strequal(oid
, oids
[j
])) {
153 names
[j
] = tldap_talloc_single_attribute(
154 msg
, "lDAPDisplayName", mem_ctx
);
159 return TLDAP_SUCCESS
;
162 static TLDAPRC
get_posix_schema_names(struct tldap_context
*ld
,
163 const char *schema_mode
,
165 struct idmap_ad_schema_names
**pschema
)
168 struct idmap_ad_schema_names
*schema
;
170 const char *oids_sfu
[] = {
171 ADS_ATTR_SFU_UIDNUMBER_OID
,
172 ADS_ATTR_SFU_GIDNUMBER_OID
,
173 ADS_ATTR_SFU_HOMEDIR_OID
,
174 ADS_ATTR_SFU_SHELL_OID
,
175 ADS_ATTR_SFU_GECOS_OID
,
178 const char *oids_sfu20
[] = {
179 ADS_ATTR_SFU20_UIDNUMBER_OID
,
180 ADS_ATTR_SFU20_GIDNUMBER_OID
,
181 ADS_ATTR_SFU20_HOMEDIR_OID
,
182 ADS_ATTR_SFU20_SHELL_OID
,
183 ADS_ATTR_SFU20_GECOS_OID
,
184 ADS_ATTR_SFU20_UID_OID
186 const char *oids_rfc2307
[] = {
187 ADS_ATTR_RFC2307_UIDNUMBER_OID
,
188 ADS_ATTR_RFC2307_GIDNUMBER_OID
,
189 ADS_ATTR_RFC2307_HOMEDIR_OID
,
190 ADS_ATTR_RFC2307_SHELL_OID
,
191 ADS_ATTR_RFC2307_GECOS_OID
,
192 ADS_ATTR_RFC2307_UID_OID
198 schema
= talloc(mem_ctx
, struct idmap_ad_schema_names
);
199 if (schema
== NULL
) {
200 return TLDAP_NO_MEMORY
;
203 schema_path
= get_schema_path(schema
, ld
);
204 if (schema_path
== NULL
) {
206 return TLDAP_NO_MEMORY
;
211 if ((schema_mode
!= NULL
) && (schema_mode
[0] != '\0')) {
212 if (strequal(schema_mode
, "sfu")) {
214 } else if (strequal(schema_mode
, "sfu20")) {
216 } else if (strequal(schema_mode
, "rfc2307" )) {
219 DBG_WARNING("Unknown schema mode %s\n", schema_mode
);
223 rc
= get_attrnames_by_oids(ld
, schema
, schema_path
, 6, oids
, names
);
224 TALLOC_FREE(schema_path
);
225 if (!TLDAP_RC_IS_SUCCESS(rc
)) {
230 schema
->uid
= names
[0];
231 schema
->gid
= names
[1];
232 schema
->dir
= names
[2];
233 schema
->shell
= names
[3];
234 schema
->gecos
= names
[4];
235 schema
->name
= names
[5];
239 return TLDAP_SUCCESS
;
242 static NTSTATUS
idmap_ad_get_tldap_ctx(TALLOC_CTX
*mem_ctx
,
244 struct tldap_context
**pld
)
246 struct db_context
*db_ctx
;
247 struct netr_DsRGetDCNameInfo
*dcinfo
;
248 struct sockaddr_storage dcaddr
;
249 struct cli_credentials
*creds
;
250 struct loadparm_context
*lp_ctx
;
251 struct tldap_context
*ld
;
257 status
= wb_dsgetdcname_gencache_get(mem_ctx
, domname
, &dcinfo
);
258 if (!NT_STATUS_IS_OK(status
)) {
259 DBG_DEBUG("Could not get dcinfo for %s: %s\n", domname
,
264 if (dcinfo
->dc_unc
== NULL
) {
266 return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
;
268 if (dcinfo
->dc_unc
[0] == '\\') {
271 if (dcinfo
->dc_unc
[0] == '\\') {
275 ok
= resolve_name(dcinfo
->dc_unc
, &dcaddr
, 0x20, true);
277 DBG_DEBUG("Could not resolve name %s\n", dcinfo
->dc_unc
);
279 return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
;
282 status
= open_socket_out(&dcaddr
, 389, 10000, &fd
);
283 if (!NT_STATUS_IS_OK(status
)) {
284 DBG_DEBUG("open_socket_out failed: %s\n", nt_errstr(status
));
289 ld
= tldap_context_create(dcinfo
, fd
);
291 DBG_DEBUG("tldap_context_create failed\n");
294 return NT_STATUS_NO_MEMORY
;
297 creds
= cli_credentials_init(dcinfo
);
299 DBG_DEBUG("cli_credentials_init failed\n");
301 return NT_STATUS_NO_MEMORY
;
304 lp_ctx
= loadparm_init_s3(dcinfo
, loadparm_s3_helpers());
305 if (lp_ctx
== NULL
) {
306 DBG_DEBUG("loadparm_init_s3 failed\n");
308 return NT_STATUS_NO_MEMORY
;
311 cli_credentials_set_conf(creds
, lp_ctx
);
313 db_ctx
= secrets_db_ctx();
314 if (db_ctx
== NULL
) {
315 DBG_DEBUG("Failed to open secrets.tdb.\n");
316 return NT_STATUS_INTERNAL_ERROR
;
319 status
= cli_credentials_set_machine_account_db_ctx(creds
, lp_ctx
,
321 if (!NT_STATUS_IS_OK(status
)) {
322 DBG_DEBUG("cli_credentials_set_machine_account "
323 "failed: %s\n", nt_errstr(status
));
328 rc
= tldap_gensec_bind(ld
, creds
, "ldap", dcinfo
->dc_unc
, NULL
, lp_ctx
,
329 GENSEC_FEATURE_SIGN
| GENSEC_FEATURE_SEAL
);
330 if (!TLDAP_RC_IS_SUCCESS(rc
)) {
331 DBG_DEBUG("tldap_gensec_bind failed: %s\n",
332 tldap_errstr(dcinfo
, ld
, rc
));
334 return NT_STATUS_LDAP(TLDAP_RC_V(rc
));
337 rc
= tldap_fetch_rootdse(ld
);
338 if (!TLDAP_RC_IS_SUCCESS(rc
)) {
339 DBG_DEBUG("tldap_fetch_rootdse failed: %s\n",
340 tldap_errstr(dcinfo
, ld
, rc
));
342 return NT_STATUS_LDAP(TLDAP_RC_V(rc
));
345 *pld
= talloc_move(mem_ctx
, &ld
);
350 static int idmap_ad_context_destructor(struct idmap_ad_context
*ctx
)
352 if ((ctx
->dom
!= NULL
) && (ctx
->dom
->private_data
== ctx
)) {
353 ctx
->dom
->private_data
= NULL
;
358 static NTSTATUS
idmap_ad_context_create(TALLOC_CTX
*mem_ctx
,
359 struct idmap_domain
*dom
,
361 struct idmap_ad_context
**pctx
)
363 struct idmap_ad_context
*ctx
;
364 char *schema_config_option
;
365 const char *schema_mode
;
369 ctx
= talloc(mem_ctx
, struct idmap_ad_context
);
371 return NT_STATUS_NO_MEMORY
;
375 talloc_set_destructor(ctx
, idmap_ad_context_destructor
);
377 status
= idmap_ad_get_tldap_ctx(ctx
, domname
, &ctx
->ld
);
378 if (!NT_STATUS_IS_OK(status
)) {
379 DBG_DEBUG("idmap_ad_get_tldap_ctx failed: %s\n",
385 ctx
->default_nc
= get_default_nc(ctx
, ctx
->ld
);
386 if (ctx
->default_nc
== NULL
) {
387 DBG_DEBUG("No default nc\n");
392 schema_config_option
= talloc_asprintf(
393 ctx
, "idmap config %s", domname
);
394 if (schema_config_option
== NULL
) {
396 return NT_STATUS_NO_MEMORY
;
399 schema_mode
= lp_parm_const_string(
400 -1, schema_config_option
, "schema_mode", "rfc2307");
401 TALLOC_FREE(schema_config_option
);
403 rc
= get_posix_schema_names(ctx
->ld
, schema_mode
, ctx
, &ctx
->schema
);
404 if (!TLDAP_RC_IS_SUCCESS(rc
)) {
405 DBG_DEBUG("get_posix_schema_names failed: %s\n",
406 tldap_errstr(ctx
, ctx
->ld
, rc
));
408 return NT_STATUS_LDAP(TLDAP_RC_V(rc
));
415 static NTSTATUS
idmap_ad_initialize(struct idmap_domain
*dom
)
417 dom
->private_data
= NULL
;
421 static NTSTATUS
idmap_ad_get_context(struct idmap_domain
*dom
,
422 struct idmap_ad_context
**pctx
)
424 struct idmap_ad_context
*ctx
= NULL
;
427 if (dom
->private_data
!= NULL
) {
428 *pctx
= talloc_get_type_abort(dom
->private_data
,
429 struct idmap_ad_context
);
433 status
= idmap_ad_context_create(dom
, dom
, dom
->name
, &ctx
);
434 if (!NT_STATUS_IS_OK(status
)) {
435 DBG_DEBUG("idmap_ad_context_create failed: %s\n",
440 dom
->private_data
= ctx
;
445 static NTSTATUS
idmap_ad_unixids_to_sids(struct idmap_domain
*dom
,
448 struct idmap_ad_context
*ctx
;
451 struct tldap_message
**msgs
;
454 char *u_filter
, *g_filter
, *filter
;
456 const char *attrs
[] = {
459 NULL
, /* attr_uidnumber */
460 NULL
, /* attr_gidnumber */
463 status
= idmap_ad_get_context(dom
, &ctx
);
464 if (!NT_STATUS_IS_OK(status
)) {
468 attrs
[2] = ctx
->schema
->uid
;
469 attrs
[3] = ctx
->schema
->gid
;
471 u_filter
= talloc_strdup(talloc_tos(), "");
472 if (u_filter
== NULL
) {
473 return NT_STATUS_NO_MEMORY
;
476 g_filter
= talloc_strdup(talloc_tos(), "");
477 if (g_filter
== NULL
) {
478 return NT_STATUS_NO_MEMORY
;
481 for (i
=0; ids
[i
] != NULL
; i
++) {
482 struct id_map
*id
= ids
[i
];
484 id
->status
= ID_UNKNOWN
;
486 switch (id
->xid
.type
) {
488 u_filter
= talloc_asprintf_append_buffer(
489 u_filter
, "(%s=%ju)", ctx
->schema
->uid
,
490 (uintmax_t)id
->xid
.id
);
491 if (u_filter
== NULL
) {
492 return NT_STATUS_NO_MEMORY
;
498 g_filter
= talloc_asprintf_append_buffer(
499 g_filter
, "(%s=%ju)", ctx
->schema
->gid
,
500 (uintmax_t)id
->xid
.id
);
501 if (g_filter
== NULL
) {
502 return NT_STATUS_NO_MEMORY
;
508 DBG_WARNING("Unknown id type: %u\n",
509 (unsigned)id
->xid
.type
);
514 filter
= talloc_strdup(talloc_tos(), "(|");
515 if (filter
== NULL
) {
516 return NT_STATUS_NO_MEMORY
;
519 if (*u_filter
!= '\0') {
520 filter
= talloc_asprintf_append_buffer(
522 "(&(|(sAMAccountType=%d)(sAMAccountType=%d)"
523 "(sAMAccountType=%d))(|%s))",
524 ATYPE_NORMAL_ACCOUNT
, ATYPE_WORKSTATION_TRUST
,
525 ATYPE_INTERDOMAIN_TRUST
, u_filter
);
526 if (filter
== NULL
) {
527 return NT_STATUS_NO_MEMORY
;
530 TALLOC_FREE(u_filter
);
532 if (*g_filter
!= '\0') {
533 filter
= talloc_asprintf_append_buffer(
535 "(&(|(sAMAccountType=%d)(sAMAccountType=%d))(|%s))",
536 ATYPE_SECURITY_GLOBAL_GROUP
,
537 ATYPE_SECURITY_LOCAL_GROUP
,
539 if (filter
== NULL
) {
540 return NT_STATUS_NO_MEMORY
;
543 TALLOC_FREE(g_filter
);
545 filter
= talloc_asprintf_append_buffer(filter
, ")");
546 if (filter
== NULL
) {
547 return NT_STATUS_NO_MEMORY
;
550 DBG_DEBUG("Filter: [%s]\n", filter
);
552 rc
= tldap_search(ctx
->ld
, ctx
->default_nc
, TLDAP_SCOPE_SUB
, filter
,
553 attrs
, ARRAY_SIZE(attrs
), 0, NULL
, 0, NULL
, 0,
554 0, 0, 0, talloc_tos(), &msgs
);
555 if (!TLDAP_RC_IS_SUCCESS(rc
)) {
556 return NT_STATUS_LDAP(TLDAP_RC_V(rc
));
561 num_msgs
= talloc_array_length(msgs
);
563 for (i
=0; i
<num_msgs
; i
++) {
564 struct tldap_message
*msg
= msgs
[i
];
573 if (tldap_msg_type(msg
) != TLDAP_RES_SEARCH_ENTRY
) {
577 ok
= tldap_entry_dn(msg
, &dn
);
579 DBG_DEBUG("No dn found in msg %zu\n", i
);
583 ok
= tldap_pull_uint32(msg
, "sAMAccountType", &atype
);
585 DBG_DEBUG("No atype in object %s\n", dn
);
589 switch (atype
& 0xF0000000) {
590 case ATYPE_SECURITY_GLOBAL_GROUP
:
591 case ATYPE_SECURITY_LOCAL_GROUP
:
594 case ATYPE_NORMAL_ACCOUNT
:
595 case ATYPE_WORKSTATION_TRUST
:
596 case ATYPE_INTERDOMAIN_TRUST
:
600 DBG_WARNING("unrecognized SAM account type %08x\n",
605 ok
= tldap_pull_uint32(msg
, (type
== ID_TYPE_UID
) ?
606 ctx
->schema
->uid
: ctx
->schema
->gid
,
609 DBG_WARNING("No unix id in object %s\n", dn
);
613 ok
= tldap_pull_binsid(msg
, "objectSid", &sid
);
615 DBG_DEBUG("No objectSid in object %s\n", dn
);
620 for (j
=0; ids
[j
]; j
++) {
621 if ((type
== ids
[j
]->xid
.type
) &&
622 (xid
== ids
[j
]->xid
.id
)) {
628 DBG_DEBUG("Got unexpected sid %s from object %s\n",
629 sid_string_tos(&sid
), dn
);
633 sid_copy(map
->sid
, &sid
);
634 map
->status
= ID_MAPPED
;
636 DBG_DEBUG("Mapped %s -> %ju (%d)\n", sid_string_dbg(map
->sid
),
637 (uintmax_t)map
->xid
.id
, map
->xid
.type
);
645 static NTSTATUS
idmap_ad_sids_to_unixids(struct idmap_domain
*dom
,
648 struct idmap_ad_context
*ctx
;
651 struct tldap_message
**msgs
;
656 const char *attrs
[] = {
659 NULL
, /* attr_uidnumber */
660 NULL
, /* attr_gidnumber */
663 status
= idmap_ad_get_context(dom
, &ctx
);
664 if (!NT_STATUS_IS_OK(status
)) {
668 attrs
[2] = ctx
->schema
->uid
;
669 attrs
[3] = ctx
->schema
->gid
;
671 filter
= talloc_asprintf(
673 "(&(|(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d)"
674 "(sAMAccountType=%d)(sAMAccountType=%d))(|",
675 ATYPE_NORMAL_ACCOUNT
, ATYPE_WORKSTATION_TRUST
,
676 ATYPE_INTERDOMAIN_TRUST
, ATYPE_SECURITY_GLOBAL_GROUP
,
677 ATYPE_SECURITY_LOCAL_GROUP
);
678 if (filter
== NULL
) {
679 return NT_STATUS_NO_MEMORY
;
682 for (i
=0; ids
[i
]; i
++) {
685 ids
[i
]->status
= ID_UNKNOWN
;
687 sidstr
= ldap_encode_ndr_dom_sid(talloc_tos(), ids
[i
]->sid
);
688 if (sidstr
== NULL
) {
689 return NT_STATUS_NO_MEMORY
;
692 filter
= talloc_asprintf_append_buffer(
693 filter
, "(objectSid=%s)", sidstr
);
695 if (filter
== NULL
) {
696 return NT_STATUS_NO_MEMORY
;
700 filter
= talloc_asprintf_append_buffer(filter
, "))");
701 if (filter
== NULL
) {
702 return NT_STATUS_NO_MEMORY
;
705 DBG_DEBUG("Filter: [%s]\n", filter
);
707 rc
= tldap_search(ctx
->ld
, ctx
->default_nc
, TLDAP_SCOPE_SUB
, filter
,
708 attrs
, ARRAY_SIZE(attrs
), 0, NULL
, 0, NULL
, 0,
709 0, 0, 0, talloc_tos(), &msgs
);
710 if (!TLDAP_RC_IS_SUCCESS(rc
)) {
711 return NT_STATUS_LDAP(TLDAP_RC_V(rc
));
716 num_msgs
= talloc_array_length(msgs
);
718 for (i
=0; i
<num_msgs
; i
++) {
719 struct tldap_message
*msg
= msgs
[i
];
725 uint64_t account_type
, xid
;
728 if (tldap_msg_type(msg
) != TLDAP_RES_SEARCH_ENTRY
) {
732 ok
= tldap_entry_dn(msg
, &dn
);
734 DBG_DEBUG("No dn found in msg %zu\n", i
);
738 ok
= tldap_pull_binsid(msg
, "objectSid", &sid
);
740 DBG_DEBUG("No objectSid in object %s\n", dn
);
745 for (j
=0; ids
[j
]; j
++) {
746 if (dom_sid_equal(&sid
, ids
[j
]->sid
)) {
752 DBG_DEBUG("Got unexpected sid %s from object %s\n",
753 sid_string_tos(&sid
), dn
);
757 ok
= tldap_pull_uint64(msg
, "sAMAccountType", &account_type
);
759 DBG_DEBUG("No sAMAccountType in %s\n", dn
);
763 switch (account_type
& 0xF0000000) {
764 case ATYPE_SECURITY_GLOBAL_GROUP
:
765 case ATYPE_SECURITY_LOCAL_GROUP
:
768 case ATYPE_NORMAL_ACCOUNT
:
769 case ATYPE_WORKSTATION_TRUST
:
770 case ATYPE_INTERDOMAIN_TRUST
:
774 DBG_WARNING("unrecognized SAM account type %"PRIu64
"\n",
779 ok
= tldap_pull_uint64(msg
,
780 type
== ID_TYPE_UID
?
781 ctx
->schema
->uid
: ctx
->schema
->gid
,
784 DBG_DEBUG("No xid in %s\n", dn
);
789 map
->xid
.type
= type
;
791 map
->status
= ID_MAPPED
;
793 DEBUG(10, ("Mapped %s -> %lu (%d)\n", sid_string_dbg(map
->sid
),
794 (unsigned long)map
->xid
.id
, map
->xid
.type
));
802 static NTSTATUS
idmap_ad_unixids_to_sids_retry(struct idmap_domain
*dom
,
805 const NTSTATUS status_server_down
=
806 NT_STATUS_LDAP(TLDAP_RC_V(TLDAP_SERVER_DOWN
));
809 status
= idmap_ad_unixids_to_sids(dom
, ids
);
811 if (NT_STATUS_EQUAL(status
, status_server_down
)) {
812 TALLOC_FREE(dom
->private_data
);
813 status
= idmap_ad_unixids_to_sids(dom
, ids
);
819 static NTSTATUS
idmap_ad_sids_to_unixids_retry(struct idmap_domain
*dom
,
822 const NTSTATUS status_server_down
=
823 NT_STATUS_LDAP(TLDAP_RC_V(TLDAP_SERVER_DOWN
));
826 status
= idmap_ad_sids_to_unixids(dom
, ids
);
828 if (NT_STATUS_EQUAL(status
, status_server_down
)) {
829 TALLOC_FREE(dom
->private_data
);
830 status
= idmap_ad_sids_to_unixids(dom
, ids
);
836 static struct idmap_methods ad_methods
= {
837 .init
= idmap_ad_initialize
,
838 .unixids_to_sids
= idmap_ad_unixids_to_sids_retry
,
839 .sids_to_unixids
= idmap_ad_sids_to_unixids_retry
,
843 NTSTATUS
idmap_ad_init(void)
847 status
= smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION
,
849 if (!NT_STATUS_IS_OK(status
)) {
853 status
= idmap_ad_nss_init();
854 if (!NT_STATUS_IS_OK(status
)) {