matthew chapman's ldap code, to date. plus docs!
[samba.git] / source3 / passdb / ldap.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 2.0.
4    LDAP protocol helper functions for SAMBA
5    Copyright (C) Jean Fran├žois Micouleau 1998
6    Copyright (C) Matthew Chapman 1998
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21    
22 */
23
24 #include "includes.h"
25
26 #ifdef WITH_LDAP
27
28 #include <lber.h>
29 #include <ldap.h>
30
31 extern int DEBUGLEVEL;
32
33 /* Internal state */
34 LDAP *ldap_struct;
35 LDAPMessage *ldap_results;
36 LDAPMessage *ldap_entry;
37
38 /* LDAP password */
39 static pstring ldap_secret;
40
41
42 /*******************************************************************
43   Open/close connections to the LDAP server.
44  ******************************************************************/    
45
46 BOOL ldap_open_connection(BOOL modify)
47 {
48         int err;
49
50         if (!(ldap_struct = ldap_open(lp_ldap_server(), lp_ldap_port()))) {
51                 DEBUG(0, ("open: %s\n", strerror(errno)));
52                 return (False);
53         }
54
55         err = ldap_simple_bind_s(ldap_struct, lp_ldap_bind_as(), ldap_secret);
56         if (err != LDAP_SUCCESS) {
57                 DEBUG(0, ("bind: %s\n", ldap_err2string(err)));
58                 return (False);
59         }
60
61         DEBUG(2,("Connected to LDAP server\n"));
62         return (True);
63 }
64
65 void ldap_close_connection()
66 {
67         if(!ldap_struct)
68                 return;
69
70         if(ldap_results) {
71                 ldap_msgfree(ldap_results);
72                 ldap_results = NULL; }
73
74         ldap_unbind(ldap_struct);
75         ldap_struct = NULL;
76         
77         DEBUG(2,("Connection closed\n"));
78 }
79
80
81 /*******************************************************************
82   Search the directory using a given filter.
83  ******************************************************************/    
84
85 BOOL ldap_search_for(char *filter)
86 {
87         int err;
88
89         DEBUG(2,("Searching in [%s] for [%s]\n", lp_ldap_suffix(), filter));
90
91         err = ldap_search_s(ldap_struct, lp_ldap_suffix(), LDAP_SCOPE_ONELEVEL,
92                           filter, NULL, 0, &ldap_results);
93
94         if(err != LDAP_SUCCESS) {
95                 DEBUG(0, ("search: %s\n", ldap_err2string(err)));
96         }
97
98         DEBUG(2, ("%d matching entries found\n",
99                   ldap_count_entries(ldap_struct, ldap_results)));
100
101         ldap_entry = ldap_first_entry(ldap_struct, ldap_results);
102         return (True);
103 }
104
105 BOOL ldap_search_by_name(const char *user)
106 {
107         fstring filter;
108
109         slprintf(filter, sizeof(filter)-1,
110                  "(&(uid=%s)(objectclass=sambaAccount))", user);
111         return ldap_search_for(filter);
112 }
113
114 BOOL ldap_search_by_uid(int uid)
115 {
116         fstring filter;
117         
118         slprintf(filter, sizeof(filter)-1, 
119                  "(&(uidNumber=%d)(objectclass=sambaAccount))", uid);
120         return ldap_search_for(filter);
121 }
122
123
124 /*******************************************************************
125   Get the first value of an attribute.
126  ******************************************************************/
127
128 BOOL ldap_get_attribute(char *attribute, char *value)
129 {
130         char **values;
131         
132         if(!(values = ldap_get_values(ldap_struct, ldap_entry, attribute)))
133                 return (False);
134
135         pstrcpy(value, values[0]);
136         ldap_value_free(values);
137         DEBUG(3, ("get: [%s] = [%s]\n", attribute, value));
138         
139         return (True);
140 }
141
142
143 /*******************************************************************
144   Contruct an smb_passwd structure
145  ******************************************************************/
146
147 struct smb_passwd *ldap_getpw()
148 {
149         static struct smb_passwd smbpw;
150         static pstring unix_name;
151         static pstring nt_name;
152         static unsigned char smblmpwd[16];
153         static unsigned char smbntpwd[16];
154         pstring temp;
155
156         if(!ldap_entry)
157                 return NULL;
158
159         if(!ldap_get_attribute("uid", unix_name)) {
160                 DEBUG(0,("Missing uid\n"));
161                 return NULL; }
162         smbpw.unix_name = unix_name;
163
164         DEBUG(2,("Retrieving account [%s]\n",unix_name));
165
166         if(!ldap_get_attribute("uidNumber", temp)) {
167                 DEBUG(0,("Missing uidNumber\n"));
168                 return NULL; }
169         smbpw.unix_uid = atoi(temp);
170
171         if(ldap_get_attribute("ntuid", nt_name)) {
172                 DEBUG(0,("Missing ntuid\n"));
173                 return NULL; }
174         smbpw.nt_name = nt_name;
175
176         if(!ldap_get_attribute("rid", temp)) {
177                 DEBUG(0,("Missing rid\n"));
178                 return NULL; }
179         smbpw.user_rid = atoi(temp);
180
181         if(ldap_get_attribute("acctFlags", temp))
182                 smbpw.acct_ctrl = pwdb_decode_acct_ctrl(temp);
183         else
184                 smbpw.acct_ctrl = ACB_NORMAL;
185
186         if(ldap_get_attribute("lmPassword", temp)) {
187                 pwdb_gethexpwd(temp, smblmpwd);
188                 smbpw.smb_passwd = smblmpwd;
189         } else {
190                 smbpw.smb_passwd = NULL;
191                 smbpw.acct_ctrl |= ACB_DISABLED;
192         }
193
194         if(ldap_get_attribute("ntPassword", temp)) {
195                 pwdb_gethexpwd(temp, smbntpwd);
196                 smbpw.smb_nt_passwd = smbntpwd;
197         } else {
198                 smbpw.smb_nt_passwd = NULL;
199         }
200
201         if(ldap_get_attribute("pwdLastSet", temp))
202                 smbpw.pass_last_set_time = (time_t)strtol(temp, NULL, 16);
203         else
204                 smbpw.pass_last_set_time = (time_t)(-1);
205
206         ldap_entry = ldap_next_entry(ldap_struct, ldap_entry);
207         return &smbpw;
208 }
209
210
211 /************************************************************************
212   Adds a modification to a LDAPMod queue.
213  ************************************************************************/
214
215  void ldap_make_mod(LDAPMod ***modlist,int modop, char *attribute, char *value)
216 {
217         LDAPMod **mods;
218         int i;
219         int j;
220
221         DEBUG(3, ("set: [%s] = [%s]\n", attribute, value));
222         
223         mods = *modlist;
224         
225         if (mods == NULL) {
226                 mods = (LDAPMod **)malloc(sizeof(LDAPMod *));
227                 mods[0] = NULL;
228         }
229         
230         for (i = 0; mods[i] != NULL; ++i) {
231                 if (mods[i]->mod_op == modop && 
232                     !strcasecmp(mods[i]->mod_type, attribute)) {
233                         break;
234                 }
235         }
236         
237         if (mods[i] == NULL) {
238                 mods = (LDAPMod **)realloc(mods, (i+2) * sizeof(LDAPMod *));
239                 mods[i] = (LDAPMod *)malloc(sizeof(LDAPMod));
240                 mods[i]->mod_op = modop;
241                 mods[i]->mod_values = NULL;
242                 mods[i]->mod_type = strdup(attribute);
243                 mods[i+1] = NULL;
244         }
245
246         if (value) {
247                 j = 0;
248                 if (mods[i]->mod_values) {
249                         for (; mods[i]->mod_values[j]; j++);
250                 }
251                 mods[i]->mod_values = (char **)realloc(mods[i]->mod_values,
252                                                   (j+2) * sizeof(char *));
253                 mods[i]->mod_values[j] = strdup(value);
254                 mods[i]->mod_values[j+1] = NULL;
255         }
256
257         *modlist = mods;
258 }
259
260
261 /************************************************************************
262   Queues the necessary modifications to save a smb_passwd structure
263  ************************************************************************/
264
265  void ldap_smbpwmods(struct smb_passwd *newpwd, LDAPMod ***mods, int operation)
266 {
267         fstring temp;
268         int i;
269
270         *mods = NULL;
271         if(operation == LDAP_MOD_ADD) { /* immutable attributes */
272               ldap_make_mod(mods, LDAP_MOD_ADD, "objectclass", "sambaAccount");
273
274               ldap_make_mod(mods, LDAP_MOD_ADD, "uid", newpwd->unix_name);
275               slprintf(temp, sizeof(temp)-1, "%d", newpwd->unix_uid);
276               ldap_make_mod(mods, LDAP_MOD_ADD, "uidNumber", temp);
277
278               ldap_make_mod(mods, LDAP_MOD_ADD, "ntuid", newpwd->nt_name);
279               slprintf(temp, sizeof(temp)-1, "%d", newpwd->user_rid);
280               ldap_make_mod(mods, LDAP_MOD_ADD, "rid", temp);
281         }
282
283         if (newpwd->smb_passwd) {
284               for( i = 0; i < 16; i++) {
285                      slprintf(&temp[2*i], 3, "%02X", newpwd->smb_passwd[i]);
286               }
287               ldap_make_mod(mods, operation, "lmPassword", temp);
288         }
289
290         if (newpwd->smb_nt_passwd) {
291               for( i = 0; i < 16; i++) {
292                      slprintf(&temp[2*i], 3, "%02X", newpwd->smb_nt_passwd[i]);
293               }
294               ldap_make_mod(mods, operation, "ntPassword", temp);
295         }
296
297         newpwd->pass_last_set_time = time(NULL);
298         slprintf(temp, sizeof(temp)-1, "%08X", newpwd->pass_last_set_time);
299         ldap_make_mod(mods, operation, "pwdLastSet", temp);
300
301         ldap_make_mod(mods, operation, "acctFlags",
302                             pwdb_encode_acct_ctrl(newpwd->acct_ctrl,
303                                    NEW_PW_FORMAT_SPACE_PADDED_LEN));
304 }
305
306
307 /************************************************************************
308   Commit changes to a directory entry.
309  *************************************************************************/
310  BOOL ldap_makemods(char *attribute, char *value, LDAPMod **mods, BOOL add)
311 {
312         pstring dn;
313         int entries;
314         int err = 0;
315         BOOL rc;
316
317         slprintf(dn, sizeof(dn)-1, "%s=%s, %s", attribute, value,
318                  lp_ldap_suffix());
319
320         if(!ldap_open_connection(True))
321                 return (False);
322
323         if(add)
324                 err = ldap_add_s(ldap_struct, dn, mods);
325
326         if(!add || (err = LDAP_ALREADY_EXISTS))
327                 err = ldap_modify_s(ldap_struct, dn, mods);
328
329         if(err == LDAP_SUCCESS) {
330                 DEBUG(2,("Updated entry [%s]\n",value));
331                 rc = True;
332         } else {
333                 DEBUG(0,("update: %s\n", ldap_err2string(err)));
334                 rc = False;
335         }
336
337         ldap_close_connection();
338         ldap_mods_free(mods, 1);
339         return rc;
340 }
341
342
343 /***************************************************************
344   Begin/end account enumeration.
345  ****************************************************************/
346
347 static void *ldap_enumfirst(BOOL update)
348 {
349         if (!ldap_open_connection(False))
350                 return NULL;
351
352         ldap_search_for("objectclass=sambaAccount");
353
354         return ldap_struct;
355 }
356
357 static void ldap_enumclose(void *vp)
358 {
359         ldap_close_connection();
360 }
361
362
363 /*************************************************************************
364   Save/restore the current position in a query
365  *************************************************************************/
366
367 static SMB_BIG_UINT ldap_getdbpos(void *vp)
368 {
369         return (SMB_BIG_UINT)((ulong)ldap_entry);
370 }
371
372 static BOOL ldap_setdbpos(void *vp, SMB_BIG_UINT tok)
373 {
374         ldap_entry = (LDAPMessage *)((ulong)tok);
375         return (True);
376 }
377
378
379 /*************************************************************************
380   Return smb_passwd information.
381  *************************************************************************/
382
383 static struct smb_passwd *ldap_getpwbynam(const char *name)
384 {
385         struct smb_passwd *ret;
386
387         if(!ldap_open_connection(False))
388                 return NULL;
389
390         ldap_search_by_name(name);
391         ret = ldap_getpw();
392
393         ldap_close_connection();
394         return ret;
395 }
396
397 static struct smb_passwd *ldap_getpwbyuid(uid_t userid)
398 {
399         struct smb_passwd *ret;
400
401         if(!ldap_open_connection(False))
402                 return NULL;
403
404         ldap_search_by_uid(userid);
405         ret = ldap_getpw();
406
407         ldap_close_connection();
408         return ret;
409 }
410
411 static struct smb_passwd *ldap_getcurrentpw(void *vp)
412 {
413         return ldap_getpw();
414 }
415
416
417 /************************************************************************
418   Modify user information given an smb_passwd struct.
419  *************************************************************************/
420 static BOOL ldap_addpw(struct smb_passwd *newpwd)
421 {
422         LDAPMod **mods;
423
424         ldap_smbpwmods(newpwd, &mods, LDAP_MOD_ADD);
425         return ldap_makemods("uid", newpwd->unix_name, mods, True);
426 }
427
428 static BOOL ldap_modpw(struct smb_passwd *pwd, BOOL override)
429 {
430         LDAPMod **mods;
431
432         ldap_smbpwmods(pwd, &mods, LDAP_MOD_REPLACE);
433         return ldap_makemods("uid", pwd->unix_name, mods, False);
434 }
435
436
437 static struct smb_passdb_ops ldap_ops =
438 {
439         ldap_enumfirst,
440         ldap_enumclose,
441         ldap_getdbpos,
442         ldap_setdbpos,
443
444         ldap_getpwbynam,
445         ldap_getpwbyuid,
446         ldap_getcurrentpw,
447         ldap_addpw,
448         ldap_modpw
449 };
450
451 struct smb_passdb_ops *ldap_initialise_password_db(void)
452 {
453         FILE *pwdfile;
454         char *pwdfilename;
455         char *p;
456
457         pwdfilename = lp_ldap_passwd_file();
458
459         if(pwdfilename[0]) {
460                 if(pwdfile = sys_fopen(pwdfilename, "r")) {
461                         fgets(ldap_secret, sizeof(ldap_secret), pwdfile);
462                         if(p = strchr(ldap_secret, '\n'))
463                                 *p = 0;
464                         fclose(pwdfile);
465                 } else {
466                         DEBUG(0,("Failed to open LDAP passwd file\n"));
467                 }
468         }
469
470         return &ldap_ops;
471 }
472
473 #else
474  void ldap_dummy_function(void);
475  void ldap_dummy_function(void) { } /* stop some compilers complaining */
476 #endif