s3-idmap-hash: Make "hashed_domains" properly talloced
[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 be_init(struct idmap_domain *dom,
108                         const char *params)
109 {
110         struct sid_hash_table *hashed_domains;
111         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
112         struct winbindd_tdc_domain *dom_list = NULL;
113         size_t num_domains = 0;
114         int i;
115
116         /* If the domain SID hash table has been initialized, assume
117            that we completed this function previously */
118
119         if (dom->private_data != NULL) {
120                 nt_status = NT_STATUS_OK;
121                 goto done;
122         }
123
124         if (!wcache_tdc_fetch_list(&dom_list, &num_domains)) {
125                 nt_status = NT_STATUS_TRUSTED_DOMAIN_FAILURE;
126                 BAIL_ON_NTSTATUS_ERROR(nt_status);
127         }
128
129         /* Create the hash table of domain SIDs */
130
131         hashed_domains = TALLOC_ZERO_ARRAY(dom, struct sid_hash_table, 4096);
132         BAIL_ON_PTR_NT_ERROR(hashed_domains, nt_status);
133
134         /* create the hash table of domain SIDs */
135
136         for (i=0; i<num_domains; i++) {
137                 uint32_t hash;
138
139                 if (is_null_sid(&dom_list[i].sid))
140                         continue;
141                 if ((hash = hash_domain_sid(&dom_list[i].sid)) == 0)
142                         continue;
143
144                 DEBUG(5,("hash:be_init() Adding %s (%s) -> %d\n",
145                          dom_list[i].domain_name,
146                          sid_string_dbg(&dom_list[i].sid),
147                          hash));
148
149                 hashed_domains[hash].sid = talloc(hashed_domains, struct dom_sid);
150                 sid_copy(hashed_domains[hash].sid, &dom_list[i].sid);
151         }
152
153         dom->private_data = hashed_domains;
154
155 done:
156         return nt_status;
157 }
158
159 /*********************************************************************
160  ********************************************************************/
161
162 static NTSTATUS unixids_to_sids(struct idmap_domain *dom,
163                                 struct id_map **ids)
164 {
165         struct sid_hash_table *hashed_domains = talloc_get_type_abort(
166                 dom->private_data, struct sid_hash_table);
167         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
168         int i;
169
170         /* initialize the status to avoid suprise */
171         for (i = 0; ids[i]; i++) {
172                 ids[i]->status = ID_UNKNOWN;
173         }
174
175         nt_status = be_init(dom, NULL);
176         BAIL_ON_NTSTATUS_ERROR(nt_status);
177
178         if (!ids) {
179                 nt_status = NT_STATUS_INVALID_PARAMETER;
180                 BAIL_ON_NTSTATUS_ERROR(nt_status);
181         }
182
183         for (i=0; ids[i]; i++) {
184                 uint32_t h_domain, h_rid;
185
186                 ids[i]->status = ID_UNMAPPED;
187
188                 separate_hashes(ids[i]->xid.id, &h_domain, &h_rid);
189
190                 /* Make sure the caller allocated memor for us */
191
192                 if (!ids[i]->sid) {
193                         nt_status = NT_STATUS_INVALID_PARAMETER;
194                         BAIL_ON_NTSTATUS_ERROR(nt_status);
195                 }
196
197                 /* If the domain hash doesn't find a SID in the table,
198                    skip it */
199
200                 if (!hashed_domains[h_domain].sid)
201                         continue;
202
203                 sid_compose(ids[i]->sid, hashed_domains[h_domain].sid, h_rid);
204                 ids[i]->status = ID_MAPPED;
205         }
206
207 done:
208         return nt_status;
209 }
210
211 /*********************************************************************
212  ********************************************************************/
213
214 static NTSTATUS sids_to_unixids(struct idmap_domain *dom,
215                                 struct id_map **ids)
216 {
217         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
218         int i;
219
220         /* initialize the status to avoid suprise */
221         for (i = 0; ids[i]; i++) {
222                 ids[i]->status = ID_UNKNOWN;
223         }
224
225         nt_status = be_init(dom, NULL);
226         BAIL_ON_NTSTATUS_ERROR(nt_status);
227
228         if (!ids) {
229                 nt_status = NT_STATUS_INVALID_PARAMETER;
230                 BAIL_ON_NTSTATUS_ERROR(nt_status);
231         }
232
233         for (i=0; ids[i]; i++) {
234                 struct dom_sid sid;
235                 uint32_t rid;
236                 uint32_t h_domain, h_rid;
237
238                 ids[i]->status = ID_UNMAPPED;
239
240                 sid_copy(&sid, ids[i]->sid);
241                 sid_split_rid(&sid, &rid);
242
243                 h_domain = hash_domain_sid(&sid);
244                 h_rid = hash_rid(rid);
245
246                 /* Check that both hashes are non-zero*/
247
248                 if (h_domain && h_rid) {
249                         ids[i]->xid.id = combine_hashes(h_domain, h_rid);
250                         ids[i]->status = ID_MAPPED;
251                 }
252         }
253
254 done:
255         return nt_status;
256 }
257
258 /*********************************************************************
259  ********************************************************************/
260
261 static NTSTATUS be_close(struct idmap_domain *dom)
262 {
263         return NT_STATUS_OK;
264 }
265
266 /*********************************************************************
267  ********************************************************************/
268
269 static NTSTATUS nss_hash_init(struct nss_domain_entry *e )
270 {
271         return be_init(NULL, NULL);
272 }
273
274 /**********************************************************************
275  *********************************************************************/
276
277 static NTSTATUS nss_hash_get_info(struct nss_domain_entry *e,
278                                     const struct dom_sid *sid,
279                                     TALLOC_CTX *ctx,
280                                     const char **homedir,
281                                     const char **shell,
282                                     const char **gecos,
283                                     gid_t *p_gid )
284 {
285         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
286
287         nt_status = nss_hash_init(e);
288         BAIL_ON_NTSTATUS_ERROR(nt_status);
289
290         if (!homedir || !shell || !gecos) {
291                 nt_status = NT_STATUS_INVALID_PARAMETER;
292                 BAIL_ON_NTSTATUS_ERROR(nt_status);
293         }
294
295         *homedir = talloc_strdup(ctx, lp_template_homedir());
296         BAIL_ON_PTR_NT_ERROR(*homedir, nt_status);
297
298         *shell   = talloc_strdup(ctx, lp_template_shell());
299         BAIL_ON_PTR_NT_ERROR(*shell, nt_status);
300
301         *gecos   = NULL;
302
303         /* Initialize the gid so that the upper layer fills
304            in the proper Windows primary group */
305
306         if (*p_gid) {
307                 *p_gid = (gid_t)-1;
308         }
309
310 done:
311         return nt_status;
312 }
313
314 /**********************************************************************
315  *********************************************************************/
316
317 static NTSTATUS nss_hash_map_to_alias(TALLOC_CTX *mem_ctx,
318                                         struct nss_domain_entry *e,
319                                         const char *name,
320                                         char **alias)
321 {
322         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
323         const char *value;
324
325         value = talloc_asprintf(mem_ctx, "%s\\%s", e->domain, name);
326         BAIL_ON_PTR_NT_ERROR(value, nt_status);
327
328         nt_status = mapfile_lookup_key(mem_ctx, value, alias);
329         BAIL_ON_NTSTATUS_ERROR(nt_status);
330
331 done:
332         return nt_status;
333 }
334
335 /**********************************************************************
336  *********************************************************************/
337
338 static NTSTATUS nss_hash_map_from_alias(TALLOC_CTX *mem_ctx,
339                                           struct nss_domain_entry *e,
340                                           const char *alias,
341                                           char **name)
342 {
343         return mapfile_lookup_value(mem_ctx, alias, name);
344 }
345
346 /**********************************************************************
347  *********************************************************************/
348
349 static NTSTATUS nss_hash_close(void)
350 {
351         return NT_STATUS_OK;
352 }
353
354 /*********************************************************************
355  Dispatch Tables for IDMap and NssInfo Methods
356 ********************************************************************/
357
358 static struct idmap_methods hash_idmap_methods = {
359         .init            = be_init,
360         .unixids_to_sids = unixids_to_sids,
361         .sids_to_unixids = sids_to_unixids,
362         .close_fn        = be_close
363 };
364
365 static struct nss_info_methods hash_nss_methods = {
366         .init           = nss_hash_init,
367         .get_nss_info   = nss_hash_get_info,
368         .map_to_alias   = nss_hash_map_to_alias,
369         .map_from_alias = nss_hash_map_from_alias,
370         .close_fn       = nss_hash_close
371 };
372
373 /**********************************************************************
374  Register with the idmap and idmap_nss subsystems. We have to protect
375  against the idmap and nss_info interfaces being in a half-registered
376  state.
377  **********************************************************************/
378
379 NTSTATUS idmap_hash_init(void)
380 {
381         static NTSTATUS idmap_status = NT_STATUS_UNSUCCESSFUL;
382         static NTSTATUS nss_status = NT_STATUS_UNSUCCESSFUL;
383
384         if ( !NT_STATUS_IS_OK(idmap_status) ) {
385                 idmap_status =  smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION,
386                                                    "hash", &hash_idmap_methods);
387
388                 if ( !NT_STATUS_IS_OK(idmap_status) ) {
389                         DEBUG(0,("Failed to register hash idmap plugin.\n"));
390                         return idmap_status;
391                 }
392         }
393
394         if ( !NT_STATUS_IS_OK(nss_status) ) {
395                 nss_status = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
396                                                     "hash", &hash_nss_methods);
397                 if ( !NT_STATUS_IS_OK(nss_status) ) {
398                         DEBUG(0,("Failed to register hash idmap nss plugin.\n"));
399                         return nss_status;
400                 }
401         }
402
403         return NT_STATUS_OK;
404 }