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