Initial import
[samba] / source / 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 BOOL 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 False;
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 False;
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 False;
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                 cli_shutdown(&cli);
71                 return False;
72         }
73   
74         /* Given things like SMB signing, restrict anonymous and the like, 
75            try an authenticated connection first */
76         if (!cli_session_setup(&cli, user_name, old_passwd, strlen(old_passwd)+1, old_passwd, strlen(old_passwd)+1, "")) {
77
78                 result = cli_nt_error(&cli);
79
80                 if (!NT_STATUS_IS_OK(result)) {
81
82                         /* Password must change is the only valid error
83                          * condition here from where we can proceed, the rest
84                          * like account locked out or logon failure will lead
85                          * to errors later anyway */
86
87                         if (!NT_STATUS_EQUAL(result,
88                                              NT_STATUS_PASSWORD_MUST_CHANGE)) {
89                                 slprintf(err_str, err_str_len-1, "Could not "
90                                          "connect to machine %s: %s\n",
91                                          remote_machine, cli_errstr(&cli));
92                                 cli_shutdown(&cli);
93                                 return False;
94                         }
95
96                         pass_must_change = True;
97                 }
98
99                 /*
100                  * We should connect as the anonymous user here, in case
101                  * the server has "must change password" checked...
102                  * Thanks to <Nicholas.S.Jenkins@cdc.com> for this fix.
103                  */
104
105                 if (!cli_session_setup(&cli, "", "", 0, "", 0, "")) {
106                         slprintf(err_str, err_str_len-1, "machine %s rejected the session setup. Error was : %s.\n",        
107                                  remote_machine, cli_errstr(&cli) );
108                         cli_shutdown(&cli);
109                         return False;
110                 }
111
112                 cli_init_creds(&cli, "", "", NULL);
113         } else {
114                 cli_init_creds(&cli, user_name, "", old_passwd);
115         }
116
117         if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
118                 slprintf(err_str, err_str_len-1, "machine %s rejected the tconX on the IPC$ share. Error was : %s.\n",
119                         remote_machine, cli_errstr(&cli) );
120                 cli_shutdown(&cli);
121                 return False;
122         }
123
124         /* Try not to give the password away too easily */
125
126         if (!pass_must_change) {
127                 pipe_hnd = cli_rpc_pipe_open_ntlmssp(&cli,
128                                                 PI_SAMR,
129                                                 PIPE_AUTH_LEVEL_PRIVACY,
130                                                 "", /* what domain... ? */
131                                                 user_name,
132                                                 old_passwd,
133                                                 &result);
134         } else {
135                 /*
136                  * If the user password must be changed the ntlmssp bind will
137                  * fail the same way as the session setup above did. The
138                  * difference ist that with a pipe bind we don't get a good
139                  * error message, the result will be that the rpc call below
140                  * will just fail. So we do it anonymously, there's no other
141                  * way.
142                  */
143                 pipe_hnd = cli_rpc_pipe_open_noauth(&cli, PI_SAMR, &result);
144         }
145
146         if (!pipe_hnd) {
147                 if (lp_client_lanman_auth()) {
148                         /* Use the old RAP method. */
149                         if (!cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) {
150                                 slprintf(err_str, err_str_len-1, "machine %s rejected the password change: Error was : %s.\n",
151                                          remote_machine, cli_errstr(&cli) );
152                                 cli_shutdown(&cli);
153                                 return False;
154                         }
155                 } else {
156                         slprintf(err_str, err_str_len-1,
157                                 "SAMR connection to machine %s failed. Error was %s, "
158                                 "but LANMAN password changed are disabled\n",
159                                 nt_errstr(result), remote_machine);
160                         cli_shutdown(&cli);
161                         return False;
162                 }
163         }
164
165         if (NT_STATUS_IS_OK(result = rpccli_samr_chgpasswd_user(pipe_hnd, cli.mem_ctx, user_name, 
166                                                              new_passwd, old_passwd))) {
167                 /* Great - it all worked! */
168                 cli_shutdown(&cli);
169                 return True;
170
171         } else if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) 
172                      || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) {
173                 /* it failed, but for reasons such as wrong password, too short etc ... */
174                 
175                 slprintf(err_str, err_str_len-1, "machine %s rejected the password change: Error was : %s.\n",
176                          remote_machine, get_friendly_nt_error_msg(result));
177                 cli_shutdown(&cli);
178                 return False;
179         }
180
181         /* OK, that failed, so try again... */
182         cli_rpc_pipe_close(pipe_hnd);
183         
184         /* Try anonymous NTLMSSP... */
185         cli_init_creds(&cli, "", "", NULL);
186         
187         result = NT_STATUS_UNSUCCESSFUL;
188         
189         /* OK, this is ugly, but... try an anonymous pipe. */
190         pipe_hnd = cli_rpc_pipe_open_noauth(&cli, PI_SAMR, &result);
191
192         if ( pipe_hnd &&
193                 (NT_STATUS_IS_OK(result = rpccli_samr_chgpasswd_user(pipe_hnd,
194                                                 cli.mem_ctx,
195                                                 user_name, 
196                                                 new_passwd,
197                                                 old_passwd)))) {
198                 /* Great - it all worked! */
199                 cli_shutdown(&cli);
200                 return True;
201         } else {
202                 if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) 
203                       || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) {
204                         /* it failed, but again it was due to things like new password too short */
205
206                         slprintf(err_str, err_str_len-1, 
207                                  "machine %s rejected the (anonymous) password change: Error was : %s.\n",
208                                  remote_machine, get_friendly_nt_error_msg(result));
209                         cli_shutdown(&cli);
210                         return False;
211                 }
212                 
213                 /* We have failed to change the user's password, and we think the server
214                    just might not support SAMR password changes, so fall back */
215                 
216                 if (lp_client_lanman_auth()) {
217                         /* Use the old RAP method. */
218                         if (cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) {
219                                 /* SAMR failed, but the old LanMan protocol worked! */
220
221                                 cli_shutdown(&cli);
222                                 return True;
223                         }
224                         slprintf(err_str, err_str_len-1, 
225                                  "machine %s rejected the password change: Error was : %s.\n",
226                                  remote_machine, cli_errstr(&cli) );
227                         cli_shutdown(&cli);
228                         return False;
229                 } else {
230                         slprintf(err_str, err_str_len-1,
231                                 "SAMR connection to machine %s failed. Error was %s, "
232                                 "but LANMAN password changed are disabled\n",
233                                 nt_errstr(result), remote_machine);
234                         cli_shutdown(&cli);
235                         return False;
236                 }
237         }
238 }