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