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