winbindd: Use dom_sid_str_buf
[nivanova/samba-autobuild/.git] / source3 / winbindd / idmap_hash / idmap_hash.c
1 /*
2  *  idmap_hash.c
3  *
4  * Copyright (C) Gerald Carter  <jerry@samba.org>      2007 - 2008
5  *
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.
10  *
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.
15  *
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/>.
18  *
19  */
20
21 #include "includes.h"
22 #include "winbindd/winbindd.h"
23 #include "idmap.h"
24 #include "idmap_hash.h"
25 #include "ads.h"
26 #include "nss_info.h"
27 #include "../libcli/security/dom_sid.h"
28
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_IDMAP
31
32 struct sid_hash_table {
33         struct dom_sid *sid;
34 };
35
36 /*********************************************************************
37  Hash a domain SID (S-1-5-12-aaa-bbb-ccc) to a 12bit number
38  ********************************************************************/
39
40 static uint32_t hash_domain_sid(const struct dom_sid *sid)
41 {
42         uint32_t hash;
43
44         if (sid->num_auths != 4)
45                 return 0;
46
47         /* XOR the last three subauths */
48
49         hash = ((sid->sub_auths[1] ^ sid->sub_auths[2]) ^ sid->sub_auths[3]);
50
51         /* Take all 32-bits into account when generating the 12-bit
52            hash value */
53         hash = (((hash & 0xFFF00000) >> 20)
54                 + ((hash & 0x000FFF00) >> 8)
55                 + (hash & 0x000000FF)) & 0x0000FFF;
56
57         /* return a 12-bit hash value */
58
59         return hash;
60 }
61
62 /*********************************************************************
63  Hash a Relative ID to a 20 bit number
64  ********************************************************************/
65
66 static uint32_t hash_rid(uint32_t rid)
67 {
68         /* 20 bits for the rid which allows us to support
69            the first 100K users/groups in a domain */
70
71         return (rid & 0x0007FFFF);
72 }
73
74 /*********************************************************************
75  ********************************************************************/
76
77 static uint32_t combine_hashes(uint32_t h_domain,
78                                uint32_t h_rid)
79 {
80         uint32_t return_id = 0;
81
82         /* shift the hash_domain 19 bits to the left and OR with the
83            hash_rid */
84
85         return_id = ((h_domain<<19) | h_rid);
86
87         return return_id;
88 }
89
90 /*********************************************************************
91  ********************************************************************/
92
93 static void separate_hashes(uint32_t id,
94                             uint32_t *h_domain,
95                             uint32_t *h_rid)
96 {
97         *h_rid = id & 0x0007FFFF;
98         *h_domain = (id & 0x7FF80000) >> 19;
99
100         return;
101 }
102
103
104 /*********************************************************************
105  ********************************************************************/
106
107 static NTSTATUS idmap_hash_initialize(struct idmap_domain *dom)
108 {
109         struct sid_hash_table *hashed_domains;
110         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
111         struct winbindd_tdc_domain *dom_list = NULL;
112         size_t num_domains = 0;
113         size_t i;
114
115         DBG_ERR("The idmap_hash module is deprecated and should not be used. "
116                 "Please migrate to a different plugin. This module will be "
117                 "removed in a future version of Samba\n");
118
119         if (!strequal(dom->name, "*")) {
120                 DBG_ERR("Error: idmap_hash configured for domain '%s'. "
121                         "But the hash module can only be used for the default "
122                         "idmap configuration.\n", dom->name);
123                 return NT_STATUS_INVALID_PARAMETER;
124         }
125
126         /* If the domain SID hash table has been initialized, assume
127            that we completed this function previously */
128
129         if (dom->private_data != NULL) {
130                 nt_status = NT_STATUS_OK;
131                 goto done;
132         }
133
134         if (!wcache_tdc_fetch_list(&dom_list, &num_domains)) {
135                 nt_status = NT_STATUS_TRUSTED_DOMAIN_FAILURE;
136                 BAIL_ON_NTSTATUS_ERROR(nt_status);
137         }
138
139         /* Create the hash table of domain SIDs */
140
141         hashed_domains = talloc_zero_array(dom, struct sid_hash_table, 4096);
142         BAIL_ON_PTR_NT_ERROR(hashed_domains, nt_status);
143
144         /* create the hash table of domain SIDs */
145
146         for (i=0; i<num_domains; i++) {
147                 struct dom_sid_buf buf;
148                 uint32_t hash;
149
150                 if (is_null_sid(&dom_list[i].sid))
151                         continue;
152
153                 /*
154                  * Check if the domain from the list is not already configured
155                  * to use another idmap backend. Not checking this makes the
156                  * idmap_hash module map IDs for *all* domains implicitly.  This
157                  * is quite dangerous in setups that use multiple idmap
158                  * configurations.
159                  */
160
161                 if (domain_has_idmap_config(dom_list[i].domain_name)) {
162                         continue;
163                 }
164
165                 if ((hash = hash_domain_sid(&dom_list[i].sid)) == 0)
166                         continue;
167
168                 DBG_INFO("Adding %s (%s) -> %d\n",
169                          dom_list[i].domain_name,
170                          dom_sid_str_buf(&dom_list[i].sid, &buf),
171                          hash);
172
173                 hashed_domains[hash].sid = talloc(hashed_domains, struct dom_sid);
174                 sid_copy(hashed_domains[hash].sid, &dom_list[i].sid);
175         }
176
177         dom->private_data = hashed_domains;
178
179 done:
180         return nt_status;
181 }
182
183 /*********************************************************************
184  ********************************************************************/
185
186 static NTSTATUS unixids_to_sids(struct idmap_domain *dom,
187                                 struct id_map **ids)
188 {
189         struct sid_hash_table *hashed_domains = talloc_get_type_abort(
190                 dom->private_data, struct sid_hash_table);
191         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
192         int i;
193
194         if (!ids) {
195                 nt_status = NT_STATUS_INVALID_PARAMETER;
196                 BAIL_ON_NTSTATUS_ERROR(nt_status);
197         }
198
199         /* initialize the status to avoid suprise */
200         for (i = 0; ids[i]; i++) {
201                 ids[i]->status = ID_UNKNOWN;
202         }
203
204         nt_status = idmap_hash_initialize(dom);
205         BAIL_ON_NTSTATUS_ERROR(nt_status);
206
207         for (i=0; ids[i]; i++) {
208                 uint32_t h_domain, h_rid;
209
210                 ids[i]->status = ID_UNMAPPED;
211
212                 separate_hashes(ids[i]->xid.id, &h_domain, &h_rid);
213
214                 /* Make sure the caller allocated memor for us */
215
216                 if (!ids[i]->sid) {
217                         nt_status = NT_STATUS_INVALID_PARAMETER;
218                         BAIL_ON_NTSTATUS_ERROR(nt_status);
219                 }
220
221                 /* If the domain hash doesn't find a SID in the table,
222                    skip it */
223
224                 if (!hashed_domains[h_domain].sid)
225                         continue;
226
227                 sid_compose(ids[i]->sid, hashed_domains[h_domain].sid, h_rid);
228                 ids[i]->status = ID_MAPPED;
229         }
230
231 done:
232         return nt_status;
233 }
234
235 /*********************************************************************
236  ********************************************************************/
237
238 static NTSTATUS sids_to_unixids(struct idmap_domain *dom,
239                                 struct id_map **ids)
240 {
241         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
242         int i;
243
244         if (!ids) {
245                 nt_status = NT_STATUS_INVALID_PARAMETER;
246                 BAIL_ON_NTSTATUS_ERROR(nt_status);
247         }
248
249         /* initialize the status to avoid suprise */
250         for (i = 0; ids[i]; i++) {
251                 ids[i]->status = ID_UNKNOWN;
252         }
253
254         nt_status = idmap_hash_initialize(dom);
255         BAIL_ON_NTSTATUS_ERROR(nt_status);
256
257         for (i=0; ids[i]; i++) {
258                 struct dom_sid sid;
259                 uint32_t rid;
260                 uint32_t h_domain, h_rid;
261
262                 ids[i]->status = ID_UNMAPPED;
263
264                 sid_copy(&sid, ids[i]->sid);
265                 sid_split_rid(&sid, &rid);
266
267                 h_domain = hash_domain_sid(&sid);
268                 h_rid = hash_rid(rid);
269
270                 /* Check that both hashes are non-zero*/
271
272                 if (h_domain && h_rid) {
273                         ids[i]->xid.id = combine_hashes(h_domain, h_rid);
274                         ids[i]->status = ID_MAPPED;
275                 }
276         }
277
278 done:
279         return nt_status;
280 }
281
282 /*********************************************************************
283  ********************************************************************/
284
285 static NTSTATUS nss_hash_init(struct nss_domain_entry *e )
286 {
287         return NT_STATUS_OK;
288 }
289
290 /**********************************************************************
291  *********************************************************************/
292
293 static NTSTATUS nss_hash_map_to_alias(TALLOC_CTX *mem_ctx,
294                                         struct nss_domain_entry *e,
295                                         const char *name,
296                                         char **alias)
297 {
298         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
299         const char *value;
300
301         value = talloc_asprintf(mem_ctx, "%s\\%s", e->domain, name);
302         BAIL_ON_PTR_NT_ERROR(value, nt_status);
303
304         nt_status = mapfile_lookup_key(mem_ctx, value, alias);
305         BAIL_ON_NTSTATUS_ERROR(nt_status);
306
307 done:
308         return nt_status;
309 }
310
311 /**********************************************************************
312  *********************************************************************/
313
314 static NTSTATUS nss_hash_map_from_alias(TALLOC_CTX *mem_ctx,
315                                           struct nss_domain_entry *e,
316                                           const char *alias,
317                                           char **name)
318 {
319         return mapfile_lookup_value(mem_ctx, alias, name);
320 }
321
322 /**********************************************************************
323  *********************************************************************/
324
325 static NTSTATUS nss_hash_close(void)
326 {
327         return NT_STATUS_OK;
328 }
329
330 /*********************************************************************
331  Dispatch Tables for IDMap and NssInfo Methods
332 ********************************************************************/
333
334 static struct idmap_methods hash_idmap_methods = {
335         .init            = idmap_hash_initialize,
336         .unixids_to_sids = unixids_to_sids,
337         .sids_to_unixids = sids_to_unixids,
338 };
339
340 static struct nss_info_methods hash_nss_methods = {
341         .init           = nss_hash_init,
342         .map_to_alias   = nss_hash_map_to_alias,
343         .map_from_alias = nss_hash_map_from_alias,
344         .close_fn       = nss_hash_close
345 };
346
347 /**********************************************************************
348  Register with the idmap and idmap_nss subsystems. We have to protect
349  against the idmap and nss_info interfaces being in a half-registered
350  state.
351  **********************************************************************/
352
353 static_decl_idmap;
354 NTSTATUS idmap_hash_init(TALLOC_CTX *ctx)
355 {
356         static NTSTATUS idmap_status = NT_STATUS_UNSUCCESSFUL;
357         static NTSTATUS nss_status = NT_STATUS_UNSUCCESSFUL;
358
359         if ( !NT_STATUS_IS_OK(idmap_status) ) {
360                 idmap_status =  smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION,
361                                                    "hash", &hash_idmap_methods);
362
363                 if ( !NT_STATUS_IS_OK(idmap_status) ) {
364                         DEBUG(0,("Failed to register hash idmap plugin.\n"));
365                         return idmap_status;
366                 }
367         }
368
369         if ( !NT_STATUS_IS_OK(nss_status) ) {
370                 nss_status = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
371                                                     "hash", &hash_nss_methods);
372                 if ( !NT_STATUS_IS_OK(nss_status) ) {
373                         DEBUG(0,("Failed to register hash idmap nss plugin.\n"));
374                         return nss_status;
375                 }
376         }
377
378         return NT_STATUS_OK;
379 }