r13316: Let the carnage begin....
[kai/samba.git] / source3 / libsmb / passchange.c
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 /*************************************************************
24  Change a password on a remote machine using IPC calls.
25 *************************************************************/
26
27 NTSTATUS remote_password_change(const char *remote_machine, const char *user_name, 
28                             const char *old_passwd, const char *new_passwd,
29                             char *err_str, size_t err_str_len)
30 {
31         struct nmb_name calling, called;
32         struct cli_state cli;
33         struct rpc_pipe_client *pipe_hnd;
34         struct in_addr ip;
35
36         NTSTATUS result;
37         BOOL pass_must_change = False;
38
39         *err_str = '\0';
40
41         if(!resolve_name( remote_machine, &ip, 0x20)) {
42                 slprintf(err_str, err_str_len-1, "unable to find an IP address for machine %s.\n",
43                         remote_machine );
44                 return NT_STATUS_UNSUCCESSFUL;
45         }
46  
47         ZERO_STRUCT(cli);
48  
49         if (!cli_initialise(&cli) || !cli_connect(&cli, remote_machine, &ip)) {
50                 slprintf(err_str, err_str_len-1, "unable to connect to SMB server on machine %s. Error was : %s.\n",
51                         remote_machine, cli_errstr(&cli) );
52                 return NT_STATUS_UNSUCCESSFUL;
53         }
54   
55         make_nmb_name(&calling, global_myname() , 0x0);
56         make_nmb_name(&called , remote_machine, 0x20);
57         
58         if (!cli_session_request(&cli, &calling, &called)) {
59                 slprintf(err_str, err_str_len-1, "machine %s rejected the session setup. Error was : %s.\n",
60                         remote_machine, cli_errstr(&cli) );
61                 cli_shutdown(&cli);
62                 return NT_STATUS_UNSUCCESSFUL;
63         }
64   
65         cli.protocol = PROTOCOL_NT1;
66
67         if (!cli_negprot(&cli)) {
68                 slprintf(err_str, err_str_len-1, "machine %s rejected the negotiate protocol. Error was : %s.\n",        
69                         remote_machine, cli_errstr(&cli) );
70                 result = cli_nt_error(&cli);
71                 cli_shutdown(&cli);
72                 return result;
73         }
74   
75         /* Given things like SMB signing, restrict anonymous and the like, 
76            try an authenticated connection first */
77         if (!cli_session_setup(&cli, user_name, old_passwd, strlen(old_passwd)+1, old_passwd, strlen(old_passwd)+1, "")) {
78
79                 result = cli_nt_error(&cli);
80
81                 if (!NT_STATUS_IS_OK(result)) {
82
83                         /* Password must change is the only valid error
84                          * condition here from where we can proceed, the rest
85                          * like account locked out or logon failure will lead
86                          * to errors later anyway */
87
88                         if (!NT_STATUS_EQUAL(result,
89                                              NT_STATUS_PASSWORD_MUST_CHANGE)) {
90                                 slprintf(err_str, err_str_len-1, "Could not "
91                                          "connect to machine %s: %s\n",
92                                          remote_machine, cli_errstr(&cli));
93                                 cli_shutdown(&cli);
94                                 return result;
95                         }
96
97                         pass_must_change = True;
98                 }
99
100                 /*
101                  * We should connect as the anonymous user here, in case
102                  * the server has "must change password" checked...
103                  * Thanks to <Nicholas.S.Jenkins@cdc.com> for this fix.
104                  */
105
106                 if (!cli_session_setup(&cli, "", "", 0, "", 0, "")) {
107                         slprintf(err_str, err_str_len-1, "machine %s rejected the session setup. Error was : %s.\n",        
108                                  remote_machine, cli_errstr(&cli) );
109                         result = cli_nt_error(&cli);
110                         cli_shutdown(&cli);
111                         return result;
112                 }
113
114                 cli_init_creds(&cli, "", "", NULL);
115         } else {
116                 cli_init_creds(&cli, user_name, "", old_passwd);
117         }
118
119         if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
120                 slprintf(err_str, err_str_len-1, "machine %s rejected the tconX on the IPC$ share. Error was : %s.\n",
121                         remote_machine, cli_errstr(&cli) );
122                 result = cli_nt_error(&cli);
123                 cli_shutdown(&cli);
124                 return result;
125         }
126
127         /* Try not to give the password away too easily */
128
129         if (!pass_must_change) {
130                 pipe_hnd = cli_rpc_pipe_open_ntlmssp(&cli,
131                                                 PI_SAMR,
132                                                 PIPE_AUTH_LEVEL_PRIVACY,
133                                                 "", /* what domain... ? */
134                                                 user_name,
135                                                 old_passwd,
136                                                 &result);
137         } else {
138                 /*
139                  * If the user password must be changed the ntlmssp bind will
140                  * fail the same way as the session setup above did. The
141                  * difference ist that with a pipe bind we don't get a good
142                  * error message, the result will be that the rpc call below
143                  * will just fail. So we do it anonymously, there's no other
144                  * way.
145                  */
146                 pipe_hnd = cli_rpc_pipe_open_noauth(&cli, PI_SAMR, &result);
147         }
148
149         if (!pipe_hnd) {
150                 if (lp_client_lanman_auth()) {
151                         /* Use the old RAP method. */
152                         if (!cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) {
153                                 slprintf(err_str, err_str_len-1, "machine %s rejected the password change: Error was : %s.\n",
154                                          remote_machine, cli_errstr(&cli) );
155                                 result = cli_nt_error(&cli);
156                                 cli_shutdown(&cli);
157                                 return result;
158                         }
159                 } else {
160                         slprintf(err_str, err_str_len-1,
161                                 "SAMR connection to machine %s failed. Error was %s, "
162                                 "but LANMAN password changed are disabled\n",
163                                 nt_errstr(result), remote_machine);
164                         result = cli_nt_error(&cli);
165                         cli_shutdown(&cli);
166                         return result;
167                 }
168         }
169
170         if (NT_STATUS_IS_OK(result = rpccli_samr_chgpasswd_user(pipe_hnd, cli.mem_ctx, user_name, 
171                                                              new_passwd, old_passwd))) {
172                 /* Great - it all worked! */
173                 cli_shutdown(&cli);
174                 return NT_STATUS_OK;
175
176         } else if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) 
177                      || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) {
178                 /* it failed, but for reasons such as wrong password, too short etc ... */
179                 
180                 slprintf(err_str, err_str_len-1, "machine %s rejected the password change: Error was : %s.\n",
181                          remote_machine, get_friendly_nt_error_msg(result));
182                 cli_shutdown(&cli);
183                 return result;
184         }
185
186         /* OK, that failed, so try again... */
187         cli_rpc_pipe_close(pipe_hnd);
188         
189         /* Try anonymous NTLMSSP... */
190         cli_init_creds(&cli, "", "", NULL);
191         
192         result = NT_STATUS_UNSUCCESSFUL;
193         
194         /* OK, this is ugly, but... try an anonymous pipe. */
195         pipe_hnd = cli_rpc_pipe_open_noauth(&cli, PI_SAMR, &result);
196
197         if ( pipe_hnd &&
198                 (NT_STATUS_IS_OK(result = rpccli_samr_chgpasswd_user(pipe_hnd,
199                                                 cli.mem_ctx,
200                                                 user_name, 
201                                                 new_passwd,
202                                                 old_passwd)))) {
203                 /* Great - it all worked! */
204                 cli_shutdown(&cli);
205                 return NT_STATUS_OK;
206         } else {
207                 if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) 
208                       || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) {
209                         /* it failed, but again it was due to things like new password too short */
210
211                         slprintf(err_str, err_str_len-1, 
212                                  "machine %s rejected the (anonymous) password change: Error was : %s.\n",
213                                  remote_machine, get_friendly_nt_error_msg(result));
214                         cli_shutdown(&cli);
215                         return result;
216                 }
217                 
218                 /* We have failed to change the user's password, and we think the server
219                    just might not support SAMR password changes, so fall back */
220                 
221                 if (lp_client_lanman_auth()) {
222                         /* Use the old RAP method. */
223                         if (cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) {
224                                 /* SAMR failed, but the old LanMan protocol worked! */
225
226                                 cli_shutdown(&cli);
227                                 return NT_STATUS_OK;
228                         }
229                         slprintf(err_str, err_str_len-1, 
230                                  "machine %s rejected the password change: Error was : %s.\n",
231                                  remote_machine, cli_errstr(&cli) );
232                         result = cli_nt_error(&cli);
233                         cli_shutdown(&cli);
234                         return result;
235                 } else {
236                         slprintf(err_str, err_str_len-1,
237                                 "SAMR connection to machine %s failed. Error was %s, "
238                                 "but LANMAN password changed are disabled\n",
239                                 nt_errstr(result), remote_machine);
240                         cli_shutdown(&cli);
241                         return NT_STATUS_UNSUCCESSFUL;
242                 }
243         }
244 }