r20110: Fix interaction between paranoid malloc checker
[tprouty/samba.git] / source / sam / idmap.c
1 /* 
2    Unix SMB/CIFS implementation.
3    ID Mapping
4    Copyright (C) Tim Potter 2000
5    Copyright (C) Jim McDonough <jmcd@us.ibm.com>        2003
6    Copyright (C) Simo Sorce 2003
7    Copyright (C) Jeremy Allison 2006
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/
22
23 #include "includes.h"
24
25 #undef DBGC_CLASS
26 #define DBGC_CLASS DBGC_IDMAP
27
28 static_decl_idmap;
29
30 struct idmap_function_entry {
31         const char *name;
32         struct idmap_methods *methods;
33         struct idmap_function_entry *prev,*next;
34 };
35
36 static struct idmap_function_entry *backends = NULL;
37
38 static struct idmap_methods *cache_map;
39 static struct idmap_methods *remote_map;
40
41 static BOOL proxyonly = False;
42
43 /**********************************************************************
44  Get idmap methods. Don't allow tdb to be a remote method.
45 **********************************************************************/
46
47 static struct idmap_methods *get_methods(const char *name, BOOL cache_method)
48 {
49         struct idmap_function_entry *entry = backends;
50
51         for(entry = backends; entry; entry = entry->next) {
52                 if (!cache_method && strequal(entry->name, "tdb"))
53                         continue; /* tdb is only cache method. */
54                 if (strequal(entry->name, name))
55                         return entry->methods;
56         }
57
58         return NULL;
59 }
60
61 /**********************************************************************
62  Allow a module to register itself as a method.
63 **********************************************************************/
64
65 NTSTATUS smb_register_idmap(int version, const char *name, struct idmap_methods *methods)
66 {
67         struct idmap_function_entry *entry;
68
69         if ((version != SMB_IDMAP_INTERFACE_VERSION)) {
70                 DEBUG(0, ("smb_register_idmap: Failed to register idmap module.\n"
71                           "The module was compiled against SMB_IDMAP_INTERFACE_VERSION %d,\n"
72                           "current SMB_IDMAP_INTERFACE_VERSION is %d.\n"
73                           "Please recompile against the current version of samba!\n",  
74                           version, SMB_IDMAP_INTERFACE_VERSION));
75                 return NT_STATUS_OBJECT_TYPE_MISMATCH;
76         }
77
78         if (!name || !name[0] || !methods) {
79                 DEBUG(0,("smb_register_idmap: called with NULL pointer or empty name!\n"));
80                 return NT_STATUS_INVALID_PARAMETER;
81         }
82
83         if (get_methods(name, False)) {
84                 DEBUG(0,("smb_register_idmap: idmap module %s already registered!\n", name));
85                 return NT_STATUS_OBJECT_NAME_COLLISION;
86         }
87
88         entry = SMB_XMALLOC_P(struct idmap_function_entry);
89         entry->name = smb_xstrdup(name);
90         entry->methods = methods;
91
92         DLIST_ADD(backends, entry);
93         DEBUG(5, ("smb_register_idmap: Successfully added idmap backend '%s'\n", name));
94         return NT_STATUS_OK;
95 }
96
97 /**********************************************************************
98  Initialise idmap cache and a remote backend (if configured).
99 **********************************************************************/
100
101 BOOL idmap_init(const char **remote_backend)
102 {
103         if (!backends)
104                 static_init_idmap;
105
106         if (!cache_map) {
107                 cache_map = get_methods("tdb", True);
108
109                 if (!cache_map) {
110                         DEBUG(0, ("idmap_init: could not find tdb cache backend!\n"));
111                         return False;
112                 }
113                 
114                 if (!NT_STATUS_IS_OK(cache_map->init( NULL ))) {
115                         DEBUG(0, ("idmap_init: could not initialise tdb cache backend!\n"));
116                         return False;
117                 }
118         }
119         
120         if ((remote_map == NULL) && (remote_backend != NULL) &&
121             (*remote_backend != NULL) && (**remote_backend != '\0'))  {
122                 char *rem_backend = smb_xstrdup(*remote_backend);
123                 fstring params = "";
124                 char *pparams;
125                 BOOL idmap_prefix_workaround = False;
126                 
127                 /* get any mode parameters passed in */
128                 
129                 if ( (pparams = strchr( rem_backend, ':' )) != NULL ) {
130                         *pparams = '\0';
131                         pparams++;
132                         fstrcpy( params, pparams );
133                 }
134
135                 /* strip any leading idmap_ prefix of */
136                 if ( strncmp( rem_backend, "idmap_", 6) == 0 ) {
137                         rem_backend += 6;
138                         idmap_prefix_workaround = True;
139                         DEBUG(0, ("idmap_init: idmap backend uses deprecated 'idmap_' prefix.  Please replace 'idmap_%s' by '%s' in %s\n", rem_backend, rem_backend, dyn_CONFIGFILE));
140                 }
141                 
142                 DEBUG(3, ("idmap_init: using '%s' as remote backend\n", rem_backend));
143                 
144                 if((remote_map = get_methods(rem_backend, False)) ||
145                     (NT_STATUS_IS_OK(smb_probe_module("idmap", rem_backend)) && 
146                     (remote_map = get_methods(rem_backend, False)))) {
147                         if (!NT_STATUS_IS_OK(remote_map->init(params))) {
148                                 DEBUG(0, ("idmap_init: failed to initialize remote backend!\n"));
149                                 return False;
150                         }
151                 } else {
152                         DEBUG(0, ("idmap_init: could not load remote backend '%s'\n", rem_backend));
153                         if (idmap_prefix_workaround)
154                                 rem_backend -= 6;
155                         SAFE_FREE(rem_backend);
156                         return False;
157                 }
158                 if (idmap_prefix_workaround)
159                         rem_backend -= 6;
160                 SAFE_FREE(rem_backend);
161         }
162
163         return True;
164 }
165
166 /**************************************************************************
167  Don't do id mapping. This is used to make winbind a netlogon proxy only.
168 **************************************************************************/
169
170 void idmap_set_proxyonly(void)
171 {
172         proxyonly = True;
173 }
174
175 BOOL idmap_proxyonly(void)
176 {
177         return proxyonly;
178 }
179
180 /**************************************************************************
181  This is a rare operation, designed to allow an explicit mapping to be
182  set up for a sid to a POSIX id.
183 **************************************************************************/
184
185 NTSTATUS idmap_set_mapping(const DOM_SID *sid, unid_t id, enum idmap_type id_type)
186 {
187         struct idmap_methods *map = remote_map;
188         DOM_SID tmp_sid;
189
190         if (proxyonly) {
191                 return NT_STATUS_UNSUCCESSFUL;
192         }
193
194         if (sid_check_is_in_our_domain(sid)) {
195                 DEBUG(3, ("Refusing to add SID %s to idmap, it's our own "
196                           "domain\n", sid_string_static(sid)));
197                 return NT_STATUS_ACCESS_DENIED;
198         }
199                 
200         if (sid_check_is_in_builtin(sid)) {
201                 DEBUG(3, ("Refusing to add SID %s to idmap, it's our builtin "
202                           "domain\n", sid_string_static(sid)));
203                 return NT_STATUS_ACCESS_DENIED;
204         }
205
206         DEBUG(10, ("idmap_set_mapping: Set %s to %s %lu\n",
207                    sid_string_static(sid),
208                    (id_type == ID_USERID) ? "UID" : "GID",
209                    (id_type == ID_USERID) ? (unsigned long)id.uid : 
210                    (unsigned long)id.gid));
211
212         if ( (NT_STATUS_IS_OK(cache_map->get_sid_from_id(&tmp_sid, id, id_type, IDMAP_FLAG_QUERY_ONLY))) &&
213                                 sid_equal(sid, &tmp_sid) ) {
214                 /* Nothing to do, we already have that mapping */
215                 DEBUG(10, ("idmap_set_mapping: Mapping already there\n"));
216                 return NT_STATUS_OK;
217         }
218
219         if (map == NULL) {
220                 /* Ok, we don't have a authoritative remote
221                         mapping. So update our local cache only. */
222                 map = cache_map;
223         }
224
225         return map->set_mapping(sid, id, id_type);
226 }
227
228 /**************************************************************************
229  Get ID from SID. This can create a mapping for a SID to a POSIX id.
230 **************************************************************************/
231
232 NTSTATUS idmap_get_id_from_sid(unid_t *id, enum idmap_type *id_type, const DOM_SID *sid, int flags)
233 {
234         NTSTATUS ret;
235         int cache_flags = flags;
236
237         if (proxyonly) {
238                 return NT_STATUS_UNSUCCESSFUL;
239         }
240
241         if (sid_check_is_in_our_domain(sid)) {
242                 DEBUG(9, ("sid %s is in our domain -- go look in passdb\n",
243                           sid_string_static(sid)));
244                 return NT_STATUS_NONE_MAPPED;
245         }
246
247         if (sid_check_is_in_builtin(sid)) {
248                 DEBUG(9, ("sid %s is in builtin domain -- go look in passdb\n",
249                           sid_string_static(sid)));
250                 return NT_STATUS_NONE_MAPPED;
251         }
252
253         if (remote_map) {
254                 /* We have a central remote idmap so only look in
255                    cache, ensure we don't allocate */
256                 cache_flags |= IDMAP_FLAG_QUERY_ONLY;
257         }
258
259         ret = cache_map->get_id_from_sid(id, id_type, sid, cache_flags);
260         if (NT_STATUS_IS_OK(ret)) {
261                 return NT_STATUS_OK;
262         }
263
264         if ((remote_map == NULL) || (flags & IDMAP_FLAG_CACHE_ONLY)) {
265                 return ret;
266         }
267
268         /* Ok, the mapping was not in the cache, give the remote map a try. */
269
270         ret = remote_map->get_id_from_sid(id, id_type, sid, flags);
271         
272         if (NT_STATUS_IS_OK(ret)) {
273                 /* The remote backend gave us a valid mapping, cache it. */
274                 ret = cache_map->set_mapping(sid, *id, *id_type);
275         }
276
277         return ret;
278 }
279
280 /**************************************************************************
281  Get SID from ID. This must have been created before.
282 **************************************************************************/
283
284 NTSTATUS idmap_get_sid_from_id(DOM_SID *sid, unid_t id, enum idmap_type id_type, int flags)
285 {
286         NTSTATUS ret;
287         int cache_flags = flags;
288
289         if (proxyonly) {
290                 return NT_STATUS_UNSUCCESSFUL;
291         }
292
293         if (remote_map) {
294                 /* We have a central remote idmap so only look in
295                    cache, ensure we don't allocate */
296                 cache_flags |= IDMAP_FLAG_QUERY_ONLY;
297         }
298
299         ret = cache_map->get_sid_from_id(sid, id, id_type, cache_flags);
300
301         if (NT_STATUS_IS_OK(ret)) {
302                 return ret;
303         }
304
305         if ((remote_map == NULL) || (flags & IDMAP_FLAG_CACHE_ONLY)) {
306                 return ret;
307         }
308
309         /* Not in cache, ask our authoritative backend */
310
311         ret = remote_map->get_sid_from_id(sid, id, id_type, flags);
312
313         if (NT_STATUS_IS_OK(ret)) {
314                 /* The remote backend gave us a valid mapping, cache it. */
315                 ret = cache_map->set_mapping(sid, id, id_type);
316         }
317
318         return ret;
319 }
320
321 /**************************************************************************
322  Alloocate a new UNIX uid/gid
323 **************************************************************************/
324
325 NTSTATUS idmap_allocate_id(unid_t *id, enum idmap_type id_type)
326 {
327         /* we have to allocate from the authoritative backend */
328         
329         if (proxyonly) {
330                 return NT_STATUS_UNSUCCESSFUL;
331         }
332
333         if ( remote_map ) {
334                 return remote_map->allocate_id( id, id_type );
335         }
336
337         return cache_map->allocate_id( id, id_type );
338 }
339
340 /**************************************************************************
341  Shutdown maps.
342 **************************************************************************/
343
344 NTSTATUS idmap_close(void)
345 {
346         NTSTATUS ret;
347
348         if (proxyonly) {
349                 return NT_STATUS_OK;
350         }
351
352         ret = cache_map->close_fn();
353         if (!NT_STATUS_IS_OK(ret)) {
354                 DEBUG(3, ("idmap_close: failed to close local tdb cache!\n"));
355         }
356         cache_map = NULL;
357
358         if (remote_map) {
359                 ret = remote_map->close_fn();
360                 if (!NT_STATUS_IS_OK(ret)) {
361                         DEBUG(3, ("idmap_close: failed to close remote idmap repository!\n"));
362                 }
363                 remote_map = NULL;
364         }
365
366         return ret;
367 }
368
369 /**************************************************************************
370  Dump backend status.
371 **************************************************************************/
372
373 void idmap_status(void)
374 {
375         cache_map->status();
376         if (remote_map) {
377                 remote_map->status();
378         }
379 }