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