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