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 rc=0, 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 = (char *)malloc(retOctStrLen);
245		if(!retOctStr)
246		{
247			err = LDAP_OPERATIONS_ERROR;
248			goto Cleanup;
249		}
250
251		if( (rc = 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( (rc = 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 = (char *)malloc(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	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 = (char *)malloc(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	int *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, 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		/* This will fail if Universal Password is not enabled for the user's context */
718		DEBUG(3,("NDS Universal Password could not be changed for user %s: %d\n",
719				 object_dn, rc));
720	}
721
722	/* Set eDirectory Password */
723	smbldap_set_mod(&tmpmods, LDAP_MOD_REPLACE, "userPassword", pwd);
724	rc = smbldap_modify(ldap_state, object_dn, tmpmods);
725
726	return rc;
727}
728
729/**********************************************************************
730 Allow ldap server to update internal login attempt counters by
731  performing a simple bind. If the samba authentication failed attempt
732  the bind with a bogus, randomly generated password to count the
733  failed attempt. If the bind fails even though samba authentication
734  succeeded, this would indicate that the user's account is disabled,
735  time restrictions are in place or some other password policy
736  violation.
737*********************************************************************/
738
739static NTSTATUS pdb_nds_update_login_attempts(struct pdb_methods *methods,
740					SAM_ACCOUNT *sam_acct, BOOL success)
741{
742	struct ldapsam_privates *ldap_state;
743
744	if ((!methods) || (!sam_acct)) {
745		DEBUG(3,("pdb_nds_update_login_attempts: invalid parameter.\n"));
746		return NT_STATUS_MEMORY_NOT_ALLOCATED;
747	}
748
749	ldap_state = (struct ldapsam_privates *)methods->private_data;
750
751	if (ldap_state) {
752		/* Attempt simple bind with user credentials to update eDirectory
753		   password policy */
754		int rc = 0;
755		char *dn;
756		LDAPMessage *result = NULL;
757		LDAPMessage *entry = NULL;
758		const char **attr_list;
759		size_t pwd_len;
760		uchar clear_text_pw[512];
761		const char *p = NULL;
762		LDAP *ld = NULL;
763		int ldap_port = 0;
764		char protocol[12];
765		char ldap_server[256];
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 = 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				/* Got clear text password. Use simple ldap bind */
800				got_clear_text_pw = True;
801			}
802		} else {
803			generate_random_buffer(clear_text_pw, 24);
804			clear_text_pw[24] = '\0';
805			DEBUG(5,("pdb_nds_update_login_attempts: using random password %s\n", clear_text_pw));
806		}
807
808		/* Parse the location string */
809		p = ldap_state->location;
810
811		/* skip leading "URL:" (if any) */
812		if ( strnequal( p, "URL:", 4 ) ) {
813			p += 4;
814		}
815
816		sscanf(p, "%10[^:]://%254[^:/]:%d", protocol, ldap_server, &ldap_port);
817
818		if (ldap_port == 0) {
819			if (strequal(protocol, "ldap")) {
820				ldap_port = LDAP_PORT;
821			} else if (strequal(protocol, "ldaps")) {
822				ldap_port = LDAPS_PORT;
823			} else {
824				DEBUG(0, ("unrecognised protocol (%s)!\n", protocol));
825			}
826		}
827
828		ld = ldap_init(ldap_server, ldap_port);
829
830		if(ld != NULL) {
831			int version;
832
833			/* LDAP version 3 required for ldap_sasl */
834			if (ldap_get_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS) {
835				if (version != LDAP_VERSION3) {
836					version = LDAP_VERSION3;
837					if (ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS) {
838						DEBUG(4, ("pdb_nds_update_login_attempts: Set protocol version to LDAP_VERSION3\n"));
839					}
840				}
841			}
842
843			/* Turn on ssl if required */
844			if(strequal(protocol, "ldaps")) {
845				int tls = LDAP_OPT_X_TLS_HARD;
846				if (ldap_set_option (ld, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) {
847					DEBUG(1, ("pdb_nds_update_login_attempts: Failed to setup a TLS session\n"));
848				} else {
849					DEBUG(4, ("pdb_nds_update_login_attempts: Activated TLS on session\n"));
850				}
851			}
852		}
853
854		if((success != True) || (got_clear_text_pw == True)) {
855			/* Attempt simple bind with real or bogus password */
856			rc = ldap_simple_bind_s(ld, dn, clear_text_pw);
857			if (rc == LDAP_SUCCESS) {
858				DEBUG(5,("pdb_nds_update_login_attempts: ldap_simple_bind_s Successful for %s\n", username));
859				ldap_unbind_ext(ld, NULL, NULL);
860			} else {
861				NTSTATUS nt_status = NT_STATUS_ACCOUNT_RESTRICTION;
862				DEBUG(5,("pdb_nds_update_login_attempts: ldap_simple_bind_s Failed for %s\n", username));
863				switch(rc) {
864					case LDAP_INVALID_CREDENTIALS:
865						nt_status = NT_STATUS_WRONG_PASSWORD;
866						break;
867					default:
868						break;
869				}
870				return nt_status;
871			}
872		}
873	}
874
875	return NT_STATUS_OK;
876}
877
878/**********************************************************************
879 Intitalise the parts of the pdb_context that are common to NDS_ldapsam modes
880 *********************************************************************/
881
882static NTSTATUS pdb_init_NDS_ldapsam_common(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
883{
884	struct ldapsam_privates *ldap_state = (*pdb_method)->private_data;
885
886	/* Mark this as eDirectory ldap */
887	ldap_state->is_nds_ldap = True;
888
889	/* Add pdb_nds specific method for updating login attempts. */
890	(*pdb_method)->update_login_attempts = pdb_nds_update_login_attempts;
891
892	/* Save location for use in pdb_nds_update_login_attempts */
893	ldap_state->location = strdup(location);
894
895	return NT_STATUS_OK;
896}
897
898
899/**********************************************************************
900 Initialise the 'nds compat' mode for pdb_ldap
901 *********************************************************************/
902
903static NTSTATUS pdb_init_NDS_ldapsam_compat(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
904{
905	NTSTATUS nt_status = pdb_init_ldapsam_compat(pdb_context, pdb_method, location);
906
907	(*pdb_method)->name = "NDS_ldapsam_compat";
908
909	pdb_init_NDS_ldapsam_common(pdb_context, pdb_method, location);
910
911	return nt_status;
912}
913
914
915/**********************************************************************
916 Initialise the 'nds' normal mode for pdb_ldap
917 *********************************************************************/
918
919static NTSTATUS pdb_init_NDS_ldapsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
920{
921	NTSTATUS nt_status = pdb_init_ldapsam(pdb_context, pdb_method, location);
922
923	(*pdb_method)->name = "NDS_ldapsam";
924
925	pdb_init_NDS_ldapsam_common(pdb_context, pdb_method, location);
926
927	return nt_status;
928}
929
930NTSTATUS pdb_nds_init(void)
931{
932	NTSTATUS nt_status;
933	if (!NT_STATUS_IS_OK(nt_status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "NDS_ldapsam", pdb_init_NDS_ldapsam)))
934		return nt_status;
935
936	if (!NT_STATUS_IS_OK(nt_status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "NDS_ldapsam_compat", pdb_init_NDS_ldapsam_compat)))
937		return nt_status;
938
939	return NT_STATUS_OK;
940}
941