initial version of idmap_ldap.c; lots of updates to come
[sfrench/samba-autobuild/.git] / source3 / sam / idmap_ldap.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    idmap LDAP backend
5
6    Copyright (C) Tim Potter 2000
7    Copyright (C) Anthony Liguori 2003
8    Copyright (C) Simo Sorce 2003
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_IDMAP
29
30
31 #include <lber.h>
32 #include <ldap.h>
33
34 struct ldap_idmap_state {
35         LDAP *ldap_struct;
36         time_t last_ping;
37         const char *uri;
38         char *bind_dn;
39         char *bind_secret;
40         unsigned int num_failures;
41         struct ldap_idmap_state *prev, *next;
42 };
43
44 #define LDAP_IDMAP_DONT_PING_TIME 10       /* ping only all 10 seconds */
45 #define LDAP_MAX_ALLOC_ID 128              /* number tries while allocating
46                                               new id */
47
48 static struct ldap_idmap_state ldap_state;
49
50 static int ldap_idmap_connect_system(struct ldap_idmap_state *state);
51 static NTSTATUS ldap_set_mapping(const DOM_SID *sid, unid_t id, int id_type);
52 static NTSTATUS ldap_idmap_close(void);
53
54
55 /*******************************************************************
56  find the ldap password
57 ******************************************************************/
58 static BOOL fetch_ldapsam_pw(char **dn, char** pw)
59 {
60         char *key = NULL;
61         size_t size;
62         
63         *dn = smb_xstrdup(lp_ldap_admin_dn());
64         
65         if (asprintf(&key, "%s/%s", SECRETS_LDAP_BIND_PW, *dn) < 0) {
66                 SAFE_FREE(*dn);
67                 DEBUG(0, ("fetch_ldapsam_pw: asprintf failed!\n"));
68         }
69         
70         *pw=secrets_fetch(key, &size);
71         SAFE_FREE(key);
72
73         if (!size) {
74                 /* Upgrade 2.2 style entry */
75                 char *p;
76                 char* old_style_key = strdup(*dn);
77                 char *data;
78                 fstring old_style_pw;
79                 
80                 if (!old_style_key) {
81                         DEBUG(0, ("fetch_ldapsam_pw: strdup failed!\n"));
82                         return False;
83                 }
84
85                 for (p=old_style_key; *p; p++)
86                         if (*p == ',') *p = '/';
87         
88                 data=secrets_fetch(old_style_key, &size);
89                 if (!size && size < sizeof(old_style_pw)) {
90                         DEBUG(0,("fetch_ldap_pw: neither ldap secret retrieved!\n"));
91                         SAFE_FREE(old_style_key);
92                         SAFE_FREE(*dn);
93                         return False;
94                 }
95
96                 strncpy(old_style_pw, data, size);
97                 old_style_pw[size] = 0;
98
99                 SAFE_FREE(data);
100
101                 if (!secrets_store_ldap_pw(*dn, old_style_pw)) {
102                         DEBUG(0,("fetch_ldap_pw: ldap secret could not be upgraded!\n"));
103                         SAFE_FREE(old_style_key);
104                         SAFE_FREE(*dn);
105                         return False;                   
106                 }
107                 if (!secrets_delete(old_style_key)) {
108                         DEBUG(0,("fetch_ldap_pw: old ldap secret could not be deleted!\n"));
109                 }
110
111                 SAFE_FREE(old_style_key);
112
113                 *pw = smb_xstrdup(old_style_pw);                
114         }
115         
116         return True;
117 }
118
119 /*******************************************************************
120  open a connection to the ldap server.
121 ******************************************************************/
122 static int ldap_idmap_open_connection(struct ldap_idmap_state *state)
123 {
124         int rc = LDAP_SUCCESS;
125         int version;
126         BOOL ldap_v3 = False;
127
128 #ifdef HAVE_LDAP_INITIALIZE
129         DEBUG(10, ("ldap_idmap_open_connection: %s\n", state->uri));
130         
131         if ((rc = ldap_initialize(&state->ldap_struct, state->uri)) 
132             != LDAP_SUCCESS) {
133                 DEBUG(0, ("ldap_initialize: %s\n", ldap_err2string(rc)));
134                 return rc;
135         }
136 #else 
137         /* Parse the string manually */
138         {
139                 int port = 0;
140                 fstring protocol;
141                 fstring host;
142                 const char *p = state->uri; 
143                 SMB_ASSERT(sizeof(protocol)>10 && sizeof(host)>254);
144                 
145                 /* skip leading "URL:" (if any) */
146                 if ( strncasecmp( p, "URL:", 4 ) == 0 ) {
147                         p += 4;
148                 }
149                 
150                 sscanf(p, "%10[^:]://%254s[^:]:%d", protocol, host, &port);
151                 
152                 if (port == 0) {
153                         if (strequal(protocol, "ldap")) {
154                                 port = LDAP_PORT;
155                         } else if (strequal(protocol, "ldaps")) {
156                                 port = LDAPS_PORT;
157                         } else {
158                                 DEBUG(0, ("unrecognised protocol (%s)!\n",
159                                           protocol));
160                         }
161                 }
162                 
163                 if ((state->ldap_struct = ldap_init(host, port)) == NULL) {
164                         DEBUG(0, ("ldap_init failed !\n"));
165                         return LDAP_OPERATIONS_ERROR;
166                 }
167                 
168                 if (strequal(protocol, "ldaps")) {
169 #ifdef LDAP_OPT_X_TLS
170                         int tls = LDAP_OPT_X_TLS_HARD;
171                         if (ldap_set_option (state->ldap_struct, 
172                                              LDAP_OPT_X_TLS, &tls) != 
173                             LDAP_SUCCESS)
174                         {
175                                 DEBUG(0, ("Failed to setup a TLS session\n"));
176                         }
177                         
178                         DEBUG(3,("LDAPS option set...!\n"));
179 #else
180                         DEBUG(0,("ldap_idmap_open_connection: Secure "
181                                  "connection not supported by LDAP client "
182                                  "libraries!\n"));
183                         return LDAP_OPERATIONS_ERROR;
184 #endif
185                 }
186         }
187 #endif
188
189         if (ldap_get_option(state->ldap_struct, LDAP_OPT_PROTOCOL_VERSION,
190                             &version) == LDAP_OPT_SUCCESS) {
191                 if (version != LDAP_VERSION3) {
192                         version = LDAP_VERSION3;
193                         if (ldap_set_option(state->ldap_struct,
194                                             LDAP_OPT_PROTOCOL_VERSION,
195                                             &version) == LDAP_OPT_SUCCESS) {
196                                 ldap_v3 = True;
197                         }
198                 } else {
199                         ldap_v3 = True;
200                 }
201         }
202
203         if (lp_ldap_ssl() == LDAP_SSL_START_TLS) {
204 #ifdef LDAP_OPT_X_TLS
205                 if (ldap_v3) {
206                         if ((rc = ldap_start_tls_s(state->ldap_struct, NULL,
207                                                    NULL)) != LDAP_SUCCESS) {
208                                 DEBUG(0,("Failed to issue the StartTLS "
209                                          "instruction: %s\n",
210                                          ldap_err2string(rc)));
211                                 return rc;
212                         }
213                         DEBUG (3, ("StartTLS issued: using a TLS "
214                                    "connection\n"));
215                 } else {
216                         
217                         DEBUG(0, ("Need LDAPv3 for Start TLS\n"));
218                         return LDAP_OPERATIONS_ERROR;
219                 }
220 #else
221                 DEBUG(0,("ldap_idmap_open_connection: StartTLS not supported by "
222                          "LDAP client libraries!\n"));
223                 return LDAP_OPERATIONS_ERROR;
224 #endif
225         }
226
227         DEBUG(2, ("ldap_idmap_open_connection: connection opened\n"));
228         return rc;
229 }
230
231 /**********************************************************************
232 Connect to LDAP server 
233 *********************************************************************/
234 static int ldap_idmap_open(struct ldap_idmap_state *state)
235 {
236         int rc;
237         SMB_ASSERT(state);
238                 
239 #ifndef NO_LDAP_SECURITY
240         if (geteuid() != 0) {
241                 DEBUG(0, 
242                       ("ldap_idmap_open: cannot access LDAP when not root\n"));
243                 return  LDAP_INSUFFICIENT_ACCESS;
244         }
245 #endif
246
247         if ((state->ldap_struct != NULL) && 
248             ((state->last_ping + LDAP_IDMAP_DONT_PING_TIME)<time(NULL))) {
249                 struct sockaddr_un addr;
250                 socklen_t len = sizeof(addr);
251                 int sd;
252
253                 if (!ldap_get_option(state->ldap_struct,  LDAP_OPT_DESC, &sd)&&
254                     getpeername(sd, (struct sockaddr *) &addr, &len) < 0) {
255                         /* the other end has died. reopen. */
256                         ldap_unbind_ext(state->ldap_struct, NULL, NULL);
257                         state->ldap_struct = NULL;
258                         state->last_ping = (time_t)0;
259                 } else {
260                         state->last_ping = time(NULL);
261                 } 
262         }
263
264         if (state->ldap_struct != NULL) {
265                 DEBUG(5,("ldap_idmap_open: already connected to the LDAP "
266                          "server\n"));
267                 return LDAP_SUCCESS;
268         }
269
270         if ((rc = ldap_idmap_open_connection(state))) {
271                 return rc;
272         }
273
274         if ((rc = ldap_idmap_connect_system(state))) {
275                 ldap_unbind_ext(state->ldap_struct, NULL, NULL);
276                 state->ldap_struct = NULL;
277                 return rc;
278         }
279
280
281         state->last_ping = time(NULL);
282         DEBUG(4,("The LDAP server is succesful connected\n"));
283
284         return LDAP_SUCCESS;
285 }
286
287 static int ldap_idmap_retry_open(struct ldap_idmap_state *state, int *attempts)
288 {
289         int rc;
290
291         SMB_ASSERT(state && attempts);
292
293         if (*attempts != 0) {
294                 unsigned int sleep_time;
295                 uint8 rand_byte = 128; /* a reasonable place to start */
296
297                 generate_random_buffer(&rand_byte, 1, False);
298
299                 sleep_time = (((*attempts)*(*attempts))/2)*rand_byte*2; 
300                 /* we retry after (0.5, 1, 2, 3, 4.5, 6) seconds
301                    on average.  
302                  */
303                 DEBUG(3, ("Sleeping for %u milliseconds before reconnecting\n",
304                           sleep_time));
305                 msleep(sleep_time);
306         }
307         (*attempts)++;
308
309         if ((rc = ldap_idmap_open(state))) {
310                 DEBUG(1,("Connection to LDAP Server failed for the %d try!\n",
311                          *attempts));
312                 return rc;
313         } 
314         
315         return LDAP_SUCCESS;            
316 }
317
318 /*******************************************************************
319  a rebind function for authenticated referrals
320  This version takes a void* that we can shove useful stuff in :-)
321 ******************************************************************/
322 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
323 #else
324 static int rebindproc_with_state  (LDAP * ld, char **whop, char **credp, 
325                                    int *methodp, int freeit, void *arg)
326 {
327         struct ldap_idmap_state *state = arg;
328         
329         /** @TODO Should we be doing something to check what servers we rebind
330             to?  Could we get a referral to a machine that we don't want to
331             give our username and password to? */
332         
333         if (freeit) {
334                 SAFE_FREE(*whop);
335                 memset(*credp, '\0', strlen(*credp));
336                 SAFE_FREE(*credp);
337         } else {
338                 DEBUG(5,("rebind_proc_with_state: Rebinding as \"%s\"\n", 
339                           state->bind_dn));
340
341                 *whop = strdup(state->bind_dn);
342                 if (!*whop) {
343                         return LDAP_NO_MEMORY;
344                 }
345                 *credp = strdup(state->bind_secret);
346                 if (!*credp) {
347                         SAFE_FREE(*whop);
348                         return LDAP_NO_MEMORY;
349                 }
350                 *methodp = LDAP_AUTH_SIMPLE;
351         }
352         return 0;
353 }
354 #endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
355
356 /*******************************************************************
357  a rebind function for authenticated referrals
358  This version takes a void* that we can shove useful stuff in :-)
359  and actually does the connection.
360 ******************************************************************/
361 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
362 static int rebindproc_connect_with_state (LDAP *ldap_struct, 
363                                           LDAP_CONST char *url, 
364                                           ber_tag_t request,
365                                           ber_int_t msgid, void *arg)
366 {
367         struct ldap_idmap_state *state = arg;
368         int rc;
369         DEBUG(5,("rebindproc_connect_with_state: Rebinding as \"%s\"\n", 
370                  state->bind_dn));
371         
372         /** @TODO Should we be doing something to check what servers we rebind
373             to?  Could we get a referral to a machine that we don't want to
374             give our username and password to? */
375
376         rc = ldap_simple_bind_s(ldap_struct, state->bind_dn, 
377                                 state->bind_secret);
378         
379         return rc;
380 }
381 #endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
382
383 /*******************************************************************
384  Add a rebind function for authenticated referrals
385 ******************************************************************/
386 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
387 #else
388 # if LDAP_SET_REBIND_PROC_ARGS == 2
389 static int rebindproc (LDAP *ldap_struct, char **whop, char **credp,
390                        int *method, int freeit )
391 {
392         return rebindproc_with_state(ldap_struct, whop, credp,
393                                    method, freeit, &ldap_state);
394         
395 }
396 # endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/
397 #endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
398
399 /*******************************************************************
400  a rebind function for authenticated referrals
401  this also does the connection, but no void*.
402 ******************************************************************/
403 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
404 # if LDAP_SET_REBIND_PROC_ARGS == 2
405 static int rebindproc_connect (LDAP * ld, LDAP_CONST char *url, int request,
406                                ber_int_t msgid)
407 {
408         return rebindproc_connect_with_state(ld, url, (ber_tag_t)request,
409                                              msgid, &ldap_state);
410 }
411 # endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/
412 #endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
413
414 /*******************************************************************
415  connect to the ldap server under system privilege.
416 ******************************************************************/
417 static int ldap_idmap_connect_system(struct ldap_idmap_state *state)
418 {
419         int rc;
420         char *ldap_dn;
421         char *ldap_secret;
422
423         /* get the password */
424         if (!fetch_ldapsam_pw(&ldap_dn, &ldap_secret))
425         {
426                 DEBUG(0, ("ldap_idmap_connect_system: Failed to retrieve "
427                           "password from secrets.tdb\n"));
428                 return LDAP_INVALID_CREDENTIALS;
429         }
430
431         state->bind_dn = ldap_dn;
432         state->bind_secret = ldap_secret;
433
434         /* removed the sasl_bind_s "EXTERNAL" stuff, as my testsuite 
435            (OpenLDAP) doesnt' seem to support it */
436            
437         DEBUG(10,("ldap_idmap_connect_system: Binding to ldap server %s as "
438                   "\"%s\"\n", state->uri, ldap_dn));
439
440 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
441 # if LDAP_SET_REBIND_PROC_ARGS == 2     
442         ldap_set_rebind_proc(state->ldap_struct, &rebindproc_connect);  
443 # endif
444 # if LDAP_SET_REBIND_PROC_ARGS == 3     
445         ldap_set_rebind_proc(state->ldap_struct, 
446                              &rebindproc_connect_with_state, (void *)state);
447 # endif
448 #else /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
449 # if LDAP_SET_REBIND_PROC_ARGS == 2     
450         ldap_set_rebind_proc(state->ldap_struct, &rebindproc);  
451 # endif
452 # if LDAP_SET_REBIND_PROC_ARGS == 3     
453         ldap_set_rebind_proc(state->ldap_struct, &rebindproc_with_state,
454                              (void *)state);    
455 # endif
456 #endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
457
458         rc = ldap_simple_bind_s(state->ldap_struct, ldap_dn, ldap_secret);
459
460         if (rc != LDAP_SUCCESS) {
461                 char *ld_error = NULL;
462                 ldap_get_option(state->ldap_struct, LDAP_OPT_ERROR_STRING,
463                                 &ld_error);
464                 DEBUG(state->num_failures ? 2 : 0,
465                       ("failed to bind to server with dn= %s Error: "
466                        "%s\n\t%s\n",
467                        ldap_dn ? ld_error : "(unknown)",
468                        ldap_err2string(rc), ld_error));
469                 SAFE_FREE(ld_error);
470                 state->num_failures++;
471                 return rc;
472         }
473
474         state->num_failures = 0;
475
476         DEBUG(3, ("ldap_idmap_connect_system: succesful connection to the "
477                   "LDAP server\n"));
478         return rc;
479 }
480
481 static int ldap_idmap_search(struct ldap_idmap_state *state, 
482                              const char *base, int scope, const char *filter, 
483                              const char *attrs[], int attrsonly, 
484                              LDAPMessage **res)
485 {
486         int rc = LDAP_SERVER_DOWN;
487         int attempts = 0;
488         char *utf8_filter;
489
490         SMB_ASSERT(state);
491
492         if (push_utf8_allocate(&utf8_filter, filter) == (size_t)-1) {
493                 return LDAP_NO_MEMORY;
494         }
495
496         while ((rc == LDAP_SERVER_DOWN) && (attempts < 8)) {
497                 if ((rc = ldap_idmap_retry_open(state, &attempts)) !=
498                     LDAP_SUCCESS) continue;
499                 
500                 rc = ldap_search_s(state->ldap_struct, base, scope, 
501                                    utf8_filter, (char**)attrs, attrsonly, res);
502         }
503         
504         if (rc == LDAP_SERVER_DOWN) {
505                 DEBUG(0,("ldap_idmap_search: LDAP server is down!\n"));
506                 ldap_idmap_close();
507         }
508
509         SAFE_FREE(utf8_filter);
510         return rc;
511 }
512
513 /*******************************************************************
514 search an attribute and return the first value found.
515 ******************************************************************/
516 static BOOL ldap_idmap_attribute (struct ldap_idmap_state *state,
517                                   LDAPMessage * entry,
518                                   const char *attribute, pstring value)
519 {
520         char **values;
521         value[0] = '\0';
522
523         if ((values = ldap_get_values (state->ldap_struct, entry, attribute))
524             == NULL) {
525                 DEBUG(10,("get_single_attribute: [%s] = [<does not exist>]\n",
526                           attribute));
527                 return False;
528         }
529         if (convert_string(CH_UTF8, CH_UNIX,
530                 values[0], -1,
531                 value, sizeof(pstring)) == (size_t)-1)
532         {
533                 DEBUG(1, ("ldap_idmap_attribute: string conversion of [%s] = "
534                           "[%s] failed!\n",  attribute, values[0]));
535                 ldap_value_free(values);
536                 return False;
537         }
538         ldap_value_free(values);
539
540         return True;
541 }
542
543 static const char *attrs[] = {"objectClass", "uidNumber", "gidNumber", 
544                               "ntSid", NULL};
545 static const char *pool_attr[] = {"uidNumber", "gidNumber", NULL};
546
547 static NTSTATUS ldap_allocate_id(unid_t *id, int id_type)
548 {
549         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
550         int rc = LDAP_SERVER_DOWN;
551         int count = 0;
552         LDAPMessage *result = 0;
553         LDAPMessage *entry = 0;
554         pstring id_str, new_id_str;
555         LDAPMod mod[2];
556         LDAPMod *mods[3];
557         const char *type = (id_type & ID_USERID) ? "uidNumber" : "gidNumber";
558         char *val[4];
559         char *dn;
560
561         rc = ldap_idmap_search(&ldap_state, lp_ldap_suffix(),
562                                LDAP_SCOPE_SUBTREE, "(objectClass=unixIdPool)",
563                                pool_attr, 0, &result);
564         if (rc != LDAP_SUCCESS) {
565                 DEBUG(0,("ldap_allocate_id: unixIdPool object not found\n"));
566                 goto out;
567         }
568         
569         count = ldap_count_entries(ldap_state.ldap_struct, result);
570         if (count != 1) {
571                 DEBUG(0,("ldap_allocate_id: single unixIdPool not found\n"));
572                 goto out;
573         }
574
575         dn = ldap_get_dn(ldap_state.ldap_struct, result);
576         entry = ldap_first_entry(ldap_state.ldap_struct, result);
577
578         if (!ldap_idmap_attribute(&ldap_state, entry, type, id_str)) {
579                 DEBUG(0,("ldap_allocate_id: %s attribute not found\n",
580                          type));
581                 goto out;
582         }
583         if (id_type & ID_USERID) {
584                 id->uid = strtoul(id_str, NULL, 10);
585         } else {
586                 id->gid = strtoul(id_str, NULL, 10);
587         }
588
589         mod[0].mod_op = LDAP_MOD_DELETE;
590         mod[0].mod_type = strdup(type);
591         val[0] = id_str; val[1] = NULL;
592         mod[0].mod_values = val;
593
594         pstr_sprintf(new_id_str, "%ud", 
595                  ((id_type & ID_USERID) ? id->uid : id->gid) + 1);
596         mod[1].mod_op = LDAP_MOD_ADD;
597         mod[1].mod_type = strdup(type);
598         val[3] = new_id_str; val[4] = NULL;
599         mod[1].mod_values = val + 2;
600
601         mods[0] = mod; mods[1] = mod + 1; mods[2] = NULL;
602         rc = ldap_modify_s(ldap_state.ldap_struct, dn, mods);
603         ldap_memfree(dn);
604
605         if (rc == LDAP_SUCCESS) ret = NT_STATUS_OK;
606 out:
607         return ret;
608 }
609
610 /* Get a sid from an id */
611 static NTSTATUS ldap_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
612 {
613         LDAPMessage *result = 0;
614         LDAPMessage *entry = 0;
615         pstring sid_str;
616         pstring filter;
617         char type = (id_type & ID_USERID) ? 'u' : 'g';
618         int rc;
619         int count;
620         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
621            
622         pstr_sprintf(filter, "(&(%cidNumber=%ud)(objectClass=sambaAccount))",
623                  type, ((id_type & ID_USERID) ? id.uid : id.gid));
624         rc = ldap_idmap_search(&ldap_state, lp_ldap_suffix(), 
625                                LDAP_SCOPE_SUBTREE, filter, attrs, 0, 
626                                &result);
627         if (rc != LDAP_SUCCESS) {
628                 goto out;
629         }
630         
631         count = ldap_count_entries(ldap_state.ldap_struct, result);
632         if (count == 0) {
633                    pstr_sprintf(filter,
634                             "(&(objectClass=idmapEntry)(%cidNumber=%ud))",
635                             type, ((id_type & ID_USERID) ? id.uid : id.gid));
636                    rc = ldap_idmap_search(&ldap_state, lp_ldap_suffix(),
637                                           LDAP_SCOPE_SUBTREE, filter,
638                                           attrs, 0, &result);
639                    if (rc != LDAP_SUCCESS) {
640                            goto out;
641                    }
642                    count = ldap_count_entries(ldap_state.ldap_struct, result);
643         }
644         
645         if (count != 1) {
646                 DEBUG(0,("ldap_get_sid_from_id: mapping not found for "
647                          "%cid: %ud\n", (id_type&ID_USERID)?'u':'g',
648                          ((id_type & ID_USERID) ? id.uid : id.gid)));
649                 goto out;
650         }
651         
652         entry = ldap_first_entry(ldap_state.ldap_struct, result);
653         
654         if (!ldap_idmap_attribute(&ldap_state, entry, "ntSid", sid_str)) {
655                 goto out;
656         }
657            
658         if (!string_to_sid(sid, sid_str)) {
659                 goto out;
660         }
661
662         ret = NT_STATUS_OK;
663 out:
664         return ret;
665 }
666
667 /* Get an id from a sid */
668 static NTSTATUS ldap_get_id_from_sid(unid_t *id, int *id_type,
669                                      const DOM_SID *sid)
670 {
671         LDAPMessage *result = 0;
672         LDAPMessage *entry = 0;
673         pstring sid_str;
674         pstring filter;
675         pstring id_str;
676         const char *type = (*id_type & ID_USERID) ? "uidNumber" : "gidNumber";
677         const char *class =
678                 (*id_type & ID_USERID) ? "sambaAccount" : "sambaGroupMapping";
679         int rc;
680         int count;
681         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
682            
683         sid_to_string(sid_str, sid);
684         pstr_sprintf(filter, "(&(objectClass=%s)(ntSid=%s)", class, sid_str);
685         rc = ldap_idmap_search(&ldap_state, lp_ldap_suffix(), 
686                                LDAP_SCOPE_SUBTREE, filter, attrs, 0, &result);
687         if (rc != LDAP_SUCCESS) {
688                 goto out;
689         }
690         count = ldap_count_entries(ldap_state.ldap_struct, result);
691         if (count == 0) {
692                    pstr_sprintf(filter,
693                             "(&(objectClass=idmapEntry)(ntSid=%s))", sid_str);
694
695                    rc = ldap_idmap_search(&ldap_state, lp_ldap_suffix(),
696                                           LDAP_SCOPE_SUBTREE, filter,
697                                           attrs, 0, &result);
698                    if (rc != LDAP_SUCCESS) {
699                            goto out;
700                    }
701                    count = ldap_count_entries(ldap_state.ldap_struct, result);
702         }
703
704         /* our search filters may 2 objects in the case that a user and group
705            rid are the same */
706         if (count != 1 && count != 2) {
707                 DEBUG(0,
708                       ("ldap_get_id_from_sid: incorrect number of objects\n"));
709                 goto out;
710         }
711
712         entry = ldap_first_entry(ldap_state.ldap_struct, result);
713         if (!ldap_idmap_attribute(&ldap_state, entry, type, id_str)) {
714                 entry = ldap_next_entry(ldap_state.ldap_struct, entry);
715
716                 if (!ldap_idmap_attribute(&ldap_state, entry, type, id_str)) {
717                         int i;
718
719                         for (i = 0; i < LDAP_MAX_ALLOC_ID; i++) {
720                                 ret = ldap_allocate_id(id, *id_type);
721                                 if (NT_STATUS_IS_OK(ret)) {
722                                         break;
723                                 }
724                         }
725                         if (NT_STATUS_IS_OK(ret)) {
726                                 ret = ldap_set_mapping(sid, *id, *id_type);
727                         } else {
728                                 DEBUG(0,("ldap_allocate_id: cannot acquire id"
729                                          " lock\n"));
730                         }
731                 } else {
732                         if ((*id_type & ID_USERID)) {
733                                 id->uid = strtoul(id_str, NULL, 10);
734                         } else {
735                                 id->gid = strtoul(id_str, NULL, 10);
736                         }
737                         ret = NT_STATUS_OK;
738                 }
739         } else {
740                 if ((*id_type & ID_USERID)) {
741                         id->uid = strtoul(id_str, NULL, 10);
742                 } else {
743                         id->gid = strtoul(id_str, NULL, 10);
744                 }
745                 ret = NT_STATUS_OK;
746         }
747 out:
748         return ret;
749 }
750
751 /* This function cannot be called to modify a mapping, only set a new one */
752 static NTSTATUS ldap_set_mapping(const DOM_SID *sid, unid_t id, int id_type)
753 {
754         pstring dn, sid_str, id_str;
755         const char *type = (id_type & ID_USERID) ? "uidNumber" : "gidNumber";
756         LDAPMod *mods[3];
757         LDAPMod mod[2];
758         char *val[4];
759         int rc;
760         int attempts = 0;
761
762         pstr_sprintf(id_str, "%ud", ((id_type & ID_USERID) ? id.uid : id.gid));
763         sid_to_string(sid_str, sid);
764         pstr_sprintf(dn, "%s=%ud,%s", type, ((id_type & ID_USERID) ? id.uid : id.gid), lp_ldap_suffix());
765         mod[0].mod_op = LDAP_MOD_REPLACE;
766         mod[0].mod_type = strdup(type);
767         val[0] = id_str; val[1] = NULL;
768         mod[0].mod_values = val;
769
770         mod[1].mod_op = LDAP_MOD_REPLACE;
771         mod[1].mod_type = strdup("ntSid");
772         val[2] = sid_str; val[3] = NULL;
773         mod[1].mod_values = val + 2;
774
775         mods[0] = mod; mods[1] = mod + 1; mods[2] = NULL;
776
777         do {
778                 if ((rc = ldap_idmap_retry_open(&ldap_state, &attempts)) !=
779                     LDAP_SUCCESS) continue;
780                 
781                 rc = ldap_modify_s(ldap_state.ldap_struct, dn, mods);
782         } while ((rc == LDAP_SERVER_DOWN) && (attempts <= 8));
783
784         if (rc != LDAP_SUCCESS) {
785                 return NT_STATUS_UNSUCCESSFUL;
786         }
787
788         return NT_STATUS_OK;
789 }
790
791 /*****************************************************************************
792  Initialise idmap database. 
793 *****************************************************************************/
794 static NTSTATUS ldap_idmap_init(void)
795 {
796         /* We wait for the first search request before we try to connect to
797            the LDAP server.  We may want to connect upon initialization though
798            -- aliguori */
799         return NT_STATUS_OK;
800 }
801
802 /* End the LDAP session */
803 static NTSTATUS ldap_idmap_close(void)
804 {
805         if (ldap_state.ldap_struct != NULL) {
806                 ldap_unbind_ext(ldap_state.ldap_struct, NULL, NULL);
807                 ldap_state.ldap_struct = NULL;
808         }
809         
810         DEBUG(5,("The connection to the LDAP server was closed\n"));
811         /* maybe free the results here --metze */
812         
813         return NT_STATUS_OK;
814 }
815
816
817 /* This function doesn't make as much sense in an LDAP world since the calling
818    node doesn't really control the ID ranges */
819 static void ldap_idmap_status(void)
820 {
821         DEBUG(0, ("LDAP IDMAP Status not available\n"));
822 }
823
824 static struct idmap_methods ldap_methods = {
825         ldap_idmap_init,
826         ldap_get_sid_from_id,
827         ldap_get_id_from_sid,
828         ldap_set_mapping,
829         ldap_idmap_close,
830         ldap_idmap_status
831
832 };
833
834 NTSTATUS idmap_ldap_init(void)
835 {
836         DEBUG(0,("idmap_reg_ldap: no LDAP support\n"));
837         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "ldap", &ldap_methods);
838 }