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