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