Initial import
[samba] / 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 RID
91 *****************************************************************************/
92
93 static NTSTATUS ldap_allocate_rid(uint32 *rid, int rid_type)
94 {
95         return NT_STATUS_UNSUCCESSFUL;
96 }
97
98 /*****************************************************************************
99  Allocate a new uid or gid
100 *****************************************************************************/
101
102 static NTSTATUS ldap_allocate_id(unid_t *id, int id_type)
103 {
104         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
105         uid_t   luid, huid;
106         gid_t   lgid, hgid;
107         const char *attrs[] = { "uidNumber", "gidNumber" };
108         struct ldap_message *idpool_s = NULL;
109         struct ldap_message *idpool = NULL;
110         struct ldap_message *mod_msg = NULL;
111         struct ldap_message *mod_res = NULL;
112         int value;
113         const char *id_attrib;
114         char *mod;
115
116         id_attrib = (id_type & ID_USERID) ? "uidNumber" : "gidNumber";
117
118         idpool_s = new_ldap_search_message(lp_ldap_suffix(),
119                                            LDAP_SEARCH_SCOPE_SUB,
120                                            "(objectclass=sambaUnixIdPool)",
121                                            2, attrs);
122
123         if (idpool_s == NULL)
124                 return NT_STATUS_NO_MEMORY;
125
126         idpool = ldap_searchone(ldap_conn, idpool_s, NULL);
127
128         if (idpool == NULL)
129                 goto out;
130
131         if (!ldap_find_single_int(idpool, id_attrib, &value))
132                 goto out;
133
134         /* this must succeed or else we wouldn't have initialized */
135                 
136         lp_idmap_uid( &luid, &huid);
137         lp_idmap_gid( &lgid, &hgid);
138         
139         /* make sure we still have room to grow */
140         
141         if (id_type & ID_USERID) {
142                 id->uid = value;
143                 if (id->uid > huid ) {
144                         DEBUG(0,("ldap_allocate_id: Cannot allocate uid "
145                                  "above %lu!\n",  (unsigned long)huid));
146                         goto out;
147                 }
148         }
149         else { 
150                 id->gid = value;
151                 if (id->gid > hgid ) {
152                         DEBUG(0,("ldap_allocate_id: Cannot allocate gid "
153                                  "above %lu!\n", (unsigned long)hgid));
154                         goto out;
155                 }
156         }
157         
158         asprintf(&mod,
159                  "dn: %s\n"
160                  "changetype: modify\n"
161                  "delete: %s\n"
162                  "%s: %d\n"
163                  "-\n"
164                  "add: %s\n"
165                  "%s: %d\n",
166                  idpool->r.SearchResultEntry.dn, id_attrib, id_attrib, value,
167                  id_attrib, id_attrib, value+1);
168
169         mod_msg = ldap_ldif2msg(mod);
170
171         SAFE_FREE(mod);
172
173         if (mod_msg == NULL)
174                 goto out;
175
176         mod_res = ldap_transaction(ldap_conn, mod_msg);
177
178         if ((mod_res == NULL) || (mod_res->r.ModifyResponse.resultcode != 0))
179                 goto out;
180
181         ret = NT_STATUS_OK;
182 out:
183         destroy_ldap_message(idpool_s);
184         destroy_ldap_message(idpool);
185         destroy_ldap_message(mod_msg);
186         destroy_ldap_message(mod_res);
187
188         return ret;
189 }
190
191 /*****************************************************************************
192  get a sid from an id
193 *****************************************************************************/
194
195 static NTSTATUS ldap_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
196 {
197         pstring filter;
198         const char *type;
199         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
200         const char *attr_list[] = { "sambaSID" };
201         struct ldap_message *msg;
202         struct ldap_message *entry = NULL;
203         char *sid_str;
204
205         type = (id_type & ID_USERID) ? "uidNumber" : "gidNumber";
206
207         pstr_sprintf(filter, "(&(objectClass=%s)(%s=%lu))", "sambaIdmapEntry",
208                      type,
209                      ((id_type & ID_USERID) ?
210                       (unsigned long)id.uid : (unsigned long)id.gid));
211
212         msg = new_ldap_search_message(lp_ldap_idmap_suffix(),
213                                       LDAP_SEARCH_SCOPE_SUB,
214                                       filter, 1, attr_list);
215
216         if (msg == NULL)
217                 return NT_STATUS_NO_MEMORY;
218
219         entry = ldap_searchone(ldap_conn, msg, NULL);
220
221         if (entry == NULL)
222                 goto out;
223
224         if (!ldap_find_single_string(entry, "sambaSID", entry->mem_ctx,
225                                      &sid_str))
226                 goto out;
227
228         if (!string_to_sid(sid, sid_str))
229                 goto out;
230
231         ret = NT_STATUS_OK;
232 out:
233         destroy_ldap_message(msg);
234         destroy_ldap_message(entry);
235
236         return ret;
237 }
238
239 /***********************************************************************
240  Get an id from a sid 
241 ***********************************************************************/
242
243 static NTSTATUS ldap_get_id_from_sid(unid_t *id, int *id_type,
244                                      const DOM_SID *sid)
245 {
246         pstring filter;
247         const char *type;
248         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
249         struct ldap_message *msg;
250         struct ldap_message *entry = NULL;
251         int i;
252
253         DEBUG(8,("ldap_get_id_from_sid: %s (%s)\n", sid_string_static(sid),
254                 (*id_type & ID_GROUPID ? "group" : "user") ));
255
256         type = ((*id_type) & ID_USERID) ? "uidNumber" : "gidNumber";
257
258         pstr_sprintf(filter, "(&(objectClass=%s)(%s=%s))", 
259                      "sambaIdmapEntry", "sambaSID", sid_string_static(sid));
260
261         msg = new_ldap_search_message(lp_ldap_idmap_suffix(),
262                                       LDAP_SEARCH_SCOPE_SUB,
263                                       filter, 1, &type);
264
265         if (msg == NULL)
266                 return NT_STATUS_NO_MEMORY;
267
268         entry = ldap_searchone(ldap_conn, msg, NULL);
269
270         if (entry != NULL) {
271                 int value;
272
273                 if (!ldap_find_single_int(entry, type, &value))
274                         goto out;
275
276                 if ((*id_type) & ID_USERID)
277                         id->uid = value;
278                 else
279                         id->gid = value;
280
281                 ret = NT_STATUS_OK;
282                 goto out;
283         }
284
285         if ((*id_type) & ID_QUERY_ONLY)
286                 goto out;
287
288         /* Allocate a new RID */
289
290         for (i = 0; i < LDAP_MAX_ALLOC_ID; i++) {
291                 ret = ldap_allocate_id(id, *id_type);
292                 if ( NT_STATUS_IS_OK(ret) )
293                         break;
294         }
295                 
296         if ( !NT_STATUS_IS_OK(ret) ) {
297                 DEBUG(0,("Could not allocate id\n"));
298                 goto out;
299         }
300
301         DEBUG(10,("ldap_get_id_from_sid: Allocated new %cid [%ul]\n",
302                   (*id_type & ID_GROUPID ? 'g' : 'u'), (uint32)id->uid ));
303
304         ret = ldap_set_mapping(sid, *id, *id_type);
305
306 out:
307         destroy_ldap_message(msg);
308         destroy_ldap_message(entry);
309
310         return ret;
311 }
312
313 /**********************************************************************
314  Verify the sambaUnixIdPool entry in the directory.  
315 **********************************************************************/
316 static NTSTATUS verify_idpool(void)
317 {
318         const char *attr_list[3] = { "uidnumber", "gidnumber", "objectclass" };
319         BOOL result;
320         char *mod;
321         struct ldap_message *msg, *entry, *res;
322
323         uid_t   luid, huid;
324         gid_t   lgid, hgid;
325
326         msg = new_ldap_search_message(lp_ldap_suffix(),
327                                       LDAP_SEARCH_SCOPE_SUB,
328                                       "(objectClass=sambaUnixIdPool)",
329                                       3, attr_list);
330
331         if (msg == NULL)
332                 return NT_STATUS_NO_MEMORY;
333
334         entry = ldap_searchone(ldap_conn, msg, NULL);
335
336         result = (entry != NULL);
337
338         destroy_ldap_message(msg);
339         destroy_ldap_message(entry);
340
341         if (result)
342                 return NT_STATUS_OK;
343
344         if ( !lp_idmap_uid(&luid, &huid) || !lp_idmap_gid( &lgid, &hgid ) ) {
345                 DEBUG(3,("ldap_idmap_init: idmap uid/gid parameters not "
346                          "specified\n"));
347                 return NT_STATUS_UNSUCCESSFUL;
348         }
349
350         asprintf(&mod,
351                  "dn: %s\n"
352                  "changetype: modify\n"
353                  "add: objectClass\n"
354                  "objectClass: sambaUnixIdPool\n"
355                  "-\n"
356                  "add: uidNumber\n"
357                  "uidNumber: %lu\n"
358                  "-\n"
359                  "add: gidNumber\n"
360                  "gidNumber: %lu\n",
361                  lp_ldap_idmap_suffix(),
362                  (unsigned long)luid, (unsigned long)lgid);
363                  
364         msg = ldap_ldif2msg(mod);
365
366         SAFE_FREE(mod);
367
368         if (msg == NULL)
369                 return NT_STATUS_NO_MEMORY;
370
371         res = ldap_transaction(ldap_conn, msg);
372
373         if ((res == NULL) || (res->r.ModifyResponse.resultcode != 0)) {
374                 destroy_ldap_message(msg);
375                 destroy_ldap_message(res);
376                 DEBUG(5, ("Could not add sambaUnixIdPool\n"));
377                 return NT_STATUS_UNSUCCESSFUL;
378         }
379
380         destroy_ldap_message(msg);
381         destroy_ldap_message(res);
382         return NT_STATUS_OK;
383 }
384
385 /*****************************************************************************
386  Initialise idmap database. 
387 *****************************************************************************/
388
389 static NTSTATUS ldap_idmap_init( char *params )
390 {
391         NTSTATUS nt_status;
392         char *dn, *pw;
393
394         ldap_conn = new_ldap_connection();
395
396         if (!fetch_ldap_pw(&dn, &pw))
397                 return NT_STATUS_UNSUCCESSFUL;
398
399         ldap_conn->auth_dn = talloc_strdup(ldap_conn->mem_ctx, dn);
400         ldap_conn->simple_pw = talloc_strdup(ldap_conn->mem_ctx, pw);
401
402         SAFE_FREE(dn);
403         SAFE_FREE(pw);
404
405         if (!ldap_setup_connection(ldap_conn, params, NULL, NULL))
406                 return NT_STATUS_UNSUCCESSFUL;
407
408         /* see if the idmap suffix and sub entries exists */
409         
410         nt_status = verify_idpool();    
411         if ( !NT_STATUS_IS_OK(nt_status) )
412                 return nt_status;
413                 
414         return NT_STATUS_OK;
415 }
416
417 /*****************************************************************************
418  End the LDAP session
419 *****************************************************************************/
420
421 static NTSTATUS ldap_idmap_close(void)
422 {
423
424         DEBUG(5,("The connection to the LDAP server was closed\n"));
425         /* maybe free the results here --metze */
426         
427         return NT_STATUS_OK;
428 }
429
430
431 /* This function doesn't make as much sense in an LDAP world since the calling
432    node doesn't really control the ID ranges */
433 static void ldap_idmap_status(void)
434 {
435         DEBUG(0, ("LDAP IDMAP Status not available\n"));
436 }
437
438 static struct idmap_methods ldap_methods = {
439         ldap_idmap_init,
440         ldap_allocate_rid,
441         ldap_allocate_id,
442         ldap_get_sid_from_id,
443         ldap_get_id_from_sid,
444         ldap_set_mapping,
445         ldap_idmap_close,
446         ldap_idmap_status
447
448 };
449
450 NTSTATUS idmap_smbldap_init(void)
451 {
452         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "smbldap", &ldap_methods);
453 }