jean-francois micouleau's well-alpha code for ldap password database stuff!
[ira/wip.git] / source3 / passdb / ldap.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    LDAP protocol helper functions for SAMBA
5    Copyright (C) Jean François Micouleau 1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20    
21 */
22
23 #ifdef USE_LDAP
24
25 #include "includes.h"
26 #include "lber.h"
27 #include "ldap.h"
28
29 extern int DEBUGLEVEL;
30
31 /*******************************************************************
32  open a connection to the ldap serve.
33 ******************************************************************/     
34 BOOL ldap_open_connection(LDAP **ldap_struct)
35 {
36         if ( (*ldap_struct = ldap_open(lp_ldap_server(),lp_ldap_port()) )== NULL)
37         {
38                 DEBUG(0,("%s: The LDAP server is not responding !\n",timestring()));
39                 return(False);
40         }
41         DEBUG(2,("ldap_open_connection: connection opened\n"));
42         return (True);
43 }
44
45
46 /*******************************************************************
47  connect anonymously to the ldap server.
48  FIXME: later (jfm)
49 ******************************************************************/     
50 static BOOL ldap_connect_anonymous(LDAP *ldap_struct)
51 {
52         if ( ldap_simple_bind_s(ldap_struct,lp_ldap_root(),lp_ldap_rootpasswd()) != LDAP_SUCCESS)
53         {
54                 DEBUG(0,("%s: Couldn't bind to the LDAP server !\n", timestring() ));
55                 return(False);
56         }
57         return (True);
58 }
59
60
61 /*******************************************************************
62  connect to the ldap server under system privileg.
63 ******************************************************************/     
64 BOOL ldap_connect_system(LDAP *ldap_struct)
65 {
66         if ( ldap_simple_bind_s(ldap_struct,lp_ldap_root(),lp_ldap_rootpasswd()) != LDAP_SUCCESS)
67         {
68                 DEBUG(0,("%s: Couldn't bind to the LDAP server !\n", timestring() ));
69                 return(False);
70         }
71         DEBUG(2,("ldap_connect_system: succesfull connection to the LDAP server\n"));
72         return (True);
73 }
74
75 /*******************************************************************
76  connect to the ldap server under a particular user.
77 ******************************************************************/     
78 static BOOL ldap_connect_user(LDAP *ldap_struct, char *user, char *password)
79 {
80         if ( ldap_simple_bind_s(ldap_struct,lp_ldap_root(),lp_ldap_rootpasswd()) != LDAP_SUCCESS)
81         {
82                 DEBUG(0,("%s: Couldn't bind to the LDAP server !\n", timestring() ));
83                 return(False);
84         }
85         DEBUG(2,("ldap_connect_user: succesfull connection to the LDAP server\n"));
86         return (True);
87 }
88
89 /*******************************************************************
90  run the search by name.
91 ******************************************************************/     
92 static BOOL ldap_search_one_user(LDAP *ldap_struct, char *filter, LDAPMessage **result)
93 {       
94         int scope = LDAP_SCOPE_ONELEVEL;
95         int rc;
96                 
97         DEBUG(2,("ldap_search_one_user: searching for:[%s]\n", filter));
98                 
99         rc=ldap_search_s(ldap_struct, lp_ldap_suffix(), scope, filter, NULL, 0, result);
100
101         if (rc != LDAP_SUCCESS )
102         {
103                 DEBUG(0,("%s: Problem during the LDAP search\n",timestring()));
104                 return(False);
105         }
106         return (True);
107 }
108
109 /*******************************************************************
110  run the search by name.
111 ******************************************************************/     
112 BOOL ldap_search_one_user_by_name(LDAP *ldap_struct, char *user, LDAPMessage **result)
113 {       
114         pstring filter;
115         /*
116            in the filter expression, replace %u with the real name
117            so in ldap filter, %u MUST exist :-)
118         */      
119         strcpy(filter,lp_ldap_filter());
120         string_sub(filter,"%u",user);
121         
122         if ( !ldap_search_one_user(ldap_struct, filter, result) )
123         {
124                 return(False);
125         }
126         return (True);
127 }
128
129 /*******************************************************************
130  run the search by uid.
131 ******************************************************************/     
132 BOOL ldap_search_one_user_by_uid(LDAP *ldap_struct, int uid, LDAPMessage **result)
133 {       
134         pstring filter;
135         /*
136            in the filter expression, replace %u with the real name
137            so in ldap filter, %u MUST exist :-)
138         */
139         snprintf(filter, sizeof(pstring), "uidAccount=%d", uid);
140         
141         if ( !ldap_search_one_user(ldap_struct, filter, result) )
142         {       
143                 return(False);
144         }
145         return (True);
146 }
147
148 /*******************************************************************
149  search an attribute and return the first value found.
150 ******************************************************************/
151 void get_single_attribute(LDAP *ldap_struct, LDAPMessage *entry, char *attribute, char *value)
152 {
153         char **valeurs;
154         
155         if ( (valeurs=ldap_get_values(ldap_struct, entry, attribute)) != NULL) 
156         {
157                 strcpy(value, valeurs[0]);
158                 ldap_value_free(valeurs);
159                 DEBUG(3,("get_single_attribute: [%s]=[%s]\n", attribute, value));       
160         }
161         else
162         {
163                 value=NULL;
164         }
165 }
166
167 /*******************************************************************
168  find a user or a machine return a smbpass struct.
169 ******************************************************************/
170 struct passwd *Get_ldap_Pwnam(char *user)
171 {
172         LDAP *ldap_struct;
173         LDAPMessage *result;
174         LDAPMessage *entry;
175         char **valeur;
176         BOOL machine=False;
177         BOOL sambaAccount=False;
178         int i;
179         
180         static struct passwd ldap_passwd;
181         static char pw_name[256];
182         static char pw_passwd[256];
183         static char pw_gecos[256];
184         static char pw_dir[256];
185         static char pw_shell[256];
186         ldap_passwd.pw_name=pw_name;
187         ldap_passwd.pw_passwd=pw_passwd;
188         ldap_passwd.pw_gecos=pw_gecos;
189         ldap_passwd.pw_dir=pw_dir;
190         ldap_passwd.pw_shell=pw_shell;
191         
192         DEBUG(0,("XXXX XXXX XXXX, ca merde serieux!\n"));
193
194         /* first clear the struct */
195         bzero(pw_name,sizeof(pw_name));
196         bzero(pw_passwd,sizeof(pw_passwd));
197         bzero(pw_gecos,sizeof(pw_gecos));
198         bzero(pw_dir,sizeof(pw_dir));
199         bzero(pw_shell,sizeof(pw_shell));       
200         ldap_passwd.pw_uid=-1;
201         ldap_passwd.pw_gid=-1;
202
203         
204         ldap_open_connection(&ldap_struct);
205         
206         /* 
207            to get all the attributes (specially the userPassword )
208            we have to connect under the system administrator account
209         */
210         ldap_connect_system(ldap_struct);
211         
212         ldap_search_one_user(ldap_struct, user, &result);
213
214         if (ldap_count_entries(ldap_struct, result) != 1)
215         {
216                 DEBUG(0,("%s: Strange %d user in the base!\n",
217                          timestring(), ldap_count_entries(ldap_struct, result) ));
218                 return(False);  
219         }
220         /* take the first and unique entry */
221         entry=ldap_first_entry(ldap_struct, result);
222
223         /* check what kind of account it is */
224         /* as jeremy doesn't want to split getpwnam in 2 functions :-( */
225
226         if (user[strlen(user)-1]=='$')
227         {
228                 machine=True;
229         }
230
231         if (!machine)
232         {
233                 valeur=ldap_get_values(ldap_struct,entry, "objectclass");
234
235                 /* check if the entry is a person objectclass*/
236                 if (valeur!=NULL)
237                 for (i=0;valeur[i]!=NULL;i++)
238                 {
239                         if (!strcmp(valeur[i],"sambaAccount")) sambaAccount=True;
240                 }
241                 ldap_value_free(valeur);
242                                 
243                 if (sambaAccount)
244                 {
245                 /* we should have enough info to fill the struct */
246                         strncpy(ldap_passwd.pw_name,user,strlen(user));
247
248                         valeur=ldap_get_values(ldap_struct,entry, "uidAccount");
249                         if (valeur != NULL)
250                         {
251                                 ldap_passwd.pw_uid=atoi(valeur[0]);
252                         }
253                         ldap_value_free(valeur);
254                         
255                         valeur=ldap_get_values(ldap_struct,entry, "gidAccount");
256                         if (valeur != NULL)
257                         {
258                                 ldap_passwd.pw_gid=atoi(valeur[0]);
259                         }
260                         ldap_value_free(valeur);
261
262                         valeur=ldap_get_values(ldap_struct,entry, "userPassword");
263                         if (valeur != NULL) 
264                         {
265                         /*
266                          as we have the clear-text password, we have to crypt it !
267                          hum hum hum currently pass the clear text password to wait
268                         */
269                         strncpy(ldap_passwd.pw_passwd,valeur[0],strlen(valeur[0]));
270                         }
271                         ldap_value_free(valeur);
272                         
273                         valeur=ldap_get_values(ldap_struct,entry, "gecos");
274                         if (valeur != NULL) 
275                         {
276                                 strncpy(ldap_passwd.pw_gecos,valeur[0],strlen(valeur[0]));
277                         }
278                         ldap_value_free(valeur);
279                         
280                         valeur=ldap_get_values(ldap_struct,entry, "homeDirectory");
281                         if (valeur != NULL) 
282                         {
283                                 strncpy(ldap_passwd.pw_dir,valeur[0],strlen(valeur[0]));
284                         }
285                         ldap_value_free(valeur);
286
287                         valeur=ldap_get_values(ldap_struct,entry, "loginShell");
288                         if (valeur != NULL) 
289                         {
290                                 strncpy(ldap_passwd.pw_shell,valeur[0],strlen(valeur[0]));
291                         }
292                         ldap_value_free(valeur);                
293                 }
294         }
295         else
296         {
297         }
298
299         ldap_unbind(ldap_struct);       
300 }
301
302 /*******************************************************************
303  check if the returned entry is a sambaAccount objectclass.
304 ******************************************************************/     
305 BOOL ldap_check_user(LDAP *ldap_struct, LDAPMessage *entry)
306 {
307         BOOL sambaAccount=False;
308         char **valeur;
309         int i;
310
311         DEBUG(2,("ldap_check_user: "));
312         valeur=ldap_get_values(ldap_struct, entry, "objectclass");
313         if (valeur!=NULL)
314         {
315                 for (i=0;valeur[i]!=NULL;i++)
316                 {
317                         if (!strcmp(valeur[i],"sambaAccount")) sambaAccount=True;
318                 }
319         }
320         DEBUG(2,("%s\n",sambaAccount?"yes":"no"));
321         ldap_value_free(valeur);
322         return (sambaAccount);
323 }
324
325 /*******************************************************************
326  check if the returned entry is a sambaMachine objectclass.
327 ******************************************************************/     
328 BOOL ldap_check_machine(LDAP *ldap_struct, LDAPMessage *entry)
329 {
330         BOOL sambaMachine=False;
331         char **valeur;
332         int i;
333         
334         DEBUG(2,("ldap_check_machine: "));
335         valeur=ldap_get_values(ldap_struct, entry, "objectclass");
336         if (valeur!=NULL)
337         {
338                 for (i=0;valeur[i]!=NULL;i++)
339                 {
340                         if (!strcmp(valeur[i],"sambaMachine")) sambaMachine=True;
341                 }
342         }       
343         DEBUG(2,("%s\n",sambaMachine?"yes":"no"));
344         ldap_value_free(valeur);        
345         return (sambaMachine);
346 }
347
348 /*******************************************************************
349  retrieve the user's info and contruct a smb_passwd structure.
350 ******************************************************************/
351 static void ldap_get_user(LDAP *ldap_struct,LDAPMessage *entry, 
352                           struct smb_passwd *ldap_passwd)
353 {       
354         static pstring user_name;
355         static unsigned char smbpwd[16];
356         static unsigned char smbntpwd[16];
357         char **valeur;
358
359         get_single_attribute(ldap_struct, entry, "cn", user_name);
360                 
361         DEBUG(2,("ldap_get_user: user: %s\n",user_name));
362                 
363         if ( (valeur=ldap_get_values(ldap_struct, entry, "uidAccount")) != NULL)
364         {
365                 ldap_passwd->smb_userid=atoi(valeur[0]);
366                 ldap_value_free(valeur);
367         }
368                         
369         if ( (valeur=ldap_get_values(ldap_struct, entry, "userPassword")) != NULL) 
370         {
371                 memset(smbntpwd, '\0', 16);
372                 E_md4hash((uchar *) valeur[0], smbntpwd);
373                 valeur[0][14] = '\0';
374                 strupper(valeur[0]);
375                 memset(smbpwd, '\0', 16);
376                 E_P16((uchar *) valeur[0], smbpwd);             
377                 ldap_value_free(valeur);                
378         }
379                         
380         if ( (valeur=ldap_get_values(ldap_struct,entry, "userAccountControl") ) != NULL)
381         {
382                 ldap_passwd->acct_ctrl=atoi(valeur[0]);
383                 if (ldap_passwd->acct_ctrl & (ACB_DOMTRUST|ACB_WSTRUST|ACB_SVRTRUST) )
384                 {
385                         DEBUG(0,("Inconsistency in the LDAP database\n"));
386                                  
387                 }
388                 if (ldap_passwd->acct_ctrl & ACB_NORMAL)
389                 {
390                         ldap_passwd->smb_name=user_name;
391                         ldap_passwd->smb_passwd=smbpwd;
392                         ldap_passwd->smb_nt_passwd=smbntpwd;
393                 }
394                 ldap_value_free(valeur); 
395         }
396         
397         if ( (valeur=ldap_get_values(ldap_struct,entry, "pwdLastSet")) != NULL)
398         {       
399                 ldap_passwd->last_change_time=(time_t)strtol(valeur[0], NULL, 16);
400                 ldap_value_free(valeur);
401         }
402 }
403
404 /*************************************************************
405  Routine to get the next 32 hex characters and turn them
406  into a 16 byte array.
407 **************************************************************/
408
409 static int gethexpwd(char *p, char *pwd)
410 {
411   int i;
412   unsigned char   lonybble, hinybble;
413   char           *hexchars = "0123456789ABCDEF";
414   char           *p1, *p2;
415
416   for (i = 0; i < 32; i += 2) {
417     hinybble = toupper(p[i]);
418     lonybble = toupper(p[i + 1]);
419  
420     p1 = strchr(hexchars, hinybble);
421     p2 = strchr(hexchars, lonybble);
422     if (!p1 || !p2)
423       return (False);
424     hinybble = PTR_DIFF(p1, hexchars);
425     lonybble = PTR_DIFF(p2, hexchars);
426  
427     pwd[i / 2] = (hinybble << 4) | lonybble;
428   }
429   return (True);
430 }
431
432 /*******************************************************************
433  retrieve the machine's info and contruct a smb_passwd structure.
434 ******************************************************************/
435 static void ldap_get_machine(LDAP *ldap_struct,LDAPMessage *entry, 
436                              struct smb_passwd *ldap_passwd)
437 {       
438         static pstring  user_name;
439         static unsigned char smbntpwd[16];
440         char **valeur;
441         
442         /* by default it's a station */
443         ldap_passwd->acct_ctrl = ACB_WSTRUST;
444
445         get_single_attribute(ldap_struct, entry, "cn", user_name);
446         DEBUG(2,("ldap_get_machine: machine: %s\n", user_name));
447                 
448         if ( (valeur=ldap_get_values(ldap_struct, entry, "uidAccount")) != NULL)
449         {
450                 ldap_passwd->smb_userid=atoi(valeur[0]);
451                 ldap_value_free(valeur);
452         }
453                         
454         if ( (valeur=ldap_get_values(ldap_struct, entry, "machinePassword")) != NULL) 
455         {
456                 gethexpwd(valeur[0],smbntpwd);          
457                 ldap_value_free(valeur);                
458         }
459                         
460         if ( (valeur=ldap_get_values(ldap_struct,entry, "machineRole") ) != NULL)
461         {
462                 if ( !strcmp(valeur[0],"workstation") )
463                         ldap_passwd->acct_ctrl=ACB_WSTRUST;
464                 else
465                 if  ( !strcmp(valeur[0],"server") )
466                         ldap_passwd->acct_ctrl=ACB_SVRTRUST;            
467                 ldap_value_free(valeur); 
468         }
469
470         ldap_passwd->smb_name=user_name;
471         ldap_passwd->smb_passwd=smbntpwd;
472         ldap_passwd->smb_nt_passwd=smbntpwd;
473 }
474
475 /*******************************************************************
476  find a user or a machine return a smbpass struct.
477 ******************************************************************/
478 struct smb_passwd *ldap_get_smbpwd_entry(char *name, int smb_userid)
479 {
480         LDAP *ldap_struct;
481         LDAPMessage *result;
482         LDAPMessage *entry;
483         BOOL machine=False;
484
485         static struct smb_passwd ldap_passwd;
486
487         ldap_passwd.smb_name      = NULL;
488         ldap_passwd.smb_passwd    = NULL;
489         ldap_passwd.smb_nt_passwd = NULL;
490         
491         ldap_passwd.smb_userid       = -1;
492         ldap_passwd.acct_ctrl        = ACB_DISABLED;
493         ldap_passwd.last_change_time = 0;
494
495         ldap_struct=NULL;
496
497         if (name != NULL)
498         {
499                 DEBUG(10, ("ldap_get_smbpwd_entry: search by name: %s\n", name));
500         }
501         else 
502         {
503                 DEBUG(10, ("ldap_get_smbpwd_entry: search by smb_userid: %x\n", smb_userid));
504         }
505
506         if (!ldap_open_connection(&ldap_struct))
507                 return (NULL);
508         if (!ldap_connect_system(ldap_struct))
509                 return (NULL);
510                 
511         if (name != NULL)
512         {
513                 if (!ldap_search_one_user_by_name(ldap_struct, name, &result))
514                         return (NULL);
515         } 
516         else
517         {
518                 if (!ldap_search_one_user_by_uid(ldap_struct, smb_userid, &result))
519                         return (NULL);
520         }
521         
522         if (ldap_count_entries(ldap_struct, result) == 0)
523         {
524                 DEBUG(2,("%s: Non existant user!\n", timestring() ));
525                 return (NULL);  
526         }
527                 
528         if (ldap_count_entries(ldap_struct, result) > 1)
529         {
530                 DEBUG(2,("%s: Strange %d users in the base!\n",
531                          timestring(), ldap_count_entries(ldap_struct, result) ));
532         }
533         /* take the first and unique entry */
534         entry=ldap_first_entry(ldap_struct, result);
535
536         if (name != NULL)
537         {
538                 DEBUG(0,("ldap_get_smbpwd_entry: Found user: %s\n",name));
539
540                 if (name[strlen(name)-1]=='$')
541                         machine=True;
542                 else 
543                         machine=False;
544         }
545                 
546         if (machine==False)
547         {
548                 if (ldap_check_user(ldap_struct, entry)==True)
549                         ldap_get_user(ldap_struct, entry, &ldap_passwd);
550         }
551         else
552         {
553                 if (ldap_check_machine(ldap_struct, entry)==True)
554                         ldap_get_machine(ldap_struct, entry, &ldap_passwd);
555         }
556                                 
557         ldap_msgfree(result);
558         result=NULL;
559         ldap_unbind(ldap_struct);
560                 
561         return(&ldap_passwd);
562 }
563 #endif