cf7c8fc87f273f9aafe4b681ebbbca101357a0b6
[vlendec/samba-autobuild/.git] / source3 / lib / smbldap.c
1 /* 
2    Unix SMB/CIFS implementation.
3    LDAP protocol helper functions for SAMBA
4    Copyright (C) Jean François Micouleau       1998
5    Copyright (C) Gerald Carter                  2001-2003
6    Copyright (C) Shahms King                    2001
7    Copyright (C) Andrew Bartlett                2002-2003
8    Copyright (C) Stefan (metze) Metzmacher      2002-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
26 #include "includes.h"
27 #include "smbldap.h"
28
29 #ifndef LDAP_OPT_SUCCESS
30 #define LDAP_OPT_SUCCESS 0
31 #endif
32
33 /* Try not to hit the up or down server forever */
34
35 #define SMBLDAP_DONT_PING_TIME 10       /* ping only all 10 seconds */
36 #define SMBLDAP_NUM_RETRIES 8           /* retry only 8 times */
37
38 #define SMBLDAP_IDLE_TIME 150           /* After 2.5 minutes disconnect */
39
40
41 /* attributes used by Samba 2.2 */
42
43 ATTRIB_MAP_ENTRY attrib_map_v22[] = {
44         { LDAP_ATTR_UID,                "uid"           },
45         { LDAP_ATTR_UIDNUMBER,          LDAP_ATTRIBUTE_UIDNUMBER},
46         { LDAP_ATTR_GIDNUMBER,          LDAP_ATTRIBUTE_GIDNUMBER},
47         { LDAP_ATTR_UNIX_HOME,          "homeDirectory" },
48         { LDAP_ATTR_PWD_LAST_SET,       "pwdLastSet"    },
49         { LDAP_ATTR_PWD_CAN_CHANGE,     "pwdCanChange"  },
50         { LDAP_ATTR_PWD_MUST_CHANGE,    "pwdMustChange" },
51         { LDAP_ATTR_LOGON_TIME,         "logonTime"     },
52         { LDAP_ATTR_LOGOFF_TIME,        "logoffTime"    },
53         { LDAP_ATTR_KICKOFF_TIME,       "kickoffTime"   },
54         { LDAP_ATTR_CN,                 "cn"            },
55         { LDAP_ATTR_DISPLAY_NAME,       "displayName"   },
56         { LDAP_ATTR_HOME_PATH,          "smbHome"       },
57         { LDAP_ATTR_HOME_DRIVE,         "homeDrive"     },
58         { LDAP_ATTR_LOGON_SCRIPT,       "scriptPath"    },
59         { LDAP_ATTR_PROFILE_PATH,       "profilePath"   },
60         { LDAP_ATTR_DESC,               "description"   },
61         { LDAP_ATTR_USER_WKS,           "userWorkstations"},
62         { LDAP_ATTR_USER_RID,           "rid"           },
63         { LDAP_ATTR_PRIMARY_GROUP_RID,  "primaryGroupID"},
64         { LDAP_ATTR_LMPW,               "lmPassword"    },
65         { LDAP_ATTR_NTPW,               "ntPassword"    },
66         { LDAP_ATTR_DOMAIN,             "domain"        },
67         { LDAP_ATTR_OBJCLASS,           "objectClass"   },
68         { LDAP_ATTR_ACB_INFO,           "acctFlags"     },
69         { LDAP_ATTR_MOD_TIMESTAMP,      "modifyTimestamp"       },
70         { LDAP_ATTR_LIST_END,           NULL            }
71 };
72
73 ATTRIB_MAP_ENTRY attrib_map_to_delete_v22[] = {
74         { LDAP_ATTR_PWD_LAST_SET,       "pwdLastSet"    },
75         { LDAP_ATTR_PWD_CAN_CHANGE,     "pwdCanChange"  },
76         { LDAP_ATTR_PWD_MUST_CHANGE,    "pwdMustChange" },
77         { LDAP_ATTR_LOGON_TIME,         "logonTime"     },
78         { LDAP_ATTR_LOGOFF_TIME,        "logoffTime"    },
79         { LDAP_ATTR_KICKOFF_TIME,       "kickoffTime"   },
80         { LDAP_ATTR_DISPLAY_NAME,       "displayName"   },
81         { LDAP_ATTR_HOME_PATH,          "smbHome"       },
82         { LDAP_ATTR_HOME_DRIVE,         "homeDrives"    },
83         { LDAP_ATTR_LOGON_SCRIPT,       "scriptPath"    },
84         { LDAP_ATTR_PROFILE_PATH,       "profilePath"   },
85         { LDAP_ATTR_USER_WKS,           "userWorkstations"},
86         { LDAP_ATTR_USER_RID,           "rid"           },
87         { LDAP_ATTR_PRIMARY_GROUP_RID,  "primaryGroupID"},
88         { LDAP_ATTR_LMPW,               "lmPassword"    },
89         { LDAP_ATTR_NTPW,               "ntPassword"    },
90         { LDAP_ATTR_DOMAIN,             "domain"        },
91         { LDAP_ATTR_ACB_INFO,           "acctFlags"     },
92         { LDAP_ATTR_LIST_END,           NULL            }
93 };
94
95 /* attributes used by Samba 3.0's sambaSamAccount */
96
97 ATTRIB_MAP_ENTRY attrib_map_v30[] = {
98         { LDAP_ATTR_UID,                "uid"                   },
99         { LDAP_ATTR_UIDNUMBER,          LDAP_ATTRIBUTE_UIDNUMBER},
100         { LDAP_ATTR_GIDNUMBER,          LDAP_ATTRIBUTE_GIDNUMBER},
101         { LDAP_ATTR_UNIX_HOME,          "homeDirectory"         },
102         { LDAP_ATTR_PWD_LAST_SET,       "sambaPwdLastSet"       },
103         { LDAP_ATTR_PWD_CAN_CHANGE,     "sambaPwdCanChange"     },
104         { LDAP_ATTR_PWD_MUST_CHANGE,    "sambaPwdMustChange"    },
105         { LDAP_ATTR_LOGON_TIME,         "sambaLogonTime"        },
106         { LDAP_ATTR_LOGOFF_TIME,        "sambaLogoffTime"       },
107         { LDAP_ATTR_KICKOFF_TIME,       "sambaKickoffTime"      },
108         { LDAP_ATTR_CN,                 "cn"                    },
109         { LDAP_ATTR_DISPLAY_NAME,       "displayName"           },
110         { LDAP_ATTR_HOME_DRIVE,         "sambaHomeDrive"        },
111         { LDAP_ATTR_HOME_PATH,          "sambaHomePath"         },
112         { LDAP_ATTR_LOGON_SCRIPT,       "sambaLogonScript"      },
113         { LDAP_ATTR_PROFILE_PATH,       "sambaProfilePath"      },
114         { LDAP_ATTR_DESC,               "description"           },
115         { LDAP_ATTR_USER_WKS,           "sambaUserWorkstations" },
116         { LDAP_ATTR_USER_SID,           LDAP_ATTRIBUTE_SID      },
117         { LDAP_ATTR_PRIMARY_GROUP_SID,  "sambaPrimaryGroupSID"  },
118         { LDAP_ATTR_LMPW,               "sambaLMPassword"       },
119         { LDAP_ATTR_NTPW,               "sambaNTPassword"       },
120         { LDAP_ATTR_DOMAIN,             "sambaDomainName"       },
121         { LDAP_ATTR_OBJCLASS,           "objectClass"           },
122         { LDAP_ATTR_ACB_INFO,           "sambaAcctFlags"        },
123         { LDAP_ATTR_MUNGED_DIAL,        "sambaMungedDial"       },
124         { LDAP_ATTR_BAD_PASSWORD_COUNT, "sambaBadPasswordCount" },
125         { LDAP_ATTR_BAD_PASSWORD_TIME,  "sambaBadPasswordTime"  },
126         { LDAP_ATTR_PWD_HISTORY,        "sambaPasswordHistory"  },
127         { LDAP_ATTR_MOD_TIMESTAMP,      "modifyTimestamp"       },
128         { LDAP_ATTR_LOGON_HOURS,        "sambaLogonHours"       },
129         { LDAP_ATTR_LIST_END,           NULL                    }
130 };
131
132 ATTRIB_MAP_ENTRY attrib_map_to_delete_v30[] = {
133         { LDAP_ATTR_PWD_LAST_SET,       "sambaPwdLastSet"       },
134         { LDAP_ATTR_PWD_CAN_CHANGE,     "sambaPwdCanChange"     },
135         { LDAP_ATTR_PWD_MUST_CHANGE,    "sambaPwdMustChange"    },
136         { LDAP_ATTR_LOGON_TIME,         "sambaLogonTime"        },
137         { LDAP_ATTR_LOGOFF_TIME,        "sambaLogoffTime"       },
138         { LDAP_ATTR_KICKOFF_TIME,       "sambaKickoffTime"      },
139         { LDAP_ATTR_HOME_DRIVE,         "sambaHomeDrive"        },
140         { LDAP_ATTR_HOME_PATH,          "sambaHomePath"         },
141         { LDAP_ATTR_LOGON_SCRIPT,       "sambaLogonScript"      },
142         { LDAP_ATTR_PROFILE_PATH,       "sambaProfilePath"      },
143         { LDAP_ATTR_USER_WKS,           "sambaUserWorkstations" },
144         { LDAP_ATTR_USER_SID,           LDAP_ATTRIBUTE_SID      },
145         { LDAP_ATTR_PRIMARY_GROUP_SID,  "sambaPrimaryGroupSID"  },
146         { LDAP_ATTR_LMPW,               "sambaLMPassword"       },
147         { LDAP_ATTR_NTPW,               "sambaNTPassword"       },
148         { LDAP_ATTR_DOMAIN,             "sambaDomainName"       },
149         { LDAP_ATTR_ACB_INFO,           "sambaAcctFlags"        },
150         { LDAP_ATTR_MUNGED_DIAL,        "sambaMungedDial"       },
151         { LDAP_ATTR_BAD_PASSWORD_COUNT, "sambaBadPasswordCount" },
152         { LDAP_ATTR_BAD_PASSWORD_TIME,  "sambaBadPasswordTime"  },
153         { LDAP_ATTR_PWD_HISTORY,        "sambaPasswordHistory"  },
154         { LDAP_ATTR_LOGON_HOURS,        "sambaLogonHours"       },
155         { LDAP_ATTR_LIST_END,           NULL                    }
156 };
157
158 /* attributes used for allocating RIDs */
159
160 ATTRIB_MAP_ENTRY dominfo_attr_list[] = {
161         { LDAP_ATTR_DOMAIN,             "sambaDomainName"       },
162         { LDAP_ATTR_NEXT_RID,           "sambaNextRid"          },
163         { LDAP_ATTR_NEXT_USERRID,       "sambaNextUserRid"      },
164         { LDAP_ATTR_NEXT_GROUPRID,      "sambaNextGroupRid"     },
165         { LDAP_ATTR_DOM_SID,            LDAP_ATTRIBUTE_SID      },
166         { LDAP_ATTR_ALGORITHMIC_RID_BASE,"sambaAlgorithmicRidBase"},
167         { LDAP_ATTR_OBJCLASS,           "objectClass"           },
168         { LDAP_ATTR_LIST_END,           NULL                    },
169 };
170
171 /* Samba 3.0 group mapping attributes */
172
173 ATTRIB_MAP_ENTRY groupmap_attr_list[] = {
174         { LDAP_ATTR_GIDNUMBER,          LDAP_ATTRIBUTE_GIDNUMBER},
175         { LDAP_ATTR_GROUP_SID,          LDAP_ATTRIBUTE_SID      },
176         { LDAP_ATTR_GROUP_TYPE,         "sambaGroupType"        },
177         { LDAP_ATTR_SID_LIST,           "sambaSIDList"          },
178         { LDAP_ATTR_DESC,               "description"           },
179         { LDAP_ATTR_DISPLAY_NAME,       "displayName"           },
180         { LDAP_ATTR_CN,                 "cn"                    },
181         { LDAP_ATTR_OBJCLASS,           "objectClass"           },
182         { LDAP_ATTR_LIST_END,           NULL                    }       
183 };
184
185 ATTRIB_MAP_ENTRY groupmap_attr_list_to_delete[] = {
186         { LDAP_ATTR_GROUP_SID,          LDAP_ATTRIBUTE_SID      },
187         { LDAP_ATTR_GROUP_TYPE,         "sambaGroupType"        },
188         { LDAP_ATTR_DESC,               "description"           },
189         { LDAP_ATTR_DISPLAY_NAME,       "displayName"           },
190         { LDAP_ATTR_SID_LIST,           "sambaSIDList"          },
191         { LDAP_ATTR_LIST_END,           NULL                    }       
192 };
193
194 /* idmap_ldap sambaUnixIdPool */
195
196 ATTRIB_MAP_ENTRY idpool_attr_list[] = {
197         { LDAP_ATTR_UIDNUMBER,          LDAP_ATTRIBUTE_UIDNUMBER},
198         { LDAP_ATTR_GIDNUMBER,          LDAP_ATTRIBUTE_GIDNUMBER},
199         { LDAP_ATTR_OBJCLASS,           "objectClass"           },
200         { LDAP_ATTR_LIST_END,           NULL                    }       
201 };
202
203 ATTRIB_MAP_ENTRY sidmap_attr_list[] = {
204         { LDAP_ATTR_SID,                LDAP_ATTRIBUTE_SID      },
205         { LDAP_ATTR_UIDNUMBER,          LDAP_ATTRIBUTE_UIDNUMBER},
206         { LDAP_ATTR_GIDNUMBER,          LDAP_ATTRIBUTE_GIDNUMBER},
207         { LDAP_ATTR_OBJCLASS,           "objectClass"           },
208         { LDAP_ATTR_LIST_END,           NULL                    }       
209 };
210
211 /**********************************************************************
212  perform a simple table lookup and return the attribute name 
213  **********************************************************************/
214  
215  const char* get_attr_key2string( ATTRIB_MAP_ENTRY table[], int key )
216 {
217         int i = 0;
218         
219         while ( table[i].attrib != LDAP_ATTR_LIST_END ) {
220                 if ( table[i].attrib == key )
221                         return table[i].name;
222                 i++;
223         }
224         
225         return NULL;
226 }
227
228
229 /**********************************************************************
230  Return the list of attribute names from a mapping table
231  **********************************************************************/
232
233  const char** get_attr_list( ATTRIB_MAP_ENTRY table[] )
234 {
235         const char **names;
236         int i = 0;
237         
238         while ( table[i].attrib != LDAP_ATTR_LIST_END )
239                 i++;
240         i++;
241
242         names = SMB_MALLOC_ARRAY( const char*, i );
243         if ( !names ) {
244                 DEBUG(0,("get_attr_list: out of memory\n"));
245                 return NULL;
246         }
247
248         i = 0;
249         while ( table[i].attrib != LDAP_ATTR_LIST_END ) {
250                 names[i] = SMB_STRDUP( table[i].name );
251                 i++;
252         }
253         names[i] = NULL;
254         
255         return names;
256 }
257
258 /*********************************************************************
259  Cleanup 
260  ********************************************************************/
261
262  void free_attr_list( const char **list )
263 {
264         int i = 0;
265
266         if ( !list )
267                 return; 
268
269         while ( list[i] ) {
270                 SAFE_FREE( list[i] );
271                 i+=1;
272         }
273
274         SAFE_FREE( list );
275 }
276
277 /*******************************************************************
278  Search an attribute and return the first value found.
279 ******************************************************************/
280
281  BOOL smbldap_get_single_attribute (LDAP * ldap_struct, LDAPMessage * entry,
282                                     const char *attribute, char *value,
283                                     int max_len)
284 {
285         char **values;
286         
287         if ( !attribute )
288                 return False;
289                 
290         value[0] = '\0';
291
292         if ((values = ldap_get_values (ldap_struct, entry, attribute)) == NULL) {
293                 DEBUG (10, ("smbldap_get_single_attribute: [%s] = [<does not exist>]\n", attribute));
294                 
295                 return False;
296         }
297         
298         if (convert_string(CH_UTF8, CH_UNIX,values[0], -1, value, max_len, False) == (size_t)-1) {
299                 DEBUG(1, ("smbldap_get_single_attribute: string conversion of [%s] = [%s] failed!\n", 
300                           attribute, values[0]));
301                 ldap_value_free(values);
302                 return False;
303         }
304         
305         ldap_value_free(values);
306 #ifdef DEBUG_PASSWORDS
307         DEBUG (100, ("smbldap_get_single_attribute: [%s] = [%s]\n", attribute, value));
308 #endif  
309         return True;
310 }
311
312  BOOL smbldap_get_single_pstring (LDAP * ldap_struct, LDAPMessage * entry,
313                                   const char *attribute, pstring value)
314 {
315         return smbldap_get_single_attribute(ldap_struct, entry,
316                                             attribute, value, 
317                                             sizeof(pstring));
318 }
319
320 /************************************************************************
321  Routine to manage the LDAPMod structure array
322  manage memory used by the array, by each struct, and values
323  ***********************************************************************/
324
325  void smbldap_set_mod (LDAPMod *** modlist, int modop, const char *attribute, const char *value)
326 {
327         LDAPMod **mods;
328         int i;
329         int j;
330
331         mods = *modlist;
332
333         /* sanity checks on the mod values */
334
335         if (attribute == NULL || *attribute == '\0') {
336                 return; 
337         }
338
339 #if 0   /* commented out after discussion with abartlet.  Do not reenable.
340            left here so other do not re-add similar code   --jerry */
341         if (value == NULL || *value == '\0')
342                 return;
343 #endif
344
345         if (mods == NULL) {
346                 mods = SMB_MALLOC_P(LDAPMod *);
347                 if (mods == NULL) {
348                         DEBUG(0, ("make_a_mod: out of memory!\n"));
349                         return;
350                 }
351                 mods[0] = NULL;
352         }
353
354         for (i = 0; mods[i] != NULL; ++i) {
355                 if (mods[i]->mod_op == modop && strequal(mods[i]->mod_type, attribute))
356                         break;
357         }
358
359         if (mods[i] == NULL) {
360                 mods = SMB_REALLOC_ARRAY (mods, LDAPMod *, i + 2);
361                 if (mods == NULL) {
362                         DEBUG(0, ("make_a_mod: out of memory!\n"));
363                         return;
364                 }
365                 mods[i] = SMB_MALLOC_P(LDAPMod);
366                 if (mods[i] == NULL) {
367                         DEBUG(0, ("make_a_mod: out of memory!\n"));
368                         return;
369                 }
370                 mods[i]->mod_op = modop;
371                 mods[i]->mod_values = NULL;
372                 mods[i]->mod_type = SMB_STRDUP(attribute);
373                 mods[i + 1] = NULL;
374         }
375
376         if (value != NULL) {
377                 char *utf8_value = NULL;
378
379                 j = 0;
380                 if (mods[i]->mod_values != NULL) {
381                         for (; mods[i]->mod_values[j] != NULL; j++);
382                 }
383                 mods[i]->mod_values = SMB_REALLOC_ARRAY(mods[i]->mod_values, char *, j + 2);
384                                                
385                 if (mods[i]->mod_values == NULL) {
386                         DEBUG (0, ("make_a_mod: Memory allocation failure!\n"));
387                         return;
388                 }
389
390                 if (push_utf8_allocate(&utf8_value, value) == (size_t)-1) {
391                         DEBUG (0, ("make_a_mod: String conversion failure!\n"));
392                         return;
393                 }
394
395                 mods[i]->mod_values[j] = utf8_value;
396
397                 mods[i]->mod_values[j + 1] = NULL;
398         }
399         *modlist = mods;
400 }
401
402 /**********************************************************************
403   Set attribute to newval in LDAP, regardless of what value the
404   attribute had in LDAP before.
405 *********************************************************************/
406
407  void smbldap_make_mod(LDAP *ldap_struct, LDAPMessage *existing,
408                       LDAPMod ***mods,
409                       const char *attribute, const char *newval)
410 {
411         char oldval[2048]; /* current largest allowed value is mungeddial */
412         BOOL existed;
413
414         if (attribute == NULL) {
415                 /* This can actually happen for ldapsam_compat where we for
416                  * example don't have a password history */
417                 return;
418         }
419
420         if (existing != NULL) {
421                 existed = smbldap_get_single_attribute(ldap_struct, existing, attribute, oldval, sizeof(oldval));
422         } else {
423                 existed = False;
424                 *oldval = '\0';
425         }
426
427         /* all of our string attributes are case insensitive */
428         
429         if (existed && newval && (StrCaseCmp(oldval, newval) == 0)) {
430                 
431                 /* Believe it or not, but LDAP will deny a delete and
432                    an add at the same time if the values are the
433                    same... */
434                 DEBUG(10,("smbldap_make_mod: attribute |%s| not changed.\n", attribute));
435                 return;
436         }
437
438         if (existed) {
439                 /* There has been no value before, so don't delete it.
440                  * Here's a possible race: We might end up with
441                  * duplicate attributes */
442                 /* By deleting exactly the value we found in the entry this
443                  * should be race-free in the sense that the LDAP-Server will
444                  * deny the complete operation if somebody changed the
445                  * attribute behind our back. */
446                 /* This will also allow modifying single valued attributes 
447                  * in Novell NDS. In NDS you have to first remove attribute and then
448                  * you could add new value */
449                 
450                 DEBUG(10,("smbldap_make_mod: deleting attribute |%s| values |%s|\n", attribute, oldval));
451                 smbldap_set_mod(mods, LDAP_MOD_DELETE, attribute, oldval);
452         }
453
454         /* Regardless of the real operation (add or modify)
455            we add the new value here. We rely on deleting
456            the old value, should it exist. */
457
458         if ((newval != NULL) && (strlen(newval) > 0)) {
459                 DEBUG(10,("smbldap_make_mod: adding attribute |%s| value |%s|\n", attribute, newval));
460                 smbldap_set_mod(mods, LDAP_MOD_ADD, attribute, newval);
461         }
462 }
463
464 /**********************************************************************
465  Some varients of the LDAP rebind code do not pass in the third 'arg' 
466  pointer to a void*, so we try and work around it by assuming that the 
467  value of the 'LDAP *' pointer is the same as the one we had passed in
468  **********************************************************************/
469
470 struct smbldap_state_lookup {
471         LDAP *ld;
472         struct smbldap_state *smbldap_state;
473         struct smbldap_state_lookup *prev, *next;
474 };
475
476 static struct smbldap_state_lookup *smbldap_state_lookup_list;
477
478 static struct smbldap_state *smbldap_find_state(LDAP *ld) 
479 {
480         struct smbldap_state_lookup *t;
481
482         for (t = smbldap_state_lookup_list; t; t = t->next) {
483                 if (t->ld == ld) {
484                         return t->smbldap_state;
485                 }
486         }
487         return NULL;
488 }
489
490 static void smbldap_delete_state(struct smbldap_state *smbldap_state) 
491 {
492         struct smbldap_state_lookup *t;
493
494         for (t = smbldap_state_lookup_list; t; t = t->next) {
495                 if (t->smbldap_state == smbldap_state) {
496                         DLIST_REMOVE(smbldap_state_lookup_list, t);
497                         SAFE_FREE(t);
498                         return;
499                 }
500         }
501 }
502
503 static void smbldap_store_state(LDAP *ld, struct smbldap_state *smbldap_state) 
504 {
505         struct smbldap_state *tmp_ldap_state;
506         struct smbldap_state_lookup *t;
507         struct smbldap_state_lookup *tmp;
508         
509         if ((tmp_ldap_state = smbldap_find_state(ld))) {
510                 SMB_ASSERT(tmp_ldap_state == smbldap_state);
511                 return;
512         }
513
514         t = SMB_XMALLOC_P(struct smbldap_state_lookup);
515         ZERO_STRUCTP(t);
516         
517         DLIST_ADD_END(smbldap_state_lookup_list, t, tmp);
518         t->ld = ld;
519         t->smbldap_state = smbldap_state;
520 }
521
522 /*******************************************************************
523  open a connection to the ldap server.
524 ******************************************************************/
525 static int smbldap_open_connection (struct smbldap_state *ldap_state)
526
527 {
528         int rc = LDAP_SUCCESS;
529         int version;
530         BOOL ldap_v3 = False;
531         LDAP **ldap_struct = &ldap_state->ldap_struct;
532
533 #ifdef HAVE_LDAP_INITIALIZE
534         DEBUG(10, ("smbldap_open_connection: %s\n", ldap_state->uri));
535         
536         if ((rc = ldap_initialize(ldap_struct, ldap_state->uri)) != LDAP_SUCCESS) {
537                 DEBUG(0, ("ldap_initialize: %s\n", ldap_err2string(rc)));
538                 return rc;
539         }
540 #else 
541
542         /* Parse the string manually */
543
544         {
545                 int port = 0;
546                 fstring protocol;
547                 fstring host;
548                 const char *p = ldap_state->uri; 
549                 SMB_ASSERT(sizeof(protocol)>10 && sizeof(host)>254);
550                 
551                 /* skip leading "URL:" (if any) */
552                 if ( strnequal( p, "URL:", 4 ) ) {
553                         p += 4;
554                 }
555                 
556                 sscanf(p, "%10[^:]://%254[^:/]:%d", protocol, host, &port);
557                 
558                 if (port == 0) {
559                         if (strequal(protocol, "ldap")) {
560                                 port = LDAP_PORT;
561                         } else if (strequal(protocol, "ldaps")) {
562                                 port = LDAPS_PORT;
563                         } else {
564                                 DEBUG(0, ("unrecognised protocol (%s)!\n", protocol));
565                         }
566                 }
567                 
568                 if ((*ldap_struct = ldap_init(host, port)) == NULL)     {
569                         DEBUG(0, ("ldap_init failed !\n"));
570                         return LDAP_OPERATIONS_ERROR;
571                 }
572                 
573                 if (strequal(protocol, "ldaps")) {
574 #ifdef LDAP_OPT_X_TLS
575                         int tls = LDAP_OPT_X_TLS_HARD;
576                         if (ldap_set_option (*ldap_struct, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS)
577                         {
578                                 DEBUG(0, ("Failed to setup a TLS session\n"));
579                         }
580                         
581                         DEBUG(3,("LDAPS option set...!\n"));
582 #else
583                         DEBUG(0,("smbldap_open_connection: Secure connection not supported by LDAP client libraries!\n"));
584                         return LDAP_OPERATIONS_ERROR;
585 #endif
586                 }
587         }
588 #endif
589
590         /* Store the LDAP pointer in a lookup list */
591
592         smbldap_store_state(*ldap_struct, ldap_state);
593
594         /* Upgrade to LDAPv3 if possible */
595
596         if (ldap_get_option(*ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS)
597         {
598                 if (version != LDAP_VERSION3)
599                 {
600                         version = LDAP_VERSION3;
601                         if (ldap_set_option (*ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS) {
602                                 ldap_v3 = True;
603                         }
604                 } else {
605                         ldap_v3 = True;
606                 }
607         }
608
609         if (lp_ldap_ssl() == LDAP_SSL_START_TLS) {
610 #ifdef LDAP_OPT_X_TLS
611                 if (ldap_v3) {
612                         if ((rc = ldap_start_tls_s (*ldap_struct, NULL, NULL)) != LDAP_SUCCESS)
613                         {
614                                 DEBUG(0,("Failed to issue the StartTLS instruction: %s\n",
615                                          ldap_err2string(rc)));
616                                 return rc;
617                         }
618                         DEBUG (3, ("StartTLS issued: using a TLS connection\n"));
619                 } else {
620                         
621                         DEBUG(0, ("Need LDAPv3 for Start TLS\n"));
622                         return LDAP_OPERATIONS_ERROR;
623                 }
624 #else
625                 DEBUG(0,("smbldap_open_connection: StartTLS not supported by LDAP client libraries!\n"));
626                 return LDAP_OPERATIONS_ERROR;
627 #endif
628         }
629
630         DEBUG(2, ("smbldap_open_connection: connection opened\n"));
631         return rc;
632 }
633
634
635 /*******************************************************************
636  a rebind function for authenticated referrals
637  This version takes a void* that we can shove useful stuff in :-)
638 ******************************************************************/
639 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
640 #else
641 static int rebindproc_with_state  (LDAP * ld, char **whop, char **credp, 
642                                    int *methodp, int freeit, void *arg)
643 {
644         struct smbldap_state *ldap_state = arg;
645         
646         /** @TODO Should we be doing something to check what servers we rebind to?
647             Could we get a referral to a machine that we don't want to give our
648             username and password to? */
649         
650         if (freeit) {
651                 SAFE_FREE(*whop);
652                 memset(*credp, '\0', strlen(*credp));
653                 SAFE_FREE(*credp);
654         } else {
655                 DEBUG(5,("rebind_proc_with_state: Rebinding as \"%s\"\n", 
656                           ldap_state->bind_dn));
657
658                 *whop = SMB_STRDUP(ldap_state->bind_dn);
659                 if (!*whop) {
660                         return LDAP_NO_MEMORY;
661                 }
662                 *credp = SMB_STRDUP(ldap_state->bind_secret);
663                 if (!*credp) {
664                         SAFE_FREE(*whop);
665                         return LDAP_NO_MEMORY;
666                 }
667                 *methodp = LDAP_AUTH_SIMPLE;
668         }
669
670         GetTimeOfDay(&ldap_state->last_rebind);
671                 
672         return 0;
673 }
674 #endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
675
676 /*******************************************************************
677  a rebind function for authenticated referrals
678  This version takes a void* that we can shove useful stuff in :-)
679  and actually does the connection.
680 ******************************************************************/
681 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
682 static int rebindproc_connect_with_state (LDAP *ldap_struct, 
683                                           LDAP_CONST char *url, 
684                                           ber_tag_t request,
685                                           ber_int_t msgid, void *arg)
686 {
687         struct smbldap_state *ldap_state = arg;
688         int rc;
689         DEBUG(5,("rebindproc_connect_with_state: Rebinding as \"%s\"\n", 
690                  ldap_state->bind_dn));
691         
692         /** @TODO Should we be doing something to check what servers we rebind to?
693             Could we get a referral to a machine that we don't want to give our
694             username and password to? */
695
696         rc = ldap_simple_bind_s(ldap_struct, ldap_state->bind_dn, ldap_state->bind_secret);
697         
698         GetTimeOfDay(&ldap_state->last_rebind);
699
700         return rc;
701 }
702 #endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
703
704 /*******************************************************************
705  Add a rebind function for authenticated referrals
706 ******************************************************************/
707 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
708 #else
709 # if LDAP_SET_REBIND_PROC_ARGS == 2
710 static int rebindproc (LDAP *ldap_struct, char **whop, char **credp,
711                        int *method, int freeit )
712 {
713         struct smbldap_state *ldap_state = smbldap_find_state(ldap_struct);
714
715         return rebindproc_with_state(ldap_struct, whop, credp,
716                                      method, freeit, ldap_state);
717         
718 }
719 # endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/
720 #endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
721
722 /*******************************************************************
723  a rebind function for authenticated referrals
724  this also does the connection, but no void*.
725 ******************************************************************/
726 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
727 # if LDAP_SET_REBIND_PROC_ARGS == 2
728 static int rebindproc_connect (LDAP * ld, LDAP_CONST char *url, int request,
729                                ber_int_t msgid)
730 {
731         struct smbldap_state *ldap_state = smbldap_find_state(ld);
732
733         return rebindproc_connect_with_state(ld, url, (ber_tag_t)request, msgid, 
734                                              ldap_state);
735 }
736 # endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/
737 #endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
738
739 /*******************************************************************
740  connect to the ldap server under system privilege.
741 ******************************************************************/
742 static int smbldap_connect_system(struct smbldap_state *ldap_state, LDAP * ldap_struct)
743 {
744         int rc;
745         char *ldap_dn;
746         char *ldap_secret;
747         int version;
748
749         /* get the password */
750         if (!fetch_ldap_pw(&ldap_dn, &ldap_secret)) {
751                 DEBUG(0, ("ldap_connect_system: Failed to retrieve password from secrets.tdb\n"));
752                 return LDAP_INVALID_CREDENTIALS;
753         }
754
755         ldap_state->bind_dn = ldap_dn;
756         ldap_state->bind_secret = ldap_secret;
757
758         /* removed the sasl_bind_s "EXTERNAL" stuff, as my testsuite 
759            (OpenLDAP) doesnt' seem to support it */
760            
761         DEBUG(10,("ldap_connect_system: Binding to ldap server %s as \"%s\"\n",
762                   ldap_state->uri, ldap_dn));
763
764 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
765 # if LDAP_SET_REBIND_PROC_ARGS == 2     
766         ldap_set_rebind_proc(ldap_struct, &rebindproc_connect); 
767 # endif
768 # if LDAP_SET_REBIND_PROC_ARGS == 3     
769         ldap_set_rebind_proc(ldap_struct, &rebindproc_connect_with_state, (void *)ldap_state);  
770 # endif
771 #else /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
772 # if LDAP_SET_REBIND_PROC_ARGS == 2     
773         ldap_set_rebind_proc(ldap_struct, &rebindproc); 
774 # endif
775 # if LDAP_SET_REBIND_PROC_ARGS == 3     
776         ldap_set_rebind_proc(ldap_struct, &rebindproc_with_state, (void *)ldap_state);  
777 # endif
778 #endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
779
780         rc = ldap_simple_bind_s(ldap_struct, ldap_dn, ldap_secret);
781
782         if (rc != LDAP_SUCCESS) {
783                 char *ld_error = NULL;
784                 ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
785                                 &ld_error);
786                 DEBUG(ldap_state->num_failures ? 2 : 0,
787                       ("failed to bind to server %s with dn=\"%s\" Error: %s\n\t%s\n",
788                                ldap_state->uri,
789                                ldap_dn ? ldap_dn : "(unknown)", ldap_err2string(rc),
790                                ld_error ? ld_error : "(unknown)"));
791                 SAFE_FREE(ld_error);
792                 ldap_state->num_failures++;
793                 return rc;
794         }
795
796         ldap_state->num_failures = 0;
797         ldap_state->paged_results = False;
798
799         ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version);
800
801         if (smbldap_has_control(ldap_state, ADS_PAGE_CTL_OID) && version == 3) {
802                 ldap_state->paged_results = True;
803         }
804
805         DEBUG(3, ("ldap_connect_system: succesful connection to the LDAP server\n"));
806         DEBUGADD(10, ("ldap_connect_system: LDAP server %s support paged results\n", 
807                 ldap_state->paged_results ? "does" : "does not"));
808         return rc;
809 }
810
811 /**********************************************************************
812  Connect to LDAP server (called before every ldap operation)
813 *********************************************************************/
814 static int smbldap_open(struct smbldap_state *ldap_state)
815 {
816         int rc, opt_rc;
817         BOOL reopen = False;
818         SMB_ASSERT(ldap_state);
819                 
820 #ifndef NO_LDAP_SECURITY
821         if (geteuid() != 0) {
822                 DEBUG(0, ("smbldap_open: cannot access LDAP when not root..\n"));
823                 return  LDAP_INSUFFICIENT_ACCESS;
824         }
825 #endif
826
827         if ((ldap_state->ldap_struct != NULL) && ((ldap_state->last_ping + SMBLDAP_DONT_PING_TIME) < time(NULL))) {
828
829                 struct sockaddr_un addr;
830                 socklen_t len = sizeof(addr);
831                 int sd;
832
833                 opt_rc = ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_DESC, &sd);
834                 if (opt_rc == 0 && (getpeername(sd, (struct sockaddr *) &addr, &len)) < 0 )
835                         reopen = True;
836
837 #ifdef HAVE_UNIXSOCKET
838                 if (opt_rc == 0 && addr.sun_family == AF_UNIX)
839                         reopen = True;
840 #endif
841                 if (reopen) {
842                         /* the other end has died. reopen. */
843                         ldap_unbind_ext(ldap_state->ldap_struct, NULL, NULL);
844                         ldap_state->ldap_struct = NULL;
845                         ldap_state->last_ping = (time_t)0;
846                 } else {
847                         ldap_state->last_ping = time(NULL);
848                 } 
849         }
850
851         if (ldap_state->ldap_struct != NULL) {
852                 DEBUG(11,("smbldap_open: already connected to the LDAP server\n"));
853                 return LDAP_SUCCESS;
854         }
855
856         if ((rc = smbldap_open_connection(ldap_state))) {
857                 return rc;
858         }
859
860         if ((rc = smbldap_connect_system(ldap_state, ldap_state->ldap_struct))) {
861                 ldap_unbind_ext(ldap_state->ldap_struct, NULL, NULL);
862                 ldap_state->ldap_struct = NULL;
863                 return rc;
864         }
865
866
867         ldap_state->last_ping = time(NULL);
868         ldap_state->pid = sys_getpid();
869         DEBUG(4,("The LDAP server is succesfully connected\n"));
870
871         return LDAP_SUCCESS;
872 }
873
874 /**********************************************************************
875 Disconnect from LDAP server 
876 *********************************************************************/
877 static NTSTATUS smbldap_close(struct smbldap_state *ldap_state)
878 {
879         if (!ldap_state)
880                 return NT_STATUS_INVALID_PARAMETER;
881                 
882         if (ldap_state->ldap_struct != NULL) {
883                 ldap_unbind_ext(ldap_state->ldap_struct, NULL, NULL);
884                 ldap_state->ldap_struct = NULL;
885         }
886
887         smbldap_delete_state(ldap_state);
888         
889         DEBUG(5,("The connection to the LDAP server was closed\n"));
890         /* maybe free the results here --metze */
891         
892         
893
894         return NT_STATUS_OK;
895 }
896
897 static BOOL got_alarm;
898
899 static void (*old_handler)(int);
900
901 static void gotalarm_sig(int dummy)
902 {
903         got_alarm = True;
904 }
905
906 static int another_ldap_try(struct smbldap_state *ldap_state, int *rc,
907                             int *attempts, time_t endtime)
908 {
909         time_t now = time(NULL);
910         int open_rc = LDAP_SERVER_DOWN;
911
912         if (*rc != LDAP_SERVER_DOWN)
913                 goto no_next;
914
915         if (now >= endtime) {
916                 smbldap_close(ldap_state);
917                 *rc = LDAP_TIMEOUT;
918                 goto no_next;
919         }
920
921         if (*attempts == 0) {
922                 got_alarm = False;
923                 old_handler = CatchSignal(SIGALRM, gotalarm_sig);
924                 alarm(endtime - now);
925
926                 if (ldap_state->pid != sys_getpid())
927                         smbldap_close(ldap_state);
928         }
929
930         while (1) {
931
932                 if (*attempts != 0)
933                         smb_msleep(1000);
934
935                 *attempts += 1;
936
937                 open_rc = smbldap_open(ldap_state);
938
939                 if (open_rc == LDAP_SUCCESS) {
940                         ldap_state->last_use = now;
941                         return True;
942                 }
943
944                 if (got_alarm) {
945                         *rc = LDAP_TIMEOUT;
946                         break;
947                 }
948
949                 if (open_rc != LDAP_SUCCESS) {
950                         DEBUG(1, ("Connection to LDAP server failed for the "
951                                   "%d try!\n", *attempts));
952                 }
953         }
954
955  no_next:
956         CatchSignal(SIGALRM, old_handler);
957         alarm(0);
958         ldap_state->last_use = now;
959         return False;
960 }
961
962 /*********************************************************************
963  ********************************************************************/
964
965 static int smbldap_search_ext(struct smbldap_state *ldap_state,
966                               const char *base, int scope, const char *filter, 
967                               const char *attrs[], int attrsonly,
968                               LDAPControl **sctrls, LDAPControl **cctrls, 
969                               int sizelimit, LDAPMessage **res)
970 {
971         int             rc = LDAP_SERVER_DOWN;
972         int             attempts = 0;
973         char           *utf8_filter;
974         time_t          endtime = time(NULL)+lp_ldap_timeout();
975         struct          timeval timeout;
976
977         SMB_ASSERT(ldap_state);
978         
979         DEBUG(5,("smbldap_search_ext: base => [%s], filter => [%s], "
980                  "scope => [%d]\n", base, filter, scope));
981
982         if (ldap_state->last_rebind.tv_sec > 0) {
983                 struct timeval  tval;
984                 SMB_BIG_INT     tdiff = 0;
985                 int             sleep_time = 0;
986
987                 ZERO_STRUCT(tval);
988                 GetTimeOfDay(&tval);
989
990                 tdiff = usec_time_diff(&tval, &ldap_state->last_rebind);
991                 tdiff /= 1000; /* Convert to milliseconds. */
992
993                 sleep_time = lp_ldap_replication_sleep()-(int)tdiff;
994                 sleep_time = MIN(sleep_time, MAX_LDAP_REPLICATION_SLEEP_TIME);
995
996                 if (sleep_time > 0) {
997                         /* we wait for the LDAP replication */
998                         DEBUG(5,("smbldap_search_ext: waiting %d milliseconds "
999                                  "for LDAP replication.\n",sleep_time));
1000                         smb_msleep(sleep_time);
1001                         DEBUG(5,("smbldap_search_ext: go on!\n"));
1002                 }
1003                 ZERO_STRUCT(ldap_state->last_rebind);
1004         }
1005
1006         if (push_utf8_allocate(&utf8_filter, filter) == (size_t)-1) {
1007                 return LDAP_NO_MEMORY;
1008         }
1009
1010         /* Setup timeout for the ldap_search_ext_s call - local and remote. */
1011         timeout.tv_sec = lp_ldap_timeout();
1012         timeout.tv_usec = 0;
1013
1014         /* Setup alarm timeout.... Do we need both of these ? JRA.
1015          * Yes, I think we do need both of these. The server timeout only
1016          * covers the case where the server's operation takes too long. It
1017          * does not cover the case where the request hangs on its way to the
1018          * server. The server side timeout is not strictly necessary, it's
1019          * just a bit more kind to the server. VL. */
1020
1021         got_alarm = 0;
1022         CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
1023         alarm(lp_ldap_timeout());
1024         /* End setup timeout. */
1025
1026         while (another_ldap_try(ldap_state, &rc, &attempts, endtime))
1027                 rc = ldap_search_ext_s(ldap_state->ldap_struct, base, scope, 
1028                                        utf8_filter,
1029                                        CONST_DISCARD(char **, attrs),
1030                                        attrsonly, sctrls, cctrls, &timeout,
1031                                        sizelimit, res);
1032
1033         SAFE_FREE(utf8_filter);
1034
1035         /* Teardown timeout. */
1036         CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
1037         alarm(0);
1038
1039         if (got_alarm != 0)
1040                 return LDAP_TIMELIMIT_EXCEEDED;
1041
1042         return rc;
1043 }
1044
1045 int smbldap_search(struct smbldap_state *ldap_state, 
1046                    const char *base, int scope, const char *filter, 
1047                    const char *attrs[], int attrsonly, 
1048                    LDAPMessage **res)
1049 {
1050         return smbldap_search_ext(ldap_state, base, scope, filter, attrs,
1051                                   attrsonly, NULL, NULL, LDAP_NO_LIMIT, res);
1052 }
1053
1054 int smbldap_search_paged(struct smbldap_state *ldap_state, 
1055                          const char *base, int scope, const char *filter, 
1056                          const char **attrs, int attrsonly, int pagesize,
1057                          LDAPMessage **res, void **cookie)
1058 {
1059         LDAPControl     pr;
1060         LDAPControl     **rcontrols;
1061         LDAPControl     *controls[2] = { NULL, NULL};
1062         BerElement      *cookie_be = NULL;
1063         struct berval   *cookie_bv = NULL;
1064         int             tmp = 0, i, rc;
1065         BOOL            critical = True;
1066
1067         *res = NULL;
1068
1069         DEBUG(3,("smbldap_search_paged: base => [%s], filter => [%s],"
1070                  "scope => [%d], pagesize => [%d]\n",
1071                  base, filter, scope, pagesize));
1072
1073         cookie_be = ber_alloc_t(LBER_USE_DER);
1074         if (cookie_be == NULL) {
1075                 DEBUG(0,("smbldap_create_page_control: ber_alloc_t returns "
1076                          "NULL\n"));
1077                 return LDAP_NO_MEMORY;
1078         }
1079
1080         /* construct cookie */
1081         if (*cookie != NULL) {
1082                 ber_printf(cookie_be, "{iO}", (ber_int_t) pagesize, *cookie);
1083                 ber_bvfree(*cookie); /* don't need it from last time */
1084                 *cookie = NULL;
1085         } else {
1086                 ber_printf(cookie_be, "{io}", (ber_int_t) pagesize, "", 0);
1087         }
1088         ber_flatten(cookie_be, &cookie_bv);
1089
1090         pr.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
1091         pr.ldctl_iscritical = (char) critical;
1092         pr.ldctl_value.bv_len = cookie_bv->bv_len;
1093         pr.ldctl_value.bv_val = cookie_bv->bv_val;
1094
1095         controls[0] = &pr;
1096         controls[1] = NULL;
1097
1098         rc = smbldap_search_ext(ldap_state, base, scope, filter, attrs, 
1099                                  0, controls, NULL, LDAP_NO_LIMIT, res);
1100
1101         ber_free(cookie_be, 1);
1102         ber_bvfree(cookie_bv);
1103
1104         if (rc != 0) {
1105                 DEBUG(3,("smbldap_search_paged: smbldap_search_ext(%s) "
1106                          "failed with [%s]\n", filter, ldap_err2string(rc)));
1107                 goto done;
1108         }
1109
1110         DEBUG(3,("smbldap_search_paged: search was successfull\n"));
1111
1112         rc = ldap_parse_result(ldap_state->ldap_struct, *res, NULL, NULL, 
1113                                NULL, NULL, &rcontrols,  0);
1114         if (rc != 0) {
1115                 DEBUG(3,("smbldap_search_paged: ldap_parse_result failed " \
1116                          "with [%s]\n", ldap_err2string(rc)));
1117                 goto done;
1118         }
1119
1120         if (rcontrols == NULL)
1121                 goto done;
1122
1123         for (i=0; rcontrols[i]; i++) {
1124
1125                 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) != 0)
1126                         continue;
1127
1128                 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1129                 ber_scanf(cookie_be,"{iO}", &tmp, &cookie_bv);
1130                 /* the berval is the cookie, but must be freed when it is all
1131                    done */
1132                 if (cookie_bv->bv_len)
1133                         *cookie=ber_bvdup(cookie_bv);
1134                 else
1135                         *cookie=NULL;
1136                 ber_bvfree(cookie_bv);
1137                 ber_free(cookie_be, 1);
1138                 break;
1139         }
1140         ldap_controls_free(rcontrols);
1141 done:   
1142         return rc;
1143 }
1144
1145 int smbldap_modify(struct smbldap_state *ldap_state, const char *dn, LDAPMod *attrs[])
1146 {
1147         int             rc = LDAP_SERVER_DOWN;
1148         int             attempts = 0;
1149         char           *utf8_dn;
1150         time_t          endtime = time(NULL)+lp_ldap_timeout();
1151
1152         SMB_ASSERT(ldap_state);
1153
1154         DEBUG(5,("smbldap_modify: dn => [%s]\n", dn ));
1155
1156         if (push_utf8_allocate(&utf8_dn, dn) == (size_t)-1) {
1157                 return LDAP_NO_MEMORY;
1158         }
1159
1160         while (another_ldap_try(ldap_state, &rc, &attempts, endtime))
1161                 rc = ldap_modify_s(ldap_state->ldap_struct, utf8_dn, attrs);
1162                 
1163         SAFE_FREE(utf8_dn);
1164         return rc;
1165 }
1166
1167 int smbldap_add(struct smbldap_state *ldap_state, const char *dn, LDAPMod *attrs[])
1168 {
1169         int             rc = LDAP_SERVER_DOWN;
1170         int             attempts = 0;
1171         char           *utf8_dn;
1172         time_t          endtime = time(NULL)+lp_ldap_timeout();
1173         
1174         SMB_ASSERT(ldap_state);
1175
1176         DEBUG(5,("smbldap_add: dn => [%s]\n", dn ));
1177
1178         if (push_utf8_allocate(&utf8_dn, dn) == (size_t)-1) {
1179                 return LDAP_NO_MEMORY;
1180         }
1181
1182         while (another_ldap_try(ldap_state, &rc, &attempts, endtime))
1183                 rc = ldap_add_s(ldap_state->ldap_struct, utf8_dn, attrs);
1184         
1185         SAFE_FREE(utf8_dn);
1186         return rc;
1187 }
1188
1189 int smbldap_delete(struct smbldap_state *ldap_state, const char *dn)
1190 {
1191         int             rc = LDAP_SERVER_DOWN;
1192         int             attempts = 0;
1193         char           *utf8_dn;
1194         time_t          endtime = time(NULL)+lp_ldap_timeout();
1195         
1196         SMB_ASSERT(ldap_state);
1197
1198         DEBUG(5,("smbldap_delete: dn => [%s]\n", dn ));
1199
1200         if (push_utf8_allocate(&utf8_dn, dn) == (size_t)-1) {
1201                 return LDAP_NO_MEMORY;
1202         }
1203
1204         while (another_ldap_try(ldap_state, &rc, &attempts, endtime))
1205                 rc = ldap_delete_s(ldap_state->ldap_struct, utf8_dn);
1206         
1207         SAFE_FREE(utf8_dn);
1208         return rc;
1209 }
1210
1211 int smbldap_extended_operation(struct smbldap_state *ldap_state, 
1212                                LDAP_CONST char *reqoid, struct berval *reqdata, 
1213                                LDAPControl **serverctrls, LDAPControl **clientctrls, 
1214                                char **retoidp, struct berval **retdatap)
1215 {
1216         int             rc = LDAP_SERVER_DOWN;
1217         int             attempts = 0;
1218         time_t          endtime = time(NULL)+lp_ldap_timeout();
1219         
1220         if (!ldap_state)
1221                 return (-1);
1222
1223         while (another_ldap_try(ldap_state, &rc, &attempts, endtime))
1224                 rc = ldap_extended_operation_s(ldap_state->ldap_struct, reqoid,
1225                                                reqdata, serverctrls,
1226                                                clientctrls, retoidp, retdatap);
1227         return rc;
1228 }
1229
1230 /*******************************************************************
1231  run the search by name.
1232 ******************************************************************/
1233 int smbldap_search_suffix (struct smbldap_state *ldap_state, const char *filter, 
1234                            const char **search_attr, LDAPMessage ** result)
1235 {
1236         int scope = LDAP_SCOPE_SUBTREE;
1237         int rc;
1238
1239         rc = smbldap_search(ldap_state, lp_ldap_suffix(), scope, filter, search_attr, 0, result);
1240
1241         if (rc != LDAP_SUCCESS) {
1242                 char *ld_error = NULL;
1243                 ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
1244                                 &ld_error);
1245                 DEBUG(0,("smbldap_search_suffix: Problem during the LDAP search: %s (%s)\n", 
1246                         ld_error?ld_error:"(unknown)", ldap_err2string (rc)));
1247                 SAFE_FREE(ld_error);
1248         }
1249         
1250         return rc;
1251 }
1252
1253 static void smbldap_idle_fn(void **data, time_t *interval, time_t now)
1254 {
1255         struct smbldap_state *state = (struct smbldap_state *)(*data);
1256
1257         if (state->ldap_struct == NULL) {
1258                 DEBUG(10,("ldap connection not connected...\n"));
1259                 return;
1260         }
1261                 
1262         if ((state->last_use+SMBLDAP_IDLE_TIME) > now) {
1263                 DEBUG(10,("ldap connection not idle...\n"));
1264                 return;
1265         }
1266                 
1267         DEBUG(7,("ldap connection idle...closing connection\n"));
1268         smbldap_close(state);
1269 }
1270
1271 /**********************************************************************
1272  Housekeeping
1273  *********************************************************************/
1274
1275 void smbldap_free_struct(struct smbldap_state **ldap_state) 
1276 {
1277         smbldap_close(*ldap_state);
1278         
1279         if ((*ldap_state)->bind_secret) {
1280                 memset((*ldap_state)->bind_secret, '\0', strlen((*ldap_state)->bind_secret));
1281         }
1282
1283         SAFE_FREE((*ldap_state)->bind_dn);
1284         SAFE_FREE((*ldap_state)->bind_secret);
1285
1286         smb_unregister_idle_event((*ldap_state)->event_id);
1287
1288         *ldap_state = NULL;
1289
1290         /* No need to free any further, as it is talloc()ed */
1291 }
1292
1293
1294 /**********************************************************************
1295  Intitalise the 'general' ldap structures, on which ldap operations may be conducted
1296  *********************************************************************/
1297
1298 NTSTATUS smbldap_init(TALLOC_CTX *mem_ctx, const char *location, struct smbldap_state **smbldap_state) 
1299 {
1300         *smbldap_state = TALLOC_ZERO_P(mem_ctx, struct smbldap_state);
1301         if (!*smbldap_state) {
1302                 DEBUG(0, ("talloc() failed for ldapsam private_data!\n"));
1303                 return NT_STATUS_NO_MEMORY;
1304         }
1305
1306         if (location) {
1307                 (*smbldap_state)->uri = talloc_strdup(mem_ctx, location);
1308         } else {
1309                 (*smbldap_state)->uri = "ldap://localhost";
1310         }
1311
1312         (*smbldap_state)->event_id =
1313                 smb_register_idle_event(smbldap_idle_fn, (void *)(*smbldap_state),
1314                                         SMBLDAP_IDLE_TIME);
1315
1316         if ((*smbldap_state)->event_id == SMB_EVENT_ID_INVALID) {
1317                 DEBUG(0,("Failed to register LDAP idle event!\n"));
1318                 return NT_STATUS_INVALID_HANDLE;
1319         }
1320
1321         return NT_STATUS_OK;
1322 }
1323
1324 /*******************************************************************
1325  Return a copy of the DN for a LDAPMessage. Convert from utf8 to CH_UNIX.
1326 ********************************************************************/
1327
1328 char *smbldap_get_dn(LDAP *ld, LDAPMessage *entry)
1329 {
1330         char *utf8_dn, *unix_dn;
1331
1332         utf8_dn = ldap_get_dn(ld, entry);
1333         if (!utf8_dn) {
1334                 DEBUG (5, ("smbldap_get_dn: ldap_get_dn failed\n"));
1335                 return NULL;
1336         }
1337         if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1338                 DEBUG (0, ("smbldap_get_dn: String conversion failure utf8 [%s]\n", utf8_dn));
1339                 return NULL;
1340         }
1341         ldap_memfree(utf8_dn);
1342         return unix_dn;
1343 }
1344
1345 /*******************************************************************
1346  Check if root-dse has a certain Control or Extension
1347 ********************************************************************/
1348
1349 static BOOL smbldap_check_root_dse(struct smbldap_state *ldap_state, const char **attrs, const char *value) 
1350 {
1351         LDAPMessage *msg = NULL;
1352         LDAPMessage *entry = NULL;
1353         char **values = NULL;
1354         int rc, num_result, num_values, i;
1355         BOOL result = False;
1356
1357         if (!attrs[0]) {
1358                 DEBUG(3,("smbldap_check_root_dse: nothing to look for\n"));
1359                 return False;
1360         }
1361
1362         if (!strequal(attrs[0], "supportedExtension") && 
1363             !strequal(attrs[0], "supportedControl") && 
1364             !strequal(attrs[0], "namingContexts")) {
1365                 DEBUG(3,("smbldap_check_root_dse: no idea what to query root-dse for: %s ?\n", attrs[0]));
1366                 return False;
1367         }
1368
1369         rc = ldap_search_s(ldap_state->ldap_struct, "", LDAP_SCOPE_BASE, 
1370                            "(objectclass=*)", CONST_DISCARD(char **, attrs), 0 , &msg);
1371
1372         if (rc != LDAP_SUCCESS) {
1373                 DEBUG(3,("smbldap_check_root_dse: Could not search rootDSE\n"));
1374                 return False;
1375         }
1376
1377         num_result = ldap_count_entries(ldap_state->ldap_struct, msg);
1378
1379         if (num_result != 1) {
1380                 DEBUG(3,("smbldap_check_root_dse: Expected one rootDSE, got %d\n", num_result));
1381                 goto done;
1382         }
1383
1384         entry = ldap_first_entry(ldap_state->ldap_struct, msg);
1385
1386         if (entry == NULL) {
1387                 DEBUG(3,("smbldap_check_root_dse: Could not retrieve rootDSE\n"));
1388                 goto done;
1389         }
1390
1391         values = ldap_get_values(ldap_state->ldap_struct, entry, attrs[0]);
1392
1393         if (values == NULL) {
1394                 DEBUG(5,("smbldap_check_root_dse: LDAP Server does not support any %s\n", attrs[0]));
1395                 goto done;
1396         }
1397
1398         num_values = ldap_count_values(values);
1399
1400         if (num_values == 0) {
1401                 DEBUG(5,("smbldap_check_root_dse: LDAP Server does not have any %s\n", attrs[0]));
1402                 goto done;
1403         }
1404
1405         for (i=0; i<num_values; i++) {
1406                 if (strcmp(values[i], value) == 0)
1407                         result = True;
1408         }
1409
1410
1411  done:
1412         if (values != NULL)
1413                 ldap_value_free(values);
1414         if (msg != NULL)
1415                 ldap_msgfree(msg);
1416
1417         return result;
1418
1419 }
1420
1421 /*******************************************************************
1422  Check if LDAP-Server supports a certain Control (OID in string format)
1423 ********************************************************************/
1424
1425 BOOL smbldap_has_control(struct smbldap_state *ldap_state, const char *control)
1426 {
1427         const char *attrs[] = { "supportedControl", NULL };
1428         return smbldap_check_root_dse(ldap_state, attrs, control);
1429 }
1430
1431 /*******************************************************************
1432  Check if LDAP-Server supports a certain Extension (OID in string format)
1433 ********************************************************************/
1434
1435 BOOL smbldap_has_extension(struct smbldap_state *ldap_state, const char *extension)
1436 {
1437         const char *attrs[] = { "supportedExtension", NULL };
1438         return smbldap_check_root_dse(ldap_state, attrs, extension);
1439 }
1440
1441 /*******************************************************************
1442  Check if LDAP-Server holds a given namingContext
1443 ********************************************************************/
1444
1445 BOOL smbldap_has_naming_context(struct smbldap_state *ldap_state, const char *naming_context)
1446 {
1447         const char *attrs[] = { "namingContexts", NULL };
1448         return smbldap_check_root_dse(ldap_state, attrs, naming_context);
1449 }