r20110: Fix interaction between paranoid malloc checker
[samba.git] / source / sam / idmap_smbldap.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    idmap LDAP backend
5
6    Copyright (C) Tim Potter             2000
7    Copyright (C) Jim McDonough <jmcd@us.ibm.com>        2003
8    Copyright (C) Simo Sorce             2003
9    Copyright (C) Gerald Carter          2003
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26 #include "includes.h"
27
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_IDMAP
30
31 struct ldap_connection *ldap_conn = NULL;
32
33 /* number tries while allocating new id */
34 #define LDAP_MAX_ALLOC_ID 128
35
36
37 /***********************************************************************
38  This function cannot be called to modify a mapping, only set a new one
39 ***********************************************************************/
40
41 static NTSTATUS ldap_set_mapping(const DOM_SID *sid, unid_t id, int id_type)
42 {
43         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
44         pstring id_str;
45         const char *type;
46         fstring sid_string;
47         struct ldap_message *msg;
48         struct ldap_message *mod_res = NULL;
49         char *mod;
50
51         type = (id_type & ID_USERID) ? "uidNumber" : "gidNumber";
52
53         sid_to_string( sid_string, sid );
54
55         pstr_sprintf(id_str, "%lu",
56                      ((id_type & ID_USERID) ?
57                       (unsigned long)id.uid : (unsigned long)id.gid));
58
59         asprintf(&mod,
60                  "dn: sambaSID=%s,%s\n"
61                  "changetype: add\n"
62                  "objectClass: sambaIdmapEntry\n"
63                  "objectClass: sambaSidEntry\n"
64                  "sambaSID: %s\n"
65                  "%s: %lu\n",
66                  sid_string, lp_ldap_idmap_suffix(), sid_string, type,
67                  ((id_type & ID_USERID) ?
68                   (unsigned long)id.uid : (unsigned long)id.gid));
69
70         msg = ldap_ldif2msg(mod);
71
72         SAFE_FREE(mod);
73
74         if (msg == NULL)
75                 return NT_STATUS_NO_MEMORY;
76
77         mod_res = ldap_transaction(ldap_conn, msg);
78
79         if ((mod_res == NULL) || (mod_res->r.ModifyResponse.resultcode != 0))
80                 goto out;
81
82         ret = NT_STATUS_OK;
83  out:
84         destroy_ldap_message(msg);
85         destroy_ldap_message(mod_res);
86         return ret;
87 }
88
89 /*****************************************************************************
90  Allocate a new uid or gid
91 *****************************************************************************/
92
93 static NTSTATUS ldap_allocate_id(unid_t *id, enum idmap_type id_type)
94 {
95         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
96         uid_t   luid, huid;
97         gid_t   lgid, hgid;
98         const char *attrs[] = { "uidNumber", "gidNumber" };
99         struct ldap_message *idpool_s = NULL;
100         struct ldap_message *idpool = NULL;
101         struct ldap_message *mod_msg = NULL;
102         struct ldap_message *mod_res = NULL;
103         int value;
104         const char *id_attrib;
105         char *mod;
106
107         if (id_type != ID_USERID && id_type != ID_GROUPID) {
108                 return NT_STATUS_INVALID_PARAMETER;
109         }
110
111         id_attrib = (id_type == ID_USERID) ? "uidNumber" : "gidNumber";
112
113         idpool_s = new_ldap_search_message(lp_ldap_suffix(),
114                                            LDAP_SEARCH_SCOPE_SUB,
115                                            "(objectclass=sambaUnixIdPool)",
116                                            2, attrs);
117
118         if (idpool_s == NULL)
119                 return NT_STATUS_NO_MEMORY;
120
121         idpool = ldap_searchone(ldap_conn, idpool_s, NULL);
122
123         if (idpool == NULL)
124                 goto out;
125
126         if (!ldap_find_single_int(idpool, id_attrib, &value))
127                 goto out;
128
129         /* this must succeed or else we wouldn't have initialized */
130                 
131         lp_idmap_uid( &luid, &huid);
132         lp_idmap_gid( &lgid, &hgid);
133         
134         /* make sure we still have room to grow */
135         
136         if (id_type == ID_USERID) {
137                 id->uid = value;
138                 if (id->uid > huid ) {
139                         DEBUG(0,("ldap_allocate_id: Cannot allocate uid "
140                                  "above %lu!\n",  (unsigned long)huid));
141                         goto out;
142                 }
143         }
144         else { 
145                 id->gid = value;
146                 if (id->gid > hgid ) {
147                         DEBUG(0,("ldap_allocate_id: Cannot allocate gid "
148                                  "above %lu!\n", (unsigned long)hgid));
149                         goto out;
150                 }
151         }
152         
153         asprintf(&mod,
154                  "dn: %s\n"
155                  "changetype: modify\n"
156                  "delete: %s\n"
157                  "%s: %d\n"
158                  "-\n"
159                  "add: %s\n"
160                  "%s: %d\n",
161                  idpool->r.SearchResultEntry.dn, id_attrib, id_attrib, value,
162                  id_attrib, id_attrib, value+1);
163
164         mod_msg = ldap_ldif2msg(mod);
165
166         SAFE_FREE(mod);
167
168         if (mod_msg == NULL)
169                 goto out;
170
171         mod_res = ldap_transaction(ldap_conn, mod_msg);
172
173         if ((mod_res == NULL) || (mod_res->r.ModifyResponse.resultcode != 0))
174                 goto out;
175
176         ret = NT_STATUS_OK;
177 out:
178         destroy_ldap_message(idpool_s);
179         destroy_ldap_message(idpool);
180         destroy_ldap_message(mod_msg);
181         destroy_ldap_message(mod_res);
182
183         return ret;
184 }
185
186 /*****************************************************************************
187  get a sid from an id
188 *****************************************************************************/
189
190 static NTSTATUS ldap_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
191 {
192         pstring filter;
193         const char *type;
194         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
195         const char *attr_list[] = { "sambaSID" };
196         struct ldap_message *msg;
197         struct ldap_message *entry = NULL;
198         char *sid_str;
199
200         type = (id_type & ID_USERID) ? "uidNumber" : "gidNumber";
201
202         pstr_sprintf(filter, "(&(objectClass=%s)(%s=%lu))", "sambaIdmapEntry",
203                      type,
204                      ((id_type & ID_USERID) ?
205                       (unsigned long)id.uid : (unsigned long)id.gid));
206
207         msg = new_ldap_search_message(lp_ldap_idmap_suffix(),
208                                       LDAP_SEARCH_SCOPE_SUB,
209                                       filter, 1, attr_list);
210
211         if (msg == NULL)
212                 return NT_STATUS_NO_MEMORY;
213
214         entry = ldap_searchone(ldap_conn, msg, NULL);
215
216         if (entry == NULL)
217                 goto out;
218
219         if (!ldap_find_single_string(entry, "sambaSID", entry->mem_ctx,
220                                      &sid_str))
221                 goto out;
222
223         if (!string_to_sid(sid, sid_str))
224                 goto out;
225
226         ret = NT_STATUS_OK;
227 out:
228         destroy_ldap_message(msg);
229         destroy_ldap_message(entry);
230
231         return ret;
232 }
233
234 /***********************************************************************
235  Get an id from a sid 
236 ***********************************************************************/
237
238 static NTSTATUS ldap_get_id_from_sid(unid_t *id, int *id_type,
239                                      const DOM_SID *sid)
240 {
241         pstring filter;
242         const char *type;
243         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
244         struct ldap_message *msg;
245         struct ldap_message *entry = NULL;
246         int i;
247
248         DEBUG(8,("ldap_get_id_from_sid: %s (%s)\n", sid_string_static(sid),
249                 (*id_type & ID_GROUPID ? "group" : "user") ));
250
251         type = ((*id_type) & ID_USERID) ? "uidNumber" : "gidNumber";
252
253         pstr_sprintf(filter, "(&(objectClass=%s)(%s=%s))", 
254                      "sambaIdmapEntry", "sambaSID", sid_string_static(sid));
255
256         msg = new_ldap_search_message(lp_ldap_idmap_suffix(),
257                                       LDAP_SEARCH_SCOPE_SUB,
258                                       filter, 1, &type);
259
260         if (msg == NULL)
261                 return NT_STATUS_NO_MEMORY;
262
263         entry = ldap_searchone(ldap_conn, msg, NULL);
264
265         if (entry != NULL) {
266                 int value;
267
268                 if (!ldap_find_single_int(entry, type, &value))
269                         goto out;
270
271                 if ((*id_type) & ID_USERID)
272                         id->uid = value;
273                 else
274                         id->gid = value;
275
276                 ret = NT_STATUS_OK;
277                 goto out;
278         }
279
280         if ((*id_type) & ID_QUERY_ONLY)
281                 goto out;
282
283         /* Allocate a new RID */
284
285         for (i = 0; i < LDAP_MAX_ALLOC_ID; i++) {
286                 ret = ldap_allocate_id(id, *id_type);
287                 if ( NT_STATUS_IS_OK(ret) )
288                         break;
289         }
290                 
291         if ( !NT_STATUS_IS_OK(ret) ) {
292                 DEBUG(0,("Could not allocate id\n"));
293                 goto out;
294         }
295
296         DEBUG(10,("ldap_get_id_from_sid: Allocated new %cid [%ul]\n",
297                   (*id_type & ID_GROUPID ? 'g' : 'u'), (uint32)id->uid ));
298
299         ret = ldap_set_mapping(sid, *id, *id_type);
300
301 out:
302         destroy_ldap_message(msg);
303         destroy_ldap_message(entry);
304
305         return ret;
306 }
307
308 /**********************************************************************
309  Verify the sambaUnixIdPool entry in the directory.  
310 **********************************************************************/
311 static NTSTATUS verify_idpool(void)
312 {
313         const char *attr_list[3] = { "uidnumber", "gidnumber", "objectclass" };
314         BOOL result;
315         char *mod;
316         struct ldap_message *msg, *entry, *res;
317
318         uid_t   luid, huid;
319         gid_t   lgid, hgid;
320
321         msg = new_ldap_search_message(lp_ldap_suffix(),
322                                       LDAP_SEARCH_SCOPE_SUB,
323                                       "(objectClass=sambaUnixIdPool)",
324                                       3, attr_list);
325
326         if (msg == NULL)
327                 return NT_STATUS_NO_MEMORY;
328
329         entry = ldap_searchone(ldap_conn, msg, NULL);
330
331         result = (entry != NULL);
332
333         destroy_ldap_message(msg);
334         destroy_ldap_message(entry);
335
336         if (result)
337                 return NT_STATUS_OK;
338
339         if ( !lp_idmap_uid(&luid, &huid) || !lp_idmap_gid( &lgid, &hgid ) ) {
340                 DEBUG(3,("ldap_idmap_init: idmap uid/gid parameters not "
341                          "specified\n"));
342                 return NT_STATUS_UNSUCCESSFUL;
343         }
344
345         asprintf(&mod,
346                  "dn: %s\n"
347                  "changetype: modify\n"
348                  "add: objectClass\n"
349                  "objectClass: sambaUnixIdPool\n"
350                  "-\n"
351                  "add: uidNumber\n"
352                  "uidNumber: %lu\n"
353                  "-\n"
354                  "add: gidNumber\n"
355                  "gidNumber: %lu\n",
356                  lp_ldap_idmap_suffix(),
357                  (unsigned long)luid, (unsigned long)lgid);
358                  
359         msg = ldap_ldif2msg(mod);
360
361         SAFE_FREE(mod);
362
363         if (msg == NULL)
364                 return NT_STATUS_NO_MEMORY;
365
366         res = ldap_transaction(ldap_conn, msg);
367
368         if ((res == NULL) || (res->r.ModifyResponse.resultcode != 0)) {
369                 destroy_ldap_message(msg);
370                 destroy_ldap_message(res);
371                 DEBUG(5, ("Could not add sambaUnixIdPool\n"));
372                 return NT_STATUS_UNSUCCESSFUL;
373         }
374
375         destroy_ldap_message(msg);
376         destroy_ldap_message(res);
377         return NT_STATUS_OK;
378 }
379
380 /*****************************************************************************
381  Initialise idmap database. 
382 *****************************************************************************/
383
384 static NTSTATUS ldap_idmap_init( char *params )
385 {
386         NTSTATUS nt_status;
387         char *dn, *pw;
388
389         ldap_conn = new_ldap_connection();
390
391         if (!fetch_ldap_pw(&dn, &pw))
392                 return NT_STATUS_UNSUCCESSFUL;
393
394         ldap_conn->auth_dn = talloc_strdup(ldap_conn->mem_ctx, dn);
395         ldap_conn->simple_pw = talloc_strdup(ldap_conn->mem_ctx, pw);
396
397         SAFE_FREE(dn);
398         SAFE_FREE(pw);
399
400         if (!ldap_setup_connection(ldap_conn, params, NULL, NULL))
401                 return NT_STATUS_UNSUCCESSFUL;
402
403         /* see if the idmap suffix and sub entries exists */
404         
405         nt_status = verify_idpool();    
406         if ( !NT_STATUS_IS_OK(nt_status) )
407                 return nt_status;
408                 
409         return NT_STATUS_OK;
410 }
411
412 /*****************************************************************************
413  End the LDAP session
414 *****************************************************************************/
415
416 static NTSTATUS ldap_idmap_close(void)
417 {
418
419         DEBUG(5,("The connection to the LDAP server was closed\n"));
420         /* maybe free the results here --metze */
421         
422         return NT_STATUS_OK;
423 }
424
425
426 /* This function doesn't make as much sense in an LDAP world since the calling
427    node doesn't really control the ID ranges */
428 static void ldap_idmap_status(void)
429 {
430         DEBUG(0, ("LDAP IDMAP Status not available\n"));
431 }
432
433 static struct idmap_methods ldap_methods = {
434         ldap_idmap_init,
435         ldap_allocate_id,
436         ldap_get_sid_from_id,
437         ldap_get_id_from_sid,
438         ldap_set_mapping,
439         ldap_idmap_close,
440         ldap_idmap_status
441
442 };
443
444 NTSTATUS idmap_smbldap_init(void)
445 {
446         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "smbldap", &ldap_methods);
447 }