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)) & 0x0000FFF;
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, h_rid;
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 return NT_STATUS_NONE_MAPPED;
205 sid_compose(id->sid, hashed_domains[h_domain].sid, h_rid);
206 id->status = ID_MAPPED;
211 static NTSTATUS unixids_to_sids(struct idmap_domain *dom,
214 struct sid_hash_table *hashed_domains = talloc_get_type_abort(
215 dom->private_data, struct sid_hash_table);
220 /* initialize the status to avoid surprise */
221 for (i = 0; ids[i]; i++) {
222 ids[i]->status = ID_UNKNOWN;
226 for (i=0; ids[i]; i++) {
229 ret = idmap_hash_id_to_sid(hashed_domains, dom, ids[i]);
231 if ((!NT_STATUS_IS_OK(ret)) &&
232 (!NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) {
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 (NT_STATUS_IS_OK(ret) && 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 return NT_STATUS_NONE_MAPPED;
278 return NT_STATUS_NONE_MAPPED;
282 * If the domain hash already exists find a SID in the table,
283 * just return the mapping.
285 if (hashed_domains[h_domain].sid != NULL) {
290 * If the caller already did a lookup sid and made sure the
291 * domain sid is valid, we can allocate a new range.
293 * Currently the winbindd parent already does a lookup sids
294 * first, but hopefully changes in future. If the
295 * caller knows the domain sid, ID_TYPE_BOTH should be
296 * passed instead of ID_TYPE_NOT_SPECIFIED.
298 if (id->xid.type == ID_TYPE_NOT_SPECIFIED) {
300 * We keep the legacy behavior and
301 * just return the mapping, but
302 * the reverse mapping would not
305 * Maybe we should return NT_STATUS_NONE_MAPPED,
306 * but for now we keep what we already have.
312 * Check of last resort: A domain is valid if a user from that
313 * domain has recently logged in. The samlogon_cache these
314 * days also stores the domain sid.
316 * We used to check the list of trusted domains we received
317 * from "our" dc, but this is not reliable enough.
319 if (!netsamlogon_cache_have(&sid)) {
321 * We keep the legacy behavior and
322 * just return the mapping, but
323 * the reverse mapping would not
326 * Maybe we should return NT_STATUS_NONE_MAPPED,
327 * but for now we keep what we already have.
333 * Now we're sure the domain exist, remember
334 * the domain in order to return reverse mappings
337 hashed_domains[h_domain].sid = dom_sid_dup(hashed_domains, &sid);
338 if (hashed_domains[h_domain].sid == NULL) {
339 return NT_STATUS_NO_MEMORY;
343 id->xid.id = combine_hashes(h_domain, h_rid);
344 id->status = ID_MAPPED;
349 static NTSTATUS sids_to_unixids(struct idmap_domain *dom,
352 struct sid_hash_table *hashed_domains = talloc_get_type_abort(
353 dom->private_data, struct sid_hash_table);
358 /* initialize the status to avoid surprise */
359 for (i = 0; ids[i]; i++) {
360 ids[i]->status = ID_UNKNOWN;
364 for (i=0; ids[i]; i++) {
367 ret = idmap_hash_sid_to_id(hashed_domains, dom, ids[i]);
369 if ((!NT_STATUS_IS_OK(ret)) &&
370 (!NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) {
371 struct dom_sid_buf buf;
372 /* some fatal error occurred, log it */
373 DBG_NOTICE("Unexpected error resolving a SID "
375 dom_sid_str_buf(ids[i]->sid, &buf),
380 if (NT_STATUS_IS_OK(ret) && ids[i]->status == ID_MAPPED) {
385 if (num_tomap == num_mapped) {
387 } else if (num_mapped == 0) {
388 return NT_STATUS_NONE_MAPPED;
391 return STATUS_SOME_UNMAPPED;
394 /*********************************************************************
395 ********************************************************************/
397 static NTSTATUS nss_hash_init(struct nss_domain_entry *e )
402 /**********************************************************************
403 *********************************************************************/
405 static NTSTATUS nss_hash_map_to_alias(TALLOC_CTX *mem_ctx,
406 struct nss_domain_entry *e,
410 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
413 value = talloc_asprintf(mem_ctx, "%s\\%s", e->domain, name);
414 BAIL_ON_PTR_NT_ERROR(value, nt_status);
416 nt_status = mapfile_lookup_key(mem_ctx, value, alias);
417 BAIL_ON_NTSTATUS_ERROR(nt_status);
423 /**********************************************************************
424 *********************************************************************/
426 static NTSTATUS nss_hash_map_from_alias(TALLOC_CTX *mem_ctx,
427 struct nss_domain_entry *e,
431 return mapfile_lookup_value(mem_ctx, alias, name);
434 /**********************************************************************
435 *********************************************************************/
437 static NTSTATUS nss_hash_close(void)
442 /*********************************************************************
443 Dispatch Tables for IDMap and NssInfo Methods
444 ********************************************************************/
446 static const struct idmap_methods hash_idmap_methods = {
447 .init = idmap_hash_initialize,
448 .unixids_to_sids = unixids_to_sids,
449 .sids_to_unixids = sids_to_unixids,
452 static const struct nss_info_methods hash_nss_methods = {
453 .init = nss_hash_init,
454 .map_to_alias = nss_hash_map_to_alias,
455 .map_from_alias = nss_hash_map_from_alias,
456 .close_fn = nss_hash_close
459 /**********************************************************************
460 Register with the idmap and idmap_nss subsystems. We have to protect
461 against the idmap and nss_info interfaces being in a half-registered
463 **********************************************************************/
466 NTSTATUS idmap_hash_init(TALLOC_CTX *ctx)
468 static NTSTATUS idmap_status = NT_STATUS_UNSUCCESSFUL;
469 static NTSTATUS nss_status = NT_STATUS_UNSUCCESSFUL;
471 if ( !NT_STATUS_IS_OK(idmap_status) ) {
472 idmap_status = smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION,
473 "hash", &hash_idmap_methods);
475 if ( !NT_STATUS_IS_OK(idmap_status) ) {
476 DEBUG(0,("Failed to register hash idmap plugin.\n"));
481 if ( !NT_STATUS_IS_OK(nss_status) ) {
482 nss_status = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
483 "hash", &hash_nss_methods);
484 if ( !NT_STATUS_IS_OK(nss_status) ) {
485 DEBUG(0,("Failed to register hash idmap nss plugin.\n"));