4 * Copyright (C) Gerald Carter <jerry@samba.org> 2007 - 2008
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/>.
22 #include "winbindd/winbindd.h"
24 #include "idmap_hash.h"
27 #include "../libcli/security/dom_sid.h"
28 #include "libsmb/samlogon_cache.h"
31 #define DBGC_CLASS DBGC_IDMAP
33 struct sid_hash_table
{
37 /*********************************************************************
38 Hash a domain SID (S-1-5-12-aaa-bbb-ccc) to a 12bit number
39 ********************************************************************/
41 static uint32_t hash_domain_sid(const struct dom_sid
*sid
)
45 if (sid
->num_auths
!= 4)
48 /* XOR the last three subauths */
50 hash
= ((sid
->sub_auths
[1] ^ sid
->sub_auths
[2]) ^ sid
->sub_auths
[3]);
52 /* Take all 32-bits into account when generating the 12-bit
54 hash
= (((hash
& 0xFFF00000) >> 20)
55 + ((hash
& 0x000FFF00) >> 8)
56 + (hash
& 0x000000FF)) & 0x00000FFF;
58 /* return a 12-bit hash value */
63 /*********************************************************************
64 Hash a Relative ID to a 19 bit number
65 ********************************************************************/
67 static uint32_t hash_rid(uint32_t rid
)
70 * 19 bits for the rid which allows us to support
71 * the first 50K users/groups in a domain
75 return (rid
& 0x0007FFFF);
78 /*********************************************************************
79 ********************************************************************/
81 static uint32_t combine_hashes(uint32_t h_domain
,
84 uint32_t return_id
= 0;
87 * shift the hash_domain 19 bits to the left and OR with the
90 * This will generate a 31 bit number out of
91 * 12 bit domain and 19 bit rid.
94 return_id
= ((h_domain
<<19) | h_rid
);
99 /*********************************************************************
100 ********************************************************************/
102 static void separate_hashes(uint32_t id
,
106 *h_rid
= id
& 0x0007FFFF;
107 *h_domain
= (id
& 0x7FF80000) >> 19;
113 /*********************************************************************
114 ********************************************************************/
116 static NTSTATUS
idmap_hash_initialize(struct idmap_domain
*dom
)
118 struct sid_hash_table
*hashed_domains
;
119 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
120 struct winbindd_tdc_domain
*dom_list
= NULL
;
121 size_t num_domains
= 0;
124 DBG_ERR("The idmap_hash module is deprecated and should not be used. "
125 "Please migrate to a different plugin. This module will be "
126 "removed in a future version of Samba\n");
128 if (!strequal(dom
->name
, "*")) {
129 DBG_ERR("Error: idmap_hash configured for domain '%s'. "
130 "But the hash module can only be used for the default "
131 "idmap configuration.\n", dom
->name
);
132 return NT_STATUS_INVALID_PARAMETER
;
135 if (!wcache_tdc_fetch_list(&dom_list
, &num_domains
)) {
136 nt_status
= NT_STATUS_TRUSTED_DOMAIN_FAILURE
;
137 BAIL_ON_NTSTATUS_ERROR(nt_status
);
140 /* Create the hash table of domain SIDs */
142 hashed_domains
= talloc_zero_array(dom
, struct sid_hash_table
, 4096);
143 BAIL_ON_PTR_NT_ERROR(hashed_domains
, nt_status
);
145 /* create the hash table of domain SIDs */
147 for (i
=0; i
<num_domains
; i
++) {
148 struct dom_sid_buf buf
;
151 if (is_null_sid(&dom_list
[i
].sid
))
155 * Check if the domain from the list is not already configured
156 * to use another idmap backend. Not checking this makes the
157 * idmap_hash module map IDs for *all* domains implicitly. This
158 * is quite dangerous in setups that use multiple idmap
162 if (domain_has_idmap_config(dom_list
[i
].domain_name
)) {
166 if ((hash
= hash_domain_sid(&dom_list
[i
].sid
)) == 0)
169 DBG_INFO("Adding %s (%s) -> %d\n",
170 dom_list
[i
].domain_name
,
171 dom_sid_str_buf(&dom_list
[i
].sid
, &buf
),
174 hashed_domains
[hash
].sid
= talloc(hashed_domains
, struct dom_sid
);
175 sid_copy(hashed_domains
[hash
].sid
, &dom_list
[i
].sid
);
178 dom
->private_data
= hashed_domains
;
184 /*********************************************************************
185 ********************************************************************/
187 static NTSTATUS
idmap_hash_id_to_sid(struct sid_hash_table
*hashed_domains
,
188 struct idmap_domain
*dom
,
191 uint32_t h_domain
= 0, h_rid
= 0;
193 id
->status
= ID_UNMAPPED
;
195 separate_hashes(id
->xid
.id
, &h_domain
, &h_rid
);
198 * If the domain hash doesn't find a SID in the table,
201 if (hashed_domains
[h_domain
].sid
== NULL
) {
202 /* keep ID_UNMAPPED */
206 id
->xid
.type
= ID_TYPE_BOTH
;
207 sid_compose(id
->sid
, hashed_domains
[h_domain
].sid
, h_rid
);
208 id
->status
= ID_MAPPED
;
213 static NTSTATUS
unixids_to_sids(struct idmap_domain
*dom
,
216 struct sid_hash_table
*hashed_domains
= talloc_get_type_abort(
217 dom
->private_data
, struct sid_hash_table
);
219 size_t num_tomap
= 0;
220 size_t num_mapped
= 0;
222 /* initialize the status to avoid surprise */
223 for (i
= 0; ids
[i
]; i
++) {
224 ids
[i
]->status
= ID_UNKNOWN
;
228 for (i
=0; ids
[i
]; i
++) {
231 ret
= idmap_hash_id_to_sid(hashed_domains
, dom
, ids
[i
]);
232 if (!NT_STATUS_IS_OK(ret
)) {
233 /* some fatal error occurred, log it */
234 DBG_NOTICE("Unexpected error resolving an ID "
235 "(%d): %s\n", ids
[i
]->xid
.id
,
240 if (ids
[i
]->status
== ID_MAPPED
) {
245 if (num_tomap
== num_mapped
) {
247 } else if (num_mapped
== 0) {
248 return NT_STATUS_NONE_MAPPED
;
251 return STATUS_SOME_UNMAPPED
;
254 /*********************************************************************
255 ********************************************************************/
257 static NTSTATUS
idmap_hash_sid_to_id(struct sid_hash_table
*hashed_domains
,
258 struct idmap_domain
*dom
,
263 uint32_t h_domain
, h_rid
;
265 id
->status
= ID_UNMAPPED
;
267 sid_copy(&sid
, id
->sid
);
268 sid_split_rid(&sid
, &rid
);
270 h_domain
= hash_domain_sid(&sid
);
271 h_rid
= hash_rid(rid
);
273 /* Check that both hashes are non-zero*/
275 /* keep ID_UNMAPPED */
279 /* keep ID_UNMAPPED */
284 * If the domain hash already exists find a SID in the table,
285 * just return the mapping.
287 if (hashed_domains
[h_domain
].sid
!= NULL
) {
292 * Check of last resort: A domain is valid if a user from that
293 * domain has recently logged in. The samlogon_cache these
294 * days also stores the domain sid.
296 if (netsamlogon_cache_have(&sid
)) {
298 * The domain is valid, so we'll
299 * remember it in order to
300 * allow reverse mappings to work.
302 goto remember_domain
;
305 if (id
->xid
.type
== ID_TYPE_NOT_SPECIFIED
) {
307 * idmap_hash used to bounce back the requested type,
308 * which was ID_TYPE_UID, ID_TYPE_GID or
309 * ID_TYPE_NOT_SPECIFIED before as the winbindd parent
310 * always used a lookupsids. When the lookupsids
311 * failed because of an unknown domain, the idmap child
312 * weren't requested at all and the caller sees
313 * ID_TYPE_NOT_SPECIFIED.
315 * Now that the winbindd parent will pass ID_TYPE_BOTH
316 * in order to indicate that the domain exists.
317 * We should ask the parent to fallback to lookupsids
318 * if the domain is not known yet.
320 id
->status
= ID_REQUIRE_TYPE
;
325 * Now we're sure the domain exist, remember
326 * the domain in order to return reverse mappings
330 hashed_domains
[h_domain
].sid
= dom_sid_dup(hashed_domains
, &sid
);
331 if (hashed_domains
[h_domain
].sid
== NULL
) {
332 return NT_STATUS_NO_MEMORY
;
336 * idmap_hash used to bounce back the requested type,
337 * which was ID_TYPE_UID, ID_TYPE_GID or
338 * ID_TYPE_NOT_SPECIFIED before as the winbindd parent
339 * always used a lookupsids.
341 * This module should have supported ID_TYPE_BOTH since
342 * samba-4.1.0, similar to idmap_rid and idmap_autorid.
344 * Now that the winbindd parent will pass ID_TYPE_BOTH
345 * in order to indicate that the domain exists, it's
346 * better to always return ID_TYPE_BOTH instead of a
347 * random mix of ID_TYPE_UID, ID_TYPE_GID or
351 id
->xid
.type
= ID_TYPE_BOTH
;
352 id
->xid
.id
= combine_hashes(h_domain
, h_rid
);
353 id
->status
= ID_MAPPED
;
358 static NTSTATUS
sids_to_unixids(struct idmap_domain
*dom
,
361 struct sid_hash_table
*hashed_domains
= talloc_get_type_abort(
362 dom
->private_data
, struct sid_hash_table
);
364 size_t num_tomap
= 0;
365 size_t num_mapped
= 0;
366 size_t num_required
= 0;
368 /* initialize the status to avoid surprise */
369 for (i
= 0; ids
[i
]; i
++) {
370 ids
[i
]->status
= ID_UNKNOWN
;
374 for (i
=0; ids
[i
]; i
++) {
377 ret
= idmap_hash_sid_to_id(hashed_domains
, dom
, ids
[i
]);
378 if (!NT_STATUS_IS_OK(ret
)) {
379 struct dom_sid_buf buf
;
380 /* some fatal error occurred, log it */
381 DBG_NOTICE("Unexpected error resolving a SID "
383 dom_sid_str_buf(ids
[i
]->sid
, &buf
),
388 if (ids
[i
]->status
== ID_MAPPED
) {
391 if (ids
[i
]->status
== ID_REQUIRE_TYPE
) {
396 if (num_tomap
== num_mapped
) {
398 } else if (num_required
> 0) {
399 return STATUS_SOME_UNMAPPED
;
400 } else if (num_mapped
== 0) {
401 return NT_STATUS_NONE_MAPPED
;
404 return STATUS_SOME_UNMAPPED
;
407 /*********************************************************************
408 ********************************************************************/
410 static NTSTATUS
nss_hash_init(struct nss_domain_entry
*e
)
415 /**********************************************************************
416 *********************************************************************/
418 static NTSTATUS
nss_hash_map_to_alias(TALLOC_CTX
*mem_ctx
,
419 struct nss_domain_entry
*e
,
423 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
426 value
= talloc_asprintf(mem_ctx
, "%s\\%s", e
->domain
, name
);
427 BAIL_ON_PTR_NT_ERROR(value
, nt_status
);
429 nt_status
= mapfile_lookup_key(mem_ctx
, value
, alias
);
430 BAIL_ON_NTSTATUS_ERROR(nt_status
);
436 /**********************************************************************
437 *********************************************************************/
439 static NTSTATUS
nss_hash_map_from_alias(TALLOC_CTX
*mem_ctx
,
440 struct nss_domain_entry
*e
,
444 return mapfile_lookup_value(mem_ctx
, alias
, name
);
447 /**********************************************************************
448 *********************************************************************/
450 static NTSTATUS
nss_hash_close(void)
455 /*********************************************************************
456 Dispatch Tables for IDMap and NssInfo Methods
457 ********************************************************************/
459 static const struct idmap_methods hash_idmap_methods
= {
460 .init
= idmap_hash_initialize
,
461 .unixids_to_sids
= unixids_to_sids
,
462 .sids_to_unixids
= sids_to_unixids
,
465 static const struct nss_info_methods hash_nss_methods
= {
466 .init
= nss_hash_init
,
467 .map_to_alias
= nss_hash_map_to_alias
,
468 .map_from_alias
= nss_hash_map_from_alias
,
469 .close_fn
= nss_hash_close
472 /**********************************************************************
473 Register with the idmap and idmap_nss subsystems. We have to protect
474 against the idmap and nss_info interfaces being in a half-registered
476 **********************************************************************/
479 NTSTATUS
idmap_hash_init(TALLOC_CTX
*ctx
)
481 static NTSTATUS idmap_status
= NT_STATUS_UNSUCCESSFUL
;
482 static NTSTATUS nss_status
= NT_STATUS_UNSUCCESSFUL
;
484 if ( !NT_STATUS_IS_OK(idmap_status
) ) {
485 idmap_status
= smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION
,
486 "hash", &hash_idmap_methods
);
488 if ( !NT_STATUS_IS_OK(idmap_status
) ) {
489 DEBUG(0,("Failed to register hash idmap plugin.\n"));
494 if ( !NT_STATUS_IS_OK(nss_status
) ) {
495 nss_status
= smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION
,
496 "hash", &hash_nss_methods
);
497 if ( !NT_STATUS_IS_OK(nss_status
) ) {
498 DEBUG(0,("Failed to register hash idmap nss plugin.\n"));