Line data Source code
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 3 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, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include "includes.h"
21 : #include "../librpc/gen_ndr/ndr_samr.h"
22 : #include "rpc_client/cli_pipe.h"
23 : #include "rpc_client/cli_samr.h"
24 : #include "libsmb/libsmb.h"
25 : #include "libsmb/clirap.h"
26 : #include "libsmb/nmblib.h"
27 : #include "../libcli/smb/smbXcli_base.h"
28 :
29 : /*************************************************************
30 : Change a password on a remote machine using IPC calls.
31 : *************************************************************/
32 :
33 3 : NTSTATUS remote_password_change(const char *remote_machine,
34 : const char *domain, const char *user_name,
35 : const char *old_passwd, const char *new_passwd,
36 : char **err_str)
37 : {
38 3 : struct cli_state *cli = NULL;
39 3 : struct cli_credentials *creds = NULL;
40 3 : struct rpc_pipe_client *pipe_hnd = NULL;
41 : NTSTATUS status;
42 : NTSTATUS result;
43 3 : bool pass_must_change = False;
44 :
45 3 : *err_str = NULL;
46 :
47 3 : result = cli_connect_nb(remote_machine, NULL, 0, 0x20, NULL,
48 : SMB_SIGNING_IPC_DEFAULT, 0, &cli);
49 3 : if (!NT_STATUS_IS_OK(result)) {
50 0 : if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) {
51 0 : if (asprintf(err_str, "Unable to connect to SMB server on "
52 : "machine %s. NetBIOS support disabled\n",
53 : remote_machine) == -1) {
54 0 : *err_str = NULL;
55 : }
56 : } else {
57 0 : if (asprintf(err_str, "Unable to connect to SMB server on "
58 : "machine %s. Error was : %s.\n",
59 : remote_machine, nt_errstr(result))==-1) {
60 0 : *err_str = NULL;
61 : }
62 : }
63 0 : return result;
64 : }
65 :
66 3 : creds = cli_session_creds_init(cli,
67 : user_name,
68 : domain,
69 : NULL, /* realm */
70 : old_passwd,
71 : false, /* use_kerberos */
72 : false, /* fallback_after_kerberos */
73 : false, /* use_ccache */
74 : false); /* password_is_nt_hash */
75 3 : SMB_ASSERT(creds != NULL);
76 :
77 3 : result = smbXcli_negprot(cli->conn, cli->timeout,
78 3 : lp_client_ipc_min_protocol(),
79 3 : lp_client_ipc_max_protocol());
80 :
81 3 : if (!NT_STATUS_IS_OK(result)) {
82 0 : if (asprintf(err_str, "machine %s rejected the negotiate "
83 : "protocol. Error was : %s.\n",
84 : remote_machine, nt_errstr(result)) == -1) {
85 0 : *err_str = NULL;
86 : }
87 0 : cli_shutdown(cli);
88 0 : return result;
89 : }
90 :
91 : /* Given things like SMB signing, restrict anonymous and the like,
92 : try an authenticated connection first */
93 3 : result = cli_session_setup_creds(cli, creds);
94 :
95 3 : if (!NT_STATUS_IS_OK(result)) {
96 :
97 : /* Password must change or Password expired are the only valid
98 : * error conditions here from where we can proceed, the rest like
99 : * account locked out or logon failure will lead to errors later
100 : * anyway */
101 :
102 0 : if (!NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) &&
103 0 : !NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED)) {
104 0 : if (asprintf(err_str, "Could not connect to machine %s: "
105 : "%s\n", remote_machine, nt_errstr(result)) == -1) {
106 0 : *err_str = NULL;
107 : }
108 0 : cli_shutdown(cli);
109 0 : return result;
110 : }
111 :
112 0 : pass_must_change = True;
113 :
114 : /*
115 : * We should connect as the anonymous user here, in case
116 : * the server has "must change password" checked...
117 : * Thanks to <Nicholas.S.Jenkins@cdc.com> for this fix.
118 : */
119 :
120 0 : result = cli_session_setup_anon(cli);
121 :
122 0 : if (!NT_STATUS_IS_OK(result)) {
123 0 : if (asprintf(err_str, "machine %s rejected the session "
124 : "setup. Error was : %s.\n",
125 : remote_machine, nt_errstr(result)) == -1) {
126 0 : *err_str = NULL;
127 : }
128 0 : cli_shutdown(cli);
129 0 : return result;
130 : }
131 : }
132 :
133 3 : result = cli_tree_connect(cli, "IPC$", "IPC", NULL);
134 3 : if (!NT_STATUS_IS_OK(result)) {
135 0 : if (asprintf(err_str, "machine %s rejected the tconX on the "
136 : "IPC$ share. Error was : %s.\n",
137 : remote_machine, nt_errstr(result))) {
138 0 : *err_str = NULL;
139 : }
140 0 : cli_shutdown(cli);
141 0 : return result;
142 : }
143 :
144 : /* Try not to give the password away too easily */
145 :
146 3 : if (!pass_must_change) {
147 2 : const struct sockaddr_storage *remote_sockaddr =
148 3 : smbXcli_conn_remote_sockaddr(cli->conn);
149 :
150 3 : result = cli_rpc_pipe_open_with_creds(cli,
151 : &ndr_table_samr,
152 : NCACN_NP,
153 : DCERPC_AUTH_TYPE_NTLMSSP,
154 : DCERPC_AUTH_LEVEL_PRIVACY,
155 : remote_machine,
156 : remote_sockaddr,
157 : creds,
158 : &pipe_hnd);
159 : } else {
160 : /*
161 : * If the user password must be changed the ntlmssp bind will
162 : * fail the same way as the session setup above did. The
163 : * difference ist that with a pipe bind we don't get a good
164 : * error message, the result will be that the rpc call below
165 : * will just fail. So we do it anonymously, there's no other
166 : * way.
167 : */
168 0 : result = cli_rpc_pipe_open_noauth(
169 : cli, &ndr_table_samr, &pipe_hnd);
170 : }
171 :
172 3 : if (!NT_STATUS_IS_OK(result)) {
173 0 : if (lp_client_lanman_auth()) {
174 : /* Use the old RAP method. */
175 0 : if (!cli_oem_change_password(cli, user_name, new_passwd, old_passwd)) {
176 0 : result = cli_nt_error(cli);
177 0 : if (asprintf(err_str, "machine %s rejected the "
178 : "password change: Error was : %s.\n",
179 : remote_machine, nt_errstr(result)) == -1) {
180 0 : *err_str = NULL;
181 : }
182 0 : cli_shutdown(cli);
183 0 : return result;
184 : }
185 : } else {
186 0 : if (asprintf(err_str, "SAMR connection to machine %s "
187 : "failed. Error was %s, but LANMAN password "
188 : "changes are disabled\n",
189 : remote_machine, nt_errstr(result)) == -1) {
190 0 : *err_str = NULL;
191 : }
192 0 : cli_shutdown(cli);
193 0 : return result;
194 : }
195 : }
196 :
197 3 : status = dcerpc_samr_chgpasswd_user4(pipe_hnd->binding_handle,
198 : talloc_tos(),
199 3 : pipe_hnd->srv_name_slash,
200 : user_name,
201 : old_passwd,
202 : new_passwd,
203 : &result);
204 3 : if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
205 : /* All good, password successfully changed. */
206 1 : cli_shutdown(cli);
207 1 : return NT_STATUS_OK;
208 : }
209 2 : if (!NT_STATUS_IS_OK(status)) {
210 2 : if (NT_STATUS_EQUAL(status,
211 2 : NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
212 4 : NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED) ||
213 2 : NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
214 : /* DO NOT FALLBACK TO RC4 */
215 2 : if (lp_weak_crypto() == SAMBA_WEAK_CRYPTO_DISALLOWED) {
216 0 : cli_shutdown(cli);
217 0 : return NT_STATUS_STRONG_CRYPTO_NOT_SUPPORTED;
218 : }
219 : }
220 : } else {
221 0 : if (!NT_STATUS_IS_OK(result)) {
222 0 : int rc = asprintf(
223 : err_str,
224 : "machine %s rejected to change the password"
225 : "with error: %s",
226 : remote_machine,
227 : get_friendly_nt_error_msg(result));
228 0 : if (rc <= 0) {
229 0 : *err_str = NULL;
230 : }
231 0 : cli_shutdown(cli);
232 0 : return result;
233 : }
234 : }
235 :
236 2 : result = rpccli_samr_chgpasswd_user2(pipe_hnd, talloc_tos(),
237 : user_name, new_passwd, old_passwd);
238 2 : if (NT_STATUS_IS_OK(result)) {
239 : /* Great - it all worked! */
240 2 : cli_shutdown(cli);
241 2 : return NT_STATUS_OK;
242 :
243 0 : } else if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)
244 0 : || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) {
245 : /* it failed, but for reasons such as wrong password, too short etc ... */
246 :
247 0 : if (asprintf(err_str, "machine %s rejected the password change: "
248 : "Error was : %s.\n",
249 : remote_machine, get_friendly_nt_error_msg(result)) == -1) {
250 0 : *err_str = NULL;
251 : }
252 0 : cli_shutdown(cli);
253 0 : return result;
254 : }
255 :
256 : /* OK, that failed, so try again... */
257 0 : TALLOC_FREE(pipe_hnd);
258 :
259 : /* Try anonymous NTLMSSP... */
260 0 : result = NT_STATUS_UNSUCCESSFUL;
261 :
262 : /* OK, this is ugly, but... try an anonymous pipe. */
263 0 : result = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr,
264 : &pipe_hnd);
265 :
266 0 : if ( NT_STATUS_IS_OK(result) &&
267 0 : (NT_STATUS_IS_OK(result = rpccli_samr_chgpasswd_user2(
268 : pipe_hnd, talloc_tos(), user_name,
269 : new_passwd, old_passwd)))) {
270 : /* Great - it all worked! */
271 0 : cli_shutdown(cli);
272 0 : return NT_STATUS_OK;
273 : } else {
274 0 : if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)
275 0 : || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) {
276 : /* it failed, but again it was due to things like new password too short */
277 :
278 0 : if (asprintf(err_str, "machine %s rejected the "
279 : "(anonymous) password change: Error was : "
280 : "%s.\n", remote_machine,
281 : get_friendly_nt_error_msg(result)) == -1) {
282 0 : *err_str = NULL;
283 : }
284 0 : cli_shutdown(cli);
285 0 : return result;
286 : }
287 :
288 : /* We have failed to change the user's password, and we think the server
289 : just might not support SAMR password changes, so fall back */
290 :
291 0 : if (lp_client_lanman_auth()) {
292 : /* Use the old RAP method. */
293 0 : if (cli_oem_change_password(cli, user_name, new_passwd, old_passwd)) {
294 : /* SAMR failed, but the old LanMan protocol worked! */
295 :
296 0 : cli_shutdown(cli);
297 0 : return NT_STATUS_OK;
298 : }
299 :
300 0 : result = cli_nt_error(cli);
301 0 : if (asprintf(err_str, "machine %s rejected the password "
302 : "change: Error was : %s.\n",
303 : remote_machine, nt_errstr(result)) == -1) {
304 0 : *err_str = NULL;
305 : }
306 0 : cli_shutdown(cli);
307 0 : return result;
308 : } else {
309 0 : if (asprintf(err_str, "SAMR connection to machine %s "
310 : "failed. Error was %s, but LANMAN password "
311 : "changes are disabled\n",
312 : remote_machine, nt_errstr(result)) == -1) {
313 0 : *err_str = NULL;
314 : }
315 0 : cli_shutdown(cli);
316 0 : return NT_STATUS_UNSUCCESSFUL;
317 : }
318 : }
319 : }
|