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