1/*
2   Unix SMB/CIFS implementation.
3   Samba utility functions
4   Copyright (C) Andrew Tridgell 1992-1998
5   Copyright (C) Andrew Bartlett 2001-2004
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20*/
21
22/* These comments regard the code to change the user's unix password: */
23
24/* fork a child process to exec passwd and write to its
25 * tty to change a users password. This is running as the
26 * user who is attempting to change the password.
27 */
28
29/*
30 * This code was copied/borrowed and stolen from various sources.
31 * The primary source was the poppasswd.c from the authors of POPMail. This software
32 * was included as a client to change passwords using the 'passwd' program
33 * on the remote machine.
34 *
35 * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
36 * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
37 * and rights to modify, distribute or incorporate this change to the CAP suite or
38 * using it for any other reason are granted, so long as this disclaimer is left intact.
39 */
40
41/*
42   This code was hacked considerably for inclusion in Samba, primarily
43   by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
44   of the "password chat" option, which allows the easy runtime
45   specification of the expected sequence of events to change a
46   password.
47   */
48
49#include "includes.h"
50
51extern struct passdb_ops pdb_ops;
52
53static NTSTATUS check_oem_password(const char *user,
54				   uchar password_encrypted_with_lm_hash[516],
55				   const uchar old_lm_hash_encrypted[16],
56				   uchar password_encrypted_with_nt_hash[516],
57				   const uchar old_nt_hash_encrypted[16],
58				   SAM_ACCOUNT **hnd, char *new_passwd,
59				   int new_passwd_size);
60
61#if ALLOW_CHANGE_PASSWORD
62
63static int findpty(char **slave)
64{
65	int master;
66	static fstring line;
67	DIR *dirp;
68	const char *dpname;
69
70#if defined(HAVE_GRANTPT)
71	/* Try to open /dev/ptmx. If that fails, fall through to old method. */
72	if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0)
73	{
74		grantpt(master);
75		unlockpt(master);
76		*slave = (char *)ptsname(master);
77		if (*slave == NULL)
78		{
79			DEBUG(0,
80			      ("findpty: Unable to create master/slave pty pair.\n"));
81			/* Stop fd leak on error. */
82			close(master);
83			return -1;
84		}
85		else
86		{
87			DEBUG(10,
88			      ("findpty: Allocated slave pty %s\n", *slave));
89			return (master);
90		}
91	}
92#endif /* HAVE_GRANTPT */
93
94	fstrcpy(line, "/dev/ptyXX");
95
96	dirp = opendir("/dev");
97	if (!dirp)
98		return (-1);
99	while ((dpname = readdirname(dirp)) != NULL)
100	{
101		if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5)
102		{
103			DEBUG(3,
104			      ("pty: try to open %s, line was %s\n", dpname,
105			       line));
106			line[8] = dpname[3];
107			line[9] = dpname[4];
108			if ((master = sys_open(line, O_RDWR, 0)) >= 0)
109			{
110				DEBUG(3, ("pty: opened %s\n", line));
111				line[5] = 't';
112				*slave = line;
113				closedir(dirp);
114				return (master);
115			}
116		}
117	}
118	closedir(dirp);
119	return (-1);
120}
121
122static int dochild(int master, const char *slavedev, const struct passwd *pass,
123		   const char *passwordprogram, BOOL as_root)
124{
125	int slave;
126	struct termios stermios;
127	gid_t gid;
128	uid_t uid;
129
130	if (pass == NULL)
131	{
132		DEBUG(0,
133		      ("dochild: user doesn't exist in the UNIX password database.\n"));
134		return False;
135	}
136
137	gid = pass->pw_gid;
138	uid = pass->pw_uid;
139
140	gain_root_privilege();
141
142	/* Start new session - gets rid of controlling terminal. */
143	if (setsid() < 0)
144	{
145		DEBUG(3,
146		      ("Weirdness, couldn't let go of controlling terminal\n"));
147		return (False);
148	}
149
150	/* Open slave pty and acquire as new controlling terminal. */
151	if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
152	{
153		DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
154		return (False);
155	}
156#ifdef I_PUSH
157	ioctl(slave, I_PUSH, "ptem");
158	ioctl(slave, I_PUSH, "ldterm");
159#elif defined(TIOCSCTTY)
160	if (ioctl(slave, TIOCSCTTY, 0) < 0)
161	{
162		DEBUG(3, ("Error in ioctl call for slave pty\n"));
163		/* return(False); */
164	}
165#endif
166
167	/* Close master. */
168	close(master);
169
170	/* Make slave stdin/out/err of child. */
171
172	if (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO)
173	{
174		DEBUG(3, ("Could not re-direct stdin\n"));
175		return (False);
176	}
177	if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
178	{
179		DEBUG(3, ("Could not re-direct stdout\n"));
180		return (False);
181	}
182	if (sys_dup2(slave, STDERR_FILENO) != STDERR_FILENO)
183	{
184		DEBUG(3, ("Could not re-direct stderr\n"));
185		return (False);
186	}
187	if (slave > 2)
188		close(slave);
189
190	/* Set proper terminal attributes - no echo, canonical input processing,
191	   no map NL to CR/NL on output. */
192
193	if (tcgetattr(0, &stermios) < 0)
194	{
195		DEBUG(3,
196		      ("could not read default terminal attributes on pty\n"));
197		return (False);
198	}
199	stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
200	stermios.c_lflag |= ICANON;
201#ifdef ONLCR
202 	stermios.c_oflag &= ~(ONLCR);
203#endif
204	if (tcsetattr(0, TCSANOW, &stermios) < 0)
205	{
206		DEBUG(3, ("could not set attributes of pty\n"));
207		return (False);
208	}
209
210	/* make us completely into the right uid */
211	if (!as_root)
212	{
213		become_user_permanently(uid, gid);
214	}
215
216	DEBUG(10,
217	      ("Invoking '%s' as password change program.\n",
218	       passwordprogram));
219
220	/* execl() password-change application */
221	if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
222	{
223		DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
224		return (False);
225	}
226	return (True);
227}
228
229static int expect(int master, char *issue, char *expected)
230{
231	pstring buffer;
232	int attempts, timeout, nread, len;
233	BOOL match = False;
234
235	for (attempts = 0; attempts < 2; attempts++) {
236		if (!strequal(issue, ".")) {
237			if (lp_passwd_chat_debug())
238				DEBUG(100, ("expect: sending [%s]\n", issue));
239
240			if ((len = write(master, issue, strlen(issue))) != strlen(issue)) {
241				DEBUG(2,("expect: (short) write returned %d\n", len ));
242				return False;
243			}
244		}
245
246		if (strequal(expected, "."))
247			return True;
248
249		/* Initial timeout. */
250		timeout = lp_passwd_chat_timeout() * 1000;
251		nread = 0;
252		buffer[nread] = 0;
253
254		while ((len = read_socket_with_timeout(master, buffer + nread, 1,
255						       sizeof(buffer) - nread - 1,
256						       timeout)) > 0) {
257			nread += len;
258			buffer[nread] = 0;
259
260			{
261				/* Eat leading/trailing whitespace before match. */
262				pstring str;
263				pstrcpy( str, buffer);
264				trim_char( str, ' ', ' ');
265
266				if ((match = (unix_wild_match(expected, str) == 0))) {
267					/* Now data has started to return, lower timeout. */
268					timeout = lp_passwd_chat_timeout() * 100;
269				}
270			}
271		}
272
273		if (lp_passwd_chat_debug())
274			DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
275				    expected, buffer, match ? "yes" : "no" ));
276
277		if (match)
278			break;
279
280		if (len < 0) {
281			DEBUG(2, ("expect: %s\n", strerror(errno)));
282			return False;
283		}
284	}
285
286	DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
287	return match;
288}
289
290static void pwd_sub(char *buf)
291{
292	all_string_sub(buf, "\\n", "\n", 0);
293	all_string_sub(buf, "\\r", "\r", 0);
294	all_string_sub(buf, "\\s", " ", 0);
295	all_string_sub(buf, "\\t", "\t", 0);
296}
297
298static int talktochild(int master, const char *seq)
299{
300	int count = 0;
301	fstring issue, expected;
302
303	fstrcpy(issue, ".");
304
305	while (next_token(&seq, expected, NULL, sizeof(expected)))
306	{
307		pwd_sub(expected);
308		count++;
309
310		if (!expect(master, issue, expected))
311		{
312			DEBUG(3, ("Response %d incorrect\n", count));
313			return False;
314		}
315
316		if (!next_token(&seq, issue, NULL, sizeof(issue)))
317			fstrcpy(issue, ".");
318
319		pwd_sub(issue);
320	}
321	if (!strequal(issue, ".")) {
322		/* we have one final issue to send */
323		fstrcpy(expected, ".");
324		if (!expect(master, issue, expected))
325			return False;
326	}
327
328	return (count > 0);
329}
330
331static BOOL chat_with_program(char *passwordprogram, const struct passwd *pass,
332			      char *chatsequence, BOOL as_root)
333{
334	char *slavedev;
335	int master;
336	pid_t pid, wpid;
337	int wstat;
338	BOOL chstat = False;
339
340	if (pass == NULL) {
341		DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
342		return False;
343	}
344
345	/* allocate a pseudo-terminal device */
346	if ((master = findpty(&slavedev)) < 0) {
347		DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
348		return (False);
349	}
350
351	/*
352	 * We need to temporarily stop CatchChild from eating
353	 * SIGCLD signals as it also eats the exit status code. JRA.
354	 */
355
356	CatchChildLeaveStatus();
357
358	if ((pid = sys_fork()) < 0) {
359		DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
360		close(master);
361		CatchChild();
362		return (False);
363	}
364
365	/* we now have a pty */
366	if (pid > 0) {			/* This is the parent process */
367		if ((chstat = talktochild(master, chatsequence)) == False) {
368			DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
369			kill(pid, SIGKILL);	/* be sure to end this process */
370		}
371
372		while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
373			if (errno == EINTR) {
374				errno = 0;
375				continue;
376			}
377			break;
378		}
379
380		if (wpid < 0) {
381			DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
382			close(master);
383			CatchChild();
384			return (False);
385		}
386
387		/*
388		 * Go back to ignoring children.
389		 */
390		CatchChild();
391
392		close(master);
393
394		if (pid != wpid) {
395			DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
396			return (False);
397		}
398		if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
399			DEBUG(3, ("chat_with_program: The process exited with status %d \
400while we were waiting\n", WEXITSTATUS(wstat)));
401			return (False);
402		}
403#if defined(WIFSIGNALLED) && defined(WTERMSIG)
404		else if (WIFSIGNALLED(wstat)) {
405                        DEBUG(3, ("chat_with_program: The process was killed by signal %d \
406while we were waiting\n", WTERMSIG(wstat)));
407			return (False);
408		}
409#endif
410	} else {
411		/* CHILD */
412
413		/*
414		 * Lose any oplock capabilities.
415		 */
416		oplock_set_capability(False, False);
417
418		/* make sure it doesn't freeze */
419		alarm(20);
420
421		if (as_root)
422			become_root();
423
424		DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
425		       (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
426		chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
427
428		if (as_root)
429			unbecome_root();
430
431		/*
432		 * The child should never return from dochild() ....
433		 */
434
435		DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
436		exit(1);
437	}
438
439	if (chstat)
440		DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
441		       (chstat ? "" : "un"), pass->pw_name));
442	return (chstat);
443}
444
445BOOL chgpasswd(const char *name, const struct passwd *pass,
446	       const char *oldpass, const char *newpass, BOOL as_root)
447{
448	pstring passwordprogram;
449	pstring chatsequence;
450	size_t i;
451	size_t len;
452
453	if (!oldpass) {
454		oldpass = "";
455	}
456
457	DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
458
459#ifdef DEBUG_PASSWORD
460	DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
461#endif
462
463	/* Take the passed information and test it for minimum criteria */
464
465	/* Password is same as old password */
466	if (strcmp(oldpass, newpass) == 0) {
467		/* don't allow same password */
468		DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name));	/* log the attempt */
469		return (False);	/* inform the user */
470	}
471
472	/*
473	 * Check the old and new passwords don't contain any control
474	 * characters.
475	 */
476
477	len = strlen(oldpass);
478	for (i = 0; i < len; i++) {
479		if (iscntrl((int)oldpass[i])) {
480			DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
481			return False;
482		}
483	}
484
485	len = strlen(newpass);
486	for (i = 0; i < len; i++) {
487		if (iscntrl((int)newpass[i])) {
488			DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
489			return False;
490		}
491	}
492
493#ifdef WITH_PAM
494	if (lp_pam_password_change()) {
495		BOOL ret;
496
497		if (as_root)
498			become_root();
499
500		if (pass) {
501			ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
502		} else {
503			ret = smb_pam_passchange(name, oldpass, newpass);
504		}
505
506		if (as_root)
507			unbecome_root();
508
509		return ret;
510	}
511#endif
512
513	/* A non-PAM password change just doen't make sense without a valid local user */
514
515	if (pass == NULL) {
516		DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
517		return False;
518	}
519
520	pstrcpy(passwordprogram, lp_passwd_program());
521	pstrcpy(chatsequence, lp_passwd_chat());
522
523	if (!*chatsequence) {
524		DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
525		return (False);
526	}
527
528	if (!*passwordprogram) {
529		DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
530		return (False);
531	}
532
533	if (as_root) {
534		/* The password program *must* contain the user name to work. Fail if not. */
535		if (strstr_m(passwordprogram, "%u") == NULL) {
536			DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
537the string %%u, and the given string %s does not.\n", passwordprogram ));
538			return False;
539		}
540	}
541
542	pstring_sub(passwordprogram, "%u", name);
543	/* note that we do NOT substitute the %o and %n in the password program
544	   as this would open up a security hole where the user could use
545	   a new password containing shell escape characters */
546
547	pstring_sub(chatsequence, "%u", name);
548	all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
549	all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
550	return (chat_with_program
551		(passwordprogram, pass, chatsequence, as_root));
552}
553
554#else /* ALLOW_CHANGE_PASSWORD */
555
556BOOL chgpasswd(const char *name, const struct passwd *pass,
557	       const char *oldpass, const char *newpass, BOOL as_root)
558{
559	DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
560	return (False);
561}
562#endif /* ALLOW_CHANGE_PASSWORD */
563
564/***********************************************************
565 Code to check the lanman hashed password.
566************************************************************/
567
568BOOL check_lanman_password(char *user, uchar * pass1,
569			   uchar * pass2, SAM_ACCOUNT **hnd)
570{
571	uchar unenc_new_pw[16];
572	uchar unenc_old_pw[16];
573	SAM_ACCOUNT *sampass = NULL;
574	uint16 acct_ctrl;
575	const uint8 *lanman_pw;
576	BOOL ret;
577
578	become_root();
579	ret = pdb_getsampwnam(sampass, user);
580	unbecome_root();
581
582	if (ret == False) {
583		DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
584		pdb_free_sam(&sampass);
585		return False;
586	}
587
588	acct_ctrl = pdb_get_acct_ctrl     (sampass);
589	lanman_pw = pdb_get_lanman_passwd (sampass);
590
591	if (acct_ctrl & ACB_DISABLED) {
592		DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
593		pdb_free_sam(&sampass);
594		return False;
595	}
596
597	if (lanman_pw == NULL) {
598		if (acct_ctrl & ACB_PWNOTREQ) {
599			/* this saves the pointer for the caller */
600			*hnd = sampass;
601			return True;
602		} else {
603			DEBUG(0, ("check_lanman_password: no lanman password !\n"));
604			pdb_free_sam(&sampass);
605			return False;
606		}
607	}
608
609	/* Get the new lanman hash. */
610	D_P16(lanman_pw, pass2, unenc_new_pw);
611
612	/* Use this to get the old lanman hash. */
613	D_P16(unenc_new_pw, pass1, unenc_old_pw);
614
615	/* Check that the two old passwords match. */
616	if (memcmp(lanman_pw, unenc_old_pw, 16)) {
617		DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
618		pdb_free_sam(&sampass);
619		return False;
620	}
621
622	/* this saves the pointer for the caller */
623	*hnd = sampass;
624	return True;
625}
626
627/***********************************************************
628 Code to change the lanman hashed password.
629 It nulls out the NT hashed password as it will
630 no longer be valid.
631 NOTE this function is designed to be called as root. Check the old password
632 is correct before calling. JRA.
633************************************************************/
634
635BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar *pass2)
636{
637	static uchar null_pw[16];
638	uchar unenc_new_pw[16];
639	BOOL ret;
640	uint16 acct_ctrl;
641	const uint8 *pwd;
642
643	if (sampass == NULL) {
644		DEBUG(0,("change_lanman_password: no smb password entry.\n"));
645		return False;
646	}
647
648	acct_ctrl = pdb_get_acct_ctrl(sampass);
649	pwd = pdb_get_lanman_passwd(sampass);
650
651	if (acct_ctrl & ACB_DISABLED) {
652		DEBUG(0,("change_lanman_password: account %s disabled.\n",
653		       pdb_get_username(sampass)));
654		return False;
655	}
656
657	if (pwd == NULL) {
658		if (acct_ctrl & ACB_PWNOTREQ) {
659			uchar no_pw[14];
660			memset(no_pw, '\0', 14);
661			E_P16(no_pw, null_pw);
662
663			/* Get the new lanman hash. */
664			D_P16(null_pw, pass2, unenc_new_pw);
665		} else {
666			DEBUG(0,("change_lanman_password: no lanman password !\n"));
667			return False;
668		}
669	} else {
670		/* Get the new lanman hash. */
671		D_P16(pwd, pass2, unenc_new_pw);
672	}
673
674	if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
675		return False;
676	}
677
678	if (!pdb_set_nt_passwd    (sampass, NULL, PDB_CHANGED)) {
679		return False;	/* We lose the NT hash. Sorry. */
680	}
681
682	if (!pdb_set_pass_changed_now  (sampass)) {
683		pdb_free_sam(&sampass);
684		/* Not quite sure what this one qualifies as, but this will do */
685		return False;
686	}
687
688	/* Now flush the sam_passwd struct to persistent storage */
689	ret = pdb_update_sam_account (sampass);
690
691	return ret;
692}
693
694/***********************************************************
695 Code to check and change the OEM hashed password.
696************************************************************/
697
698NTSTATUS pass_oem_change(char *user,
699			 uchar password_encrypted_with_lm_hash[516],
700			 const uchar old_lm_hash_encrypted[16],
701			 uchar password_encrypted_with_nt_hash[516],
702			 const uchar old_nt_hash_encrypted[16])
703{
704	pstring new_passwd;
705	SAM_ACCOUNT *sampass = NULL;
706	NTSTATUS nt_status = check_oem_password(user, password_encrypted_with_lm_hash,
707						old_lm_hash_encrypted,
708						password_encrypted_with_nt_hash,
709						old_nt_hash_encrypted,
710						&sampass, new_passwd, sizeof(new_passwd));
711
712	if (!NT_STATUS_IS_OK(nt_status))
713		return nt_status;
714
715	/* We've already checked the old password here.... */
716	become_root();
717	nt_status = change_oem_password(sampass, NULL, new_passwd, True);
718	unbecome_root();
719
720	memset(new_passwd, 0, sizeof(new_passwd));
721
722	pdb_free_sam(&sampass);
723
724	return nt_status;
725}
726
727/***********************************************************
728 Decrypt and verify a user password change.
729
730 The 516 byte long buffers are encrypted with the old NT and
731 old LM passwords, and if the NT passwords are present, both
732 buffers contain a unicode string.
733
734 After decrypting the buffers, check the password is correct by
735 matching the old hashed passwords with the passwords in the passdb.
736
737************************************************************/
738
739static NTSTATUS check_oem_password(const char *user,
740				   uchar password_encrypted_with_lm_hash[516],
741				   const uchar old_lm_hash_encrypted[16],
742				   uchar password_encrypted_with_nt_hash[516],
743				   const uchar old_nt_hash_encrypted[16],
744				   SAM_ACCOUNT **hnd, char *new_passwd,
745				   int new_passwd_size)
746{
747	static uchar null_pw[16];
748	static uchar null_ntpw[16];
749	SAM_ACCOUNT *sampass = NULL;
750	char *password_encrypted;
751	const char *encryption_key;
752	const uint8 *lanman_pw, *nt_pw;
753	uint16 acct_ctrl;
754	uint32 new_pw_len;
755	uchar new_nt_hash[16];
756	uchar new_lm_hash[16];
757	uchar verifier[16];
758	char no_pw[2];
759	BOOL ret;
760
761	BOOL nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
762	BOOL lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
763
764	*hnd = NULL;
765
766	pdb_init_sam(&sampass);
767
768	become_root();
769	ret = pdb_getsampwnam(sampass, user);
770	unbecome_root();
771
772	if (ret == False) {
773		DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
774		pdb_free_sam(&sampass);
775		return NT_STATUS_NO_SUCH_USER;
776	}
777
778	acct_ctrl = pdb_get_acct_ctrl(sampass);
779
780	if (acct_ctrl & ACB_DISABLED) {
781		DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
782		pdb_free_sam(&sampass);
783		return NT_STATUS_ACCOUNT_DISABLED;
784	}
785
786	if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
787		/* construct a null password (in case one is needed */
788		no_pw[0] = 0;
789		no_pw[1] = 0;
790		nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
791		lanman_pw = null_pw;
792		nt_pw = null_pw;
793
794	} else {
795		/* save pointers to passwords so we don't have to keep looking them up */
796		if (lp_lanman_auth()) {
797			lanman_pw = pdb_get_lanman_passwd(sampass);
798		} else {
799			lanman_pw = NULL;
800		}
801		nt_pw = pdb_get_nt_passwd(sampass);
802	}
803
804	if (nt_pw && nt_pass_set) {
805		/* IDEAL Case: passwords are in unicode, and we can
806		 * read use the password encrypted with the NT hash
807		 */
808		password_encrypted = password_encrypted_with_nt_hash;
809		encryption_key = nt_pw;
810	} else if (lanman_pw && lm_pass_set) {
811		/* password may still be in unicode, but use LM hash version */
812		password_encrypted = password_encrypted_with_lm_hash;
813		encryption_key = lanman_pw;
814	} else if (nt_pass_set) {
815		DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
816			  user));
817		pdb_free_sam(&sampass);
818		return NT_STATUS_WRONG_PASSWORD;
819	} else if (lm_pass_set) {
820		if (lp_lanman_auth()) {
821			DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
822				  user));
823		} else {
824			DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
825				  user));
826		}
827			pdb_free_sam(&sampass);
828		return NT_STATUS_WRONG_PASSWORD;
829	} else {
830		DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
831			  user));
832		pdb_free_sam(&sampass);
833		return NT_STATUS_WRONG_PASSWORD;
834	}
835
836	/*
837	 * Decrypt the password with the key
838	 */
839	SamOEMhash( password_encrypted, encryption_key, 516);
840
841	if ( !decode_pw_buffer(password_encrypted, new_passwd, new_passwd_size, &new_pw_len,
842			       nt_pass_set ? STR_UNICODE : STR_ASCII)) {
843		pdb_free_sam(&sampass);
844		return NT_STATUS_WRONG_PASSWORD;
845	}
846
847	/*
848	 * To ensure we got the correct new password, hash it and
849	 * use it as a key to test the passed old password.
850	 */
851
852	if (nt_pass_set) {
853		/* NT passwords, verify the NT hash. */
854
855		/* Calculate the MD4 hash (NT compatible) of the password */
856		memset(new_nt_hash, '\0', 16);
857		E_md4hash(new_passwd, new_nt_hash);
858
859		if (nt_pw) {
860			/*
861			 * check the NT verifier
862			 */
863			E_old_pw_hash(new_nt_hash, nt_pw, verifier);
864			if (memcmp(verifier, old_nt_hash_encrypted, 16)) {
865				DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
866				pdb_free_sam(&sampass);
867				return NT_STATUS_WRONG_PASSWORD;
868			}
869
870			/* We could check the LM password here, but there is
871			 * little point, we already know the password is
872			 * correct, and the LM password might not even be
873			 * present. */
874
875			/* Further, LM hash generation algorithms
876			 * differ with charset, so we could
877			 * incorrectly fail a perfectly valid password
878			 * change */
879#ifdef DEBUG_PASSWORD
880			DEBUG(100,
881			      ("check_oem_password: password %s ok\n", new_passwd));
882#endif
883			*hnd = sampass;
884			return NT_STATUS_OK;
885		}
886
887		if (lanman_pw) {
888			/*
889			 * check the lm verifier
890			 */
891			E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
892			if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
893				DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
894				pdb_free_sam(&sampass);
895				return NT_STATUS_WRONG_PASSWORD;
896			}
897#ifdef DEBUG_PASSWORD
898			DEBUG(100,
899			      ("check_oem_password: password %s ok\n", new_passwd));
900#endif
901			*hnd = sampass;
902			return NT_STATUS_OK;
903		}
904	}
905
906	if (lanman_pw && lm_pass_set) {
907
908		E_deshash(new_passwd, new_lm_hash);
909
910		/*
911		 * check the lm verifier
912		 */
913		E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
914		if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
915			DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
916			pdb_free_sam(&sampass);
917			return NT_STATUS_WRONG_PASSWORD;
918		}
919
920#ifdef DEBUG_PASSWORD
921		DEBUG(100,
922		      ("check_oem_password: password %s ok\n", new_passwd));
923#endif
924		*hnd = sampass;
925		return NT_STATUS_OK;
926	}
927
928	/* should not be reached */
929	pdb_free_sam(&sampass);
930	return NT_STATUS_WRONG_PASSWORD;
931}
932
933/***********************************************************
934 This routine takes the given password and checks it against
935 the password history. Returns True if this password has been
936 found in the history list.
937************************************************************/
938
939static BOOL check_passwd_history(SAM_ACCOUNT *sampass, const char *plaintext)
940{
941	uchar new_nt_p16[NT_HASH_LEN];
942	uchar zero_md5_nt_pw[SALTED_MD5_HASH_LEN];
943	const uint8 *nt_pw;
944	const uint8 *pwhistory;
945	BOOL found = False;
946	int i, pwHisLen, curr_pwHisLen;
947
948	account_policy_get(AP_PASSWORD_HISTORY, &pwHisLen);
949	if (pwHisLen == 0) {
950		return False;
951	}
952
953	pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
954	if (!pwhistory || curr_pwHisLen == 0) {
955		return False;
956	}
957
958	/* Only examine the minimum of the current history len and
959	   the stored history len. Avoids race conditions. */
960	pwHisLen = MIN(pwHisLen,curr_pwHisLen);
961
962	nt_pw = pdb_get_nt_passwd(sampass);
963
964	E_md4hash(plaintext, new_nt_p16);
965
966	if (!memcmp(nt_pw, new_nt_p16, NT_HASH_LEN)) {
967		DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
968			pdb_get_username(sampass) ));
969		return True;
970	}
971
972	dump_data(100, new_nt_p16, NT_HASH_LEN);
973	dump_data(100, pwhistory, PW_HISTORY_ENTRY_LEN*pwHisLen);
974
975	memset(zero_md5_nt_pw, '\0', SALTED_MD5_HASH_LEN);
976	for (i=0; i<pwHisLen; i++) {
977		uchar new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
978		const uchar *current_salt = &pwhistory[i*PW_HISTORY_ENTRY_LEN];
979		const uchar *old_nt_pw_salted_md5_hash = &pwhistory[(i*PW_HISTORY_ENTRY_LEN)+
980							PW_HISTORY_SALT_LEN];
981		if (!memcmp(zero_md5_nt_pw, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
982			/* Ignore zero valued entries. */
983			continue;
984		}
985		/* Create salted versions of new to compare. */
986		E_md5hash(current_salt, new_nt_p16, new_nt_pw_salted_md5_hash);
987
988		if (!memcmp(new_nt_pw_salted_md5_hash, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
989			DEBUG(1,("check_passwd_history: proposed new password for user %s found in history list !\n",
990				pdb_get_username(sampass) ));
991			found = True;
992			break;
993		}
994	}
995	return found;
996}
997
998/***********************************************************
999 Code to change the oem password. Changes both the lanman
1000 and NT hashes.  Old_passwd is almost always NULL.
1001 NOTE this function is designed to be called as root. Check the old password
1002 is correct before calling. JRA.
1003************************************************************/
1004
1005NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passwd, BOOL as_root)
1006{
1007	BOOL ret;
1008	uint32 min_len;
1009	struct passwd *pass = NULL;
1010	const char *username = pdb_get_username(hnd);
1011	time_t can_change_time = pdb_get_pass_can_change_time(hnd);
1012
1013	if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
1014		DEBUG(1, ("user %s cannot change password now, must wait until %s\n",
1015			  username, http_timestring(can_change_time)));
1016		return NT_STATUS_ACCOUNT_RESTRICTION;
1017	}
1018
1019	/* FIXME: AP_MIN_PASSWORD_LEN and lp_min_passwd_length() need to be merged - gd */
1020	if (account_policy_get(AP_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
1021		DEBUG(1, ("user %s cannot change password - password too short\n",
1022			  username));
1023		DEBUGADD(1, (" account policy min password len = %d\n", min_len));
1024		return NT_STATUS_PASSWORD_RESTRICTION;
1025/* 		return NT_STATUS_PWD_TOO_SHORT; */
1026	}
1027
1028	/* Take the passed information and test it for minimum criteria */
1029	/* Minimum password length */
1030	if (str_charnum(new_passwd) < lp_min_passwd_length()) {
1031		/* too short, must be at least MINPASSWDLENGTH */
1032		DEBUG(1, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
1033		       username, lp_min_passwd_length()));
1034		return NT_STATUS_PASSWORD_RESTRICTION;
1035/* 		return NT_STATUS_PWD_TOO_SHORT; */
1036	}
1037
1038	if (check_passwd_history(hnd,new_passwd)) {
1039		return NT_STATUS_PASSWORD_RESTRICTION;
1040	}
1041
1042	pass = Get_Pwnam(username);
1043	if (!pass) {
1044		DEBUG(1, ("check_oem_password: Username %s does not exist in system !?!\n", username));
1045		return NT_STATUS_ACCESS_DENIED;
1046	}
1047
1048	/* Use external script to check password complexity */
1049	if (lp_check_password_script() && *(lp_check_password_script())) {
1050		int check_ret;
1051
1052		check_ret = smbrunsecret(lp_check_password_script(), new_passwd);
1053		DEBUG(5, ("change_oem_password: check password script (%s) returned [%d]\n", lp_check_password_script(), check_ret));
1054
1055		if (check_ret != 0) {
1056			DEBUG(1, ("change_oem_password: check password script said new password is not good enough!\n"));
1057			return NT_STATUS_PASSWORD_RESTRICTION;
1058		}
1059	}
1060
1061	/*
1062	 * If unix password sync was requested, attempt to change
1063	 * the /etc/passwd database first. Return failure if this cannot
1064	 * be done.
1065	 *
1066	 * This occurs before the oem change, because we don't want to
1067	 * update it if chgpasswd failed.
1068	 *
1069	 * Conditional on lp_unix_password_sync() because we don't want
1070	 * to touch the unix db unless we have admin permission.
1071	 */
1072
1073	if(lp_unix_password_sync() &&
1074		!chgpasswd(username, pass, old_passwd, new_passwd, as_root)) {
1075		return NT_STATUS_ACCESS_DENIED;
1076	}
1077
1078	if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1079		return NT_STATUS_ACCESS_DENIED;
1080	}
1081
1082	/* Now write it into the file. */
1083	ret = pdb_update_sam_account (hnd);
1084
1085	if (!ret) {
1086		return NT_STATUS_ACCESS_DENIED;
1087	}
1088
1089	return NT_STATUS_OK;
1090}
1091