Initial import
[samba] / source / passdb / pdb_nds.c
1 /* 
2    Unix SMB/CIFS mplementation.
3    NDS LDAP helper functions for SAMBA
4    Copyright (C) Vince Brimhall                 2004-2005
5     
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19    
20 */
21
22 #include "includes.h"
23
24 #include <lber.h>
25 #include <ldap.h>
26 #include <wchar.h>
27
28 #include "smbldap.h"
29
30 #define NMASLDAP_GET_LOGIN_CONFIG_REQUEST       "2.16.840.1.113719.1.39.42.100.3"
31 #define NMASLDAP_GET_LOGIN_CONFIG_RESPONSE      "2.16.840.1.113719.1.39.42.100.4"
32 #define NMASLDAP_SET_PASSWORD_REQUEST           "2.16.840.1.113719.1.39.42.100.11"
33 #define NMASLDAP_SET_PASSWORD_RESPONSE          "2.16.840.1.113719.1.39.42.100.12"
34 #define NMASLDAP_GET_PASSWORD_REQUEST           "2.16.840.1.113719.1.39.42.100.13"
35 #define NMASLDAP_GET_PASSWORD_RESPONSE          "2.16.840.1.113719.1.39.42.100.14"
36
37 #define NMAS_LDAP_EXT_VERSION                           1
38
39 /**********************************************************************
40  Take the request BER value and input data items and BER encodes the
41  data into the BER value
42 **********************************************************************/
43
44 static int berEncodePasswordData(
45         struct berval **requestBV,
46         const char    *objectDN,
47         const char    *password,
48         const char    *password2)
49 {
50         int err = 0, rc=0;
51         BerElement *requestBer = NULL;
52
53         const char    * utf8ObjPtr = NULL;
54         int     utf8ObjSize = 0;
55         const char    * utf8PwdPtr = NULL;
56         int     utf8PwdSize = 0;
57         const char    * utf8Pwd2Ptr = NULL;
58         int     utf8Pwd2Size = 0;
59
60
61         /* Convert objectDN and tag strings from Unicode to UTF-8 */
62         utf8ObjSize = strlen(objectDN)+1;
63         utf8ObjPtr = objectDN;
64
65         if (password != NULL)
66         {
67                 utf8PwdSize = strlen(password)+1;
68                 utf8PwdPtr = password;
69         }
70
71         if (password2 != NULL)
72         {
73                 utf8Pwd2Size = strlen(password2)+1;
74                 utf8Pwd2Ptr = password2;
75         }
76
77         /* Allocate a BerElement for the request parameters. */
78         if((requestBer = ber_alloc()) == NULL)
79         {
80                 err = LDAP_ENCODING_ERROR;
81                 goto Cleanup;
82         }
83
84         if (password != NULL && password2 != NULL)
85         {
86                 /* BER encode the NMAS Version, the objectDN, and the password */
87                 rc = ber_printf(requestBer, "{iooo}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize, utf8PwdPtr, utf8PwdSize, utf8Pwd2Ptr, utf8Pwd2Size);
88         }
89         else if (password != NULL)
90         {
91                 /* BER encode the NMAS Version, the objectDN, and the password */
92                 rc = ber_printf(requestBer, "{ioo}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize, utf8PwdPtr, utf8PwdSize);
93         }
94         else
95         {
96                 /* BER encode the NMAS Version and the objectDN */
97                 rc = ber_printf(requestBer, "{io}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize);
98         }
99
100         if (rc < 0)
101         {
102                 err = LDAP_ENCODING_ERROR;
103                 goto Cleanup;
104         }
105         else
106         {
107                 err = 0;
108         }
109
110         /* Convert the BER we just built to a berval that we'll send with the extended request. */
111         if(ber_flatten(requestBer, requestBV) == LBER_ERROR)
112         {
113                 err = LDAP_ENCODING_ERROR;
114                 goto Cleanup;
115         }
116
117 Cleanup:
118
119         if(requestBer)
120         {
121                 ber_free(requestBer, 1);
122         }
123
124         return err;
125 }
126
127 /**********************************************************************
128  Take the request BER value and input data items and BER encodes the
129  data into the BER value
130 **********************************************************************/
131
132 static int berEncodeLoginData(
133         struct berval **requestBV,
134         char     *objectDN,
135         unsigned int  methodIDLen,
136         unsigned int *methodID,
137         char     *tag,
138         size_t   putDataLen,
139         void     *putData)
140 {
141         int err = 0;
142         BerElement *requestBer = NULL;
143
144         unsigned int i;
145         unsigned int elemCnt = methodIDLen / sizeof(unsigned int);
146
147         char    *utf8ObjPtr=NULL;
148         int     utf8ObjSize = 0;
149
150         char    *utf8TagPtr = NULL;
151         int     utf8TagSize = 0;
152
153         utf8ObjPtr = objectDN;
154         utf8ObjSize = strlen(utf8ObjPtr)+1;
155
156         utf8TagPtr = tag;
157         utf8TagSize = strlen(utf8TagPtr)+1;
158
159         /* Allocate a BerElement for the request parameters. */
160         if((requestBer = ber_alloc()) == NULL)
161         {
162                 err = LDAP_ENCODING_ERROR;
163                 goto Cleanup;
164         }
165
166         /* BER encode the NMAS Version and the objectDN */
167         err = (ber_printf(requestBer, "{io", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize) < 0) ? LDAP_ENCODING_ERROR : 0;
168
169         /* BER encode the MethodID Length and value */
170         if (!err)
171         {
172                 err = (ber_printf(requestBer, "{i{", methodIDLen) < 0) ? LDAP_ENCODING_ERROR : 0;
173         }
174
175         for (i = 0; !err && i < elemCnt; i++)
176         {
177                 err = (ber_printf(requestBer, "i", methodID[i]) < 0) ? LDAP_ENCODING_ERROR : 0;
178         }
179
180         if (!err)
181         {
182                 err = (ber_printf(requestBer, "}}", 0) < 0) ? LDAP_ENCODING_ERROR : 0;
183         }
184
185         if(putData)
186         {
187                 /* BER Encode the the tag and data */
188                 err = (ber_printf(requestBer, "oio}", utf8TagPtr, utf8TagSize, putDataLen, putData, putDataLen) < 0) ? LDAP_ENCODING_ERROR : 0;
189         }
190         else
191         {
192                 /* BER Encode the the tag */
193                 err = (ber_printf(requestBer, "o}", utf8TagPtr, utf8TagSize) < 0) ? LDAP_ENCODING_ERROR : 0;
194         }
195
196         if (err)
197         {
198                 goto Cleanup;
199         }
200
201         /* Convert the BER we just built to a berval that we'll send with the extended request. */
202         if(ber_flatten(requestBer, requestBV) == LBER_ERROR)
203         {
204                 err = LDAP_ENCODING_ERROR;
205                 goto Cleanup;
206         }
207
208 Cleanup:
209
210         if(requestBer)
211         {
212                 ber_free(requestBer, 1);
213         }
214
215         return err;
216 }
217
218 /**********************************************************************
219  Takes the reply BER Value and decodes the NMAS server version and
220  return code and if a non null retData buffer was supplied, tries to
221  decode the the return data and length
222 **********************************************************************/
223
224 static int berDecodeLoginData(
225         struct berval *replyBV,
226         int      *serverVersion,
227         size_t   *retDataLen,
228         void     *retData )
229 {
230         int err = 0;
231         BerElement *replyBer = NULL;
232         char    *retOctStr = NULL;
233         size_t  retOctStrLen = 0;
234
235         if((replyBer = ber_init(replyBV)) == NULL)
236         {
237                 err = LDAP_OPERATIONS_ERROR;
238                 goto Cleanup;
239         }
240
241         if(retData)
242         {
243                 retOctStrLen = *retDataLen + 1;
244                 retOctStr = SMB_MALLOC(retOctStrLen);
245                 if(!retOctStr)
246                 {
247                         err = LDAP_OPERATIONS_ERROR;
248                         goto Cleanup;
249                 }
250         
251                 if(ber_scanf(replyBer, "{iis}", serverVersion, &err, retOctStr, &retOctStrLen) != -1)
252                 {
253                         if (*retDataLen >= retOctStrLen)
254                         {
255                                 memcpy(retData, retOctStr, retOctStrLen);
256                         }
257                         else if (!err)
258                         {       
259                                 err = LDAP_NO_MEMORY;
260                         }
261
262                         *retDataLen = retOctStrLen;
263                 }
264                 else if (!err)
265                 {
266                         err = LDAP_DECODING_ERROR;
267                 }
268         }
269         else
270         {
271                 if(ber_scanf(replyBer, "{ii}", serverVersion, &err) == -1)
272                 {
273                         if (!err)
274                         {
275                                 err = LDAP_DECODING_ERROR;
276                         }
277                 }
278         }
279
280 Cleanup:
281
282         if(replyBer)
283         {
284                 ber_free(replyBer, 1);
285         }
286
287         if (retOctStr != NULL)
288         {
289                 memset(retOctStr, 0, retOctStrLen);
290                 free(retOctStr);
291         }
292
293         return err;
294 }
295
296 /**********************************************************************
297  Retrieves data in the login configuration of the specified object
298  that is tagged with the specified methodID and tag.
299 **********************************************************************/
300
301 static int getLoginConfig(
302         LDAP     *ld,
303         char     *objectDN,
304         unsigned int  methodIDLen,
305         unsigned int *methodID,
306         char     *tag,
307         size_t   *dataLen,
308         void     *data )
309 {
310         int     err = 0;
311         struct  berval *requestBV = NULL;
312         char    *replyOID = NULL;
313         struct  berval *replyBV = NULL;
314         int     serverVersion = 0;
315
316         /* Validate unicode parameters. */
317         if((strlen(objectDN) == 0) || ld == NULL)
318         {
319                 return LDAP_NO_SUCH_ATTRIBUTE;
320         }
321
322         err = berEncodeLoginData(&requestBV, objectDN, methodIDLen, methodID, tag, 0, NULL);
323         if(err)
324         {
325                 goto Cleanup;
326         }
327
328         /* Call the ldap_extended_operation (synchronously) */
329         if((err = ldap_extended_operation_s(ld, NMASLDAP_GET_LOGIN_CONFIG_REQUEST,
330                                         requestBV, NULL, NULL, &replyOID, &replyBV)))
331         {
332                 goto Cleanup;
333         }
334
335         /* Make sure there is a return OID */
336         if(!replyOID)
337         {
338                 err = LDAP_NOT_SUPPORTED;
339                 goto Cleanup;
340         }
341
342         /* Is this what we were expecting to get back. */
343         if(strcmp(replyOID, NMASLDAP_GET_LOGIN_CONFIG_RESPONSE))
344         {
345                 err = LDAP_NOT_SUPPORTED;
346                 goto Cleanup;
347         }
348
349         /* Do we have a good returned berval? */
350         if(!replyBV)
351         {
352                 /* No; returned berval means we experienced a rather drastic error. */
353                 /* Return operations error. */
354                 err = LDAP_OPERATIONS_ERROR;
355                 goto Cleanup;
356         }
357
358         err = berDecodeLoginData(replyBV, &serverVersion, dataLen, data);
359
360         if(serverVersion != NMAS_LDAP_EXT_VERSION)
361         {
362                 err = LDAP_OPERATIONS_ERROR;
363                 goto Cleanup;
364         }
365
366 Cleanup:
367
368         if(replyBV)
369         {
370                 ber_bvfree(replyBV);
371         }
372
373         /* Free the return OID string if one was returned. */
374         if(replyOID)
375         {
376                 ldap_memfree(replyOID);
377         }
378
379         /* Free memory allocated while building the request ber and berval. */
380         if(requestBV)
381         {
382                 ber_bvfree(requestBV);
383         }
384
385         /* Return the appropriate error/success code. */
386         return err;
387 }
388
389 /**********************************************************************
390  Attempts to get the Simple Password
391 **********************************************************************/
392
393 static int nmasldap_get_simple_pwd(
394         LDAP     *ld,
395         char     *objectDN,
396         size_t   pwdLen,
397         char     *pwd )
398 {
399         int err = 0;
400         unsigned int methodID = 0;
401         unsigned int methodIDLen = sizeof(methodID);
402         char    tag[] = {'P','A','S','S','W','O','R','D',' ','H','A','S','H',0};
403         char    *pwdBuf=NULL;
404         size_t  pwdBufLen, bufferLen;
405
406         bufferLen = pwdBufLen = pwdLen+2;
407         pwdBuf = SMB_MALLOC(pwdBufLen); /* digest and null */
408         if(pwdBuf == NULL)
409         {
410                 return LDAP_NO_MEMORY;
411         }
412
413         err = getLoginConfig(ld, objectDN, methodIDLen, &methodID, tag, &pwdBufLen, pwdBuf);
414         if (err == 0)
415         {
416                 if (pwdBufLen !=0)
417                 {
418                         pwdBuf[pwdBufLen] = 0;       /* null terminate */
419
420                         switch (pwdBuf[0])
421                         {
422                                 case 1:  /* cleartext password  */
423                                         break;
424                                 case 2:  /* SHA1 HASH */
425                                 case 3:  /* MD5_ID */
426                                 case 4:  /* UNIXCrypt_ID */
427                                 case 8:  /* SSHA_ID */
428                                 default: /* Unknown digest */
429                                         err = LDAP_INAPPROPRIATE_AUTH;  /* only return clear text */
430                                         break;
431                         }
432
433                         if (!err)
434                         {
435                                 if (pwdLen >= pwdBufLen-1)
436                                 {
437                                         memcpy(pwd, &pwdBuf[1], pwdBufLen-1);  /* skip digest tag and include null */
438                                 }
439                                 else
440                                 {
441                                         err = LDAP_NO_MEMORY;
442                                 }
443                         }
444                 }
445         }
446
447         if (pwdBuf != NULL)
448         {
449                 memset(pwdBuf, 0, bufferLen);
450                 free(pwdBuf);
451         }
452
453         return err;
454 }
455
456
457 /**********************************************************************
458  Attempts to set the Universal Password
459 **********************************************************************/
460
461 static int nmasldap_set_password(
462         LDAP     *ld,
463         const char     *objectDN,
464         const char     *pwd )
465 {
466         int err = 0;
467
468         struct berval *requestBV = NULL;
469         char *replyOID = NULL;
470         struct berval *replyBV = NULL;
471         int serverVersion;
472
473         /* Validate char parameters. */
474         if(objectDN == NULL || (strlen(objectDN) == 0) || pwd == NULL || ld == NULL)
475         {
476                 return LDAP_NO_SUCH_ATTRIBUTE;
477         }
478
479         err = berEncodePasswordData(&requestBV, objectDN, pwd, NULL);
480         if(err)
481         {
482                 goto Cleanup;
483         }
484
485         /* Call the ldap_extended_operation (synchronously) */
486         if((err = ldap_extended_operation_s(ld, NMASLDAP_SET_PASSWORD_REQUEST, requestBV, NULL, NULL, &replyOID, &replyBV)))
487         {
488                 goto Cleanup;
489         }
490
491         /* Make sure there is a return OID */
492         if(!replyOID)
493         {
494                 err = LDAP_NOT_SUPPORTED;
495                 goto Cleanup;
496         }
497
498         /* Is this what we were expecting to get back. */
499         if(strcmp(replyOID, NMASLDAP_SET_PASSWORD_RESPONSE))
500         {
501                 err = LDAP_NOT_SUPPORTED;
502                 goto Cleanup;
503         }
504
505         /* Do we have a good returned berval? */
506         if(!replyBV)
507         {
508                 /* No; returned berval means we experienced a rather drastic error. */
509                 /* Return operations error. */
510                 err = LDAP_OPERATIONS_ERROR;
511                 goto Cleanup;
512         }
513
514         err = berDecodeLoginData(replyBV, &serverVersion, NULL, NULL);
515
516         if(serverVersion != NMAS_LDAP_EXT_VERSION)
517         {
518                 err = LDAP_OPERATIONS_ERROR;
519                 goto Cleanup;
520         }
521
522 Cleanup:
523
524         if(replyBV)
525         {
526                 ber_bvfree(replyBV);
527         }
528
529         /* Free the return OID string if one was returned. */
530         if(replyOID)
531         {
532                 ldap_memfree(replyOID);
533         }
534
535         /* Free memory allocated while building the request ber and berval. */
536         if(requestBV)
537         {
538                 ber_bvfree(requestBV);
539         }
540
541         /* Return the appropriate error/success code. */
542         return err;
543 }
544
545 /**********************************************************************
546  Attempts to get the Universal Password
547 **********************************************************************/
548
549 static int nmasldap_get_password(
550         LDAP     *ld,
551         char     *objectDN,
552         size_t   *pwdSize,      /* in bytes */
553         unsigned char     *pwd )
554 {
555         int err = 0;
556
557         struct berval *requestBV = NULL;
558         char *replyOID = NULL;
559         struct berval *replyBV = NULL;
560         int serverVersion;
561         char *pwdBuf;
562         size_t pwdBufLen, bufferLen;
563
564         /* Validate char parameters. */
565         if(objectDN == NULL || (strlen(objectDN) == 0) || pwdSize == NULL || ld == NULL)
566         {
567                 return LDAP_NO_SUCH_ATTRIBUTE;
568         }
569
570         bufferLen = pwdBufLen = *pwdSize;
571         pwdBuf = SMB_MALLOC(pwdBufLen+2);
572         if(pwdBuf == NULL)
573         {
574                 return LDAP_NO_MEMORY;
575         }
576
577         err = berEncodePasswordData(&requestBV, objectDN, NULL, NULL);
578         if(err)
579         {
580                 goto Cleanup;
581         }
582
583         /* Call the ldap_extended_operation (synchronously) */
584         if((err = ldap_extended_operation_s(ld, NMASLDAP_GET_PASSWORD_REQUEST, requestBV, NULL, NULL, &replyOID, &replyBV)))
585         {
586                 goto Cleanup;
587         }
588
589         /* Make sure there is a return OID */
590         if(!replyOID)
591         {
592                 err = LDAP_NOT_SUPPORTED;
593                 goto Cleanup;
594         }
595
596         /* Is this what we were expecting to get back. */
597         if(strcmp(replyOID, NMASLDAP_GET_PASSWORD_RESPONSE))
598         {
599                 err = LDAP_NOT_SUPPORTED;
600                 goto Cleanup;
601         }
602
603         /* Do we have a good returned berval? */
604         if(!replyBV)
605         {
606                 /* No; returned berval means we experienced a rather drastic error. */
607                 /* Return operations error. */
608                 err = LDAP_OPERATIONS_ERROR;
609                 goto Cleanup;
610         }
611
612         err = berDecodeLoginData(replyBV, &serverVersion, &pwdBufLen, pwdBuf);
613
614         if(serverVersion != NMAS_LDAP_EXT_VERSION)
615         {
616                 err = LDAP_OPERATIONS_ERROR;
617                 goto Cleanup;
618         }
619
620         if (!err && pwdBufLen != 0)
621         {
622                 if (*pwdSize >= pwdBufLen+1 && pwd != NULL)
623                 {
624                         memcpy(pwd, pwdBuf, pwdBufLen);
625                         pwd[pwdBufLen] = 0; /* add null termination */
626                 }
627                 *pwdSize = pwdBufLen; /* does not include null termination */
628         }
629
630 Cleanup:
631
632         if(replyBV)
633         {
634                 ber_bvfree(replyBV);
635         }
636
637         /* Free the return OID string if one was returned. */
638         if(replyOID)
639         {
640                 ldap_memfree(replyOID);
641         }
642
643         /* Free memory allocated while building the request ber and berval. */
644         if(requestBV)
645         {
646                 ber_bvfree(requestBV);
647         }
648
649         if (pwdBuf != NULL)
650         {
651                 memset(pwdBuf, 0, bufferLen);
652                 free(pwdBuf);
653         }
654
655         /* Return the appropriate error/success code. */
656         return err;
657 }
658
659 /**********************************************************************
660  Get the user's password from NDS.
661  *********************************************************************/
662
663 int pdb_nds_get_password(
664         struct smbldap_state *ldap_state,
665         char *object_dn,
666         size_t *pwd_len,
667         char *pwd )
668 {
669         LDAP *ld = ldap_state->ldap_struct;
670         int rc = -1;
671
672         rc = nmasldap_get_password(ld, object_dn, pwd_len, (unsigned char *)pwd);
673         if (rc == LDAP_SUCCESS) {
674 #ifdef DEBUG_PASSWORD
675                 DEBUG(100,("nmasldap_get_password returned %s for %s\n", pwd, object_dn));
676 #endif    
677                 DEBUG(5, ("NDS Universal Password retrieved for %s\n", object_dn));
678         } else {
679                 DEBUG(3, ("NDS Universal Password NOT retrieved for %s\n", object_dn));
680         }
681
682         if (rc != LDAP_SUCCESS) {
683                 rc = nmasldap_get_simple_pwd(ld, object_dn, *pwd_len, pwd);
684                 if (rc == LDAP_SUCCESS) {
685 #ifdef DEBUG_PASSWORD
686                         DEBUG(100,("nmasldap_get_simple_pwd returned %s for %s\n", pwd, object_dn));
687 #endif    
688                         DEBUG(5, ("NDS Simple Password retrieved for %s\n", object_dn));
689                 } else {
690                         /* We couldn't get the password */
691                         DEBUG(3, ("NDS Simple Password NOT retrieved for %s\n", object_dn));
692                         return LDAP_INVALID_CREDENTIALS;
693                 }
694         }
695
696         /* We got the password */
697         return LDAP_SUCCESS;
698 }
699
700 /**********************************************************************
701  Set the users NDS, Universal and Simple passwords.
702  ********************************************************************/
703
704 int pdb_nds_set_password(
705         struct smbldap_state *ldap_state,
706         char *object_dn,
707         const char *pwd )
708 {
709         LDAP *ld = ldap_state->ldap_struct;
710         int rc = -1;
711         LDAPMod **tmpmods = NULL;
712
713         rc = nmasldap_set_password(ld, object_dn, pwd);
714         if (rc == LDAP_SUCCESS) {
715                 DEBUG(5,("NDS Universal Password changed for user %s\n", object_dn));
716         } else {
717                 char *ld_error = NULL;
718                 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
719                 
720                 /* This will fail if Universal Password is not enabled for the user's context */
721                 DEBUG(3,("NDS Universal Password could not be changed for user %s: %s (%s)\n",
722                                  object_dn, ldap_err2string(rc), ld_error?ld_error:"unknown"));
723                 SAFE_FREE(ld_error);
724         }
725
726         /* Set eDirectory Password */
727         smbldap_set_mod(&tmpmods, LDAP_MOD_REPLACE, "userPassword", pwd);
728         rc = smbldap_modify(ldap_state, object_dn, tmpmods);
729
730         return rc;
731 }
732
733 /**********************************************************************
734  Allow ldap server to update internal login attempt counters by
735   performing a simple bind. If the samba authentication failed attempt
736   the bind with a bogus, randomly generated password to count the
737   failed attempt. If the bind fails even though samba authentication
738   succeeded, this would indicate that the user's account is disabled,
739   time restrictions are in place or some other password policy
740   violation.
741 *********************************************************************/
742
743 static NTSTATUS pdb_nds_update_login_attempts(struct pdb_methods *methods,
744                                         SAM_ACCOUNT *sam_acct, BOOL success)
745 {
746         struct ldapsam_privates *ldap_state;
747
748         if ((!methods) || (!sam_acct)) {
749                 DEBUG(3,("pdb_nds_update_login_attempts: invalid parameter.\n"));
750                 return NT_STATUS_MEMORY_NOT_ALLOCATED;
751         }
752
753         ldap_state = (struct ldapsam_privates *)methods->private_data;
754
755         if (ldap_state) {
756                 /* Attempt simple bind with user credentials to update eDirectory
757                    password policy */
758                 int rc = 0;
759                 char *dn;
760                 LDAPMessage *result = NULL;
761                 LDAPMessage *entry = NULL;
762                 const char **attr_list;
763                 size_t pwd_len;
764                 char clear_text_pw[512];
765                 LDAP *ld = NULL;
766                 const char *username = pdb_get_username(sam_acct);
767                 BOOL got_clear_text_pw = False;
768
769                 DEBUG(5,("pdb_nds_update_login_attempts: %s login for %s\n",
770                                 success ? "Successful" : "Failed", username));
771
772                 result = pdb_get_backend_private_data(sam_acct, methods);
773                 if (!result) {
774                         attr_list = get_userattr_list(ldap_state->schema_ver);
775                         rc = ldapsam_search_suffix_by_name(ldap_state, username, &result, attr_list );
776                         free_attr_list( attr_list );
777                         if (rc != LDAP_SUCCESS) {
778                                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
779                         }
780                         pdb_set_backend_private_data(sam_acct, result, private_data_free_fn, methods, PDB_CHANGED);
781                 }
782
783                 if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result) == 0) {
784                         DEBUG(0, ("pdb_nds_update_login_attempts: No user to modify!\n"));
785                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
786                 }
787
788                 entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
789                 dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
790                 if (!dn) {
791                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
792                 }
793
794                 DEBUG(3, ("pdb_nds_update_login_attempts: username %s found dn '%s'\n", username, dn));
795
796                 pwd_len = sizeof(clear_text_pw);
797                 if (success == True) {
798                         if (pdb_nds_get_password(ldap_state->smbldap_state, dn, &pwd_len, clear_text_pw) == LDAP_SUCCESS) {
799                                 /* Got clear text password. Use simple ldap bind */
800                                 got_clear_text_pw = True;
801                         }
802                 } else {
803                         generate_random_buffer((unsigned char *)clear_text_pw, 24);
804                         clear_text_pw[24] = '\0';
805                         DEBUG(5,("pdb_nds_update_login_attempts: using random password %s\n", clear_text_pw));
806                 }
807
808                 if((success != True) || (got_clear_text_pw == True)) {
809                         
810                         rc = smb_ldap_setup_full_conn(&ld, ldap_state->location);
811                         if (rc) {
812                                 return NT_STATUS_INVALID_CONNECTION;
813                         }
814
815                         /* Attempt simple bind with real or bogus password */
816                         rc = ldap_simple_bind_s(ld, dn, clear_text_pw);
817                         if (rc == LDAP_SUCCESS) {
818                                 DEBUG(5,("pdb_nds_update_login_attempts: ldap_simple_bind_s Successful for %s\n", username));
819                                 ldap_unbind_ext(ld, NULL, NULL);
820                         } else {
821                                 NTSTATUS nt_status = NT_STATUS_ACCOUNT_RESTRICTION;
822                                 DEBUG(5,("pdb_nds_update_login_attempts: ldap_simple_bind_s Failed for %s\n", username));
823                                 switch(rc) {
824                                         case LDAP_INVALID_CREDENTIALS:
825                                                 nt_status = NT_STATUS_WRONG_PASSWORD;
826                                                 break;
827                                         case LDAP_UNWILLING_TO_PERFORM:
828                                                 /* eDir returns this if the account was disabled. */
829                                                 /* The problem is we don't know if the given
830                                                    password was correct for this account or
831                                                    not. We have to return more info than we
832                                                    should and tell the client NT_STATUS_ACCOUNT_DISABLED
833                                                    so they don't think the password was bad. JRA. */
834                                                 nt_status = NT_STATUS_ACCOUNT_DISABLED;
835                                                 break;
836                                         default:
837                                                 break;
838                                 }
839                                 return nt_status;
840                         }
841                 }
842         }
843         
844         return NT_STATUS_OK;
845 }
846
847 /**********************************************************************
848  Intitalise the parts of the pdb_context that are common to NDS_ldapsam modes
849  *********************************************************************/
850
851 static NTSTATUS pdb_init_NDS_ldapsam_common(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
852 {
853         struct ldapsam_privates *ldap_state = (*pdb_method)->private_data;
854
855         /* Mark this as eDirectory ldap */
856         ldap_state->is_nds_ldap = True;
857
858         /* Add pdb_nds specific method for updating login attempts. */
859         (*pdb_method)->update_login_attempts = pdb_nds_update_login_attempts;
860
861         /* Save location for use in pdb_nds_update_login_attempts */
862         ldap_state->location = SMB_STRDUP(location);
863
864         return NT_STATUS_OK;
865 }
866
867
868 /**********************************************************************
869  Initialise the 'nds compat' mode for pdb_ldap
870  *********************************************************************/
871
872 static NTSTATUS pdb_init_NDS_ldapsam_compat(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
873 {
874         NTSTATUS nt_status = pdb_init_ldapsam_compat(pdb_context, pdb_method, location);
875
876         (*pdb_method)->name = "NDS_ldapsam_compat";
877
878         pdb_init_NDS_ldapsam_common(pdb_context, pdb_method, location);
879
880         return nt_status;
881 }
882
883
884 /**********************************************************************
885  Initialise the 'nds' normal mode for pdb_ldap
886  *********************************************************************/
887
888 static NTSTATUS pdb_init_NDS_ldapsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
889 {
890         NTSTATUS nt_status = pdb_init_ldapsam(pdb_context, pdb_method, location);
891
892         (*pdb_method)->name = "NDS_ldapsam";
893
894         pdb_init_NDS_ldapsam_common(pdb_context, pdb_method, location);
895
896         return nt_status;
897 }
898
899 NTSTATUS pdb_nds_init(void)
900 {
901         NTSTATUS nt_status;
902         if (!NT_STATUS_IS_OK(nt_status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "NDS_ldapsam", pdb_init_NDS_ldapsam)))
903                 return nt_status;
904
905         if (!NT_STATUS_IS_OK(nt_status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "NDS_ldapsam_compat", pdb_init_NDS_ldapsam_compat)))
906                 return nt_status;
907
908         return NT_STATUS_OK;
909 }