2
Unix SMB/CIFS implementation.
3
SMB client password change routine
4
Copyright (C) Andrew Tridgell 1994-1998
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.
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.
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/>.
22
/*************************************************************
23
Change a password on a remote machine using IPC calls.
24
*************************************************************/
26
NTSTATUS remote_password_change(const char *remote_machine, const char *user_name,
27
const char *old_passwd, const char *new_passwd,
30
struct nmb_name calling, called;
31
struct cli_state *cli;
32
struct rpc_pipe_client *pipe_hnd;
33
struct sockaddr_storage ss;
34
char *user, *domain, *p;
37
bool pass_must_change = False;
39
user = talloc_strdup(talloc_tos(), user_name);
40
SMB_ASSERT(user != NULL);
41
domain = talloc_strdup(talloc_tos(), "");
42
SMB_ASSERT(domain != NULL);
44
/* allow usernames of the form domain\\user or domain/user */
45
if ((p = strchr_m(user,'\\')) || (p = strchr_m(user,'/')) ||
46
(p = strchr_m(user,*lp_winbind_separator()))) {
54
if(!resolve_name( remote_machine, &ss, 0x20)) {
55
if (asprintf(err_str, "Unable to find an IP address for machine "
56
"%s.\n", remote_machine) == -1) {
59
return NT_STATUS_UNSUCCESSFUL;
62
cli = cli_initialise();
64
return NT_STATUS_NO_MEMORY;
67
result = cli_connect(cli, remote_machine, &ss);
68
if (!NT_STATUS_IS_OK(result)) {
69
if (asprintf(err_str, "Unable to connect to SMB server on "
70
"machine %s. Error was : %s.\n",
71
remote_machine, nt_errstr(result))==-1) {
78
make_nmb_name(&calling, global_myname() , 0x0);
79
make_nmb_name(&called , remote_machine, 0x20);
81
if (!cli_session_request(cli, &calling, &called)) {
82
if (asprintf(err_str, "machine %s rejected the session setup. "
84
remote_machine, cli_errstr(cli)) == -1) {
87
result = cli_nt_error(cli);
92
cli->protocol = PROTOCOL_NT1;
94
result = cli_negprot(cli);
96
if (!NT_STATUS_IS_OK(result)) {
97
if (asprintf(err_str, "machine %s rejected the negotiate "
98
"protocol. Error was : %s.\n",
99
remote_machine, nt_errstr(result)) == -1) {
102
result = cli_nt_error(cli);
107
/* Given things like SMB signing, restrict anonymous and the like,
108
try an authenticated connection first */
109
result = cli_session_setup(cli, user_name,
110
old_passwd, strlen(old_passwd)+1,
111
old_passwd, strlen(old_passwd)+1, "");
113
if (!NT_STATUS_IS_OK(result)) {
115
/* Password must change or Password expired are the only valid
116
* error conditions here from where we can proceed, the rest like
117
* account locked out or logon failure will lead to errors later
120
if (!NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) &&
121
!NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED)) {
122
if (asprintf(err_str, "Could not connect to machine %s: "
123
"%s\n", remote_machine, cli_errstr(cli)) == -1) {
130
pass_must_change = True;
133
* We should connect as the anonymous user here, in case
134
* the server has "must change password" checked...
135
* Thanks to <Nicholas.S.Jenkins@cdc.com> for this fix.
138
result = cli_session_setup(cli, "", "", 0, "", 0, "");
140
if (!NT_STATUS_IS_OK(result)) {
141
if (asprintf(err_str, "machine %s rejected the session "
142
"setup. Error was : %s.\n",
143
remote_machine, cli_errstr(cli)) == -1) {
150
result = cli_init_creds(cli, "", "", NULL);
151
if (!NT_STATUS_IS_OK(result)) {
156
result = cli_init_creds(cli, user, domain, old_passwd);
157
if (!NT_STATUS_IS_OK(result)) {
163
result = cli_tcon_andx(cli, "IPC$", "IPC", "", 1);
164
if (!NT_STATUS_IS_OK(result)) {
165
if (asprintf(err_str, "machine %s rejected the tconX on the "
166
"IPC$ share. Error was : %s.\n",
167
remote_machine, nt_errstr(result))) {
174
/* Try not to give the password away too easily */
176
if (!pass_must_change) {
177
result = cli_rpc_pipe_open_ntlmssp(cli,
178
&ndr_table_samr.syntax_id,
180
PIPE_AUTH_LEVEL_PRIVACY,
186
* If the user password must be changed the ntlmssp bind will
187
* fail the same way as the session setup above did. The
188
* difference ist that with a pipe bind we don't get a good
189
* error message, the result will be that the rpc call below
190
* will just fail. So we do it anonymously, there's no other
193
result = cli_rpc_pipe_open_noauth(
194
cli, &ndr_table_samr.syntax_id, &pipe_hnd);
197
if (!NT_STATUS_IS_OK(result)) {
198
if (lp_client_lanman_auth()) {
199
/* Use the old RAP method. */
200
if (!cli_oem_change_password(cli, user_name, new_passwd, old_passwd)) {
201
if (asprintf(err_str, "machine %s rejected the "
202
"password change: Error was : %s.\n",
203
remote_machine, cli_errstr(cli)) == -1) {
206
result = cli_nt_error(cli);
211
if (asprintf(err_str, "SAMR connection to machine %s "
212
"failed. Error was %s, but LANMAN password "
213
"changes are disabled\n",
214
remote_machine, nt_errstr(result)) == -1) {
217
result = cli_nt_error(cli);
223
result = rpccli_samr_chgpasswd_user2(pipe_hnd, talloc_tos(),
224
user_name, new_passwd, old_passwd);
225
if (NT_STATUS_IS_OK(result)) {
226
/* Great - it all worked! */
230
} else if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)
231
|| NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) {
232
/* it failed, but for reasons such as wrong password, too short etc ... */
234
if (asprintf(err_str, "machine %s rejected the password change: "
236
remote_machine, get_friendly_nt_error_msg(result)) == -1) {
243
/* OK, that failed, so try again... */
244
TALLOC_FREE(pipe_hnd);
246
/* Try anonymous NTLMSSP... */
247
result = cli_init_creds(cli, "", "", NULL);
248
if (!NT_STATUS_IS_OK(result)) {
253
result = NT_STATUS_UNSUCCESSFUL;
255
/* OK, this is ugly, but... try an anonymous pipe. */
256
result = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr.syntax_id,
259
if ( NT_STATUS_IS_OK(result) &&
260
(NT_STATUS_IS_OK(result = rpccli_samr_chgpasswd_user2(
261
pipe_hnd, talloc_tos(), user_name,
262
new_passwd, old_passwd)))) {
263
/* Great - it all worked! */
267
if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)
268
|| NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) {
269
/* it failed, but again it was due to things like new password too short */
271
if (asprintf(err_str, "machine %s rejected the "
272
"(anonymous) password change: Error was : "
273
"%s.\n", remote_machine,
274
get_friendly_nt_error_msg(result)) == -1) {
281
/* We have failed to change the user's password, and we think the server
282
just might not support SAMR password changes, so fall back */
284
if (lp_client_lanman_auth()) {
285
/* Use the old RAP method. */
286
if (cli_oem_change_password(cli, user_name, new_passwd, old_passwd)) {
287
/* SAMR failed, but the old LanMan protocol worked! */
292
if (asprintf(err_str, "machine %s rejected the password "
293
"change: Error was : %s.\n",
294
remote_machine, cli_errstr(cli)) == -1) {
297
result = cli_nt_error(cli);
301
if (asprintf(err_str, "SAMR connection to machine %s "
302
"failed. Error was %s, but LANMAN password "
303
"changed are disabled\n",
304
nt_errstr(result), remote_machine) == -1) {
308
return NT_STATUS_UNSUCCESSFUL;