2 * idmap_ad: map between Active Directory and RFC 2307 or "Services for Unix" (SFU) Accounts
4 * Unix SMB/CIFS implementation.
6 * Winbind ADS backend functions
8 * Copyright (C) Andrew Tridgell 2001
9 * Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
10 * Copyright (C) Gerald (Jerry) Carter 2004
11 * Copyright (C) Luke Howard 2001-2004
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 #define DBGC_CLASS DBGC_IDMAP
33 #define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
35 NTSTATUS
init_module(void);
37 static ADS_STRUCT
*ad_idmap_ads
= NULL
;
39 static char *attr_uidnumber
= NULL
;
40 static char *attr_gidnumber
= NULL
;
42 static ADS_STATUS
ad_idmap_check_attr_mapping(ADS_STRUCT
*ads
)
45 enum wb_posix_mapping map_type
;
47 if (attr_uidnumber
!= NULL
&& attr_gidnumber
!= NULL
) {
48 return ADS_ERROR(LDAP_SUCCESS
);
51 SMB_ASSERT(ads
->server
.workgroup
);
53 map_type
= get_nss_info(ads
->server
.workgroup
);
55 if ((map_type
== WB_POSIX_MAP_SFU
) ||
56 (map_type
== WB_POSIX_MAP_RFC2307
)) {
58 status
= ads_check_posix_schema_mapping(ads
, map_type
);
59 if (ADS_ERR_OK(status
)) {
60 attr_uidnumber
= SMB_STRDUP(ads
->schema
.posix_uidnumber_attr
);
61 attr_gidnumber
= SMB_STRDUP(ads
->schema
.posix_gidnumber_attr
);
62 ADS_ERROR_HAVE_NO_MEMORY(attr_uidnumber
);
63 ADS_ERROR_HAVE_NO_MEMORY(attr_gidnumber
);
64 return ADS_ERROR(LDAP_SUCCESS
);
66 DEBUG(0,("ads_check_posix_schema_mapping failed: %s\n", ads_errstr(status
)));
71 /* fallback to XAD defaults */
72 attr_uidnumber
= SMB_STRDUP("uidNumber");
73 attr_gidnumber
= SMB_STRDUP("gidNumber");
74 ADS_ERROR_HAVE_NO_MEMORY(attr_uidnumber
);
75 ADS_ERROR_HAVE_NO_MEMORY(attr_gidnumber
);
77 return ADS_ERROR(LDAP_SUCCESS
);
80 static ADS_STRUCT
*ad_idmap_cached_connection(void)
86 if (ad_idmap_ads
!= NULL
) {
89 /* check for a valid structure */
91 DEBUG(7, ("Current tickets expire at %d, time is now %d\n",
92 (uint32
) ads
->auth
.expire
, (uint32
) time(NULL
)));
93 if ( ads
->config
.realm
&& (ads
->auth
.expire
> time(NULL
))) {
96 /* we own this ADS_STRUCT so make sure it goes away */
99 ads_kdestroy(WINBIND_CCACHE_NAME
);
105 /* we don't want this to affect the users ccache */
106 setenv("KRB5CCNAME", WINBIND_CCACHE_NAME
, 1);
109 ads
= ads_init(lp_realm(), lp_workgroup(), NULL
);
111 DEBUG(1,("ads_init failed\n"));
115 /* the machine acct password might have change - fetch it every time */
116 SAFE_FREE(ads
->auth
.password
);
117 ads
->auth
.password
= secrets_fetch_machine_password(lp_workgroup(), NULL
, NULL
);
119 SAFE_FREE(ads
->auth
.realm
);
120 ads
->auth
.realm
= SMB_STRDUP(lp_realm());
122 status
= ads_connect(ads
);
123 if (!ADS_ERR_OK(status
)) {
124 DEBUG(1, ("ad_idmap_init: failed to connect to AD\n"));
129 ads
->is_mine
= False
;
131 status
= ad_idmap_check_attr_mapping(ads
);
132 if (!ADS_ERR_OK(status
)) {
133 DEBUG(1, ("ad_idmap_init: failed to check attribute mapping\n"));
141 struct idmap_ad_context
{
142 uint32_t filter_low_id
, filter_high_id
; /* Filter range */
145 /* Initialize and check conf is appropriate */
146 static NTSTATUS
idmap_ad_initialize(struct idmap_domain
*dom
, const char *params
)
148 struct idmap_ad_context
*ctx
;
153 /* verify AD is reachable (not critical, we may just be offline at start) */
154 ads
= ad_idmap_cached_connection();
156 DEBUG(1, ("WARNING: Could not init an AD connection! Mapping might not work.\n"));
159 ctx
= talloc_zero(dom
, struct idmap_ad_context
);
161 DEBUG(0, ("Out of memory!\n"));
162 return NT_STATUS_NO_MEMORY
;
165 config_option
= talloc_asprintf(ctx
, "idmap config %s", dom
->name
);
166 if ( ! config_option
) {
167 DEBUG(0, ("Out of memory!\n"));
169 return NT_STATUS_NO_MEMORY
;
173 range
= lp_parm_const_string(-1, config_option
, "range", NULL
);
174 if (range
&& range
[0]) {
175 if ((sscanf(range
, "%u - %u", &ctx
->filter_low_id
, &ctx
->filter_high_id
) != 2) ||
176 (ctx
->filter_low_id
> ctx
->filter_high_id
)) {
177 DEBUG(1, ("ERROR: invalid filter range [%s]", range
));
178 ctx
->filter_low_id
= 0;
179 ctx
->filter_high_id
= 0;
183 /* idmap AD can work well only if it is the default module (trusts)
184 * with additional BUILTIN and alloc using TDB */
185 if ( ! dom
->default_domain
) {
186 DEBUG(1, ("WARNING: idmap_ad is not configured as the default domain.\n"
187 "For best results we suggest you to configure this module as\n"
188 "default and configure BULTIN to use idmap_tdb\n"
189 "ex: idmap domains = BUILTIN %s\n"
190 " idmap alloc config: range = 5000 - 9999\n"
191 " idmap config %s: default = yes\n"
192 " idmap config %s: backend = ad\n"
193 " idmap config %s: range = 10000 - 10000000 #this is optional\n"
194 "NOTE: make sure the ranges do not overlap\n",
195 dom
->name
, dom
->name
, dom
->name
, dom
->name
));
197 if ( ! dom
->readonly
) {
198 DEBUG(1, ("WARNING: forcing to readonly, as idmap_ad can't write on AD.\n"));
199 dom
->readonly
= true; /* force readonly */
202 dom
->private_data
= ctx
;
204 talloc_free(config_option
);
208 #define IDMAP_AD_MAX_IDS 30
209 #define CHECK_ALLOC_DONE(mem) do { if (!mem) { DEBUG(0, ("Out of memory!\n")); ret = NT_STATUS_NO_MEMORY; goto done; } } while (0)
211 /* this function searches up to IDMAP_AD_MAX_IDS entries in maps for a match */
212 static struct id_map
*find_map_by_id(struct id_map
**maps
, enum id_type type
, uint32_t id
)
216 for (i
= 0; i
< IDMAP_AD_MAX_IDS
; i
++) {
217 if (maps
[i
] == NULL
) { /* end of the run */
220 if ((maps
[i
]->xid
.type
== type
) && (maps
[i
]->xid
.id
== id
)) {
228 static NTSTATUS
idmap_ad_unixids_to_sids(struct idmap_domain
*dom
, struct id_map
**ids
)
232 struct idmap_ad_context
*ctx
;
235 const char *attrs
[] = { "sAMAccountType",
237 NULL
, /* attr_uidnumber */
238 NULL
, /* attr_gidnumber */
240 LDAPMessage
*res
= NULL
;
248 ctx
= talloc_get_type(dom
->private_data
, struct idmap_ad_context
);
250 memctx
= talloc_new(ctx
);
252 DEBUG(0, ("Out of memory!\n"));
253 return NT_STATUS_NO_MEMORY
;
256 ads
= ad_idmap_cached_connection();
258 DEBUG(1, ("ADS uninitialized\n"));
259 ret
= NT_STATUS_UNSUCCESSFUL
;
263 /* attr_uidnumber and attr_gidnumber are surely successfully initialized now */
264 attrs
[2] = attr_uidnumber
;
265 attrs
[3] = attr_gidnumber
;
268 /* if we are requested just one mapping use the simple filter */
269 switch (ids
[0]->xid
.type
) {
272 filter
= talloc_asprintf(memctx
,
273 "(&(|(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d))(%s=%lu))",
274 ATYPE_NORMAL_ACCOUNT
, ATYPE_WORKSTATION_TRUST
, ATYPE_INTERDOMAIN_TRUST
,
276 (unsigned long)ids
[0]->xid
.id
);
280 filter
= talloc_asprintf(memctx
,
281 "(&(|(sAMAccountType=%d)(sAMAccountType=%d))(%s=%lu))",
282 ATYPE_SECURITY_GLOBAL_GROUP
, ATYPE_SECURITY_LOCAL_GROUP
,
284 (unsigned long)ids
[0]->xid
.id
);
287 DEBUG(3, ("Unknown ID type\n"));
288 ret
= NT_STATUS_INVALID_PARAMETER
;
291 CHECK_ALLOC_DONE(filter
);
292 DEBUG(10, ("Filter: [%s]\n", filter
));
294 /* multiple mappings */
300 char *u_filter
= NULL
;
301 char *g_filter
= NULL
;
304 for (i
= 0; (i
< IDMAP_AD_MAX_IDS
) && ids
[idx
]; i
++, idx
++) {
305 switch (ids
[idx
]->xid
.type
) {
309 u_filter
= talloc_asprintf(memctx
, "(&(|"
310 "(sAMAccountType=%d)"
311 "(sAMAccountType=%d)"
312 "(sAMAccountType=%d))(|",
313 ATYPE_NORMAL_ACCOUNT
,
314 ATYPE_WORKSTATION_TRUST
,
315 ATYPE_INTERDOMAIN_TRUST
);
317 u_filter
= talloc_asprintf_append(u_filter
, "(%s=%lu)",
319 (unsigned long)ids
[idx
]->xid
.id
);
320 CHECK_ALLOC_DONE(u_filter
);
325 g_filter
= talloc_asprintf(memctx
, "(&(|"
326 "(sAMAccountType=%d)"
327 "(sAMAccountType=%d))(|",
328 ATYPE_SECURITY_GLOBAL_GROUP
,
329 ATYPE_SECURITY_LOCAL_GROUP
);
331 g_filter
= talloc_asprintf_append(g_filter
, "(%s=%lu)",
333 (unsigned long)ids
[idx
]->xid
.id
);
334 CHECK_ALLOC_DONE(g_filter
);
338 DEBUG(3, ("Unknown ID type\n"));
339 ids
[idx
]->status
= ID_UNKNOWN
;
343 filter
= talloc_asprintf(memctx
, "(|");
344 CHECK_ALLOC_DONE(filter
);
346 filter
= talloc_asprintf_append(filter
, "%s))", u_filter
);
347 CHECK_ALLOC_DONE(filter
);
348 TALLOC_FREE(u_filter
);
351 filter
= talloc_asprintf_append(filter
, "%s))", g_filter
);
352 CHECK_ALLOC_DONE(filter
);
353 TALLOC_FREE(g_filter
);
355 filter
= talloc_asprintf_append(filter
, ")");
356 CHECK_ALLOC_DONE(filter
);
357 DEBUG(10, ("Filter: [%s]\n", filter
));
363 rc
= ads_search_retry(ads
, &res
, filter
, attrs
);
364 if (!ADS_ERR_OK(rc
)) {
365 DEBUG(1, ("ERROR: ads search returned: %s\n", ads_errstr(rc
)));
366 ret
= NT_STATUS_UNSUCCESSFUL
;
370 count
= ads_count_replies(ads
, res
);
372 DEBUG(10, ("No IDs found\n"));
375 for (i
= 0; i
< count
; i
++) {
376 LDAPMessage
*entry
= NULL
;
383 if (i
== 0) { /* first entry */
384 entry
= ads_first_entry(ads
, res
);
385 } else { /* following ones */
386 entry
= ads_next_entry(ads
, entry
);
389 DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n"));
393 /* first check if the SID is present */
394 if (!ads_pull_sid(ads
, entry
, "objectSid", &sid
)) {
395 DEBUG(2, ("Could not retrieve SID from entry\n"));
400 if (!ads_pull_uint32(ads
, entry
, "sAMAccountType", &atype
)) {
401 DEBUG(1, ("could not get SAM account type\n"));
405 switch (atype
& 0xF0000000) {
406 case ATYPE_SECURITY_GLOBAL_GROUP
:
407 case ATYPE_SECURITY_LOCAL_GROUP
:
410 case ATYPE_NORMAL_ACCOUNT
:
411 case ATYPE_WORKSTATION_TRUST
:
412 case ATYPE_INTERDOMAIN_TRUST
:
416 DEBUG(1, ("unrecognized SAM account type %08x\n", atype
));
420 if (!ads_pull_uint32(ads
, entry
, (type
==ID_TYPE_UID
)?attr_uidnumber
:attr_gidnumber
, &id
)) {
421 DEBUG(1, ("Could not get unix ID\n"));
425 (ctx
->filter_low_id
&& (id
< ctx
->filter_low_id
)) ||
426 (ctx
->filter_high_id
&& (id
> ctx
->filter_high_id
))) {
427 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
428 id
, ctx
->filter_low_id
, ctx
->filter_high_id
));
432 map
= find_map_by_id(&ids
[bidx
], type
, id
);
434 DEBUG(2, ("WARNING: couldn't match result with requested ID\n"));
438 sid_copy(map
->sid
, &sid
);
441 map
->status
= ID_MAPPED
;
443 DEBUG(10, ("Mapped %s -> %lu (%d)\n",
444 sid_string_static(map
->sid
),
445 (unsigned long)map
->xid
.id
,
450 ads_msgfree(ads
, res
);
453 if (multi
&& ids
[idx
]) { /* still some values to map */
459 /* mark all unknwon ones as unmapped */
460 for (i
= 0; ids
[i
]; i
++) {
461 if (ids
[i
]->status
== ID_UNKNOWN
) ids
[i
]->status
= ID_UNMAPPED
;
469 /* this function searches up to IDMAP_AD_MAX_IDS entries in maps for a match */
470 static struct id_map
*find_map_by_sid(struct id_map
**maps
, DOM_SID
*sid
)
474 for (i
= 0; i
< IDMAP_AD_MAX_IDS
; i
++) {
475 if (maps
[i
] == NULL
) { /* end of the run */
478 if (sid_equal(maps
[i
]->sid
, sid
)) {
486 static NTSTATUS
idmap_ad_sids_to_unixids(struct idmap_domain
*dom
, struct id_map
**ids
)
490 struct idmap_ad_context
*ctx
;
493 const char *attrs
[] = { "sAMAccountType",
495 NULL
, /* attr_uidnumber */
496 NULL
, /* attr_gidnumber */
498 LDAPMessage
*res
= NULL
;
506 ctx
= talloc_get_type(dom
->private_data
, struct idmap_ad_context
);
508 memctx
= talloc_new(ctx
);
510 DEBUG(0, ("Out of memory!\n"));
511 return NT_STATUS_NO_MEMORY
;
514 ads
= ad_idmap_cached_connection();
516 DEBUG(1, ("ADS uninitialized\n"));
517 ret
= NT_STATUS_UNSUCCESSFUL
;
521 /* attr_uidnumber and attr_gidnumber are surely successfully initialized now */
522 attrs
[2] = attr_uidnumber
;
523 attrs
[3] = attr_gidnumber
;
527 /* if we are requested just one mapping use the simple filter */
530 sidstr
= sid_binstring(ids
[0]->sid
);
531 filter
= talloc_asprintf(memctx
, "(&(objectSid=%s)(|" /* the requested Sid */
532 "(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d)" /* user account types */
533 "(sAMAccountType=%d)(sAMAccountType=%d)))", /* group account types */
535 ATYPE_NORMAL_ACCOUNT
, ATYPE_WORKSTATION_TRUST
, ATYPE_INTERDOMAIN_TRUST
,
536 ATYPE_SECURITY_GLOBAL_GROUP
, ATYPE_SECURITY_LOCAL_GROUP
);
539 ret
= NT_STATUS_NO_MEMORY
;
542 CHECK_ALLOC_DONE(filter
);
543 DEBUG(10, ("Filter: [%s]\n", filter
));
545 /* multiple mappings */
553 filter
= talloc_asprintf(memctx
,
555 "(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d)" /* user account types */
556 "(sAMAccountType=%d)(sAMAccountType=%d)" /* group account types */
558 ATYPE_NORMAL_ACCOUNT
, ATYPE_WORKSTATION_TRUST
, ATYPE_INTERDOMAIN_TRUST
,
559 ATYPE_SECURITY_GLOBAL_GROUP
, ATYPE_SECURITY_LOCAL_GROUP
);
561 CHECK_ALLOC_DONE(filter
);
564 for (i
= 0; (i
< IDMAP_AD_MAX_IDS
) && ids
[idx
]; i
++, idx
++) {
566 sidstr
= sid_binstring(ids
[idx
]->sid
);
567 filter
= talloc_asprintf_append(filter
, "(objectSid=%s)", sidstr
);
570 CHECK_ALLOC_DONE(filter
);
572 filter
= talloc_asprintf_append(filter
, "))");
573 CHECK_ALLOC_DONE(filter
);
574 DEBUG(10, ("Filter: [%s]\n", filter
));
580 rc
= ads_search_retry(ads
, &res
, filter
, attrs
);
581 if (!ADS_ERR_OK(rc
)) {
582 DEBUG(1, ("ERROR: ads search returned: %s\n", ads_errstr(rc
)));
583 ret
= NT_STATUS_UNSUCCESSFUL
;
587 count
= ads_count_replies(ads
, res
);
589 DEBUG(10, ("No IDs found\n"));
592 for (i
= 0; i
< count
; i
++) {
593 LDAPMessage
*entry
= NULL
;
600 if (i
== 0) { /* first entry */
601 entry
= ads_first_entry(ads
, res
);
602 } else { /* following ones */
603 entry
= ads_next_entry(ads
, entry
);
606 DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n"));
610 /* first check if the SID is present */
611 if (!ads_pull_sid(ads
, entry
, "objectSid", &sid
)) {
612 DEBUG(2, ("Could not retrieve SID from entry\n"));
616 map
= find_map_by_sid(&ids
[bidx
], &sid
);
618 DEBUG(2, ("WARNING: couldn't match result with requested SID\n"));
623 if (!ads_pull_uint32(ads
, entry
, "sAMAccountType", &atype
)) {
624 DEBUG(1, ("could not get SAM account type\n"));
628 switch (atype
& 0xF0000000) {
629 case ATYPE_SECURITY_GLOBAL_GROUP
:
630 case ATYPE_SECURITY_LOCAL_GROUP
:
633 case ATYPE_NORMAL_ACCOUNT
:
634 case ATYPE_WORKSTATION_TRUST
:
635 case ATYPE_INTERDOMAIN_TRUST
:
639 DEBUG(1, ("unrecognized SAM account type %08x\n", atype
));
643 if (!ads_pull_uint32(ads
, entry
, (type
==ID_TYPE_UID
)?attr_uidnumber
:attr_gidnumber
, &id
)) {
644 DEBUG(1, ("Could not get unix ID\n"));
648 (ctx
->filter_low_id
&& (id
< ctx
->filter_low_id
)) ||
649 (ctx
->filter_high_id
&& (id
> ctx
->filter_high_id
))) {
650 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
651 id
, ctx
->filter_low_id
, ctx
->filter_high_id
));
656 map
->xid
.type
= type
;
658 map
->status
= ID_MAPPED
;
660 DEBUG(10, ("Mapped %s -> %lu (%d)\n",
661 sid_string_static(map
->sid
),
662 (unsigned long)map
->xid
.id
,
667 ads_msgfree(ads
, res
);
670 if (multi
&& ids
[idx
]) { /* still some values to map */
676 /* mark all unknwon ones as unmapped */
677 for (i
= 0; ids
[i
]; i
++) {
678 if (ids
[i
]->status
== ID_UNKNOWN
) ids
[i
]->status
= ID_UNMAPPED
;
686 static NTSTATUS
idmap_ad_close(struct idmap_domain
*dom
)
688 ADS_STRUCT
*ads
= ad_idmap_ads
;
691 /* we own this ADS_STRUCT so make sure it goes away */
697 SAFE_FREE(attr_uidnumber
);
698 SAFE_FREE(attr_gidnumber
);
703 static struct idmap_methods ad_methods
= {
704 .init
= idmap_ad_initialize
,
705 .unixids_to_sids
= idmap_ad_unixids_to_sids
,
706 .sids_to_unixids
= idmap_ad_sids_to_unixids
,
707 .close_fn
= idmap_ad_close
710 /* support for new authentication subsystem */
711 NTSTATUS
idmap_ad_init(void)
713 return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION
, "ad", &ad_methods
);