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 2 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, write to the Free Software 19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20*/ 21 22#include "includes.h" 23 24#undef DBGC_CLASS 25#define DBGC_CLASS DBGC_AUTH 26 27extern BOOL global_machine_password_needs_changing; 28 29/** 30 * Connect to a remote server for (inter)domain security authenticaion. 31 * 32 * @param cli the cli to return containing the active connection 33 * @param server either a machine name or text IP address to 34 * connect to. 35 * @param setup_creds_as domain account to setup credentials as 36 * @param sec_chan a switch value to distinguish between domain 37 * member and interdomain authentication 38 * @param trust_passwd the trust password to establish the 39 * credentials with. 40 * 41 **/ 42 43static NTSTATUS connect_to_domain_password_server(struct cli_state **cli, 44 const char *domain, const char *dc_name, 45 struct in_addr dc_ip, 46 const char *setup_creds_as, 47 uint16 sec_chan, 48 const unsigned char *trust_passwd, 49 BOOL *retry) 50{ 51 NTSTATUS result; 52 53 /* TODO: Send a SAMLOGON request to determine whether this is a valid 54 logonserver. We can avoid a 30-second timeout if the DC is down 55 if the SAMLOGON request fails as it is only over UDP. */ 56 57 /* we use a mutex to prevent two connections at once - when a 58 Win2k PDC get two connections where one hasn't completed a 59 session setup yet it will send a TCP reset to the first 60 connection (tridge) */ 61 62 /* 63 * With NT4.x DC's *all* authentication must be serialized to avoid 64 * ACCESS_DENIED errors if 2 auths are done from the same machine. JRA. 65 */ 66 67 if (!grab_server_mutex(dc_name)) 68 return NT_STATUS_NO_LOGON_SERVERS; 69 70 /* Attempt connection */ 71 *retry = True; 72 result = cli_full_connection(cli, global_myname(), dc_name, &dc_ip, 0, 73 "IPC$", "IPC", "", "", "", 0, Undefined, retry); 74 75 if (!NT_STATUS_IS_OK(result)) { 76 /* map to something more useful */ 77 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) { 78 result = NT_STATUS_NO_LOGON_SERVERS; 79 } 80 81 release_server_mutex(); 82 return result; 83 } 84 85 /* 86 * We now have an anonymous connection to IPC$ on the domain password server. 87 */ 88 89 /* 90 * Even if the connect succeeds we need to setup the netlogon 91 * pipe here. We do this as we may just have changed the domain 92 * account password on the PDC and yet we may be talking to 93 * a BDC that doesn't have this replicated yet. In this case 94 * a successful connect to a DC needs to take the netlogon connect 95 * into account also. This patch from "Bjart Kvarme" <bjart.kvarme@usit.uio.no>. 96 */ 97 98 if(cli_nt_session_open(*cli, PI_NETLOGON) == False) { 99 DEBUG(0,("connect_to_domain_password_server: unable to open the domain client session to \ 100machine %s. Error was : %s.\n", dc_name, cli_errstr(*cli))); 101 cli_nt_session_close(*cli); 102 cli_ulogoff(*cli); 103 cli_shutdown(*cli); 104 release_server_mutex(); 105 return NT_STATUS_NO_LOGON_SERVERS; 106 } 107 108 fstr_sprintf((*cli)->mach_acct, "%s$", setup_creds_as); 109 110 /* This must be the remote domain (not ours) for schannel */ 111 112 fstrcpy( (*cli)->domain, domain ); 113 114 result = cli_nt_establish_netlogon(*cli, sec_chan, trust_passwd); 115 116 if (!NT_STATUS_IS_OK(result)) { 117 DEBUG(0,("connect_to_domain_password_server: unable to setup the NETLOGON credentials to machine \ 118%s. Error was : %s.\n", dc_name, nt_errstr(result))); 119 cli_nt_session_close(*cli); 120 cli_ulogoff(*cli); 121 cli_shutdown(*cli); 122 release_server_mutex(); 123 return result; 124 } 125 126 /* We exit here with the mutex *locked*. JRA */ 127 128 return NT_STATUS_OK; 129} 130 131/*********************************************************************** 132 Do the same as security=server, but using NT Domain calls and a session 133 key from the machine password. If the server parameter is specified 134 use it, otherwise figure out a server from the 'password server' param. 135************************************************************************/ 136 137static NTSTATUS domain_client_validate(TALLOC_CTX *mem_ctx, 138 const auth_usersupplied_info *user_info, 139 const char *domain, 140 uchar chal[8], 141 auth_serversupplied_info **server_info, 142 const char *dc_name, struct in_addr dc_ip, 143 const char *setup_creds_as, 144 uint16 sec_chan, 145 unsigned char trust_passwd[16], 146 time_t last_change_time) 147{ 148 NET_USER_INFO_3 info3; 149 struct cli_state *cli = NULL; 150 NTSTATUS nt_status = NT_STATUS_NO_LOGON_SERVERS; 151 int i; 152 BOOL retry = True; 153 154 /* 155 * At this point, smb_apasswd points to the lanman response to 156 * the challenge in local_challenge, and smb_ntpasswd points to 157 * the NT response to the challenge in local_challenge. Ship 158 * these over the secure channel to a domain controller and 159 * see if they were valid. 160 */ 161 162 /* rety loop for robustness */ 163 164 for (i = 0; !NT_STATUS_IS_OK(nt_status) && retry && (i < 3); i++) { 165 nt_status = connect_to_domain_password_server(&cli, domain, dc_name, 166 dc_ip, setup_creds_as, sec_chan, trust_passwd, &retry); 167 } 168 169 if ( !NT_STATUS_IS_OK(nt_status) ) { 170 DEBUG(0,("domain_client_validate: Domain password server not available.\n")); 171 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) { 172 return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE; 173 } 174 return nt_status; 175 } 176 177 ZERO_STRUCT(info3); 178 179 /* 180 * If this call succeeds, we now have lots of info about the user 181 * in the info3 structure. 182 */ 183 184 nt_status = cli_netlogon_sam_network_logon(cli, mem_ctx, 185 NULL, user_info->smb_name.str, user_info->domain.str, 186 user_info->wksta_name.str, chal, user_info->lm_resp, 187 user_info->nt_resp, &info3); 188 189 /* let go as soon as possible so we avoid any potential deadlocks 190 with winbind lookup up users or groups */ 191 192 release_server_mutex(); 193 194 if (!NT_STATUS_IS_OK(nt_status)) { 195 DEBUG(0,("domain_client_validate: unable to validate password " 196 "for user %s in domain %s to Domain controller %s. " 197 "Error was %s.\n", user_info->smb_name.str, 198 user_info->domain.str, cli->srv_name_slash, 199 nt_errstr(nt_status))); 200 201 /* map to something more useful */ 202 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) { 203 nt_status = NT_STATUS_NO_LOGON_SERVERS; 204 } 205 } else { 206 nt_status = make_server_info_info3(mem_ctx, user_info->internal_username.str, 207 user_info->smb_name.str, domain, server_info, &info3); 208 netsamlogon_cache_store( mem_ctx, user_info->smb_name.str, &info3 ); 209 } 210 211#if 0 212 /* 213 * We don't actually need to do this - plus it fails currently with 214 * NT_STATUS_INVALID_INFO_CLASS - we need to know *exactly* what to 215 * send here. JRA. 216 */ 217 218 if (NT_STATUS_IS_OK(status)) { 219 if(cli_nt_logoff(&cli, &ctr) == False) { 220 DEBUG(0,("domain_client_validate: unable to log off user %s in domain \ 221%s to Domain controller %s. Error was %s.\n", user, domain, dc_name, cli_errstr(&cli))); 222 nt_status = NT_STATUS_LOGON_FAILURE; 223 } 224 } 225#endif /* 0 */ 226 227 /* Note - once the cli stream is shutdown the mem_ctx used 228 to allocate the other_sids and gids structures has been deleted - so 229 these pointers are no longer valid..... */ 230 231 cli_nt_session_close(cli); 232 cli_ulogoff(cli); 233 cli_shutdown(cli); 234 return nt_status; 235} 236 237/**************************************************************************** 238 Check for a valid username and password in security=domain mode. 239****************************************************************************/ 240 241static NTSTATUS check_ntdomain_security(const struct auth_context *auth_context, 242 void *my_private_data, 243 TALLOC_CTX *mem_ctx, 244 const auth_usersupplied_info *user_info, 245 auth_serversupplied_info **server_info) 246{ 247 NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; 248 unsigned char trust_passwd[16]; 249 time_t last_change_time; 250 const char *domain = lp_workgroup(); 251 uint32 sec_channel_type = 0; 252 fstring dc_name; 253 struct in_addr dc_ip; 254 255 if ( lp_server_role() != ROLE_DOMAIN_MEMBER ) { 256 DEBUG(0,("check_ntdomain_security: Configuration error! Cannot use " 257 "ntdomain auth method when not a member of a domain.\n")); 258 return NT_STATUS_NOT_IMPLEMENTED; 259 } 260 261 if (!user_info || !server_info || !auth_context) { 262 DEBUG(1,("check_ntdomain_security: Critical variables not present. Failing.\n")); 263 return NT_STATUS_INVALID_PARAMETER; 264 } 265 266 /* 267 * Check that the requested domain is not our own machine name. 268 * If it is, we should never check the PDC here, we use our own local 269 * password file. 270 */ 271 272 if(strequal(get_global_sam_name(), user_info->domain.str)) { 273 DEBUG(3,("check_ntdomain_security: Requested domain was for this machine.\n")); 274 return NT_STATUS_NOT_IMPLEMENTED; 275 } 276 277 /* 278 * Get the machine account password for our primary domain 279 * No need to become_root() as secrets_init() is done at startup. 280 */ 281 282 if (!secrets_fetch_trust_account_password(domain, trust_passwd, &last_change_time, &sec_channel_type)) 283 { 284 DEBUG(0, ("check_ntdomain_security: could not fetch trust account password for domain '%s'\n", domain)); 285 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; 286 } 287 288 /* Test if machine password has expired and needs to be changed */ 289 if (lp_machine_password_timeout()) { 290 if (last_change_time > 0 && 291 time(NULL) > (last_change_time + 292 lp_machine_password_timeout())) { 293 global_machine_password_needs_changing = True; 294 } 295 } 296 297 /* we need our DC to send the net_sam_logon() request to */ 298 299 if ( !get_dc_name(domain, NULL, dc_name, &dc_ip) ) { 300 DEBUG(5,("check_ntdomain_security: unable to locate a DC for domain %s\n", 301 user_info->domain.str)); 302 return NT_STATUS_NO_LOGON_SERVERS; 303 } 304 305 nt_status = domain_client_validate(mem_ctx, user_info, domain, 306 (uchar *)auth_context->challenge.data, server_info, dc_name, dc_ip, 307 global_myname(), sec_channel_type,trust_passwd, last_change_time); 308 309 return nt_status; 310} 311 312/* module initialisation */ 313static NTSTATUS auth_init_ntdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method) 314{ 315 if (!make_auth_methods(auth_context, auth_method)) { 316 return NT_STATUS_NO_MEMORY; 317 } 318 319 (*auth_method)->name = "ntdomain"; 320 (*auth_method)->auth = check_ntdomain_security; 321 return NT_STATUS_OK; 322} 323 324 325/**************************************************************************** 326 Check for a valid username and password in a trusted domain 327****************************************************************************/ 328 329static NTSTATUS check_trustdomain_security(const struct auth_context *auth_context, 330 void *my_private_data, 331 TALLOC_CTX *mem_ctx, 332 const auth_usersupplied_info *user_info, 333 auth_serversupplied_info **server_info) 334{ 335 NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; 336 unsigned char trust_md4_password[16]; 337 char *trust_password; 338 time_t last_change_time; 339 DOM_SID sid; 340 fstring dc_name; 341 struct in_addr dc_ip; 342 343 if (!user_info || !server_info || !auth_context) { 344 DEBUG(1,("check_trustdomain_security: Critical variables not present. Failing.\n")); 345 return NT_STATUS_INVALID_PARAMETER; 346 } 347 348 /* 349 * Check that the requested domain is not our own machine name or domain name. 350 */ 351 352 if( strequal(get_global_sam_name(), user_info->domain.str)) { 353 DEBUG(3,("check_trustdomain_security: Requested domain [%s] was for this machine.\n", 354 user_info->domain.str)); 355 return NT_STATUS_NOT_IMPLEMENTED; 356 } 357 358 /* No point is bothering if this is not a trusted domain. 359 This return makes "map to guest = bad user" work again. 360 The logic is that if we know nothing about the domain, that 361 user is known to us and does not exist */ 362 363 if ( !is_trusted_domain( user_info->domain.str ) ) 364 return NT_STATUS_NOT_IMPLEMENTED; 365 366 /* 367 * Get the trusted account password for the trusted domain 368 * No need to become_root() as secrets_init() is done at startup. 369 */ 370 371 if (!secrets_fetch_trusted_domain_password(user_info->domain.str, &trust_password, &sid, &last_change_time)) 372 { 373 DEBUG(0, ("check_trustdomain_security: could not fetch trust account password for domain %s\n", user_info->domain.str)); 374 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; 375 } 376 377#ifdef DEBUG_PASSWORD 378 DEBUG(100, ("Trust password for domain %s is %s\n", user_info->domain.str, trust_password)); 379#endif 380 E_md4hash(trust_password, trust_md4_password); 381 SAFE_FREE(trust_password); 382 383#if 0 384 /* Test if machine password is expired and need to be changed */ 385 if (time(NULL) > last_change_time + lp_machine_password_timeout()) 386 { 387 global_machine_password_needs_changing = True; 388 } 389#endif 390 391 /* use get_dc_name() for consistency even through we know that it will be 392 a netbios name */ 393 394 if ( !get_dc_name(user_info->domain.str, NULL, dc_name, &dc_ip) ) { 395 DEBUG(5,("check_trustdomain_security: unable to locate a DC for domain %s\n", 396 user_info->domain.str)); 397 return NT_STATUS_NO_LOGON_SERVERS; 398 } 399 400 nt_status = domain_client_validate(mem_ctx, user_info, user_info->domain.str, 401 (uchar *)auth_context->challenge.data, server_info, dc_name, dc_ip, 402 lp_workgroup(), SEC_CHAN_DOMAIN, trust_md4_password, last_change_time); 403 404 return nt_status; 405} 406 407/* module initialisation */ 408static NTSTATUS auth_init_trustdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method) 409{ 410 if (!make_auth_methods(auth_context, auth_method)) { 411 return NT_STATUS_NO_MEMORY; 412 } 413 414 (*auth_method)->name = "trustdomain"; 415 (*auth_method)->auth = check_trustdomain_security; 416 return NT_STATUS_OK; 417} 418 419NTSTATUS auth_domain_init(void) 420{ 421 smb_register_auth(AUTH_INTERFACE_VERSION, "trustdomain", auth_init_trustdomain); 422 smb_register_auth(AUTH_INTERFACE_VERSION, "ntdomain", auth_init_ntdomain); 423 return NT_STATUS_OK; 424} 425