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