2 * Unix SMB/CIFS implementation.
4 * Id mapping using LDAP records as defined in RFC 2307
6 * The SID<->uid/gid mapping is performed in two steps: 1) Query the
7 * AD server for the name<->sid mapping. 2) Query an LDAP server
8 * according to RFC 2307 for the name<->uid/gid mapping.
10 * Copyright (C) Christof Schmitt 2012,2013
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <http://www.gnu.org/licenses/>.
28 #include "winbindd_ads.h"
31 #include "nsswitch/winbind_client.h"
32 #include "lib/winbind_util.h"
33 #include "libcli/security/dom_sid.h"
36 * Config and connection info per domain.
38 struct idmap_rfc2307_context {
39 const char *bind_path_user;
40 const char *bind_path_group;
41 const char *ldap_domain;
46 * Pointer to ldap struct in ads or smbldap_state, has to be
47 * updated after connecting to server
51 /* Optional function to check connection to server */
52 NTSTATUS (*check_connection)(struct idmap_domain *dom);
54 /* Issue ldap query */
55 NTSTATUS (*search)(struct idmap_rfc2307_context *ctx,
56 const char *bind_path, const char *expr,
57 const char **attrs, LDAPMessage **res);
59 /* Access to LDAP in AD server */
62 /* Access to stand-alone LDAP server */
63 struct smbldap_state *smbldap_state;
67 * backend functions for LDAP queries through ADS
70 static NTSTATUS idmap_rfc2307_ads_check_connection(struct idmap_domain *dom)
72 struct idmap_rfc2307_context *ctx;
73 const char *dom_name = dom->name;
76 DEBUG(10, ("ad_idmap_cached_connection: called for domain '%s'\n",
79 ctx = talloc_get_type(dom->private_data, struct idmap_rfc2307_context);
80 dom_name = ctx->ldap_domain ? ctx->ldap_domain : dom->name;
82 status = ads_idmap_cached_connection(&ctx->ads, dom_name);
83 if (ADS_ERR_OK(status)) {
84 ctx->ldap = ctx->ads->ldap.ld;
86 DEBUG(1, ("Could not connect to domain %s: %s\n", dom->name,
90 return ads_ntstatus(status);
93 static NTSTATUS idmap_rfc2307_ads_search(struct idmap_rfc2307_context *ctx,
94 const char *bind_path,
101 status = ads_do_search_retry(ctx->ads, bind_path,
102 LDAP_SCOPE_SUBTREE, expr, attrs, result);
104 if (!ADS_ERR_OK(status)) {
105 return ads_ntstatus(status);
108 ctx->ldap = ctx->ads->ldap.ld;
109 return ads_ntstatus(status);
112 static NTSTATUS idmap_rfc2307_init_ads(struct idmap_rfc2307_context *ctx,
113 const char *domain_name)
115 const char *ldap_domain;
117 ctx->search = idmap_rfc2307_ads_search;
118 ctx->check_connection = idmap_rfc2307_ads_check_connection;
120 ldap_domain = idmap_config_const_string(domain_name, "ldap_domain",
123 ctx->ldap_domain = talloc_strdup(ctx, ldap_domain);
124 if (ctx->ldap_domain == NULL) {
125 return NT_STATUS_NO_MEMORY;
133 * backend function for LDAP queries through stand-alone LDAP server
136 static NTSTATUS idmap_rfc2307_ldap_search(struct idmap_rfc2307_context *ctx,
137 const char *bind_path,
140 LDAPMessage **result)
144 ret = smbldap_search(ctx->smbldap_state, bind_path, LDAP_SCOPE_SUBTREE,
145 expr, attrs, 0, result);
146 ctx->ldap = smbldap_get_ldap(ctx->smbldap_state);
148 if (ret == LDAP_SUCCESS) {
152 return NT_STATUS_LDAP(ret);
155 static bool idmap_rfc2307_get_uint32(LDAP *ldap, LDAPMessage *entry,
156 const char *field, uint32_t *value)
161 b = smbldap_get_single_attribute(ldap, entry, field, str, sizeof(str));
170 static NTSTATUS idmap_rfc2307_init_ldap(struct idmap_rfc2307_context *ctx,
171 const char *domain_name)
176 const char *ldap_url, *user_dn;
177 TALLOC_CTX *mem_ctx = ctx;
179 ldap_url = idmap_config_const_string(domain_name, "ldap_url", NULL);
181 DEBUG(1, ("ERROR: missing idmap ldap url\n"));
182 return NT_STATUS_UNSUCCESSFUL;
185 url = talloc_strdup(talloc_tos(), ldap_url);
187 user_dn = idmap_config_const_string(domain_name, "ldap_user_dn", NULL);
189 secret = idmap_fetch_secret("ldap", domain_name, user_dn);
191 ret = NT_STATUS_ACCESS_DENIED;
196 /* assume anonymous if we don't have a specified user */
197 ret = smbldap_init(mem_ctx, global_event_context(), url,
198 (user_dn == NULL), user_dn, secret,
199 &ctx->smbldap_state);
201 if (!NT_STATUS_IS_OK(ret)) {
202 DEBUG(1, ("ERROR: smbldap_init (%s) failed!\n", url));
206 ctx->search = idmap_rfc2307_ldap_search;
214 * common code for stand-alone LDAP and ADS
217 static void idmap_rfc2307_map_sid_results(struct idmap_rfc2307_context *ctx,
221 const char *dom_name,
222 const char **attrs, int type)
227 count = ldap_count_entries(ctx->ldap, result);
229 for (i = 0; i < count; i++) {
231 enum lsa_SidType lsa_type;
237 entry = ldap_first_entry(ctx->ldap, result);
239 entry = ldap_next_entry(ctx->ldap, entry);
242 DEBUG(2, ("Unable to fetch entry.\n"));
246 name = smbldap_talloc_single_attribute(ctx->ldap, entry,
249 DEBUG(1, ("Could not get user name\n"));
253 b = idmap_rfc2307_get_uint32(ctx->ldap, entry, attrs[1], &id);
255 DEBUG(1, ("Could not pull id for record %s\n", name));
259 map = idmap_find_map_by_id(ids, type, id);
261 DEBUG(1, ("Could not find id %d, name %s\n", id, name));
265 if (ctx->realm != NULL) {
266 /* Strip @realm from user or group name */
269 delim = strchr(name, '@');
275 /* by default calls to winbindd are disabled
276 the following call will not recurse so this is safe */
278 /* Lookup name from PDC using lsa_lookup_names() */
279 b = winbind_lookup_name(dom_name, name, map->sid, &lsa_type);
283 DEBUG(1, ("SID lookup failed for id %d, %s\n",
288 if (type == ID_TYPE_UID && lsa_type != SID_NAME_USER) {
289 DEBUG(1, ("Wrong type %d for user name %s\n",
294 if (type == ID_TYPE_GID && lsa_type != SID_NAME_DOM_GRP &&
295 lsa_type != SID_NAME_ALIAS &&
296 lsa_type != SID_NAME_WKN_GRP) {
297 DEBUG(1, ("Wrong type %d for group name %s\n",
302 map->status = ID_MAPPED;
307 * Map unixids to names and then to sids.
309 static NTSTATUS idmap_rfc2307_unixids_to_sids(struct idmap_domain *dom,
312 struct idmap_rfc2307_context *ctx;
313 char *fltr_usr = NULL, *fltr_grp = NULL;
315 int cnt_usr = 0, cnt_grp = 0, idx = 0, bidx = 0;
316 LDAPMessage *result = NULL;
319 ctx = talloc_get_type(dom->private_data, struct idmap_rfc2307_context);
320 mem_ctx = talloc_new(ctx);
322 return NT_STATUS_NO_MEMORY;
325 if (ctx->check_connection) {
326 ret = ctx->check_connection(dom);
327 if (!NT_STATUS_IS_OK(ret)) {
336 /* prepare new user query, see getpwuid() in RFC2307 */
337 fltr_usr = talloc_asprintf(mem_ctx,
338 "(&(objectClass=posixAccount)(|");
342 /* prepare new group query, see getgrgid() in RFC2307 */
343 fltr_grp = talloc_asprintf(mem_ctx,
344 "(&(objectClass=posixGroup)(|");
347 if (!fltr_usr || !fltr_grp) {
348 ret = NT_STATUS_NO_MEMORY;
352 while (cnt_usr < IDMAP_LDAP_MAX_IDS &&
353 cnt_grp < IDMAP_LDAP_MAX_IDS && ids[idx]) {
355 switch (ids[idx]->xid.type) {
357 fltr_usr = talloc_asprintf_append_buffer(fltr_usr,
358 "(uidNumber=%d)", ids[idx]->xid.id);
362 fltr_grp = talloc_asprintf_append_buffer(fltr_grp,
363 "(gidNumber=%d)", ids[idx]->xid.id);
367 DEBUG(3, ("Error: unknown ID type %d\n",
368 ids[idx]->xid.type));
369 ret = NT_STATUS_UNSUCCESSFUL;
373 if (!fltr_usr || !fltr_grp) {
374 ret = NT_STATUS_NO_MEMORY;
381 if (cnt_usr == IDMAP_LDAP_MAX_IDS || (cnt_usr != 0 && !ids[idx])) {
382 const char *attrs[] = { NULL, /* uid or cn */
386 fltr_usr = talloc_strdup_append(fltr_usr, "))");
388 ret = NT_STATUS_NO_MEMORY;
392 attrs[0] = ctx->user_cn ? "cn" : "uid";
393 ret = ctx->search(ctx, ctx->bind_path_user, fltr_usr, attrs,
395 if (!NT_STATUS_IS_OK(ret)) {
399 idmap_rfc2307_map_sid_results(ctx, mem_ctx, &ids[bidx], result,
400 dom->name, attrs, ID_TYPE_UID);
402 TALLOC_FREE(fltr_usr);
405 if (cnt_grp == IDMAP_LDAP_MAX_IDS || (cnt_grp != 0 && !ids[idx])) {
406 const char *attrs[] = { "cn", "gidNumber", NULL };
408 fltr_grp = talloc_strdup_append(fltr_grp, "))");
410 ret = NT_STATUS_NO_MEMORY;
413 ret = ctx->search(ctx, ctx->bind_path_group, fltr_grp, attrs,
415 if (!NT_STATUS_IS_OK(ret)) {
419 idmap_rfc2307_map_sid_results(ctx, mem_ctx, &ids[bidx], result,
420 dom->name, attrs, ID_TYPE_GID);
422 TALLOC_FREE(fltr_grp);
432 talloc_free(mem_ctx);
436 struct idmap_rfc2307_map {
443 * Lookup names for SIDS and store the data in the local mapping
446 static NTSTATUS idmap_rfc_2307_sids_to_names(TALLOC_CTX *mem_ctx,
448 struct idmap_rfc2307_map *maps,
449 struct idmap_rfc2307_context *ctx)
453 for (i = 0; ids[i]; i++) {
454 const char *domain, *name;
455 enum lsa_SidType lsa_type;
456 struct id_map *id = ids[i];
457 struct idmap_rfc2307_map *map = &maps[i];
458 struct dom_sid_buf buf;
461 /* by default calls to winbindd are disabled
462 the following call will not recurse so this is safe */
464 b = winbind_lookup_sid(mem_ctx, ids[i]->sid, &domain, &name,
469 DEBUG(1, ("Lookup sid %s failed.\n",
470 dom_sid_str_buf(ids[i]->sid, &buf)));
476 id->xid.type = map->type = ID_TYPE_UID;
477 if (ctx->user_cn && ctx->realm != NULL) {
478 name = talloc_asprintf(mem_ctx, "%s@%s",
481 id->xid.type = map->type = ID_TYPE_UID;
484 case SID_NAME_DOM_GRP:
486 case SID_NAME_WKN_GRP:
487 if (ctx->realm != NULL) {
488 name = talloc_asprintf(mem_ctx, "%s@%s",
491 id->xid.type = map->type = ID_TYPE_GID;
495 DEBUG(1, ("Unknown lsa type %d for sid %s\n",
497 dom_sid_str_buf(id->sid, &buf)));
498 id->status = ID_UNMAPPED;
503 id->status = ID_UNKNOWN;
504 map->name = strupper_talloc(mem_ctx, name);
507 return NT_STATUS_NO_MEMORY;
515 * Find id_map entry by looking up the name in the internal
518 static struct id_map* idmap_rfc2307_find_map(struct idmap_rfc2307_map *maps,
524 DEBUG(10, ("Looking for name %s, type %d\n", name, type));
526 for (i = 0; maps[i].map != NULL; i++) {
527 DEBUG(10, ("Entry %d: name %s, type %d\n",
528 i, maps[i].name, maps[i].type));
529 if (type == maps[i].type && strcmp(name, maps[i].name) == 0) {
537 static void idmap_rfc2307_map_xid_results(struct idmap_rfc2307_context *ctx,
539 struct idmap_rfc2307_map *maps,
541 struct idmap_domain *dom,
542 const char **attrs, enum id_type type)
547 count = ldap_count_entries(ctx->ldap, result);
549 for (i = 0; i < count; i++) {
553 struct id_map *id_map;
556 entry = ldap_first_entry(ctx->ldap, result);
558 entry = ldap_next_entry(ctx->ldap, entry);
561 DEBUG(2, ("Unable to fetch entry.\n"));
565 name = smbldap_talloc_single_attribute(ctx->ldap, entry,
568 DEBUG(1, ("Could not get user name\n"));
572 b = idmap_rfc2307_get_uint32(ctx->ldap, entry, attrs[1], &id);
574 DEBUG(5, ("Could not pull id for record %s\n", name));
578 if (!idmap_unix_id_is_in_range(id, dom)) {
579 DEBUG(5, ("Requested id (%u) out of range (%u - %u).\n",
580 id, dom->low_id, dom->high_id));
584 if (!strupper_m(name)) {
585 DEBUG(5, ("Could not convert %s to uppercase\n", name));
588 id_map = idmap_rfc2307_find_map(maps, type, name);
590 DEBUG(0, ("Could not find mapping entry for name %s\n",
596 id_map->status = ID_MAPPED;
601 * Map sids to names and then to unixids.
603 static NTSTATUS idmap_rfc2307_sids_to_unixids(struct idmap_domain *dom,
606 struct idmap_rfc2307_context *ctx;
608 struct idmap_rfc2307_map *int_maps;
609 int cnt_usr = 0, cnt_grp = 0, idx = 0;
610 char *fltr_usr = NULL, *fltr_grp = NULL;
614 ctx = talloc_get_type(dom->private_data, struct idmap_rfc2307_context);
615 mem_ctx = talloc_new(talloc_tos());
617 return NT_STATUS_NO_MEMORY;
620 if (ctx->check_connection) {
621 ret = ctx->check_connection(dom);
622 if (!NT_STATUS_IS_OK(ret)) {
627 for (i = 0; ids[i]; i++);
628 int_maps = talloc_zero_array(mem_ctx, struct idmap_rfc2307_map, i);
630 ret = NT_STATUS_NO_MEMORY;
634 ret = idmap_rfc_2307_sids_to_names(mem_ctx, ids, int_maps, ctx);
635 if (!NT_STATUS_IS_OK(ret)) {
641 /* prepare new user query, see getpwuid() in RFC2307 */
642 fltr_usr = talloc_asprintf(mem_ctx,
643 "(&(objectClass=posixAccount)(|");
647 /* prepare new group query, see getgrgid() in RFC2307 */
648 fltr_grp = talloc_asprintf(mem_ctx,
649 "(&(objectClass=posixGroup)(|");
652 if (!fltr_usr || !fltr_grp) {
653 ret = NT_STATUS_NO_MEMORY;
657 while (cnt_usr < IDMAP_LDAP_MAX_IDS &&
658 cnt_grp < IDMAP_LDAP_MAX_IDS && ids[idx]) {
659 struct id_map *id = ids[idx];
660 struct idmap_rfc2307_map *map = &int_maps[idx];
662 switch(id->xid.type) {
664 fltr_usr = talloc_asprintf_append_buffer(fltr_usr,
665 "(%s=%s)", (ctx->user_cn ? "cn" : "uid"),
671 fltr_grp = talloc_asprintf_append_buffer(fltr_grp,
672 "(cn=%s)", map->name);
680 if (!fltr_usr || !fltr_grp) {
681 ret = NT_STATUS_NO_MEMORY;
688 if (cnt_usr == IDMAP_LDAP_MAX_IDS || (cnt_usr != 0 && !ids[idx])) {
689 const char *attrs[] = { NULL, /* uid or cn */
694 fltr_usr = talloc_strdup_append(fltr_usr, "))");
696 ret = NT_STATUS_NO_MEMORY;
700 attrs[0] = ctx->user_cn ? "cn" : "uid";
701 ret = ctx->search(ctx, ctx->bind_path_user, fltr_usr, attrs,
703 if (!NT_STATUS_IS_OK(ret)) {
707 idmap_rfc2307_map_xid_results(ctx, mem_ctx, int_maps,
708 result, dom, attrs, ID_TYPE_UID);
711 TALLOC_FREE(fltr_usr);
714 if (cnt_grp == IDMAP_LDAP_MAX_IDS || (cnt_grp != 0 && !ids[idx])) {
715 const char *attrs[] = {"cn", "gidNumber", NULL };
718 fltr_grp = talloc_strdup_append(fltr_grp, "))");
720 ret = NT_STATUS_NO_MEMORY;
724 ret = ctx->search(ctx, ctx->bind_path_group, fltr_grp, attrs,
726 if (!NT_STATUS_IS_OK(ret)) {
730 idmap_rfc2307_map_xid_results(ctx, mem_ctx, int_maps, result,
731 dom, attrs, ID_TYPE_GID);
733 TALLOC_FREE(fltr_grp);
743 talloc_free(mem_ctx);
747 static int idmap_rfc2307_context_destructor(struct idmap_rfc2307_context *ctx)
749 if (ctx->ads != NULL) {
750 /* we own this ADS_STRUCT so make sure it goes away */
751 ctx->ads->is_mine = True;
752 ads_destroy( &ctx->ads );
756 if (ctx->smbldap_state != NULL) {
757 smbldap_free_struct(&ctx->smbldap_state);
763 static NTSTATUS idmap_rfc2307_initialize(struct idmap_domain *domain)
765 struct idmap_rfc2307_context *ctx;
766 const char *bind_path_user, *bind_path_group, *ldap_server, *realm;
769 ctx = talloc_zero(domain, struct idmap_rfc2307_context);
771 return NT_STATUS_NO_MEMORY;
773 talloc_set_destructor(ctx, idmap_rfc2307_context_destructor);
775 bind_path_user = idmap_config_const_string(
776 domain->name, "bind_path_user", NULL);
777 if (bind_path_user == NULL) {
778 status = NT_STATUS_INVALID_PARAMETER;
781 ctx->bind_path_user = talloc_strdup(ctx, bind_path_user);
782 if (ctx->bind_path_user == NULL) {
783 status = NT_STATUS_NO_MEMORY;
787 bind_path_group = idmap_config_const_string(
788 domain->name, "bind_path_group", NULL);
789 if (bind_path_group == NULL) {
790 status = NT_STATUS_INVALID_PARAMETER;
793 ctx->bind_path_group = talloc_strdup(ctx, bind_path_group);
794 if (ctx->bind_path_group == NULL) {
795 status = NT_STATUS_NO_MEMORY;
799 ldap_server = idmap_config_const_string(
800 domain->name, "ldap_server", NULL);
802 status = NT_STATUS_INVALID_PARAMETER;
806 if (strcmp(ldap_server, "stand-alone") == 0) {
807 status = idmap_rfc2307_init_ldap(ctx, domain->name);
809 } else if (strcmp(ldap_server, "ad") == 0) {
810 status = idmap_rfc2307_init_ads(ctx, domain->name);
813 status = NT_STATUS_INVALID_PARAMETER;
816 if (!NT_STATUS_IS_OK(status)) {
820 realm = idmap_config_const_string(domain->name, "realm", NULL);
822 ctx->realm = talloc_strdup(ctx, realm);
823 if (ctx->realm == NULL) {
824 status = NT_STATUS_NO_MEMORY;
829 ctx->user_cn = idmap_config_bool(domain->name, "user_cn", false);
831 domain->private_data = ctx;
839 static struct idmap_methods rfc2307_methods = {
840 .init = idmap_rfc2307_initialize,
841 .unixids_to_sids = idmap_rfc2307_unixids_to_sids,
842 .sids_to_unixids = idmap_rfc2307_sids_to_unixids,
846 NTSTATUS idmap_rfc2307_init(TALLOC_CTX *ctx)
848 return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "rfc2307",