root/source3/libsmb/passchange.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. remote_password_change

   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 
  22 /*************************************************************
  23  Change a password on a remote machine using IPC calls.
  24 *************************************************************/
  25 
  26 NTSTATUS remote_password_change(const char *remote_machine, const char *user_name, 
     /* [<][>][^][v][top][bottom][index][help] */
  27                                 const char *old_passwd, const char *new_passwd,
  28                                 char **err_str)
  29 {
  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;
  35 
  36         NTSTATUS result;
  37         bool pass_must_change = False;
  38 
  39         user = talloc_strdup(talloc_tos(), user_name);
  40         SMB_ASSERT(user != NULL);
  41         domain = talloc_strdup(talloc_tos(), "");
  42         SMB_ASSERT(domain != NULL);
  43 
  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()))) {
  47                 *p = 0;
  48                 domain = user;
  49                 user = p+1;
  50         }
  51 
  52         *err_str = NULL;
  53 
  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) {
  57                         *err_str = NULL;
  58                 }
  59                 return NT_STATUS_UNSUCCESSFUL;
  60         }
  61 
  62         cli = cli_initialise();
  63         if (!cli) {
  64                 return NT_STATUS_NO_MEMORY;
  65         }
  66 
  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) {
  72                         *err_str = NULL;
  73                 }
  74                 cli_shutdown(cli);
  75                 return result;
  76         }
  77 
  78         make_nmb_name(&calling, global_myname() , 0x0);
  79         make_nmb_name(&called , remote_machine, 0x20);
  80 
  81         if (!cli_session_request(cli, &calling, &called)) {
  82                 if (asprintf(err_str, "machine %s rejected the session setup. "
  83                          "Error was : %s.\n",
  84                          remote_machine, cli_errstr(cli)) == -1) {
  85                         *err_str = NULL;
  86                 }
  87                 result = cli_nt_error(cli);
  88                 cli_shutdown(cli);
  89                 return result;
  90         }
  91 
  92         cli->protocol = PROTOCOL_NT1;
  93 
  94         result = cli_negprot(cli);
  95 
  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) {
 100                         *err_str = NULL;
 101                 }
 102                 result = cli_nt_error(cli);
 103                 cli_shutdown(cli);
 104                 return result;
 105         }
 106 
 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, "");
 112 
 113         if (!NT_STATUS_IS_OK(result)) {
 114 
 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
 118                  * anyway */
 119 
 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) {
 124                                 *err_str = NULL;
 125                         }
 126                         cli_shutdown(cli);
 127                         return result;
 128                 }
 129 
 130                 pass_must_change = True;
 131 
 132                 /*
 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.
 136                  */
 137 
 138                 result = cli_session_setup(cli, "", "", 0, "", 0, "");
 139 
 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) {
 144                                 *err_str = NULL;
 145                         }
 146                         cli_shutdown(cli);
 147                         return result;
 148                 }
 149 
 150                 result = cli_init_creds(cli, "", "", NULL);
 151                 if (!NT_STATUS_IS_OK(result)) {
 152                         cli_shutdown(cli);
 153                         return result;
 154                 }
 155         } else {
 156                 result = cli_init_creds(cli, user, domain, old_passwd);
 157                 if (!NT_STATUS_IS_OK(result)) {
 158                         cli_shutdown(cli);
 159                         return result;
 160                 }
 161         }
 162 
 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))) {
 168                         *err_str = NULL;
 169                 }
 170                 cli_shutdown(cli);
 171                 return result;
 172         }
 173 
 174         /* Try not to give the password away too easily */
 175 
 176         if (!pass_must_change) {
 177                 result = cli_rpc_pipe_open_ntlmssp(cli,
 178                                                    &ndr_table_samr.syntax_id,
 179                                                    NCACN_NP,
 180                                                    PIPE_AUTH_LEVEL_PRIVACY,
 181                                                    domain, user,
 182                                                    old_passwd,
 183                                                    &pipe_hnd);
 184         } else {
 185                 /*
 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
 191                  * way.
 192                  */
 193                 result = cli_rpc_pipe_open_noauth(
 194                         cli, &ndr_table_samr.syntax_id, &pipe_hnd);
 195         }
 196 
 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) {
 204                                         *err_str = NULL;
 205                                 }
 206                                 result = cli_nt_error(cli);
 207                                 cli_shutdown(cli);
 208                                 return result;
 209                         }
 210                 } else {
 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) {
 215                                 *err_str = NULL;
 216                         }
 217                         result = cli_nt_error(cli);
 218                         cli_shutdown(cli);
 219                         return result;
 220                 }
 221         }
 222 
 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! */
 227                 cli_shutdown(cli);
 228                 return NT_STATUS_OK;
 229 
 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 ... */
 233 
 234                 if (asprintf(err_str, "machine %s rejected the password change: "
 235                          "Error was : %s.\n",
 236                          remote_machine, get_friendly_nt_error_msg(result)) == -1) {
 237                         *err_str = NULL;
 238                 }
 239                 cli_shutdown(cli);
 240                 return result;
 241         }
 242 
 243         /* OK, that failed, so try again... */
 244         TALLOC_FREE(pipe_hnd);
 245 
 246         /* Try anonymous NTLMSSP... */
 247         result = cli_init_creds(cli, "", "", NULL);
 248         if (!NT_STATUS_IS_OK(result)) {
 249                 cli_shutdown(cli);
 250                 return result;
 251         }
 252 
 253         result = NT_STATUS_UNSUCCESSFUL;
 254 
 255         /* OK, this is ugly, but... try an anonymous pipe. */
 256         result = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr.syntax_id,
 257                                           &pipe_hnd);
 258 
 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! */
 264                 cli_shutdown(cli);
 265                 return NT_STATUS_OK;
 266         } else {
 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 */
 270 
 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) {
 275                                 *err_str = NULL;
 276                         }
 277                         cli_shutdown(cli);
 278                         return result;
 279                 }
 280 
 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 */
 283 
 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! */
 288 
 289                                 cli_shutdown(cli);
 290                                 return NT_STATUS_OK;
 291                         }
 292                         if (asprintf(err_str, "machine %s rejected the password "
 293                                  "change: Error was : %s.\n",
 294                                  remote_machine, cli_errstr(cli)) == -1) {
 295                                 *err_str = NULL;
 296                         }
 297                         result = cli_nt_error(cli);
 298                         cli_shutdown(cli);
 299                         return result;
 300                 } else {
 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) {
 305                                 *err_str = NULL;
 306                         }
 307                         cli_shutdown(cli);
 308                         return NT_STATUS_UNSUCCESSFUL;
 309                 }
 310         }
 311 }

/* [<][>][^][v][top][bottom][index][help] */