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