SAM database "set user info".
[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 connections to the LDAP server.
44  ******************************************************************/    
45
46 BOOL ldap_connect(void)
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 /*******************************************************************
66   close connections to the LDAP server.
67  ******************************************************************/    
68
69 void ldap_disconnect(void)
70 {
71         if(!ldap_struct)
72                 return;
73
74         if(ldap_results) {
75                 ldap_msgfree(ldap_results);
76                 ldap_results = NULL; }
77
78         ldap_unbind(ldap_struct);
79         ldap_struct = NULL;
80         
81         DEBUG(2,("Connection closed\n"));
82 }
83
84
85 /*******************************************************************
86   Search the directory using a given filter.
87  ******************************************************************/    
88
89 BOOL ldap_search_for(char *filter)
90 {
91         int err;
92
93         DEBUG(2,("Searching in [%s] for [%s]\n", lp_ldap_suffix(), filter));
94
95         err = ldap_search_s(ldap_struct, lp_ldap_suffix(), LDAP_SCOPE_ONELEVEL,
96                           filter, NULL, 0, &ldap_results);
97
98         if(err != LDAP_SUCCESS) {
99                 DEBUG(0, ("search: %s\n", ldap_err2string(err)));
100         }
101
102         DEBUG(2, ("%d matching entries found\n",
103                   ldap_count_entries(ldap_struct, ldap_results)));
104
105         ldap_entry = ldap_first_entry(ldap_struct, ldap_results);
106         return (True);
107 }
108
109 BOOL ldap_search_by_name(const char *user)
110 {
111         fstring filter;
112
113         slprintf(filter, sizeof(filter)-1,
114                  "(&(uid=%s)(objectclass=sambaAccount))", user);
115         return ldap_search_for(filter);
116 }
117
118 BOOL ldap_search_by_uid(int uid)
119 {
120         fstring filter;
121         
122         slprintf(filter, sizeof(filter)-1, 
123                  "(&(uidNumber=%d)(objectclass=sambaAccount))", uid);
124         return ldap_search_for(filter);
125 }
126
127
128 /*******************************************************************
129   Get the first value of an attribute.
130  ******************************************************************/
131
132 BOOL ldap_get_attribute(char *attribute, char *value)
133 {
134         char **values;
135         
136         if(!(values = ldap_get_values(ldap_struct, ldap_entry, attribute)))
137                 return (False);
138
139         pstrcpy(value, values[0]);
140         ldap_value_free(values);
141         DEBUG(3, ("get: [%s] = [%s]\n", attribute, value));
142         
143         return (True);
144 }
145
146
147 /*******************************************************************
148   Construct an smb_passwd structure
149  ******************************************************************/
150 struct smb_passwd *ldap_getpw(void)
151 {
152         static struct smb_passwd smbpw;
153         static pstring unix_name;
154         static pstring nt_name;
155         static unsigned char smblmpwd[16];
156         static unsigned char smbntpwd[16];
157         pstring temp;
158
159         if(!ldap_entry)
160                 return NULL;
161
162         if(!ldap_get_attribute("uid", unix_name)) {
163                 DEBUG(0,("Missing uid\n"));
164                 return NULL; }
165         smbpw.unix_name = unix_name;
166
167         DEBUG(2,("Retrieving account [%s]\n",unix_name));
168
169         if(!ldap_get_attribute("uidNumber", temp)) {
170                 DEBUG(0,("Missing uidNumber\n"));
171                 return NULL; }
172         smbpw.unix_uid = atoi(temp);
173
174         if(!ldap_get_attribute("ntuid", nt_name)) {
175                 DEBUG(0,("Missing ntuid\n"));
176                 return NULL; }
177         smbpw.nt_name = nt_name;
178
179         if(!ldap_get_attribute("rid", temp)) {
180                 DEBUG(0,("Missing rid\n"));
181                 return NULL; }
182         smbpw.user_rid = strtol(temp, NULL, 16);
183
184         if(ldap_get_attribute("acctFlags", temp))
185                 smbpw.acct_ctrl = pwdb_decode_acct_ctrl(temp);
186         else
187                 smbpw.acct_ctrl = ACB_NORMAL;
188
189         if(ldap_get_attribute("lmPassword", temp)) {
190                 pwdb_gethexpwd(temp, smblmpwd, NULL);
191                 smbpw.smb_passwd = smblmpwd;
192         } else {
193                 smbpw.smb_passwd = NULL;
194                 smbpw.acct_ctrl |= ACB_DISABLED;
195         }
196
197         if(ldap_get_attribute("ntPassword", temp)) {
198                 pwdb_gethexpwd(temp, smbntpwd, NULL);
199                 smbpw.smb_nt_passwd = smbntpwd;
200         } else {
201                 smbpw.smb_nt_passwd = NULL;
202         }
203
204         if(ldap_get_attribute("pwdLastSet", temp))
205                 smbpw.pass_last_set_time = (time_t)strtol(temp, NULL, 16);
206         else
207                 smbpw.pass_last_set_time = (time_t)(-1);
208
209         return &smbpw;
210 }
211
212
213 /************************************************************************
214   Adds a modification to a LDAPMod queue.
215  ************************************************************************/
216
217  void ldap_make_mod(LDAPMod ***modlist,int modop, char *attribute, char *value)
218 {
219         LDAPMod **mods;
220         int i;
221         int j;
222
223         DEBUG(3, ("set: [%s] = [%s]\n", attribute, value));
224         
225         mods = *modlist;
226         
227         if (mods == NULL) {
228                 mods = (LDAPMod **)malloc(sizeof(LDAPMod *));
229                 mods[0] = NULL;
230         }
231         
232         for (i = 0; mods[i] != NULL; ++i) {
233                 if (mods[i]->mod_op == modop && 
234                     !strcasecmp(mods[i]->mod_type, attribute)) {
235                         break;
236                 }
237         }
238         
239         if (mods[i] == NULL) {
240                 mods = (LDAPMod **)realloc(mods, (i+2) * sizeof(LDAPMod *));
241                 mods[i] = (LDAPMod *)malloc(sizeof(LDAPMod));
242                 mods[i]->mod_op = modop;
243                 mods[i]->mod_values = NULL;
244                 mods[i]->mod_type = strdup(attribute);
245                 mods[i+1] = NULL;
246         }
247
248         if (value) {
249                 j = 0;
250                 if (mods[i]->mod_values) {
251                         for (; mods[i]->mod_values[j]; j++);
252                 }
253                 mods[i]->mod_values = (char **)realloc(mods[i]->mod_values,
254                                                   (j+2) * sizeof(char *));
255                 mods[i]->mod_values[j] = strdup(value);
256                 mods[i]->mod_values[j+1] = NULL;
257         }
258
259         *modlist = mods;
260 }
261
262
263 /************************************************************************
264   Queues the necessary modifications to save a smb_passwd structure
265  ************************************************************************/
266
267  void ldap_smbpwmods(struct smb_passwd *newpwd, LDAPMod ***mods, int operation)
268 {
269         fstring temp;
270         int i;
271
272         *mods = NULL;
273         if(operation == LDAP_MOD_ADD) { /* immutable attributes */
274               ldap_make_mod(mods, LDAP_MOD_ADD, "objectclass", "sambaAccount");
275
276               ldap_make_mod(mods, LDAP_MOD_ADD, "uid", newpwd->unix_name);
277               slprintf(temp, sizeof(temp)-1, "%d", newpwd->unix_uid);
278               ldap_make_mod(mods, LDAP_MOD_ADD, "uidNumber", temp);
279
280               ldap_make_mod(mods, LDAP_MOD_ADD, "ntuid", newpwd->nt_name);
281               slprintf(temp, sizeof(temp)-1, "%x", newpwd->user_rid);
282               ldap_make_mod(mods, LDAP_MOD_ADD, "rid", temp);
283         }
284
285         if (newpwd->smb_passwd) {
286               for( i = 0; i < 16; i++) {
287                      slprintf(&temp[2*i], 3, "%02X", newpwd->smb_passwd[i]);
288               }
289               ldap_make_mod(mods, operation, "lmPassword", temp);
290         }
291
292         if (newpwd->smb_nt_passwd) {
293               for( i = 0; i < 16; i++) {
294                      slprintf(&temp[2*i], 3, "%02X", newpwd->smb_nt_passwd[i]);
295               }
296               ldap_make_mod(mods, operation, "ntPassword", temp);
297         }
298
299         newpwd->pass_last_set_time = time(NULL);
300         slprintf(temp, sizeof(temp)-1, "%08X", newpwd->pass_last_set_time);
301         ldap_make_mod(mods, operation, "pwdLastSet", temp);
302
303         ldap_make_mod(mods, operation, "acctFlags",
304                             pwdb_encode_acct_ctrl(newpwd->acct_ctrl,
305                                    NEW_PW_FORMAT_SPACE_PADDED_LEN));
306 }
307
308
309 /************************************************************************
310   Commit changes to a directory entry.
311  *************************************************************************/
312  BOOL ldap_makemods(char *attribute, char *value, LDAPMod **mods, BOOL add)
313 {
314         pstring filter;
315         char *dn;
316         int entries;
317         int err = 0;
318         BOOL rc;
319
320         slprintf(filter, sizeof(filter)-1, "%s=%s", attribute, value);
321
322         if (!ldap_connect())
323                 return (False);
324
325         ldap_search_for(filter);
326
327         if (ldap_entry)
328         {
329                 dn = ldap_get_dn(ldap_struct, ldap_entry);
330                 err = ldap_modify_s(ldap_struct, dn, mods);
331                 free(dn);
332         }
333         else if (add)
334         {
335                 pstrcat(filter, ", ");
336                 pstrcat(filter, lp_ldap_suffix());
337                 err = ldap_add_s(ldap_struct, filter, mods);
338         }
339
340         if (err == LDAP_SUCCESS)
341         {
342                 DEBUG(2,("Updated entry [%s]\n", value));
343                 rc = True;
344         } else {
345                 DEBUG(0,("update: %s\n", ldap_err2string(err)));
346                 rc = False;
347         }
348
349         ldap_disconnect();
350         ldap_mods_free(mods, 1);
351         return rc;
352 }
353
354
355 /************************************************************************
356   Return next available RID, starting from 1000
357  ************************************************************************/
358
359 BOOL ldap_allocaterid(uint32 *rid)
360 {
361         pstring newdn;
362         fstring rid_str;
363         LDAPMod **mods;
364         char *dn;
365         int err;
366
367         DEBUG(2, ("Allocating new RID\n"));
368
369         if (!ldap_connect())
370                 return (False);
371
372         ldap_search_for("(&(id=root)(objectClass=sambaConfig))");
373
374         if (ldap_entry && ldap_get_attribute("nextrid", rid_str))
375                 *rid = strtol(rid_str, NULL, 16);
376         else
377                 *rid = 1000;
378
379         mods = NULL;
380         if(!ldap_entry)
381         {
382                 ldap_make_mod(&mods, LDAP_MOD_ADD, "objectClass",
383                               "sambaConfig");
384                 ldap_make_mod(&mods, LDAP_MOD_ADD, "id", "root");
385         }
386
387         slprintf(rid_str, sizeof(fstring)-1, "%x", (*rid) + 1);
388         ldap_make_mod(&mods, LDAP_MOD_REPLACE, "nextrid", rid_str);
389
390         if (ldap_entry)
391         {
392                 dn = ldap_get_dn(ldap_struct, ldap_entry);
393                 err = ldap_modify_s(ldap_struct, dn, mods);
394                 free(dn);
395         } else {
396                 pstrcpy(newdn, "id=root, ");
397                 pstrcat(newdn, lp_ldap_suffix());
398                 ldap_add_s(ldap_struct, newdn, mods);
399         }
400
401         ldap_disconnect();
402
403         if(err != LDAP_SUCCESS)
404         {
405                 DEBUG(0,("nextrid update: %s\n", ldap_err2string(err)));
406                 return (False);
407         }
408
409         return (True);
410 }
411
412
413 /***************************************************************
414   Begin/end account enumeration.
415  ****************************************************************/
416
417 static void *ldap_enumfirst(BOOL update)
418 {
419         if (!ldap_connect())
420                 return NULL;
421
422         ldap_search_for("objectclass=sambaAccount");
423
424         return ldap_struct;
425 }
426
427 static void ldap_enumclose(void *vp)
428 {
429         ldap_disconnect();
430 }
431
432
433 /*************************************************************************
434   Save/restore the current position in a query
435  *************************************************************************/
436
437 static SMB_BIG_UINT ldap_getdbpos(void *vp)
438 {
439         return (SMB_BIG_UINT)((ulong)ldap_entry);
440 }
441
442 static BOOL ldap_setdbpos(void *vp, SMB_BIG_UINT tok)
443 {
444         ldap_entry = (LDAPMessage *)((ulong)tok);
445         return (True);
446 }
447
448
449 /*************************************************************************
450   Return smb_passwd information.
451  *************************************************************************/
452
453 static struct smb_passwd *ldap_getpwbynam(const char *name)
454 {
455         struct smb_passwd *ret;
456
457         if(!ldap_connect())
458                 return NULL;
459
460         ldap_search_by_name(name);
461         ret = ldap_getpw();
462
463         ldap_disconnect();
464         return ret;
465 }
466
467 static struct smb_passwd *ldap_getpwbyuid(uid_t userid)
468 {
469         struct smb_passwd *ret;
470
471         if(!ldap_connect())
472                 return NULL;
473
474         ldap_search_by_uid(userid);
475         ret = ldap_getpw();
476
477         ldap_disconnect();
478         return ret;
479 }
480
481 static struct smb_passwd *ldap_getcurrentpw(void *vp)
482 {
483         struct smb_passwd *ret;
484
485         ret = ldap_getpw();
486         ldap_entry = ldap_next_entry(ldap_struct, ldap_entry);
487         return ret;
488 }
489
490
491 /************************************************************************
492   Modify user information given an smb_passwd struct.
493  *************************************************************************/
494 static BOOL ldap_addpw(struct smb_passwd *newpwd)
495 {
496         LDAPMod **mods;
497
498         if (!newpwd || !ldap_allocaterid(&newpwd->user_rid))
499                 return (False);
500
501         ldap_smbpwmods(newpwd, &mods, LDAP_MOD_ADD);
502         return ldap_makemods("uid", newpwd->unix_name, mods, True);
503 }
504
505 static BOOL ldap_modpw(struct smb_passwd *pwd, BOOL override)
506 {
507         LDAPMod **mods;
508
509         if (!pwd)
510                 return (False);
511
512         ldap_smbpwmods(pwd, &mods, LDAP_MOD_REPLACE);
513         return ldap_makemods("uid", pwd->unix_name, mods, False);
514 }
515
516
517 static struct smb_passdb_ops ldap_ops =
518 {
519         ldap_enumfirst,
520         ldap_enumclose,
521         ldap_getdbpos,
522         ldap_setdbpos,
523
524         ldap_getpwbynam,
525         ldap_getpwbyuid,
526         ldap_getcurrentpw,
527         ldap_addpw,
528         ldap_modpw
529 };
530
531 struct smb_passdb_ops *ldap_initialise_password_db(void)
532 {
533         FILE *pwdfile;
534         char *pwdfilename;
535         char *p;
536
537         pwdfilename = lp_ldap_passwd_file();
538
539         if(pwdfilename[0]) {
540                 if(pwdfile = sys_fopen(pwdfilename, "r")) {
541                         fgets(ldap_secret, sizeof(ldap_secret), pwdfile);
542                         if(p = strchr(ldap_secret, '\n'))
543                                 *p = 0;
544                         fclose(pwdfile);
545                 } else {
546                         DEBUG(0,("Failed to open LDAP passwd file\n"));
547                 }
548         }
549
550         return &ldap_ops;
551 }
552
553 #else
554  void ldap_dummy_function(void);
555  void ldap_dummy_function(void) { } /* stop some compilers complaining */
556 #endif