1/* 2 Unix SMB/CIFS implementation. 3 4 Winbind daemon - pam auth funcions 5 6 Copyright (C) Andrew Tridgell 2000 7 Copyright (C) Tim Potter 2001 8 Copyright (C) Andrew Bartlett 2001-2002 9 10 This program is free software; you can redistribute it and/or modify 11 it under the terms of the GNU General Public License as published by 12 the Free Software Foundation; either version 2 of the License, or 13 (at your option) any later version. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program; if not, write to the Free Software 22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23*/ 24 25#include "includes.h" 26#include "winbindd.h" 27#undef DBGC_CLASS 28#define DBGC_CLASS DBGC_WINBIND 29 30 31static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx, 32 struct winbindd_cli_state *state, 33 NET_USER_INFO_3 *info3) 34{ 35 prs_struct ps; 36 uint32 size; 37 if (!prs_init(&ps, 256 /* Random, non-zero number */, mem_ctx, MARSHALL)) { 38 return NT_STATUS_NO_MEMORY; 39 } 40 if (!net_io_user_info3("", info3, &ps, 1, 3)) { 41 prs_mem_free(&ps); 42 return NT_STATUS_UNSUCCESSFUL; 43 } 44 45 size = prs_data_size(&ps); 46 state->response.extra_data = SMB_MALLOC(size); 47 if (!state->response.extra_data) { 48 prs_mem_free(&ps); 49 return NT_STATUS_NO_MEMORY; 50 } 51 memset( state->response.extra_data, '\0', size ); 52 prs_copy_all_data_out(state->response.extra_data, &ps); 53 state->response.length += size; 54 prs_mem_free(&ps); 55 return NT_STATUS_OK; 56} 57 58static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx, 59 NET_USER_INFO_3 *info3, 60 const char *group_sid) 61{ 62 DOM_SID require_membership_of_sid; 63 DOM_SID *all_sids; 64 size_t num_all_sids = (2 + info3->num_groups2 + info3->num_other_sids); 65 size_t i, j = 0; 66 67 /* Parse the 'required group' SID */ 68 69 if (!group_sid || !group_sid[0]) { 70 /* NO sid supplied, all users may access */ 71 return NT_STATUS_OK; 72 } 73 74 if (!string_to_sid(&require_membership_of_sid, group_sid)) { 75 DEBUG(0, ("check_info3_in_group: could not parse %s as a SID!", 76 group_sid)); 77 78 return NT_STATUS_INVALID_PARAMETER; 79 } 80 81 all_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, num_all_sids); 82 if (!all_sids) 83 return NT_STATUS_NO_MEMORY; 84 85 /* and create (by appending rids) the 'domain' sids */ 86 87 sid_copy(&all_sids[0], &(info3->dom_sid.sid)); 88 89 if (!sid_append_rid(&all_sids[0], info3->user_rid)) { 90 DEBUG(3,("could not append user's primary RID 0x%x\n", 91 info3->user_rid)); 92 93 return NT_STATUS_INVALID_PARAMETER; 94 } 95 j++; 96 97 sid_copy(&all_sids[1], &(info3->dom_sid.sid)); 98 99 if (!sid_append_rid(&all_sids[1], info3->group_rid)) { 100 DEBUG(3,("could not append additional group rid 0x%x\n", 101 info3->group_rid)); 102 103 return NT_STATUS_INVALID_PARAMETER; 104 } 105 j++; 106 107 for (i = 0; i < info3->num_groups2; i++) { 108 109 sid_copy(&all_sids[j], &(info3->dom_sid.sid)); 110 111 if (!sid_append_rid(&all_sids[j], info3->gids[i].g_rid)) { 112 DEBUG(3,("could not append additional group rid 0x%x\n", 113 info3->gids[i].g_rid)); 114 115 return NT_STATUS_INVALID_PARAMETER; 116 } 117 j++; 118 } 119 120 /* Copy 'other' sids. We need to do sid filtering here to 121 prevent possible elevation of privileges. See: 122 123 http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp 124 */ 125 126 for (i = 0; i < info3->num_other_sids; i++) { 127 sid_copy(&all_sids[info3->num_groups2 + i + 2], 128 &info3->other_sids[i].sid); 129 j++; 130 } 131 132 for (i = 0; i < j; i++) { 133 fstring sid1, sid2; 134 DEBUG(10, ("User has SID: %s\n", 135 sid_to_string(sid1, &all_sids[i]))); 136 if (sid_equal(&require_membership_of_sid, &all_sids[i])) { 137 DEBUG(10, ("SID %s matches %s - user permitted to authenticate!\n", 138 sid_to_string(sid1, &require_membership_of_sid), sid_to_string(sid2, &all_sids[i]))); 139 return NT_STATUS_OK; 140 } 141 } 142 143 /* Do not distinguish this error from a wrong username/pw */ 144 145 return NT_STATUS_LOGON_FAILURE; 146} 147 148/********************************************************************** 149 Authenticate a user with a clear text password 150**********************************************************************/ 151 152enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) 153{ 154 NTSTATUS result; 155 fstring name_domain, name_user; 156 unsigned char trust_passwd[16]; 157 time_t last_change_time; 158 uint32 sec_channel_type; 159 NET_USER_INFO_3 info3; 160 struct cli_state *cli = NULL; 161 uchar chal[8]; 162 TALLOC_CTX *mem_ctx = NULL; 163 DATA_BLOB lm_resp; 164 DATA_BLOB nt_resp; 165 DOM_CRED ret_creds; 166 int attempts = 0; 167 unsigned char local_lm_response[24]; 168 unsigned char local_nt_response[24]; 169 struct winbindd_domain *contact_domain; 170 BOOL retry; 171 172 /* Ensure null termination */ 173 state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0'; 174 175 /* Ensure null termination */ 176 state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0'; 177 178 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid, 179 state->request.data.auth.user)); 180 181 if (!(mem_ctx = talloc_init("winbind pam auth for %s", state->request.data.auth.user))) { 182 DEBUG(0, ("winbindd_pam_auth: could not talloc_init()!\n")); 183 result = NT_STATUS_NO_MEMORY; 184 goto done; 185 } 186 187 /* Parse domain and username */ 188 189 parse_domain_user(state->request.data.auth.user, name_domain, name_user); 190 191 /* do password magic */ 192 193 194 generate_random_buffer(chal, 8); 195 if (lp_client_ntlmv2_auth()) { 196 DATA_BLOB server_chal; 197 DATA_BLOB names_blob; 198 DATA_BLOB nt_response; 199 DATA_BLOB lm_response; 200 server_chal = data_blob_talloc(mem_ctx, chal, 8); 201 202 /* note that the 'workgroup' here is a best guess - we don't know 203 the server's domain at this point. The 'server name' is also 204 dodgy... 205 */ 206 names_blob = NTLMv2_generate_names_blob(global_myname(), lp_workgroup()); 207 208 if (!SMBNTLMv2encrypt(name_user, name_domain, 209 state->request.data.auth.pass, 210 &server_chal, 211 &names_blob, 212 &lm_response, &nt_response, NULL)) { 213 data_blob_free(&names_blob); 214 data_blob_free(&server_chal); 215 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n")); 216 result = NT_STATUS_NO_MEMORY; 217 goto done; 218 } 219 data_blob_free(&names_blob); 220 data_blob_free(&server_chal); 221 lm_resp = data_blob_talloc(mem_ctx, lm_response.data, lm_response.length); 222 nt_resp = data_blob_talloc(mem_ctx, nt_response.data, nt_response.length); 223 data_blob_free(&lm_response); 224 data_blob_free(&nt_response); 225 226 } else { 227 if (lp_client_lanman_auth() 228 && SMBencrypt(state->request.data.auth.pass, 229 chal, 230 local_lm_response)) { 231 lm_resp = data_blob_talloc(mem_ctx, 232 local_lm_response, 233 sizeof(local_lm_response)); 234 } else { 235 lm_resp = data_blob(NULL, 0); 236 } 237 SMBNTencrypt(state->request.data.auth.pass, 238 chal, 239 local_nt_response); 240 241 nt_resp = data_blob_talloc(mem_ctx, 242 local_nt_response, 243 sizeof(local_nt_response)); 244 } 245 246 247 /* what domain should we contact? */ 248 249 if ( IS_DC ) { 250 if (!(contact_domain = find_domain_from_name(name_domain))) { 251 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", 252 state->request.data.auth.user, name_domain, name_user, name_domain)); 253 result = NT_STATUS_NO_SUCH_USER; 254 goto done; 255 } 256 257 } else { 258 if (is_myname(name_domain)) { 259 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain)); 260 result = NT_STATUS_NO_SUCH_USER; 261 goto done; 262 } 263 264 if (!(contact_domain = find_our_domain())) { 265 DEBUG(1, ("Authentication for [%s] -> [%s]\\[%s] in our domain failed - we can't find our domain!\n", 266 state->request.data.auth.user, name_domain, name_user)); 267 result = NT_STATUS_NO_SUCH_USER; 268 goto done; 269 } 270 } 271 272 if ( !get_trust_pw(contact_domain->name, trust_passwd, &last_change_time, &sec_channel_type) ) { 273 result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; 274 goto done; 275 } 276 277 /* check authentication loop */ 278 279 do { 280 ZERO_STRUCT(info3); 281 ZERO_STRUCT(ret_creds); 282 retry = False; 283 284 /* Don't shut this down - it belongs to the connection cache code */ 285 result = cm_get_netlogon_cli(contact_domain, trust_passwd, 286 sec_channel_type, False, &cli); 287 288 if (!NT_STATUS_IS_OK(result)) { 289 DEBUG(3, ("could not open handle to NETLOGON pipe\n")); 290 goto done; 291 } 292 293 result = cli_netlogon_sam_network_logon(cli, mem_ctx, 294 &ret_creds, 295 name_user, name_domain, 296 global_myname(), chal, 297 lm_resp, nt_resp, 298 &info3); 299 attempts += 1; 300 301 /* We have to try a second time as cm_get_netlogon_cli 302 might not yet have noticed that the DC has killed 303 our connection. */ 304 305 if ( cli->fd == -1 ) { 306 retry = True; 307 continue; 308 } 309 310 /* if we get access denied, a possible cuase was that we had and open 311 connection to the DC, but someone changed our machine account password 312 out from underneath us using 'net rpc changetrustpw' */ 313 314 if ( NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) ) { 315 DEBUG(3,("winbindd_pam_auth: sam_logon returned ACCESS_DENIED. Maybe the trust account " 316 "password was changed and we didn't know it. Killing connections to domain %s\n", 317 name_domain)); 318 winbindd_cm_flush(); 319 retry = True; 320 cli = NULL; 321 } 322 323 } while ( (attempts < 2) && retry ); 324 325 if (cli != NULL) { 326 /* We might have come out of the loop above with cli == NULL, 327 so don't dereference that. */ 328 clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &ret_creds); 329 } 330 331 if (NT_STATUS_IS_OK(result)) { 332 netsamlogon_cache_store( cli->mem_ctx, name_user, &info3 ); 333 wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3); 334 335 /* Check if the user is in the right group */ 336 337 if (!NT_STATUS_IS_OK(result = check_info3_in_group(mem_ctx, &info3, state->request.data.auth.require_membership_of_sid))) { 338 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n", 339 state->request.data.auth.user, 340 state->request.data.auth.require_membership_of_sid)); 341 } 342 } 343 344done: 345 /* give us a more useful (more correct?) error code */ 346 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) || (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) { 347 result = NT_STATUS_NO_LOGON_SERVERS; 348 } 349 350 state->response.data.auth.nt_status = NT_STATUS_V(result); 351 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); 352 353 /* we might have given a more useful error above */ 354 if (!*state->response.data.auth.error_string) 355 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result)); 356 state->response.data.auth.pam_error = nt_status_to_pam(result); 357 358 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n", 359 state->request.data.auth.user, 360 state->response.data.auth.nt_status_string, 361 state->response.data.auth.pam_error)); 362 363 if ( NT_STATUS_IS_OK(result) && 364 (state->request.flags & WBFLAG_PAM_AFS_TOKEN) ) { 365 366 char *afsname = SMB_STRDUP(lp_afs_username_map()); 367 char *cell; 368 369 if (afsname == NULL) goto no_token; 370 371 afsname = realloc_string_sub(afsname, "%D", name_domain); 372 afsname = realloc_string_sub(afsname, "%u", name_user); 373 afsname = realloc_string_sub(afsname, "%U", name_user); 374 375 { 376 DOM_SID user_sid; 377 fstring sidstr; 378 379 sid_copy(&user_sid, &info3.dom_sid.sid); 380 sid_append_rid(&user_sid, info3.user_rid); 381 sid_to_string(sidstr, &user_sid); 382 afsname = realloc_string_sub(afsname, "%s", sidstr); 383 } 384 385 if (afsname == NULL) goto no_token; 386 387 strlower_m(afsname); 388 389 DEBUG(10, ("Generating token for user %s\n", afsname)); 390 391 cell = strchr(afsname, '@'); 392 393 if (cell == NULL) goto no_token; 394 395 *cell = '\0'; 396 cell += 1; 397 398 /* Append an AFS token string */ 399 state->response.extra_data = 400 afs_createtoken_str(afsname, cell); 401 402 if (state->response.extra_data != NULL) 403 state->response.length += 404 strlen(state->response.extra_data)+1; 405 406 no_token: 407 SAFE_FREE(afsname); 408 } 409 410 if (mem_ctx) 411 talloc_destroy(mem_ctx); 412 413 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; 414} 415 416/********************************************************************** 417 Challenge Response Authentication Protocol 418**********************************************************************/ 419 420enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) 421{ 422 NTSTATUS result; 423 unsigned char trust_passwd[16]; 424 time_t last_change_time; 425 uint32 sec_channel_type; 426 NET_USER_INFO_3 info3; 427 struct cli_state *cli = NULL; 428 TALLOC_CTX *mem_ctx = NULL; 429 const char *name_user = NULL; 430 const char *name_domain = NULL; 431 const char *workstation; 432 struct winbindd_domain *contact_domain; 433 DOM_CRED ret_creds; 434 int attempts = 0; 435 BOOL retry; 436 437 DATA_BLOB lm_resp, nt_resp; 438 439 if (!state->privileged) { 440 char *error_string = NULL; 441 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access denied. !\n")); 442 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions on %s are set correctly.\n", 443 get_winbind_priv_pipe_dir())); 444 /* send a better message than ACCESS_DENIED */ 445 asprintf(&error_string, "winbind client not authorized to use winbindd_pam_auth_crap. Ensure permissions on %s are set correctly.", 446 get_winbind_priv_pipe_dir()); 447 fstrcpy(state->response.data.auth.error_string, error_string); 448 SAFE_FREE(error_string); 449 result = NT_STATUS_ACCESS_DENIED; 450 goto done; 451 } 452 453 /* Ensure null termination */ 454 state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0; 455 state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0; 456 457 if (!(mem_ctx = talloc_init("winbind pam auth crap for %s", state->request.data.auth_crap.user))) { 458 DEBUG(0, ("winbindd_pam_auth_crap: could not talloc_init()!\n")); 459 result = NT_STATUS_NO_MEMORY; 460 goto done; 461 } 462 463 name_user = state->request.data.auth_crap.user; 464 465 if (*state->request.data.auth_crap.domain) { 466 name_domain = state->request.data.auth_crap.domain; 467 } else if (lp_winbind_use_default_domain()) { 468 name_domain = lp_workgroup(); 469 } else { 470 DEBUG(5,("no domain specified with username (%s) - failing auth\n", 471 name_user)); 472 result = NT_STATUS_NO_SUCH_USER; 473 goto done; 474 } 475 476 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid, 477 name_domain, name_user)); 478 479 if (*state->request.data.auth_crap.workstation) { 480 workstation = state->request.data.auth_crap.workstation; 481 } else { 482 workstation = global_myname(); 483 } 484 485 if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp) 486 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) { 487 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n", 488 state->request.data.auth_crap.lm_resp_len, 489 state->request.data.auth_crap.nt_resp_len)); 490 result = NT_STATUS_INVALID_PARAMETER; 491 goto done; 492 } 493 494 lm_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.lm_resp, state->request.data.auth_crap.lm_resp_len); 495 nt_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.nt_resp, state->request.data.auth_crap.nt_resp_len); 496 497 498 /* what domain should we contact? */ 499 500 if ( IS_DC ) { 501 if (!(contact_domain = find_domain_from_name(name_domain))) { 502 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", 503 state->request.data.auth_crap.user, name_domain, name_user, name_domain)); 504 result = NT_STATUS_NO_SUCH_USER; 505 goto done; 506 } 507 508 } else { 509 if (is_myname(name_domain)) { 510 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain)); 511 result = NT_STATUS_NO_SUCH_USER; 512 goto done; 513 } 514 515 if (!(contact_domain = find_our_domain())) { 516 DEBUG(1, ("Authenticatoin for [%s] -> [%s]\\[%s] in our domain failed - we can't find our domain!\n", 517 state->request.data.auth_crap.user, name_domain, name_user)); 518 result = NT_STATUS_NO_SUCH_USER; 519 goto done; 520 } 521 } 522 523 if ( !get_trust_pw(contact_domain->name, trust_passwd, &last_change_time, &sec_channel_type) ) { 524 result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; 525 goto done; 526 } 527 528 do { 529 ZERO_STRUCT(info3); 530 ZERO_STRUCT(ret_creds); 531 retry = False; 532 533 /* Don't shut this down - it belongs to the connection cache code */ 534 result = cm_get_netlogon_cli(contact_domain, trust_passwd, sec_channel_type, False, &cli); 535 536 if (!NT_STATUS_IS_OK(result)) { 537 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n", 538 nt_errstr(result))); 539 goto done; 540 } 541 542 result = cli_netlogon_sam_network_logon(cli, mem_ctx, 543 &ret_creds, 544 name_user, name_domain, 545 workstation, 546 state->request.data.auth_crap.chal, 547 lm_resp, nt_resp, 548 &info3); 549 550 attempts += 1; 551 552 /* We have to try a second time as cm_get_netlogon_cli 553 might not yet have noticed that the DC has killed 554 our connection. */ 555 556 if ( cli->fd == -1 ) { 557 retry = True; 558 continue; 559 } 560 561 /* if we get access denied, a possible cause was that we had and open 562 connection to the DC, but someone changed our machine account password 563 out from underneath us using 'net rpc changetrustpw' */ 564 565 if ( NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) ) { 566 DEBUG(3,("winbindd_pam_auth_crap: sam_logon returned ACCESS_DENIED. Maybe the trust account " 567 "password was changed and we didn't know it. Killing connections to domain %s\n", 568 contact_domain->name)); 569 winbindd_cm_flush(); 570 retry = True; 571 cli = NULL; 572 } 573 574 } while ( (attempts < 2) && retry ); 575 576 if (cli != NULL) { 577 /* We might have come out of the loop above with cli == NULL, 578 so don't dereference that. */ 579 clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &ret_creds); 580 } 581 582 if (NT_STATUS_IS_OK(result)) { 583 netsamlogon_cache_store( cli->mem_ctx, name_user, &info3 ); 584 wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3); 585 586 if (!NT_STATUS_IS_OK(result = check_info3_in_group(mem_ctx, &info3, state->request.data.auth_crap.require_membership_of_sid))) { 587 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n", 588 state->request.data.auth_crap.user, 589 state->request.data.auth_crap.require_membership_of_sid)); 590 goto done; 591 } 592 593 if (state->request.flags & WBFLAG_PAM_INFO3_NDR) { 594 result = append_info3_as_ndr(mem_ctx, state, &info3); 595 } else if (state->request.flags & WBFLAG_PAM_UNIX_NAME) { 596 /* ntlm_auth should return the unix username, per 597 'winbind use default domain' settings and the like */ 598 599 fstring username_out; 600 const char *nt_username, *nt_domain; 601 if (!(nt_username = unistr2_tdup(mem_ctx, &(info3.uni_user_name)))) { 602 /* If the server didn't give us one, just use the one we sent them */ 603 nt_username = name_user; 604 } 605 606 if (!(nt_domain = unistr2_tdup(mem_ctx, &(info3.uni_logon_dom)))) { 607 /* If the server didn't give us one, just use the one we sent them */ 608 nt_domain = name_domain; 609 } 610 611 fill_domain_username(username_out, nt_domain, nt_username); 612 613 DEBUG(5, ("Setting unix username to [%s]\n", username_out)); 614 615 state->response.extra_data = SMB_STRDUP(username_out); 616 if (!state->response.extra_data) { 617 result = NT_STATUS_NO_MEMORY; 618 goto done; 619 } 620 state->response.length += strlen(state->response.extra_data)+1; 621 } 622 623 if (state->request.flags & WBFLAG_PAM_USER_SESSION_KEY) { 624 memcpy(state->response.data.auth.user_session_key, info3.user_sess_key, sizeof(state->response.data.auth.user_session_key) /* 16 */); 625 } 626 if (state->request.flags & WBFLAG_PAM_LMKEY) { 627 memcpy(state->response.data.auth.first_8_lm_hash, info3.lm_sess_key, sizeof(state->response.data.auth.first_8_lm_hash) /* 8 */); 628 } 629 } 630 631done: 632 /* give us a more useful (more correct?) error code */ 633 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) || (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) { 634 result = NT_STATUS_NO_LOGON_SERVERS; 635 } 636 637 if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) { 638 result = nt_status_squash(result); 639 } 640 641 state->response.data.auth.nt_status = NT_STATUS_V(result); 642 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); 643 644 /* we might have given a more useful error above */ 645 if (!*state->response.data.auth.error_string) 646 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result)); 647 state->response.data.auth.pam_error = nt_status_to_pam(result); 648 649 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 650 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n", 651 name_domain, 652 name_user, 653 state->response.data.auth.nt_status_string, 654 state->response.data.auth.pam_error)); 655 656 if (mem_ctx) 657 talloc_destroy(mem_ctx); 658 659 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; 660} 661 662/* Change a user password */ 663 664enum winbindd_result winbindd_pam_chauthtok(struct winbindd_cli_state *state) 665{ 666 NTSTATUS result; 667 char *oldpass, *newpass; 668 fstring domain, user; 669 CLI_POLICY_HND *hnd; 670 TALLOC_CTX *mem_ctx; 671 struct winbindd_domain *contact_domain; 672 673 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid, 674 state->request.data.chauthtok.user)); 675 676 if (!(mem_ctx = talloc_init("winbind password change for %s", 677 state->request.data.chauthtok.user))) { 678 DEBUG(0, ("winbindd_pam_auth_crap: could not talloc_init()!\n")); 679 result = NT_STATUS_NO_MEMORY; 680 goto done; 681 } 682 683 /* Setup crap */ 684 685 if (state == NULL) 686 return WINBINDD_ERROR; 687 688 parse_domain_user(state->request.data.chauthtok.user, domain, user); 689 690 if (!(contact_domain = find_domain_from_name(domain))) { 691 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n", 692 state->request.data.chauthtok.user, domain, user, domain)); 693 result = NT_STATUS_NO_SUCH_USER; 694 goto done; 695 } 696 697 /* Change password */ 698 699 oldpass = state->request.data.chauthtok.oldpass; 700 newpass = state->request.data.chauthtok.newpass; 701 702 /* Get sam handle */ 703 704 if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(contact_domain, &hnd)) ) { 705 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain)); 706 goto done; 707 } 708 709 result = cli_samr_chgpasswd_user(hnd->cli, mem_ctx, user, newpass, oldpass); 710 711done: 712 state->response.data.auth.nt_status = NT_STATUS_V(result); 713 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); 714 fstrcpy(state->response.data.auth.error_string, nt_errstr(result)); 715 state->response.data.auth.pam_error = nt_status_to_pam(result); 716 717 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 718 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n", 719 domain, 720 user, 721 state->response.data.auth.nt_status_string, 722 state->response.data.auth.pam_error)); 723 724 if (mem_ctx) 725 talloc_destroy(mem_ctx); 726 727 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; 728} 729