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)) {