r5655: Added support for Novell NDS universal password. Code donated by
[ira/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 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19    
20 */
21
22 #include "includes.h"
23
24 #include <lber.h>
25 #include <ldap.h>
26 #include <wchar.h>
27 #include <ldap_utf8.h>
28
29 #include "smbldap.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 rc=0, 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 = (char *)malloc(retOctStrLen);
246                 if(!retOctStr)
247                 {
248                         err = LDAP_OPERATIONS_ERROR;
249                         goto Cleanup;
250                 }
251         
252                 if( (rc = 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( (rc = 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 = (char *)malloc(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         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 = (char *)malloc(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         int *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, 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                 /* This will fail if Universal Password is not enabled for the user's context */
719                 DEBUG(3,("NDS Universal Password could not be changed for user %s: %d\n",
720                                  object_dn, rc));
721         }
722
723         /* Set eDirectory Password */
724         smbldap_set_mod(&tmpmods, LDAP_MOD_REPLACE, "userPassword", pwd);
725         rc = smbldap_modify(ldap_state, object_dn, tmpmods);
726
727         return rc;
728 }
729
730 /**********************************************************************
731  Allow ldap server to update internal login attempt counters by
732   performing a simple bind. If the samba authentication failed attempt
733   the bind with a bogus, randomly generated password to count the
734   failed attempt. If the bind fails even though samba authentication
735   succeeded, this would indicate that the user's account is disabled,
736   time restrictions are in place or some other password policy
737   violation.
738 *********************************************************************/
739
740 static NTSTATUS pdb_nds_update_login_attempts(struct pdb_methods *methods,
741                                         SAM_ACCOUNT *sam_acct, BOOL success)
742 {
743         struct ldapsam_privates *ldap_state;
744
745         if ((!methods) || (!sam_acct)) {
746                 DEBUG(3,("pdb_nds_update_login_attempts: invalid parameter.\n"));
747                 return NT_STATUS_MEMORY_NOT_ALLOCATED;
748         }
749
750         ldap_state = (struct ldapsam_privates *)methods->private_data;
751
752         if (ldap_state) {
753                 /* Attempt simple bind with user credentials to update eDirectory
754                    password policy */
755                 int rc = 0;
756                 char *dn;
757                 LDAPMessage *result = NULL;
758                 LDAPMessage *entry = NULL;
759                 const char **attr_list;
760                 size_t pwd_len;
761                 uchar clear_text_pw[512];
762                 const char *p = NULL;
763                 LDAP *ld = NULL;
764                 int ldap_port = 0;
765                 char protocol[12];
766                 char ldap_server[256];
767                 const char *username = pdb_get_username(sam_acct);
768
769                 DEBUG(5,("pdb_nds_update_login_attempts: %s login for %s\n",
770                                 success ? "Successful" : "Failed", username));
771
772                 result = pdb_get_backend_private_data(sam_acct, methods);
773                 if (!result) {
774                         attr_list = get_userattr_list(ldap_state->schema_ver);
775                         rc = ldapsam_search_suffix_by_name(ldap_state, username, &result, attr_list );
776                         free_attr_list( attr_list );
777                         if (rc != LDAP_SUCCESS) {
778                                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
779                         }
780                         pdb_set_backend_private_data(sam_acct, result, private_data_free_fn, methods, PDB_CHANGED);
781                 }
782
783                 if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result) == 0) {
784                         DEBUG(0, ("pdb_nds_update_login_attempts: No user to modify!\n"));
785                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
786                 }
787
788                 entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
789                 dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
790                 if (!dn) {
791                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
792                 }
793
794                 DEBUG(3, ("pdb_nds_update_login_attempts: username %s found dn '%s'\n", username, dn));
795
796                 pwd_len = sizeof(clear_text_pw);
797                 if (success == True) {
798                         if (pdb_nds_get_password(ldap_state->smbldap_state, dn, &pwd_len, clear_text_pw) == LDAP_SUCCESS) {
799                                 /*  */
800                         }
801                 } else {
802                         generate_random_buffer(clear_text_pw, 24);
803                         clear_text_pw[24] = '\0';
804                         DEBUG(5,("pdb_nds_update_login_attempts: using random password %s\n", clear_text_pw));
805                 }
806
807                 /* Parse the location string */
808                 p = ldap_state->location; 
809
810                 /* skip leading "URL:" (if any) */
811                 if ( strnequal( p, "URL:", 4 ) ) {
812                         p += 4;
813                 }
814
815                 sscanf(p, "%10[^:]://%254[^:/]:%d", protocol, ldap_server, &ldap_port);
816
817                 if (ldap_port == 0) {
818                         if (strequal(protocol, "ldap")) {
819                                 ldap_port = LDAP_PORT;
820                         } else if (strequal(protocol, "ldaps")) {
821                                 ldap_port = LDAPS_PORT;
822                         } else {
823                                 DEBUG(0, ("unrecognised protocol (%s)!\n", protocol));
824                         }
825                 }
826
827                 ld = ldap_init(ldap_server, ldap_port);
828
829                 if(ld != NULL) {
830                         int version;
831
832                         /* LDAP version 3 required for ldap_sasl */
833                         if (ldap_get_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS) {
834                                 if (version != LDAP_VERSION3) {
835                                         version = LDAP_VERSION3;
836                                         if (ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS) {
837                                                 DEBUG(4, ("pdb_nds_update_login_attempts: Set protocol version to LDAP_VERSION3\n"));
838                                         }
839                                 }
840                         }
841
842                         /* Turn on ssl if required */
843                         if(strequal(protocol, "ldaps")) {
844                                 int tls = LDAP_OPT_X_TLS_HARD;
845                                 if (ldap_set_option (ld, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) {
846                                         DEBUG(1, ("pdb_nds_update_login_attempts: Failed to setup a TLS session\n"));
847                                 } else {
848                                         DEBUG(4, ("pdb_nds_update_login_attempts: Activated TLS on session\n"));
849                                 }
850                         }
851                 }
852
853                 /* Attempt simple bind with real or bogus password */
854                 rc = ldap_simple_bind_s(ld, dn, clear_text_pw);
855                 if (rc == LDAP_SUCCESS) {
856                         DEBUG(5,("pdb_nds_update_login_attempts: ldap_simple_bind_s Successful for %s\n", username));
857                         ldap_unbind_ext(ld, NULL, NULL);
858                 } else {
859                         NTSTATUS nt_status = NT_STATUS_ACCOUNT_RESTRICTION;
860                         DEBUG(5,("pdb_nds_update_login_attempts: ldap_simple_bind_s Failed for %s\n", username));
861                         switch(rc) {
862                                 case LDAP_INVALID_CREDENTIALS:
863                                         nt_status = NT_STATUS_WRONG_PASSWORD;
864                                         break;
865                                 default:
866                                         break;
867                         }
868                         return nt_status;
869                 }
870         }
871         
872         return NT_STATUS_OK;
873 }
874
875 /**********************************************************************
876  Intitalise the parts of the pdb_context that are common to NDS_ldapsam modes
877  *********************************************************************/
878
879 static NTSTATUS pdb_init_NDS_ldapsam_common(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
880 {
881         struct ldapsam_privates *ldap_state = (*pdb_method)->private_data;
882
883         /* Mark this as eDirectory ldap */
884         ldap_state->is_nds_ldap = True;
885
886         /* Add pdb_nds specific method for updating login attempts. */
887         (*pdb_method)->update_login_attempts = pdb_nds_update_login_attempts;
888
889         /* Save location for use in pdb_nds_update_login_attempts */
890         ldap_state->location = strdup(location);
891
892         return NT_STATUS_OK;
893 }
894
895
896 /**********************************************************************
897  Initialise the 'nds compat' mode for pdb_ldap
898  *********************************************************************/
899
900 static NTSTATUS pdb_init_NDS_ldapsam_compat(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
901 {
902         NTSTATUS nt_status = pdb_init_ldapsam_compat(pdb_context, pdb_method, location);
903
904         (*pdb_method)->name = "NDS_ldapsam_compat";
905
906         pdb_init_NDS_ldapsam_common(pdb_context, pdb_method, location);
907
908         return nt_status;
909 }
910
911
912 /**********************************************************************
913  Initialise the 'nds' normal mode for pdb_ldap
914  *********************************************************************/
915
916 static NTSTATUS pdb_init_NDS_ldapsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
917 {
918         NTSTATUS nt_status = pdb_init_ldapsam(pdb_context, pdb_method, location);
919
920         (*pdb_method)->name = "NDS_ldapsam";
921
922         pdb_init_NDS_ldapsam_common(pdb_context, pdb_method, location);
923
924         return nt_status;
925 }
926
927 NTSTATUS pdb_nds_init(void)
928 {
929         NTSTATUS nt_status;
930         if (!NT_STATUS_IS_OK(nt_status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "NDS_ldapsam", pdb_init_NDS_ldapsam)))
931                 return nt_status;
932
933         if (!NT_STATUS_IS_OK(nt_status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "NDS_ldapsam_compat", pdb_init_NDS_ldapsam_compat)))
934                 return nt_status;
935
936         return NT_STATUS_OK;
937 }