1/*
2   Unix SMB/CIFS implementation.
3   SMB client password change routine
4   Copyright (C) Andrew Tridgell 1994-1998
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#include "includes.h"
22
23/*************************************************************
24change a password on a remote machine using IPC calls
25*************************************************************/
26BOOL remote_password_change(const char *remote_machine, const char *user_name,
27			    const char *old_passwd, const char *new_passwd,
28			    char *err_str, size_t err_str_len)
29{
30	struct nmb_name calling, called;
31	struct cli_state cli;
32	struct in_addr ip;
33	struct ntuser_creds creds;
34
35	NTSTATUS result;
36
37	*err_str = '\0';
38
39	if(!resolve_name( remote_machine, &ip, 0x20)) {
40		slprintf(err_str, err_str_len-1, "unable to find an IP address for machine %s.\n",
41			remote_machine );
42		return False;
43	}
44
45	ZERO_STRUCT(cli);
46
47	if (!cli_initialise(&cli) || !cli_connect(&cli, remote_machine, &ip)) {
48		slprintf(err_str, err_str_len-1, "unable to connect to SMB server on machine %s. Error was : %s.\n",
49			remote_machine, cli_errstr(&cli) );
50		return False;
51	}
52
53	make_nmb_name(&calling, global_myname() , 0x0);
54	make_nmb_name(&called , remote_machine, 0x20);
55
56	if (!cli_session_request(&cli, &calling, &called)) {
57		slprintf(err_str, err_str_len-1, "machine %s rejected the session setup. Error was : %s.\n",
58			remote_machine, cli_errstr(&cli) );
59		cli_shutdown(&cli);
60		return False;
61	}
62
63	cli.protocol = PROTOCOL_NT1;
64
65	if (!cli_negprot(&cli)) {
66		slprintf(err_str, err_str_len-1, "machine %s rejected the negotiate protocol. Error was : %s.\n",
67			remote_machine, cli_errstr(&cli) );
68		cli_shutdown(&cli);
69		return False;
70	}
71
72	/* Given things like SMB signing, restrict anonymous and the like,
73	   try an authenticated connection first */
74	if (!cli_session_setup(&cli, user_name, old_passwd, strlen(old_passwd)+1, old_passwd, strlen(old_passwd)+1, "")) {
75		/*
76		 * We should connect as the anonymous user here, in case
77		 * the server has "must change password" checked...
78		 * Thanks to <Nicholas.S.Jenkins@cdc.com> for this fix.
79		 */
80
81		if (!cli_session_setup(&cli, "", "", 0, "", 0, "")) {
82			slprintf(err_str, err_str_len-1, "machine %s rejected the session setup. Error was : %s.\n",
83				 remote_machine, cli_errstr(&cli) );
84			cli_shutdown(&cli);
85			return False;
86		}
87
88		init_creds(&creds, "", "", NULL);
89		cli_init_creds(&cli, &creds);
90	} else {
91		init_creds(&creds, user_name, "", old_passwd);
92		cli_init_creds(&cli, &creds);
93	}
94
95	if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
96		slprintf(err_str, err_str_len-1, "machine %s rejected the tconX on the IPC$ share. Error was : %s.\n",
97			remote_machine, cli_errstr(&cli) );
98		cli_shutdown(&cli);
99		return False;
100	}
101
102	/* Try not to give the password away to easily */
103
104	cli.pipe_auth_flags = AUTH_PIPE_NTLMSSP;
105	cli.pipe_auth_flags |= AUTH_PIPE_SIGN;
106	cli.pipe_auth_flags |= AUTH_PIPE_SEAL;
107
108	if ( !cli_nt_session_open( &cli, PI_SAMR ) ) {
109		if (lp_client_lanman_auth()) {
110			if (!cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) {
111				slprintf(err_str, err_str_len-1, "machine %s rejected the password change: Error was : %s.\n",
112					 remote_machine, cli_errstr(&cli) );
113				cli_shutdown(&cli);
114				return False;
115			}
116		} else {
117			slprintf(err_str, err_str_len-1, "machine %s does not support SAMR connections, but LANMAN password changed are disabled\n",
118				 remote_machine);
119			cli_shutdown(&cli);
120			return False;
121		}
122	}
123
124	if (NT_STATUS_IS_OK(result = cli_samr_chgpasswd_user(&cli, cli.mem_ctx, user_name,
125							     new_passwd, old_passwd))) {
126		/* Great - it all worked! */
127		cli_shutdown(&cli);
128		return True;
129
130	} else if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)
131		     || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) {
132		/* it failed, but for reasons such as wrong password, too short etc ... */
133
134		slprintf(err_str, err_str_len-1, "machine %s rejected the password change: Error was : %s.\n",
135			 remote_machine, get_friendly_nt_error_msg(result));
136		cli_shutdown(&cli);
137		return False;
138	}
139
140	/* OK, that failed, so try again... */
141	cli_nt_session_close(&cli);
142
143	/* Try anonymous NTLMSSP... */
144	init_creds(&creds, "", "", NULL);
145	cli_init_creds(&cli, &creds);
146
147	cli.pipe_auth_flags = 0;
148
149	result = NT_STATUS_UNSUCCESSFUL;
150
151	/* OK, this is ugly, but... */
152	if ( cli_nt_session_open( &cli, PI_SAMR )
153	     && NT_STATUS_IS_OK(result
154				= cli_samr_chgpasswd_user(&cli, cli.mem_ctx, user_name,
155							  new_passwd, old_passwd))) {
156		/* Great - it all worked! */
157		cli_shutdown(&cli);
158		return True;
159
160	} else {
161		if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)
162		      || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) {
163			/* it failed, but again it was due to things like new password too short */
164
165			slprintf(err_str, err_str_len-1,
166				 "machine %s rejected the (anonymous) password change: Error was : %s.\n",
167				 remote_machine, get_friendly_nt_error_msg(result));
168			cli_shutdown(&cli);
169			return False;
170		}
171
172		/* We have failed to change the user's password, and we think the server
173		   just might not support SAMR password changes, so fall back */
174
175		if (lp_client_lanman_auth()) {
176			if (cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) {
177				/* SAMR failed, but the old LanMan protocol worked! */
178
179				cli_shutdown(&cli);
180				return True;
181			}
182			slprintf(err_str, err_str_len-1,
183				 "machine %s rejected the password change: Error was : %s.\n",
184				 remote_machine, cli_errstr(&cli) );
185			cli_shutdown(&cli);
186			return False;
187		} else {
188			slprintf(err_str, err_str_len-1,
189				 "machine %s does not support SAMR connections, but LANMAN password changed are disabled\n",
190				 remote_machine);
191			cli_shutdown(&cli);
192			return False;
193		}
194	}
195}
196