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