root/source3/auth/auth_domain.c

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

DEFINITIONS

This source file includes following definitions.
  1. attempt_machine_password_change
  2. connect_to_domain_password_server
  3. domain_client_validate
  4. check_ntdomain_security
  5. auth_init_ntdomain
  6. check_trustdomain_security
  7. auth_init_trustdomain
  8. auth_domain_init

   1 /* 
   2    Unix SMB/CIFS implementation.
   3    Authenticate against a remote domain
   4    Copyright (C) Andrew Tridgell 1992-1998
   5    Copyright (C) Andrew Bartlett 2001
   6    
   7    This program is free software; you can redistribute it and/or modify
   8    it under the terms of the GNU General Public License as published by
   9    the Free Software Foundation; either version 3 of the License, or
  10    (at your option) any later version.
  11    
  12    This program is distributed in the hope that it will be useful,
  13    but WITHOUT ANY WARRANTY; without even the implied warranty of
  14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15    GNU General Public License for more details.
  16    
  17    You should have received a copy of the GNU General Public License
  18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  19 */
  20 
  21 #include "includes.h"
  22 
  23 #undef DBGC_CLASS
  24 #define DBGC_CLASS DBGC_AUTH
  25 
  26 extern bool global_machine_password_needs_changing;
  27 static struct named_mutex *mutex;
  28 
  29 /*
  30  * Change machine password (called from main loop
  31  * idle timeout. Must be done as root.
  32  */
  33 
  34 void attempt_machine_password_change(void)
     /* [<][>][^][v][top][bottom][index][help] */
  35 {
  36         unsigned char trust_passwd_hash[16];
  37         time_t lct;
  38         void *lock;
  39 
  40         if (!global_machine_password_needs_changing) {
  41                 return;
  42         }
  43 
  44         if (lp_security() != SEC_DOMAIN) {
  45                 return;
  46         }
  47 
  48         /*
  49          * We're in domain level security, and the code that
  50          * read the machine password flagged that the machine
  51          * password needs changing.
  52          */
  53 
  54         /*
  55          * First, open the machine password file with an exclusive lock.
  56          */
  57 
  58         lock = secrets_get_trust_account_lock(NULL, lp_workgroup());
  59 
  60         if (lock == NULL) {
  61                 DEBUG(0,("attempt_machine_password_change: unable to lock "
  62                         "the machine account password for machine %s in "
  63                         "domain %s.\n",
  64                         global_myname(), lp_workgroup() ));
  65                 return;
  66         }
  67 
  68         if(!secrets_fetch_trust_account_password(lp_workgroup(),
  69                         trust_passwd_hash, &lct, NULL)) {
  70                 DEBUG(0,("attempt_machine_password_change: unable to read the "
  71                         "machine account password for %s in domain %s.\n",
  72                         global_myname(), lp_workgroup()));
  73                 TALLOC_FREE(lock);
  74                 return;
  75         }
  76 
  77         /*
  78          * Make sure someone else hasn't already done this.
  79          */
  80 
  81         if(time(NULL) < lct + lp_machine_password_timeout()) {
  82                 global_machine_password_needs_changing = false;
  83                 TALLOC_FREE(lock);
  84                 return;
  85         }
  86 
  87         /* always just contact the PDC here */
  88 
  89         change_trust_account_password( lp_workgroup(), NULL);
  90         global_machine_password_needs_changing = false;
  91         TALLOC_FREE(lock);
  92 }
  93 
  94 /**
  95  * Connect to a remote server for (inter)domain security authenticaion.
  96  *
  97  * @param cli the cli to return containing the active connection
  98  * @param server either a machine name or text IP address to
  99  *               connect to.
 100  * @param setup_creds_as domain account to setup credentials as
 101  * @param sec_chan a switch value to distinguish between domain
 102  *                 member and interdomain authentication
 103  * @param trust_passwd the trust password to establish the
 104  *                     credentials with.
 105  *
 106  **/
 107 
 108 static NTSTATUS connect_to_domain_password_server(struct cli_state **cli,
     /* [<][>][^][v][top][bottom][index][help] */
 109                                                 const char *domain,
 110                                                 const char *dc_name,
 111                                                 struct sockaddr_storage *dc_ss, 
 112                                                 struct rpc_pipe_client **pipe_ret,
 113                                                 bool *retry)
 114 {
 115         NTSTATUS result;
 116         struct rpc_pipe_client *netlogon_pipe = NULL;
 117 
 118         *cli = NULL;
 119 
 120         *pipe_ret = NULL;
 121 
 122         /* TODO: Send a SAMLOGON request to determine whether this is a valid
 123            logonserver.  We can avoid a 30-second timeout if the DC is down
 124            if the SAMLOGON request fails as it is only over UDP. */
 125 
 126         /* we use a mutex to prevent two connections at once - when a 
 127            Win2k PDC get two connections where one hasn't completed a 
 128            session setup yet it will send a TCP reset to the first 
 129            connection (tridge) */
 130 
 131         /*
 132          * With NT4.x DC's *all* authentication must be serialized to avoid
 133          * ACCESS_DENIED errors if 2 auths are done from the same machine. JRA.
 134          */
 135 
 136         mutex = grab_named_mutex(NULL, dc_name, 10);
 137         if (mutex == NULL) {
 138                 return NT_STATUS_NO_LOGON_SERVERS;
 139         }
 140         
 141         /* Attempt connection */
 142         *retry = True;
 143         result = cli_full_connection(cli, global_myname(), dc_name, dc_ss, 0, 
 144                 "IPC$", "IPC", "", "", "", 0, Undefined, retry);
 145 
 146         if (!NT_STATUS_IS_OK(result)) {
 147                 /* map to something more useful */
 148                 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
 149                         result = NT_STATUS_NO_LOGON_SERVERS;
 150                 }
 151 
 152                 if (*cli) {
 153                         cli_shutdown(*cli);
 154                         *cli = NULL;
 155                 }
 156 
 157                 TALLOC_FREE(mutex);
 158                 return result;
 159         }
 160 
 161         /*
 162          * We now have an anonymous connection to IPC$ on the domain password server.
 163          */
 164 
 165         /*
 166          * Even if the connect succeeds we need to setup the netlogon
 167          * pipe here. We do this as we may just have changed the domain
 168          * account password on the PDC and yet we may be talking to
 169          * a BDC that doesn't have this replicated yet. In this case
 170          * a successful connect to a DC needs to take the netlogon connect
 171          * into account also. This patch from "Bjart Kvarme" <bjart.kvarme@usit.uio.no>.
 172          */
 173 
 174         /* open the netlogon pipe. */
 175         if (lp_client_schannel()) {
 176                 /* We also setup the creds chain in the open_schannel call. */
 177                 result = cli_rpc_pipe_open_schannel(
 178                         *cli, &ndr_table_netlogon.syntax_id, NCACN_NP,
 179                         PIPE_AUTH_LEVEL_PRIVACY, domain, &netlogon_pipe);
 180         } else {
 181                 result = cli_rpc_pipe_open_noauth(
 182                         *cli, &ndr_table_netlogon.syntax_id, &netlogon_pipe);
 183         }
 184 
 185         if (!NT_STATUS_IS_OK(result)) {
 186                 DEBUG(0,("connect_to_domain_password_server: unable to open the domain client session to \
 187 machine %s. Error was : %s.\n", dc_name, nt_errstr(result)));
 188                 cli_shutdown(*cli);
 189                 *cli = NULL;
 190                 TALLOC_FREE(mutex);
 191                 return result;
 192         }
 193 
 194         if (!lp_client_schannel()) {
 195                 /* We need to set up a creds chain on an unauthenticated netlogon pipe. */
 196                 uint32_t neg_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
 197                 uint32 sec_chan_type = 0;
 198                 unsigned char machine_pwd[16];
 199                 const char *account_name;
 200 
 201                 if (!get_trust_pw_hash(domain, machine_pwd, &account_name,
 202                                        &sec_chan_type))
 203                 {
 204                         DEBUG(0, ("connect_to_domain_password_server: could not fetch "
 205                         "trust account password for domain '%s'\n",
 206                                 domain));
 207                         cli_shutdown(*cli);
 208                         *cli = NULL;
 209                         TALLOC_FREE(mutex);
 210                         return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
 211                 }
 212 
 213                 result = rpccli_netlogon_setup_creds(netlogon_pipe,
 214                                         dc_name, /* server name */
 215                                         domain, /* domain */
 216                                         global_myname(), /* client name */
 217                                         account_name, /* machine account name */
 218                                         machine_pwd,
 219                                         sec_chan_type,
 220                                         &neg_flags);
 221 
 222                 if (!NT_STATUS_IS_OK(result)) {
 223                         cli_shutdown(*cli);
 224                         *cli = NULL;
 225                         TALLOC_FREE(mutex);
 226                         return result;
 227                 }
 228         }
 229 
 230         if(!netlogon_pipe) {
 231                 DEBUG(0,("connect_to_domain_password_server: unable to open the domain client session to \
 232 machine %s. Error was : %s.\n", dc_name, cli_errstr(*cli)));
 233                 cli_shutdown(*cli);
 234                 *cli = NULL;
 235                 TALLOC_FREE(mutex);
 236                 return NT_STATUS_NO_LOGON_SERVERS;
 237         }
 238 
 239         /* We exit here with the mutex *locked*. JRA */
 240 
 241         *pipe_ret = netlogon_pipe;
 242 
 243         return NT_STATUS_OK;
 244 }
 245 
 246 /***********************************************************************
 247  Do the same as security=server, but using NT Domain calls and a session
 248  key from the machine password.  If the server parameter is specified
 249  use it, otherwise figure out a server from the 'password server' param.
 250 ************************************************************************/
 251 
 252 static NTSTATUS domain_client_validate(TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 253                                         const auth_usersupplied_info *user_info, 
 254                                         const char *domain,
 255                                         uchar chal[8],
 256                                         auth_serversupplied_info **server_info, 
 257                                         const char *dc_name,
 258                                         struct sockaddr_storage *dc_ss)
 259 
 260 {
 261         struct netr_SamInfo3 *info3 = NULL;
 262         struct cli_state *cli = NULL;
 263         struct rpc_pipe_client *netlogon_pipe = NULL;
 264         NTSTATUS nt_status = NT_STATUS_NO_LOGON_SERVERS;
 265         int i;
 266         bool retry = True;
 267 
 268         /*
 269          * At this point, smb_apasswd points to the lanman response to
 270          * the challenge in local_challenge, and smb_ntpasswd points to
 271          * the NT response to the challenge in local_challenge. Ship
 272          * these over the secure channel to a domain controller and
 273          * see if they were valid.
 274          */
 275 
 276         /* rety loop for robustness */
 277         
 278         for (i = 0; !NT_STATUS_IS_OK(nt_status) && retry && (i < 3); i++) {
 279                 nt_status = connect_to_domain_password_server(&cli,
 280                                                         domain,
 281                                                         dc_name,
 282                                                         dc_ss,
 283                                                         &netlogon_pipe,
 284                                                         &retry);
 285         }
 286 
 287         if ( !NT_STATUS_IS_OK(nt_status) ) {
 288                 DEBUG(0,("domain_client_validate: Domain password server not available.\n"));
 289                 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
 290                         return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
 291                 }
 292                 return nt_status;
 293         }
 294 
 295         /* store a successful connection */
 296 
 297         saf_store( domain, cli->desthost );
 298 
 299         /*
 300          * If this call succeeds, we now have lots of info about the user
 301          * in the info3 structure.  
 302          */
 303 
 304         nt_status = rpccli_netlogon_sam_network_logon(netlogon_pipe,
 305                                                       mem_ctx,
 306                                                       user_info->logon_parameters,/* flags such as 'allow workstation logon' */ 
 307                                                       dc_name,                    /* server name */
 308                                                       user_info->smb_name,        /* user name logging on. */
 309                                                       user_info->client_domain,   /* domain name */
 310                                                       user_info->wksta_name,      /* workstation name */
 311                                                       chal,                       /* 8 byte challenge. */
 312                                                       user_info->lm_resp,         /* lanman 24 byte response */
 313                                                       user_info->nt_resp,         /* nt 24 byte response */
 314                                                       &info3);                    /* info3 out */
 315 
 316         /* Let go as soon as possible so we avoid any potential deadlocks
 317            with winbind lookup up users or groups. */
 318            
 319         TALLOC_FREE(mutex);
 320 
 321         if (!NT_STATUS_IS_OK(nt_status)) {
 322                 DEBUG(0,("domain_client_validate: unable to validate password "
 323                          "for user %s in domain %s to Domain controller %s. "
 324                          "Error was %s.\n", user_info->smb_name,
 325                          user_info->client_domain, dc_name, 
 326                          nt_errstr(nt_status)));
 327 
 328                 /* map to something more useful */
 329                 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
 330                         nt_status = NT_STATUS_NO_LOGON_SERVERS;
 331                 }
 332         } else {
 333                 nt_status = make_server_info_info3(mem_ctx,
 334                                                 user_info->smb_name,
 335                                                 domain,
 336                                                 server_info,
 337                                                 info3);
 338 
 339                 if (NT_STATUS_IS_OK(nt_status)) {
 340                         (*server_info)->nss_token |= user_info->was_mapped;
 341 
 342                         if ( ! (*server_info)->guest) {
 343                                 /* if a real user check pam account restrictions */
 344                                 /* only really perfomed if "obey pam restriction" is true */
 345                                 nt_status = smb_pam_accountcheck((*server_info)->unix_name);
 346                                 if (  !NT_STATUS_IS_OK(nt_status)) {
 347                                         DEBUG(1, ("PAM account restriction prevents user login\n"));
 348                                         cli_shutdown(cli);
 349                                         TALLOC_FREE(info3);
 350                                         return nt_status;
 351                                 }
 352                         }
 353                 }
 354 
 355                 netsamlogon_cache_store(user_info->smb_name, info3);
 356                 TALLOC_FREE(info3);
 357         }
 358 
 359         /* Note - once the cli stream is shutdown the mem_ctx used
 360            to allocate the other_sids and gids structures has been deleted - so
 361            these pointers are no longer valid..... */
 362 
 363         cli_shutdown(cli);
 364         return nt_status;
 365 }
 366 
 367 /****************************************************************************
 368  Check for a valid username and password in security=domain mode.
 369 ****************************************************************************/
 370 
 371 static NTSTATUS check_ntdomain_security(const struct auth_context *auth_context,
     /* [<][>][^][v][top][bottom][index][help] */
 372                                         void *my_private_data, 
 373                                         TALLOC_CTX *mem_ctx,
 374                                         const auth_usersupplied_info *user_info, 
 375                                         auth_serversupplied_info **server_info)
 376 {
 377         NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
 378         const char *domain = lp_workgroup();
 379         fstring dc_name;
 380         struct sockaddr_storage dc_ss;
 381 
 382         if ( lp_server_role() != ROLE_DOMAIN_MEMBER ) {
 383                 DEBUG(0,("check_ntdomain_security: Configuration error!  Cannot use "
 384                         "ntdomain auth method when not a member of a domain.\n"));
 385                 return NT_STATUS_NOT_IMPLEMENTED;
 386         }
 387 
 388         if (!user_info || !server_info || !auth_context) {
 389                 DEBUG(1,("check_ntdomain_security: Critical variables not present.  Failing.\n"));
 390                 return NT_STATUS_INVALID_PARAMETER;
 391         }
 392 
 393         /* 
 394          * Check that the requested domain is not our own machine name.
 395          * If it is, we should never check the PDC here, we use our own local
 396          * password file.
 397          */
 398 
 399         if(strequal(get_global_sam_name(), user_info->domain)) {
 400                 DEBUG(3,("check_ntdomain_security: Requested domain was for this machine.\n"));
 401                 return NT_STATUS_NOT_IMPLEMENTED;
 402         }
 403 
 404         /* we need our DC to send the net_sam_logon() request to */
 405 
 406         if ( !get_dc_name(domain, NULL, dc_name, &dc_ss) ) {
 407                 DEBUG(5,("check_ntdomain_security: unable to locate a DC for domain %s\n",
 408                         user_info->domain));
 409                 return NT_STATUS_NO_LOGON_SERVERS;
 410         }
 411         
 412         nt_status = domain_client_validate(mem_ctx,
 413                                         user_info,
 414                                         domain,
 415                                         (uchar *)auth_context->challenge.data,
 416                                         server_info,
 417                                         dc_name,
 418                                         &dc_ss);
 419                 
 420         return nt_status;
 421 }
 422 
 423 /* module initialisation */
 424 static NTSTATUS auth_init_ntdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method) 
     /* [<][>][^][v][top][bottom][index][help] */
 425 {
 426         if (!make_auth_methods(auth_context, auth_method)) {
 427                 return NT_STATUS_NO_MEMORY;
 428         }
 429 
 430         (*auth_method)->name = "ntdomain";
 431         (*auth_method)->auth = check_ntdomain_security;
 432         return NT_STATUS_OK;
 433 }
 434 
 435 
 436 /****************************************************************************
 437  Check for a valid username and password in a trusted domain
 438 ****************************************************************************/
 439 
 440 static NTSTATUS check_trustdomain_security(const struct auth_context *auth_context,
     /* [<][>][^][v][top][bottom][index][help] */
 441                                            void *my_private_data, 
 442                                            TALLOC_CTX *mem_ctx,
 443                                            const auth_usersupplied_info *user_info, 
 444                                            auth_serversupplied_info **server_info)
 445 {
 446         NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
 447         unsigned char trust_md4_password[16];
 448         char *trust_password;
 449         time_t last_change_time;
 450         DOM_SID sid;
 451         fstring dc_name;
 452         struct sockaddr_storage dc_ss;
 453 
 454         if (!user_info || !server_info || !auth_context) {
 455                 DEBUG(1,("check_trustdomain_security: Critical variables not present.  Failing.\n"));
 456                 return NT_STATUS_INVALID_PARAMETER;
 457         }
 458 
 459         /* 
 460          * Check that the requested domain is not our own machine name or domain name.
 461          */
 462 
 463         if( strequal(get_global_sam_name(), user_info->domain)) {
 464                 DEBUG(3,("check_trustdomain_security: Requested domain [%s] was for this machine.\n",
 465                         user_info->domain));
 466                 return NT_STATUS_NOT_IMPLEMENTED;
 467         }
 468 
 469         /* No point is bothering if this is not a trusted domain.
 470            This return makes "map to guest = bad user" work again.
 471            The logic is that if we know nothing about the domain, that
 472            user is not known to us and does not exist */
 473         
 474         if ( !is_trusted_domain( user_info->domain ) )
 475                 return NT_STATUS_NOT_IMPLEMENTED;
 476 
 477         /*
 478          * Get the trusted account password for the trusted domain
 479          * No need to become_root() as secrets_init() is done at startup.
 480          */
 481 
 482         if (!pdb_get_trusteddom_pw(user_info->domain, &trust_password,
 483                                    &sid, &last_change_time)) {
 484                 DEBUG(0, ("check_trustdomain_security: could not fetch trust "
 485                           "account password for domain %s\n",
 486                           user_info->domain));
 487                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
 488         }
 489 
 490 #ifdef DEBUG_PASSWORD
 491         DEBUG(100, ("Trust password for domain %s is %s\n", user_info->domain,
 492                     trust_password));
 493 #endif
 494         E_md4hash(trust_password, trust_md4_password);
 495         SAFE_FREE(trust_password);
 496 
 497 #if 0
 498         /* Test if machine password is expired and need to be changed */
 499         if (time(NULL) > last_change_time + (time_t)lp_machine_password_timeout())
 500         {
 501                 global_machine_password_needs_changing = True;
 502         }
 503 #endif
 504 
 505         /* use get_dc_name() for consistency even through we know that it will be 
 506            a netbios name */
 507            
 508         if ( !get_dc_name(user_info->domain, NULL, dc_name, &dc_ss) ) {
 509                 DEBUG(5,("check_trustdomain_security: unable to locate a DC for domain %s\n",
 510                         user_info->domain));
 511                 return NT_STATUS_NO_LOGON_SERVERS;
 512         }
 513         
 514         nt_status = domain_client_validate(mem_ctx,
 515                                         user_info,
 516                                         user_info->domain,
 517                                         (uchar *)auth_context->challenge.data,
 518                                         server_info,
 519                                         dc_name,
 520                                         &dc_ss);
 521 
 522         return nt_status;
 523 }
 524 
 525 /* module initialisation */
 526 static NTSTATUS auth_init_trustdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method) 
     /* [<][>][^][v][top][bottom][index][help] */
 527 {
 528         if (!make_auth_methods(auth_context, auth_method)) {
 529                 return NT_STATUS_NO_MEMORY;
 530         }
 531 
 532         (*auth_method)->name = "trustdomain";
 533         (*auth_method)->auth = check_trustdomain_security;
 534         return NT_STATUS_OK;
 535 }
 536 
 537 NTSTATUS auth_domain_init(void) 
     /* [<][>][^][v][top][bottom][index][help] */
 538 {
 539         smb_register_auth(AUTH_INTERFACE_VERSION, "trustdomain", auth_init_trustdomain);
 540         smb_register_auth(AUTH_INTERFACE_VERSION, "ntdomain", auth_init_ntdomain);
 541         return NT_STATUS_OK;
 542 }

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