Changeset 988 for vendor/current/libcli/auth
- Timestamp:
- Nov 24, 2016, 1:14:11 PM (9 years ago)
- Location:
- vendor/current/libcli/auth
- Files:
-
- 2 added
- 8 deleted
- 14 edited
Legend:
- Unmodified
- Added
- Removed
-
vendor/current/libcli/auth/credentials.c
r746 r988 1 /* 1 /* 2 2 Unix SMB/CIFS implementation. 3 3 … … 6 6 Copyright (C) Andrew Tridgell 1997-2003 7 7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004 8 8 9 9 This program is free software; you can redistribute it and/or modify 10 10 it under the terms of the GNU General Public License as published by 11 11 the Free Software Foundation; either version 3 of the License, or 12 12 (at your option) any later version. 13 13 14 14 This program is distributed in the hope that it will be useful, 15 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 17 GNU General Public License for more details. 18 18 19 19 You should have received a copy of the GNU General Public License 20 20 along with this program. If not, see <http://www.gnu.org/licenses/>. … … 31 31 struct netr_Credential *out) 32 32 { 33 des_crypt112(out->data, in->data, creds->session_key, 1); 33 if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { 34 AES_KEY key; 35 uint8_t iv[AES_BLOCK_SIZE]; 36 37 AES_set_encrypt_key(creds->session_key, 128, &key); 38 ZERO_STRUCT(iv); 39 40 aes_cfb8_encrypt(in->data, out->data, 8, &key, iv, AES_ENCRYPT); 41 } else { 42 des_crypt112(out->data, in->data, creds->session_key, 1); 43 } 34 44 } 35 45 … … 76 86 memset(zero, 0, sizeof(zero)); 77 87 78 hmac_md5_init_rfc2104(machine_password->hash, sizeof(machine_password->hash), &ctx); 88 hmac_md5_init_rfc2104(machine_password->hash, sizeof(machine_password->hash), &ctx); 79 89 MD5Init(&md5); 80 90 MD5Update(&md5, zero, sizeof(zero)); … … 86 96 } 87 97 98 /* 99 initialise the credentials state for AES/HMAC-SHA256-style 128 bit session keys 100 101 this call is made after the netr_ServerReqChallenge call 102 */ 103 static void netlogon_creds_init_hmac_sha256(struct netlogon_creds_CredentialState *creds, 104 const struct netr_Credential *client_challenge, 105 const struct netr_Credential *server_challenge, 106 const struct samr_Password *machine_password) 107 { 108 struct HMACSHA256Context ctx; 109 uint8_t digest[SHA256_DIGEST_LENGTH]; 110 111 ZERO_STRUCT(creds->session_key); 112 113 hmac_sha256_init(machine_password->hash, 114 sizeof(machine_password->hash), 115 &ctx); 116 hmac_sha256_update(client_challenge->data, 8, &ctx); 117 hmac_sha256_update(server_challenge->data, 8, &ctx); 118 hmac_sha256_final(digest, &ctx); 119 120 memcpy(creds->session_key, digest, sizeof(creds->session_key)); 121 122 ZERO_STRUCT(digest); 123 ZERO_STRUCT(ctx); 124 } 125 88 126 static void netlogon_creds_first_step(struct netlogon_creds_CredentialState *creds, 89 127 const struct netr_Credential *client_challenge, … … 105 143 struct netr_Credential time_cred; 106 144 107 DEBUG(5,("\tseed %08x:%08x\n", 145 DEBUG(5,("\tseed %08x:%08x\n", 108 146 IVAL(creds->seed.data, 0), IVAL(creds->seed.data, 4))); 109 147 … … 115 153 netlogon_creds_step_crypt(creds, &time_cred, &creds->client); 116 154 117 DEBUG(5,("\tCLIENT %08x:%08x\n", 155 DEBUG(5,("\tCLIENT %08x:%08x\n", 118 156 IVAL(creds->client.data, 0), IVAL(creds->client.data, 4))); 119 157 … … 121 159 SIVAL(time_cred.data, 4, IVAL(creds->seed.data, 4)); 122 160 123 DEBUG(5,("\tseed+time+1 %08x:%08x\n", 161 DEBUG(5,("\tseed+time+1 %08x:%08x\n", 124 162 IVAL(time_cred.data, 0), IVAL(time_cred.data, 4))); 125 163 126 164 netlogon_creds_step_crypt(creds, &time_cred, &creds->server); 127 165 128 DEBUG(5,("\tSERVER %08x:%08x\n", 166 DEBUG(5,("\tSERVER %08x:%08x\n", 129 167 IVAL(creds->server.data, 0), IVAL(creds->server.data, 4))); 130 168 … … 183 221 184 222 data_blob_free(&session_key); 223 } 224 225 /* 226 AES encrypt a password buffer using the session key 227 */ 228 void netlogon_creds_aes_encrypt(struct netlogon_creds_CredentialState *creds, uint8_t *data, size_t len) 229 { 230 AES_KEY key; 231 uint8_t iv[AES_BLOCK_SIZE]; 232 233 AES_set_encrypt_key(creds->session_key, 128, &key); 234 ZERO_STRUCT(iv); 235 236 aes_cfb8_encrypt(data, data, len, &key, iv, AES_ENCRYPT); 237 } 238 239 /* 240 AES decrypt a password buffer using the session key 241 */ 242 void netlogon_creds_aes_decrypt(struct netlogon_creds_CredentialState *creds, uint8_t *data, size_t len) 243 { 244 AES_KEY key; 245 uint8_t iv[AES_BLOCK_SIZE]; 246 247 AES_set_encrypt_key(creds->session_key, 128, &key); 248 ZERO_STRUCT(iv); 249 250 aes_cfb8_encrypt(data, data, len, &key, iv, AES_DECRYPT); 185 251 } 186 252 … … 194 260 credentials 195 261 */ 196 197 struct netlogon_creds_CredentialState *netlogon_creds_client_init(TALLOC_CTX *mem_ctx, 262 263 struct netlogon_creds_CredentialState *netlogon_creds_client_init(TALLOC_CTX *mem_ctx, 198 264 const char *client_account, 199 const char *client_computer_name, 265 const char *client_computer_name, 266 uint16_t secure_channel_type, 200 267 const struct netr_Credential *client_challenge, 201 268 const struct netr_Credential *server_challenge, … … 205 272 { 206 273 struct netlogon_creds_CredentialState *creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState); 207 274 208 275 if (!creds) { 209 276 return NULL; 210 277 } 211 278 212 279 creds->sequence = time(NULL); 213 280 creds->negotiate_flags = negotiate_flags; 281 creds->secure_channel_type = secure_channel_type; 214 282 215 283 creds->computer_name = talloc_strdup(creds, client_computer_name); … … 228 296 dump_data_pw("Machine Pass", machine_password->hash, sizeof(machine_password->hash)); 229 297 230 if (negotiate_flags & NETLOGON_NEG_128BIT) { 298 if (negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { 299 netlogon_creds_init_hmac_sha256(creds, 300 client_challenge, 301 server_challenge, 302 machine_password); 303 } else if (negotiate_flags & NETLOGON_NEG_STRONG_KEYS) { 231 304 netlogon_creds_init_128bit(creds, client_challenge, server_challenge, machine_password); 232 305 } else { … … 247 320 */ 248 321 249 struct netlogon_creds_CredentialState *netlogon_creds_client_init_session_key(TALLOC_CTX *mem_ctx, 322 struct netlogon_creds_CredentialState *netlogon_creds_client_init_session_key(TALLOC_CTX *mem_ctx, 250 323 const uint8_t session_key[16]) 251 324 { … … 256 329 return NULL; 257 330 } 258 331 259 332 memcpy(creds->session_key, session_key, 16); 260 333 … … 266 339 current client and server credentials and the seed 267 340 268 produce the next authenticator in the sequence ready to send to 341 produce the next authenticator in the sequence ready to send to 269 342 the server 270 343 */ 271 344 void netlogon_creds_client_authenticator(struct netlogon_creds_CredentialState *creds, 272 345 struct netr_Authenticator *next) 273 { 346 { 347 uint32_t t32n = (uint32_t)time(NULL); 348 349 /* 350 * we always increment and ignore an overflow here 351 */ 274 352 creds->sequence += 2; 353 354 if (t32n > creds->sequence) { 355 /* 356 * we may increment more 357 */ 358 creds->sequence = t32n; 359 } else { 360 uint32_t d = creds->sequence - t32n; 361 362 if (d >= INT32_MAX) { 363 /* 364 * got an overflow of time_t vs. uint32_t 365 */ 366 creds->sequence = t32n; 367 } 368 } 369 275 370 netlogon_creds_step(creds); 276 371 … … 285 380 const struct netr_Credential *received_credentials) 286 381 { 287 if (!received_credentials || 382 if (!received_credentials || 288 383 memcmp(received_credentials->data, creds->server.data, 8) != 0) { 289 384 DEBUG(2,("credentials check failed\n")); … … 318 413 credentials 319 414 */ 320 struct netlogon_creds_CredentialState *netlogon_creds_server_init(TALLOC_CTX *mem_ctx, 415 struct netlogon_creds_CredentialState *netlogon_creds_server_init(TALLOC_CTX *mem_ctx, 321 416 const char *client_account, 322 const char *client_computer_name, 417 const char *client_computer_name, 323 418 uint16_t secure_channel_type, 324 419 const struct netr_Credential *client_challenge, 325 420 const struct netr_Credential *server_challenge, 326 421 const struct samr_Password *machine_password, 327 struct netr_Credential *credentials_in,422 const struct netr_Credential *credentials_in, 328 423 struct netr_Credential *credentials_out, 329 424 uint32_t negotiate_flags) 330 425 { 331 426 332 427 struct netlogon_creds_CredentialState *creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState); 333 428 334 429 if (!creds) { 335 430 return NULL; 336 431 } 337 432 338 433 creds->negotiate_flags = negotiate_flags; 339 434 creds->secure_channel_type = secure_channel_type; 435 436 dump_data_pw("Client chall", client_challenge->data, sizeof(client_challenge->data)); 437 dump_data_pw("Server chall", server_challenge->data, sizeof(server_challenge->data)); 438 dump_data_pw("Machine Pass", machine_password->hash, sizeof(machine_password->hash)); 340 439 341 440 creds->computer_name = talloc_strdup(creds, client_computer_name); … … 350 449 } 351 450 352 if (negotiate_flags & NETLOGON_NEG_128BIT) { 353 netlogon_creds_init_128bit(creds, client_challenge, server_challenge, 451 if (negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { 452 netlogon_creds_init_hmac_sha256(creds, 453 client_challenge, 454 server_challenge, 455 machine_password); 456 } else if (negotiate_flags & NETLOGON_NEG_STRONG_KEYS) { 457 netlogon_creds_init_128bit(creds, client_challenge, server_challenge, 354 458 machine_password); 355 459 } else { 356 netlogon_creds_init_64bit(creds, client_challenge, server_challenge, 460 netlogon_creds_init_64bit(creds, client_challenge, server_challenge, 357 461 machine_password); 358 462 } 359 463 360 464 netlogon_creds_first_step(creds, client_challenge, server_challenge); 465 466 dump_data_pw("Session key", creds->session_key, 16); 467 dump_data_pw("Client Credential ", creds->client.data, 8); 468 dump_data_pw("Server Credential ", creds->server.data, 8); 469 470 dump_data_pw("Credentials in", credentials_in->data, sizeof(credentials_in->data)); 361 471 362 472 /* And before we leak information about the machine account … … 369 479 *credentials_out = creds->server; 370 480 481 dump_data_pw("Credentials out", credentials_out->data, sizeof(credentials_out->data)); 482 371 483 return creds; 372 484 } 373 485 374 486 NTSTATUS netlogon_creds_server_step_check(struct netlogon_creds_CredentialState *creds, 375 struct netr_Authenticator *received_authenticator,376 struct netr_Authenticator *return_authenticator) 487 const struct netr_Authenticator *received_authenticator, 488 struct netr_Authenticator *return_authenticator) 377 489 { 378 490 if (!received_authenticator || !return_authenticator) { … … 384 496 } 385 497 386 /* TODO: this may allow the a replay attack on a non-signed387 connection. Should we check that this is increasing? */388 498 creds->sequence = received_authenticator->timestamp; 389 499 netlogon_creds_step(creds); 390 500 if (netlogon_creds_server_check_internal(creds, &received_authenticator->cred)) { 391 501 return_authenticator->cred = creds->server; 392 return_authenticator->timestamp = creds->sequence;502 return_authenticator->timestamp = 0; 393 503 return NT_STATUS_OK; 394 504 } else { … … 398 508 } 399 509 400 void netlogon_creds_decrypt_samlogon(struct netlogon_creds_CredentialState *creds, 401 uint16_t validation_level, 402 union netr_Validation *validation) 510 static void netlogon_creds_crypt_samlogon_validation(struct netlogon_creds_CredentialState *creds, 511 uint16_t validation_level, 512 union netr_Validation *validation, 513 bool do_encrypt) 403 514 { 404 515 static const char zeros[16]; 405 406 516 struct netr_SamBaseInfo *base = NULL; 517 518 if (validation == NULL) { 519 return; 520 } 521 407 522 switch (validation_level) { 408 523 case 2: … … 433 548 if (validation_level == 6) { 434 549 /* they aren't encrypted! */ 550 } else if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { 551 /* Don't crypt an all-zero key, it would give away the NETLOGON pipe session key */ 552 if (memcmp(base->key.key, zeros, 553 sizeof(base->key.key)) != 0) { 554 if (do_encrypt) { 555 netlogon_creds_aes_encrypt(creds, 556 base->key.key, 557 sizeof(base->key.key)); 558 } else { 559 netlogon_creds_aes_decrypt(creds, 560 base->key.key, 561 sizeof(base->key.key)); 562 } 563 } 564 565 if (memcmp(base->LMSessKey.key, zeros, 566 sizeof(base->LMSessKey.key)) != 0) { 567 if (do_encrypt) { 568 netlogon_creds_aes_encrypt(creds, 569 base->LMSessKey.key, 570 sizeof(base->LMSessKey.key)); 571 572 } else { 573 netlogon_creds_aes_decrypt(creds, 574 base->LMSessKey.key, 575 sizeof(base->LMSessKey.key)); 576 } 577 } 435 578 } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) { 436 if (memcmp(base->key.key, zeros, 579 /* Don't crypt an all-zero key, it would give away the NETLOGON pipe session key */ 580 if (memcmp(base->key.key, zeros, 437 581 sizeof(base->key.key)) != 0) { 438 netlogon_creds_arcfour_crypt(creds, 439 base->key.key, 582 netlogon_creds_arcfour_crypt(creds, 583 base->key.key, 440 584 sizeof(base->key.key)); 441 585 } 442 443 if (memcmp(base->LMSessKey.key, zeros, 586 587 if (memcmp(base->LMSessKey.key, zeros, 444 588 sizeof(base->LMSessKey.key)) != 0) { 445 netlogon_creds_arcfour_crypt(creds, 446 base->LMSessKey.key, 589 netlogon_creds_arcfour_crypt(creds, 590 base->LMSessKey.key, 447 591 sizeof(base->LMSessKey.key)); 448 592 } 449 593 } else { 450 if (memcmp(base->LMSessKey.key, zeros, 594 /* Don't crypt an all-zero key, it would give away the NETLOGON pipe session key */ 595 if (memcmp(base->LMSessKey.key, zeros, 451 596 sizeof(base->LMSessKey.key)) != 0) { 452 netlogon_creds_des_decrypt_LMKey(creds, 597 if (do_encrypt) { 598 netlogon_creds_des_encrypt_LMKey(creds, 453 599 &base->LMSessKey); 454 } 455 } 456 } 600 } else { 601 netlogon_creds_des_decrypt_LMKey(creds, 602 &base->LMSessKey); 603 } 604 } 605 } 606 } 607 608 void netlogon_creds_decrypt_samlogon_validation(struct netlogon_creds_CredentialState *creds, 609 uint16_t validation_level, 610 union netr_Validation *validation) 611 { 612 netlogon_creds_crypt_samlogon_validation(creds, validation_level, 613 validation, false); 614 } 615 616 void netlogon_creds_encrypt_samlogon_validation(struct netlogon_creds_CredentialState *creds, 617 uint16_t validation_level, 618 union netr_Validation *validation) 619 { 620 netlogon_creds_crypt_samlogon_validation(creds, validation_level, 621 validation, true); 622 } 623 624 static void netlogon_creds_crypt_samlogon_logon(struct netlogon_creds_CredentialState *creds, 625 enum netr_LogonInfoClass level, 626 union netr_LogonLevel *logon, 627 bool do_encrypt) 628 { 629 static const char zeros[16]; 630 631 if (logon == NULL) { 632 return; 633 } 634 635 switch (level) { 636 case NetlogonInteractiveInformation: 637 case NetlogonInteractiveTransitiveInformation: 638 case NetlogonServiceInformation: 639 case NetlogonServiceTransitiveInformation: 640 if (logon->password == NULL) { 641 return; 642 } 643 644 if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { 645 uint8_t *h; 646 647 h = logon->password->lmpassword.hash; 648 if (memcmp(h, zeros, 16) != 0) { 649 if (do_encrypt) { 650 netlogon_creds_aes_encrypt(creds, h, 16); 651 } else { 652 netlogon_creds_aes_decrypt(creds, h, 16); 653 } 654 } 655 656 h = logon->password->ntpassword.hash; 657 if (memcmp(h, zeros, 16) != 0) { 658 if (do_encrypt) { 659 netlogon_creds_aes_encrypt(creds, h, 16); 660 } else { 661 netlogon_creds_aes_decrypt(creds, h, 16); 662 } 663 } 664 } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) { 665 uint8_t *h; 666 667 h = logon->password->lmpassword.hash; 668 if (memcmp(h, zeros, 16) != 0) { 669 netlogon_creds_arcfour_crypt(creds, h, 16); 670 } 671 672 h = logon->password->ntpassword.hash; 673 if (memcmp(h, zeros, 16) != 0) { 674 netlogon_creds_arcfour_crypt(creds, h, 16); 675 } 676 } else { 677 struct samr_Password *p; 678 679 p = &logon->password->lmpassword; 680 if (memcmp(p->hash, zeros, 16) != 0) { 681 if (do_encrypt) { 682 netlogon_creds_des_encrypt(creds, p); 683 } else { 684 netlogon_creds_des_decrypt(creds, p); 685 } 686 } 687 p = &logon->password->ntpassword; 688 if (memcmp(p->hash, zeros, 16) != 0) { 689 if (do_encrypt) { 690 netlogon_creds_des_encrypt(creds, p); 691 } else { 692 netlogon_creds_des_decrypt(creds, p); 693 } 694 } 695 } 696 break; 697 698 case NetlogonNetworkInformation: 699 case NetlogonNetworkTransitiveInformation: 700 break; 701 702 case NetlogonGenericInformation: 703 if (logon->generic == NULL) { 704 return; 705 } 706 707 if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { 708 if (do_encrypt) { 709 netlogon_creds_aes_encrypt(creds, 710 logon->generic->data, 711 logon->generic->length); 712 } else { 713 netlogon_creds_aes_decrypt(creds, 714 logon->generic->data, 715 logon->generic->length); 716 } 717 } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) { 718 netlogon_creds_arcfour_crypt(creds, 719 logon->generic->data, 720 logon->generic->length); 721 } else { 722 /* Using DES to verify kerberos tickets makes no sense */ 723 } 724 break; 725 } 726 } 727 728 void netlogon_creds_decrypt_samlogon_logon(struct netlogon_creds_CredentialState *creds, 729 enum netr_LogonInfoClass level, 730 union netr_LogonLevel *logon) 731 { 732 netlogon_creds_crypt_samlogon_logon(creds, level, logon, false); 733 } 734 735 void netlogon_creds_encrypt_samlogon_logon(struct netlogon_creds_CredentialState *creds, 736 enum netr_LogonInfoClass level, 737 union netr_LogonLevel *logon) 738 { 739 netlogon_creds_crypt_samlogon_logon(creds, level, logon, true); 740 } 741 742 union netr_LogonLevel *netlogon_creds_shallow_copy_logon(TALLOC_CTX *mem_ctx, 743 enum netr_LogonInfoClass level, 744 const union netr_LogonLevel *in) 745 { 746 union netr_LogonLevel *out; 747 748 if (in == NULL) { 749 return NULL; 750 } 751 752 out = talloc(mem_ctx, union netr_LogonLevel); 753 if (out == NULL) { 754 return NULL; 755 } 756 757 *out = *in; 758 759 switch (level) { 760 case NetlogonInteractiveInformation: 761 case NetlogonInteractiveTransitiveInformation: 762 case NetlogonServiceInformation: 763 case NetlogonServiceTransitiveInformation: 764 if (in->password == NULL) { 765 return out; 766 } 767 768 out->password = talloc(out, struct netr_PasswordInfo); 769 if (out->password == NULL) { 770 talloc_free(out); 771 return NULL; 772 } 773 *out->password = *in->password; 774 775 return out; 776 777 case NetlogonNetworkInformation: 778 case NetlogonNetworkTransitiveInformation: 779 break; 780 781 case NetlogonGenericInformation: 782 if (in->generic == NULL) { 783 return out; 784 } 785 786 out->generic = talloc(out, struct netr_GenericInfo); 787 if (out->generic == NULL) { 788 talloc_free(out); 789 return NULL; 790 } 791 *out->generic = *in->generic; 792 793 if (in->generic->data == NULL) { 794 return out; 795 } 796 797 if (in->generic->length == 0) { 798 return out; 799 } 800 801 out->generic->data = talloc_memdup(out->generic, 802 in->generic->data, 803 in->generic->length); 804 if (out->generic->data == NULL) { 805 talloc_free(out); 806 return NULL; 807 } 808 809 return out; 810 } 811 812 return out; 813 } 457 814 458 815 /* -
vendor/current/libcli/auth/credentials.h
r740 r988 69 69 #define NETLOGON_NEG_AUTH2_ADS_FLAGS (0x200fbffb | NETLOGON_NEG_ARCFOUR | NETLOGON_NEG_128BIT | NETLOGON_NEG_SCHANNEL) 70 70 71 #define NETLOGON_NEG_AUTH2_RODC_FLAGS (NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_RODC_PASSTHROUGH)72 -
vendor/current/libcli/auth/msrpc_parse.c
r740 r988 79 79 if (!ret) { 80 80 va_end(ap); 81 return map_nt_error_from_unix (errno);81 return map_nt_error_from_unix_common(errno); 82 82 } 83 83 pointers[i].length = n; … … 93 93 if (!ret) { 94 94 va_end(ap); 95 return map_nt_error_from_unix (errno);95 return map_nt_error_from_unix_common(errno); 96 96 } 97 97 pointers[i].length = n; … … 109 109 if (!ret) { 110 110 va_end(ap); 111 return map_nt_error_from_unix (errno);111 return map_nt_error_from_unix_common(errno); 112 112 } 113 113 pointers[i].length = n; … … 178 178 n = pointers[i].length; 179 179 SSVAL(blob->data, data_ofs, n); data_ofs += 2; 180 if (n >= 0) { 181 memcpy(blob->data+data_ofs, pointers[i].data, n); 182 } 180 memcpy(blob->data+data_ofs, pointers[i].data, n); 183 181 data_ofs += n; 184 182 break; … … 287 285 if (!convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, 288 286 blob->data + ptr, len1, 289 ps, &pull_len , false)) {287 ps, &pull_len)) { 290 288 ret = false; 291 289 goto cleanup; … … 323 321 if (!convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, 324 322 blob->data + ptr, len1, 325 ps, &pull_len , false)) {323 ps, &pull_len)) { 326 324 ret = false; 327 325 goto cleanup; -
vendor/current/libcli/auth/ntlm_check.c
r746 r988 321 321 char *unix_pw = NULL; 322 322 bool lm_ok; 323 size_t converted_size = 0; 323 324 324 325 DEBUG(4,("ntlm_password_check: checking plaintext passwords for user %s\n", … … 329 330 (convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, 330 331 lm_response->data, lm_response->length, 331 (void *)&unix_pw, NULL, false))) {332 (void *)&unix_pw, &converted_size))) { 332 333 if (E_deshash(unix_pw, client_lm.hash)) { 333 334 lm_ok = true; … … 485 486 &tmp_sess_key)) { 486 487 if (nt_response->length > 24) { 487 /* If NTLMv2 authentication has prece eded us488 /* If NTLMv2 authentication has preceded us 488 489 * (even if it failed), then use the session 489 490 * key from that. See the RPC-SAMLOGON … … 514 515 &tmp_sess_key)) { 515 516 if (nt_response->length > 24) { 516 /* If NTLMv2 authentication has prece eded us517 /* If NTLMv2 authentication has preceded us 517 518 * (even if it failed), then use the session 518 519 * key from that. See the RPC-SAMLOGON … … 542 543 &tmp_sess_key)) { 543 544 if (nt_response->length > 24) { 544 /* If NTLMv2 authentication has prece eded us545 /* If NTLMv2 authentication has preceded us 545 546 * (even if it failed), then use the session 546 547 * key from that. See the RPC-SAMLOGON -
vendor/current/libcli/auth/proto.h
r919 r988 17 17 void netlogon_creds_des_decrypt(struct netlogon_creds_CredentialState *creds, struct samr_Password *pass); 18 18 void netlogon_creds_arcfour_crypt(struct netlogon_creds_CredentialState *creds, uint8_t *data, size_t len); 19 void netlogon_creds_aes_encrypt(struct netlogon_creds_CredentialState *creds, uint8_t *data, size_t len); 20 void netlogon_creds_aes_decrypt(struct netlogon_creds_CredentialState *creds, uint8_t *data, size_t len); 19 21 20 22 /***************************************************************** … … 25 27 const char *client_account, 26 28 const char *client_computer_name, 29 uint16_t secure_channel_type, 27 30 const struct netr_Credential *client_challenge, 28 31 const struct netr_Credential *server_challenge, … … 50 53 const struct netr_Credential *server_challenge, 51 54 const struct samr_Password *machine_password, 52 struct netr_Credential *credentials_in,55 const struct netr_Credential *credentials_in, 53 56 struct netr_Credential *credentials_out, 54 57 uint32_t negotiate_flags); 55 58 NTSTATUS netlogon_creds_server_step_check(struct netlogon_creds_CredentialState *creds, 56 struct netr_Authenticator *received_authenticator,59 const struct netr_Authenticator *received_authenticator, 57 60 struct netr_Authenticator *return_authenticator) ; 58 void netlogon_creds_decrypt_samlogon(struct netlogon_creds_CredentialState *creds, 59 uint16_t validation_level, 60 union netr_Validation *validation) ; 61 void netlogon_creds_decrypt_samlogon_validation(struct netlogon_creds_CredentialState *creds, 62 uint16_t validation_level, 63 union netr_Validation *validation); 64 void netlogon_creds_encrypt_samlogon_validation(struct netlogon_creds_CredentialState *creds, 65 uint16_t validation_level, 66 union netr_Validation *validation); 67 void netlogon_creds_decrypt_samlogon_logon(struct netlogon_creds_CredentialState *creds, 68 enum netr_LogonInfoClass level, 69 union netr_LogonLevel *logon); 70 void netlogon_creds_encrypt_samlogon_logon(struct netlogon_creds_CredentialState *creds, 71 enum netr_LogonInfoClass level, 72 union netr_LogonLevel *logon); 73 union netr_LogonLevel *netlogon_creds_shallow_copy_logon(TALLOC_CTX *mem_ctx, 74 enum netr_LogonInfoClass level, 75 const union netr_LogonLevel *in); 61 76 62 77 /* The following definitions come from /home/jeremy/src/samba/git/master/source3/../source4/../libcli/auth/session.c */ … … 112 127 uint8_t kr_buf[16]); 113 128 void SMBOWFencrypt(const uint8_t passwd[16], const uint8_t *c8, uint8_t p24[24]); 114 void SMBNTencrypt_hash(const uint8_t nt_hash[16], uint8_t *c8, uint8_t *p24);115 void SMBNTencrypt(const char *passwd, uint8_t *c8, uint8_t *p24);129 void SMBNTencrypt_hash(const uint8_t nt_hash[16], const uint8_t *c8, uint8_t *p24); 130 void SMBNTencrypt(const char *passwd, const uint8_t *c8, uint8_t *p24); 116 131 void SMBOWFencrypt_ntv2(const uint8_t kr[16], 117 132 const DATA_BLOB *srv_chal, … … 130 145 const char *user, const char *domain, const uint8_t nt_hash[16], 131 146 const DATA_BLOB *server_chal, 147 const NTTIME *server_timestamp, 132 148 const DATA_BLOB *names_blob, 133 149 DATA_BLOB *lm_response, DATA_BLOB *nt_response, … … 180 196 bool extract_pw_from_buffer(TALLOC_CTX *mem_ctx, 181 197 uint8_t in_buffer[516], DATA_BLOB *new_pass); 198 struct wkssvc_PasswordBuffer; 182 199 void encode_wkssvc_join_password_buffer(TALLOC_CTX *mem_ctx, 183 200 const char *pwd, -
vendor/current/libcli/auth/schannel.h
r414 r988 23 23 #include "libcli/auth/libcli_auth.h" 24 24 #include "libcli/auth/schannel_state.h" 25 26 enum schannel_position {27 SCHANNEL_STATE_START = 0,28 SCHANNEL_STATE_UPDATE_129 };30 31 struct schannel_state {32 enum schannel_position state;33 uint32_t seq_num;34 bool initiator;35 struct netlogon_creds_CredentialState *creds;36 };37 38 25 #include "libcli/auth/schannel_proto.h" -
vendor/current/libcli/auth/schannel_proto.h
r740 r988 26 26 struct schannel_state; 27 27 28 struct tdb_wrap *open_schannel_session_store(TALLOC_CTX *mem_ctx, 29 const char *private_dir); 30 31 NTSTATUS netsec_incoming_packet(struct schannel_state *state, 32 TALLOC_CTX *mem_ctx, 33 bool do_unseal, 34 uint8_t *data, size_t length, 35 const DATA_BLOB *sig); 36 uint32_t netsec_outgoing_sig_size(struct schannel_state *state); 37 NTSTATUS netsec_outgoing_packet(struct schannel_state *state, 38 TALLOC_CTX *mem_ctx, 39 bool do_seal, 40 uint8_t *data, size_t length, 41 DATA_BLOB *sig); 28 struct db_context *open_schannel_session_store(TALLOC_CTX *mem_ctx, 29 struct loadparm_context *lp_ctx); 42 30 43 31 #endif -
vendor/current/libcli/auth/schannel_state.h
r740 r988 25 25 26 26 NTSTATUS schannel_get_creds_state(TALLOC_CTX *mem_ctx, 27 const char *db_priv_dir,27 struct loadparm_context *lp_ctx, 28 28 const char *computer_name, 29 29 struct netlogon_creds_CredentialState **creds); 30 30 31 31 NTSTATUS schannel_save_creds_state(TALLOC_CTX *mem_ctx, 32 const char *db_priv_dir,32 struct loadparm_context *lp_ctx, 33 33 struct netlogon_creds_CredentialState *creds); 34 34 35 35 NTSTATUS schannel_check_creds_state(TALLOC_CTX *mem_ctx, 36 const char *db_priv_dir,36 struct loadparm_context *lp_ctx, 37 37 const char *computer_name, 38 38 struct netr_Authenticator *received_authenticator, -
vendor/current/libcli/auth/schannel_state_tdb.c
r914 r988 24 24 #include "includes.h" 25 25 #include "system/filesys.h" 26 #include <tdb.h>26 #include "../lib/tdb/include/tdb.h" 27 27 #include "../lib/util/util_tdb.h" 28 #include "../lib/param/param.h" 28 29 #include "../libcli/auth/schannel.h" 29 30 #include "../librpc/gen_ndr/ndr_schannel.h" 30 #include "lib/ util/tdb_wrap.h"31 #include "lib/dbwrap/dbwrap.h" 31 32 32 33 #define SECRETS_SCHANNEL_STATE "SECRETS/SCHANNEL" … … 37 38 *******************************************************************************/ 38 39 39 struct tdb_wrap*open_schannel_session_store(TALLOC_CTX *mem_ctx,40 const char *private_dir)41 { 42 struct tdb_wrap *tdb_sc = NULL;43 char *fname = talloc_asprintf(mem_ctx, "%s/schannel_store.tdb", private_dir);40 struct db_context *open_schannel_session_store(TALLOC_CTX *mem_ctx, 41 struct loadparm_context *lp_ctx) 42 { 43 struct db_context *db_sc = NULL; 44 char *fname = lpcfg_private_db_path(mem_ctx, lp_ctx, "schannel_store"); 44 45 45 46 if (!fname) { … … 47 48 } 48 49 49 tdb_sc = tdb_wrap_open(mem_ctx, fname, 0, TDB_CLEAR_IF_FIRST|TDB_NOSYNC, O_RDWR|O_CREAT, 0600); 50 51 if (!tdb_sc) { 50 db_sc = dbwrap_local_open(mem_ctx, lp_ctx, fname, 0, 51 TDB_CLEAR_IF_FIRST|TDB_NOSYNC, O_RDWR|O_CREAT, 52 0600, DBWRAP_LOCK_ORDER_NONE, 53 DBWRAP_FLAG_NONE); 54 55 if (!db_sc) { 52 56 DEBUG(0,("open_schannel_session_store: Failed to open %s - %s\n", 53 57 fname, strerror(errno))); … … 58 62 TALLOC_FREE(fname); 59 63 60 return tdb_sc;64 return db_sc; 61 65 } 62 66 … … 65 69 66 70 static 67 NTSTATUS schannel_store_session_key_tdb(struct tdb_wrap *tdb_sc,71 NTSTATUS schannel_store_session_key_tdb(struct db_context *db_sc, 68 72 TALLOC_CTX *mem_ctx, 69 73 struct netlogon_creds_CredentialState *creds) … … 72 76 DATA_BLOB blob; 73 77 TDB_DATA value; 74 int ret;75 78 char *keystr; 76 79 char *name_upper; 80 NTSTATUS status; 81 82 if (strlen(creds->computer_name) > 15) { 83 /* 84 * We may want to check for a completely 85 * valid netbios name. 86 */ 87 return STATUS_BUFFER_OVERFLOW; 88 } 77 89 78 90 name_upper = strupper_talloc(mem_ctx, creds->computer_name); … … 98 110 value.dsize = blob.length; 99 111 100 ret = tdb_store_bystring(tdb_sc->tdb, keystr, value, TDB_REPLACE);101 if ( ret != TDB_SUCCESS) {112 status = dbwrap_store_bystring(db_sc, keystr, value, TDB_REPLACE); 113 if (!NT_STATUS_IS_OK(status)) { 102 114 DEBUG(0,("Unable to add %s to session key db - %s\n", 103 keystr, tdb_errorstr(tdb_sc->tdb)));115 keystr, nt_errstr(status))); 104 116 talloc_free(keystr); 105 return NT_STATUS_INTERNAL_DB_CORRUPTION;117 return status; 106 118 } 107 119 … … 122 134 123 135 static 124 NTSTATUS schannel_fetch_session_key_tdb(struct tdb_wrap *tdb_sc,136 NTSTATUS schannel_fetch_session_key_tdb(struct db_context *db_sc, 125 137 TALLOC_CTX *mem_ctx, 126 138 const char *computer_name, … … 149 161 } 150 162 151 value = tdb_fetch_bystring(tdb_sc->tdb, keystr);152 if (! value.dptr) {163 status = dbwrap_fetch_bystring(db_sc, keystr, keystr, &value); 164 if (!NT_STATUS_IS_OK(status)) { 153 165 DEBUG(10,("schannel_fetch_session_key_tdb: Failed to find entry with key %s\n", 154 166 keystr )); 155 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;156 167 goto done; 157 168 } … … 184 195 185 196 talloc_free(keystr); 186 SAFE_FREE(value.dptr);187 197 188 198 if (!NT_STATUS_IS_OK(status)) { … … 202 212 203 213 NTSTATUS schannel_get_creds_state(TALLOC_CTX *mem_ctx, 204 const char *db_priv_dir,214 struct loadparm_context *lp_ctx, 205 215 const char *computer_name, 206 216 struct netlogon_creds_CredentialState **_creds) 207 217 { 208 218 TALLOC_CTX *tmpctx; 209 struct tdb_wrap *tdb_sc;219 struct db_context *db_sc; 210 220 struct netlogon_creds_CredentialState *creds; 211 221 NTSTATUS status; … … 216 226 } 217 227 218 tdb_sc = open_schannel_session_store(tmpctx, db_priv_dir);219 if (! tdb_sc) {228 db_sc = open_schannel_session_store(tmpctx, lp_ctx); 229 if (!db_sc) { 220 230 return NT_STATUS_ACCESS_DENIED; 221 231 } 222 232 223 status = schannel_fetch_session_key_tdb( tdb_sc, tmpctx,233 status = schannel_fetch_session_key_tdb(db_sc, tmpctx, 224 234 computer_name, &creds); 225 235 if (NT_STATUS_IS_OK(status)) { … … 240 250 241 251 NTSTATUS schannel_save_creds_state(TALLOC_CTX *mem_ctx, 242 const char *db_priv_dir,252 struct loadparm_context *lp_ctx, 243 253 struct netlogon_creds_CredentialState *creds) 244 254 { 245 255 TALLOC_CTX *tmpctx; 246 struct tdb_wrap *tdb_sc;256 struct db_context *db_sc; 247 257 NTSTATUS status; 248 258 … … 252 262 } 253 263 254 tdb_sc = open_schannel_session_store(tmpctx, db_priv_dir);255 if (! tdb_sc) {264 db_sc = open_schannel_session_store(tmpctx, lp_ctx); 265 if (!db_sc) { 256 266 return NT_STATUS_ACCESS_DENIED; 257 267 } 258 268 259 status = schannel_store_session_key_tdb( tdb_sc, tmpctx, creds);269 status = schannel_store_session_key_tdb(db_sc, tmpctx, creds); 260 270 261 271 talloc_free(tmpctx); … … 274 284 275 285 NTSTATUS schannel_check_creds_state(TALLOC_CTX *mem_ctx, 276 const char *db_priv_dir,286 struct loadparm_context *lp_ctx, 277 287 const char *computer_name, 278 288 struct netr_Authenticator *received_authenticator, … … 281 291 { 282 292 TALLOC_CTX *tmpctx; 283 struct tdb_wrap *tdb_sc;293 struct db_context *db_sc; 284 294 struct netlogon_creds_CredentialState *creds; 285 295 NTSTATUS status; 286 int ret; 296 char *name_upper = NULL; 297 char *keystr = NULL; 298 struct db_record *record; 299 TDB_DATA key; 287 300 288 301 if (creds_out != NULL) { … … 295 308 } 296 309 297 tdb_sc = open_schannel_session_store(tmpctx, db_priv_dir); 298 if (!tdb_sc) { 310 name_upper = strupper_talloc(tmpctx, computer_name); 311 if (!name_upper) { 312 status = NT_STATUS_NO_MEMORY; 313 goto done; 314 } 315 316 keystr = talloc_asprintf(tmpctx, "%s/%s", 317 SECRETS_SCHANNEL_STATE, name_upper); 318 if (!keystr) { 319 status = NT_STATUS_NO_MEMORY; 320 goto done; 321 } 322 323 key = string_term_tdb_data(keystr); 324 325 db_sc = open_schannel_session_store(tmpctx, lp_ctx); 326 if (!db_sc) { 299 327 status = NT_STATUS_ACCESS_DENIED; 300 328 goto done; 301 329 } 302 330 303 re t = tdb_transaction_start(tdb_sc->tdb);304 if ( ret != 0) {331 record = dbwrap_fetch_locked(db_sc, tmpctx, key); 332 if (!record) { 305 333 status = NT_STATUS_INTERNAL_DB_CORRUPTION; 306 334 goto done; … … 311 339 * update the structure */ 312 340 313 status = schannel_fetch_session_key_tdb( tdb_sc, tmpctx,341 status = schannel_fetch_session_key_tdb(db_sc, tmpctx, 314 342 computer_name, &creds); 315 343 if (!NT_STATUS_IS_OK(status)) { 316 tdb_transaction_cancel(tdb_sc->tdb);317 344 goto done; 318 345 } … … 322 349 return_authenticator); 323 350 if (!NT_STATUS_IS_OK(status)) { 324 tdb_transaction_cancel(tdb_sc->tdb); 325 goto done; 326 } 327 328 status = schannel_store_session_key_tdb(tdb_sc, tmpctx, creds); 329 if (!NT_STATUS_IS_OK(status)) { 330 tdb_transaction_cancel(tdb_sc->tdb); 331 goto done; 332 } 333 334 tdb_transaction_commit(tdb_sc->tdb); 351 goto done; 352 } 353 354 status = schannel_store_session_key_tdb(db_sc, tmpctx, creds); 355 if (!NT_STATUS_IS_OK(status)) { 356 goto done; 357 } 335 358 336 359 if (creds_out) { -
vendor/current/libcli/auth/smbencrypt.c
r919 r988 117 117 bool E_deshash(const char *passwd, uint8_t p16[16]) 118 118 { 119 bool ret = true; 120 char dospwd[256]; 119 bool ret; 120 uint8_t dospwd[14]; 121 TALLOC_CTX *frame = talloc_stackframe(); 122 123 size_t converted_size; 124 125 char *tmpbuf; 126 121 127 ZERO_STRUCT(dospwd); 122 128 123 /* Password must be converted to DOS charset - null terminated, uppercase. */ 124 push_string(dospwd, passwd, sizeof(dospwd), STR_ASCII|STR_UPPER|STR_TERMINATE); 125 126 /* Only the first 14 chars are considered, password need not be null terminated. */ 129 tmpbuf = strupper_talloc(frame, passwd); 130 if (tmpbuf == NULL) { 131 /* Too many callers don't check this result, we need to fill in the buffer with something */ 132 strlcpy((char *)dospwd, passwd ? passwd : "", sizeof(dospwd)); 133 E_P16(dospwd, p16); 134 talloc_free(frame); 135 return false; 136 } 137 138 ZERO_STRUCT(dospwd); 139 140 ret = convert_string_error(CH_UNIX, CH_DOS, tmpbuf, strlen(tmpbuf), dospwd, sizeof(dospwd), &converted_size); 141 talloc_free(frame); 142 143 /* Only the first 14 chars are considered, password need not 144 * be null terminated. We do this in the error and success 145 * case to avoid returning a fixed 'password' buffer, but 146 * callers should not use it when E_deshash returns false */ 147 127 148 E_P16((const uint8_t *)dospwd, p16); 128 129 if (strlen(dospwd) > 14) {130 ret = false;131 }132 149 133 150 ZERO_STRUCT(dospwd); … … 249 266 /* Does the des encryption. */ 250 267 251 void SMBNTencrypt_hash(const uint8_t nt_hash[16], uint8_t *c8, uint8_t *p24)268 void SMBNTencrypt_hash(const uint8_t nt_hash[16], const uint8_t *c8, uint8_t *p24) 252 269 { 253 270 uint8_t p21[21]; … … 267 284 /* Does the NT MD4 hash then des encryption. Plaintext version of the above. */ 268 285 269 void SMBNTencrypt(const char *passwd, uint8_t *c8, uint8_t *p24)286 void SMBNTencrypt(const char *passwd, const uint8_t *c8, uint8_t *p24) 270 287 { 271 288 uint8_t nt_hash[16]; … … 371 388 } 372 389 373 static DATA_BLOB NTLMv2_generate_client_data(TALLOC_CTX *mem_ctx, const DATA_BLOB *names_blob) 390 static DATA_BLOB NTLMv2_generate_client_data(TALLOC_CTX *mem_ctx, 391 NTTIME nttime, 392 const DATA_BLOB *names_blob) 374 393 { 375 394 uint8_t client_chal[8]; 376 395 DATA_BLOB response = data_blob(NULL, 0); 377 396 uint8_t long_date[8]; 378 NTTIME nttime;379 380 unix_to_nt_time(&nttime, time(NULL));381 397 382 398 generate_random_buffer(client_chal, sizeof(client_chal)); … … 401 417 const uint8_t ntlm_v2_hash[16], 402 418 const DATA_BLOB *server_chal, 419 NTTIME nttime, 403 420 const DATA_BLOB *names_blob) 404 421 { … … 417 434 /* generate some data to pass into the response function - including 418 435 the hostname and domain name of the server */ 419 ntlmv2_client_data = NTLMv2_generate_client_data(mem_ctx, n ames_blob);436 ntlmv2_client_data = NTLMv2_generate_client_data(mem_ctx, nttime, names_blob); 420 437 421 438 /* Given that data, and the challenge from the server, generate a response */ … … 463 480 const char *user, const char *domain, const uint8_t nt_hash[16], 464 481 const DATA_BLOB *server_chal, 482 const NTTIME *server_timestamp, 465 483 const DATA_BLOB *names_blob, 466 484 DATA_BLOB *lm_response, DATA_BLOB *nt_response, … … 478 496 479 497 if (nt_response) { 498 const NTTIME *nttime = server_timestamp; 499 NTTIME _now = 0; 500 501 if (nttime == NULL) { 502 struct timeval tv_now = timeval_current(); 503 _now = timeval_to_nttime(&tv_now); 504 nttime = &_now; 505 } 506 480 507 *nt_response = NTLMv2_generate_response(mem_ctx, 481 ntlm_v2_hash, server_chal, 508 ntlm_v2_hash, 509 server_chal, 510 *nttime, 482 511 names_blob); 483 512 if (user_session_key) { … … 493 522 494 523 if (lm_response) { 495 *lm_response = LMv2_generate_response(mem_ctx, 496 ntlm_v2_hash, server_chal); 524 if (server_timestamp != NULL) { 525 *lm_response = data_blob_talloc_zero(mem_ctx, 24); 526 } else { 527 *lm_response = LMv2_generate_response(mem_ctx, 528 ntlm_v2_hash, 529 server_chal); 530 } 497 531 if (lm_session_key) { 498 532 *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16); … … 519 553 520 554 return SMBNTLMv2encrypt_hash(mem_ctx, 521 user, domain, nt_hash, server_chal, names_blob, 555 user, domain, nt_hash, 556 server_chal, NULL, names_blob, 522 557 lm_response, nt_response, lm_session_key, user_session_key); 523 558 } … … 611 646 } 612 647 613 #ifdef SAMBA4_INTERNAL_HEIMDAL /* smbtorture4 for make test */614 648 cmp = strcasecmp_m(a, v); 615 #else /* smbd */616 cmp = StrCaseCmp(a, v);617 #endif618 649 if (cmp != 0) { 619 650 DEBUG(2,("%s: NTLMv2_RESPONSE with " … … 637 668 v = av_nb_dn->Value.AvNbDomainName; 638 669 639 #ifdef SAMBA4_INTERNAL_HEIMDAL /* smbtorture4 for make test */640 670 cmp = strcasecmp_m(workgroup, v); 641 #else /* smbd */642 cmp = StrCaseCmp(workgroup, v);643 #endif644 671 if (cmp != 0) { 645 672 DEBUG(2,("%s: NTLMv2_RESPONSE with " … … 740 767 byte_len, 741 768 (void *)pp_new_pwrd, 742 new_pw_len, 743 false)) { 769 new_pw_len)) { 744 770 DEBUG(0, ("decode_pw_buffer: failed to convert incoming password\n")); 745 771 return false; … … 891 917 892 918 if (!pwd_buf) { 893 return WERR_ BAD_PASSWORD;919 return WERR_INVALID_PASSWORD; 894 920 } 895 921 896 922 if (session_key->length != 16) { 897 923 DEBUG(10,("invalid session key\n")); 898 return WERR_ BAD_PASSWORD;924 return WERR_INVALID_PASSWORD; 899 925 } 900 926 … … 913 939 if (!decode_pw_buffer(mem_ctx, buffer, pwd, &pwd_len, CH_UTF16)) { 914 940 data_blob_free(&confounded_session_key); 915 return WERR_ BAD_PASSWORD;941 return WERR_INVALID_PASSWORD; 916 942 } 917 943 -
vendor/current/libcli/auth/spnego.h
r740 r988 46 46 SPNEGO_ACCEPT_INCOMPLETE = 1, 47 47 SPNEGO_REJECT = 2, 48 SPNEGO_NONE_RESULT = 3 48 SPNEGO_REQUEST_MIC = 3, 49 /* 50 * The max value is 0xff (255) on the wire 51 */ 52 SPNEGO_NONE_RESULT = 256 49 53 }; 50 54 51 55 struct spnego_negTokenInit { 52 const char * *mechTypes;56 const char * const *mechTypes; 53 57 DATA_BLOB reqFlags; 54 58 uint8_t reqFlagsPadding; … … 59 63 60 64 struct spnego_negTokenTarg { 61 uint8_t negResult;65 enum spnego_negResult negResult; 62 66 const char *supportedMech; 63 67 DATA_BLOB responseToken; -
vendor/current/libcli/auth/spnego_parse.c
r740 r988 30 30 ZERO_STRUCTP(token); 31 31 32 asn1_start_tag(asn1, ASN1_CONTEXT(0));33 asn1_start_tag(asn1, ASN1_SEQUENCE(0));34 35 while (!asn1 ->has_error&& 0 < asn1_tag_remaining(asn1)) {32 if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false; 33 if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false; 34 35 while (!asn1_has_error(asn1) && 0 < asn1_tag_remaining(asn1)) { 36 36 int i; 37 37 uint8_t context; 38 38 39 if (!asn1_peek_uint8(asn1, &context)) { 39 asn1 ->has_error = true;40 asn1_set_error(asn1); 40 41 break; 41 42 } … … 43 44 switch (context) { 44 45 /* Read mechTypes */ 45 case ASN1_CONTEXT(0): 46 asn1_start_tag(asn1, ASN1_CONTEXT(0)); 47 asn1_start_tag(asn1, ASN1_SEQUENCE(0)); 48 49 token->mechTypes = talloc(NULL, const char *); 50 for (i = 0; !asn1->has_error && 46 case ASN1_CONTEXT(0): { 47 const char **mechTypes; 48 49 if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false; 50 if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false; 51 52 mechTypes = talloc(mem_ctx, const char *); 53 if (mechTypes == NULL) { 54 asn1_set_error(asn1); 55 return false; 56 } 57 for (i = 0; !asn1_has_error(asn1) && 51 58 0 < asn1_tag_remaining(asn1); i++) { 52 59 char *oid; 53 token->mechTypes = talloc_realloc(NULL, 54 token->mechTypes, 55 const char *, i+2); 56 asn1_read_OID(asn1, token->mechTypes, &oid); 57 token->mechTypes[i] = oid; 58 } 59 token->mechTypes[i] = NULL; 60 const char **p; 61 p = talloc_realloc(mem_ctx, 62 mechTypes, 63 const char *, i+2); 64 if (p == NULL) { 65 talloc_free(mechTypes); 66 asn1_set_error(asn1); 67 return false; 68 } 69 mechTypes = p; 70 71 if (!asn1_read_OID(asn1, mechTypes, &oid)) return false; 72 mechTypes[i] = oid; 73 } 74 mechTypes[i] = NULL; 75 token->mechTypes = mechTypes; 60 76 61 77 asn1_end_tag(asn1); 62 78 asn1_end_tag(asn1); 63 79 break; 80 } 64 81 /* Read reqFlags */ 65 82 case ASN1_CONTEXT(1): 66 asn1_start_tag(asn1, ASN1_CONTEXT(1));67 asn1_read_BitString(asn1, mem_ctx, &token->reqFlags,68 &token->reqFlagsPadding) ;69 asn1_end_tag(asn1);83 if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false; 84 if (!asn1_read_BitString(asn1, mem_ctx, &token->reqFlags, 85 &token->reqFlagsPadding)) return false; 86 if (!asn1_end_tag(asn1)) return false; 70 87 break; 71 88 /* Read mechToken */ 72 89 case ASN1_CONTEXT(2): 73 asn1_start_tag(asn1, ASN1_CONTEXT(2));74 asn1_read_OctetString(asn1, mem_ctx, &token->mechToken);75 asn1_end_tag(asn1);90 if (!asn1_start_tag(asn1, ASN1_CONTEXT(2))) return false; 91 if (!asn1_read_OctetString(asn1, mem_ctx, &token->mechToken)) return false; 92 if (!asn1_end_tag(asn1)) return false; 76 93 break; 77 94 /* Read mecListMIC */ … … 79 96 { 80 97 uint8_t type_peek; 81 asn1_start_tag(asn1, ASN1_CONTEXT(3));98 if (!asn1_start_tag(asn1, ASN1_CONTEXT(3))) return false; 82 99 if (!asn1_peek_uint8(asn1, &type_peek)) { 83 asn1 ->has_error = true;100 asn1_set_error(asn1); 84 101 break; 85 102 } 86 103 if (type_peek == ASN1_OCTET_STRING) { 87 asn1_read_OctetString(asn1, mem_ctx,88 &token->mechListMIC) ;104 if (!asn1_read_OctetString(asn1, mem_ctx, 105 &token->mechListMIC)) return false; 89 106 } else { 90 107 /* RFC 2478 says we have an Octet String here, 91 108 but W2k sends something different... */ 92 109 char *mechListMIC; 93 asn1_start_tag(asn1, ASN1_SEQUENCE(0));94 asn1_start_tag(asn1, ASN1_CONTEXT(0));95 asn1_read_GeneralString(asn1, mem_ctx, &mechListMIC);96 asn1_end_tag(asn1);97 asn1_end_tag(asn1);110 if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false; 111 if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false; 112 if (!asn1_read_GeneralString(asn1, mem_ctx, &mechListMIC)) return false; 113 if (!asn1_end_tag(asn1)) return false; 114 if (!asn1_end_tag(asn1)) return false; 98 115 99 116 token->targetPrincipal = mechListMIC; 100 117 } 101 asn1_end_tag(asn1);118 if (!asn1_end_tag(asn1)) return false; 102 119 break; 103 120 } 104 121 default: 105 asn1 ->has_error = true;106 break; 107 } 108 } 109 110 asn1_end_tag(asn1);111 asn1_end_tag(asn1);112 113 return !asn1 ->has_error;122 asn1_set_error(asn1); 123 break; 124 } 125 } 126 127 if (!asn1_end_tag(asn1)) return false; 128 if (!asn1_end_tag(asn1)) return false; 129 130 return !asn1_has_error(asn1); 114 131 } 115 132 116 133 static bool write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token) 117 134 { 118 asn1_push_tag(asn1, ASN1_CONTEXT(0));119 asn1_push_tag(asn1, ASN1_SEQUENCE(0));135 if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false; 136 if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false; 120 137 121 138 /* Write mechTypes */ … … 123 140 int i; 124 141 125 asn1_push_tag(asn1, ASN1_CONTEXT(0));126 asn1_push_tag(asn1, ASN1_SEQUENCE(0));142 if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false; 143 if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false; 127 144 for (i = 0; token->mechTypes[i]; i++) { 128 asn1_write_OID(asn1, token->mechTypes[i]);129 } 130 asn1_pop_tag(asn1);131 asn1_pop_tag(asn1);145 if (!asn1_write_OID(asn1, token->mechTypes[i])) return false; 146 } 147 if (!asn1_pop_tag(asn1)) return false; 148 if (!asn1_pop_tag(asn1)) return false; 132 149 } 133 150 134 151 /* write reqFlags */ 135 152 if (token->reqFlags.length > 0) { 136 asn1_push_tag(asn1, ASN1_CONTEXT(1));137 asn1_write_BitString(asn1, token->reqFlags.data,153 if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false; 154 if (!asn1_write_BitString(asn1, token->reqFlags.data, 138 155 token->reqFlags.length, 139 token->reqFlagsPadding) ;140 asn1_pop_tag(asn1);156 token->reqFlagsPadding)) return false; 157 if (!asn1_pop_tag(asn1)) return false; 141 158 } 142 159 143 160 /* write mechToken */ 144 161 if (token->mechToken.data) { 145 asn1_push_tag(asn1, ASN1_CONTEXT(2));146 asn1_write_OctetString(asn1, token->mechToken.data,147 token->mechToken.length) ;148 asn1_pop_tag(asn1);162 if (!asn1_push_tag(asn1, ASN1_CONTEXT(2))) return false; 163 if (!asn1_write_OctetString(asn1, token->mechToken.data, 164 token->mechToken.length)) return false; 165 if (!asn1_pop_tag(asn1)) return false; 149 166 } 150 167 151 168 /* write mechListMIC */ 152 169 if (token->mechListMIC.data) { 153 asn1_push_tag(asn1, ASN1_CONTEXT(3));170 if (!asn1_push_tag(asn1, ASN1_CONTEXT(3))) return false; 154 171 #if 0 155 172 /* This is what RFC 2478 says ... */ … … 159 176 /* ... but unfortunately this is what Windows 160 177 sends/expects */ 161 asn1_push_tag(asn1, ASN1_SEQUENCE(0));162 asn1_push_tag(asn1, ASN1_CONTEXT(0));163 asn1_push_tag(asn1, ASN1_GENERAL_STRING);164 asn1_write(asn1, token->mechListMIC.data,165 token->mechListMIC.length) ;166 asn1_pop_tag(asn1);167 asn1_pop_tag(asn1);168 asn1_pop_tag(asn1);178 if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false; 179 if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false; 180 if (!asn1_push_tag(asn1, ASN1_GENERAL_STRING)) return false; 181 if (!asn1_write(asn1, token->mechListMIC.data, 182 token->mechListMIC.length)) return false; 183 if (!asn1_pop_tag(asn1)) return false; 184 if (!asn1_pop_tag(asn1)) return false; 185 if (!asn1_pop_tag(asn1)) return false; 169 186 #endif 170 asn1_pop_tag(asn1);171 } 172 173 asn1_pop_tag(asn1);174 asn1_pop_tag(asn1);175 176 return !asn1 ->has_error;187 if (!asn1_pop_tag(asn1)) return false; 188 } 189 190 if (!asn1_pop_tag(asn1)) return false; 191 if (!asn1_pop_tag(asn1)) return false; 192 193 return !asn1_has_error(asn1); 177 194 } 178 195 … … 182 199 ZERO_STRUCTP(token); 183 200 184 asn1_start_tag(asn1, ASN1_CONTEXT(1));185 asn1_start_tag(asn1, ASN1_SEQUENCE(0));186 187 while (!asn1 ->has_error&& 0 < asn1_tag_remaining(asn1)) {201 if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false; 202 if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false; 203 204 while (!asn1_has_error(asn1) && 0 < asn1_tag_remaining(asn1)) { 188 205 uint8_t context; 206 uint8_t neg_result; 189 207 char *oid; 208 190 209 if (!asn1_peek_uint8(asn1, &context)) { 191 asn1 ->has_error = true;210 asn1_set_error(asn1); 192 211 break; 193 212 } … … 195 214 switch (context) { 196 215 case ASN1_CONTEXT(0): 197 asn1_start_tag(asn1, ASN1_CONTEXT(0)); 198 asn1_start_tag(asn1, ASN1_ENUMERATED); 199 asn1_read_uint8(asn1, &token->negResult); 200 asn1_end_tag(asn1); 201 asn1_end_tag(asn1); 216 if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false; 217 if (!asn1_start_tag(asn1, ASN1_ENUMERATED)) return false; 218 if (!asn1_read_uint8(asn1, &neg_result)) return false; 219 token->negResult = neg_result; 220 if (!asn1_end_tag(asn1)) return false; 221 if (!asn1_end_tag(asn1)) return false; 202 222 break; 203 223 case ASN1_CONTEXT(1): 204 asn1_start_tag(asn1, ASN1_CONTEXT(1));205 asn1_read_OID(asn1, mem_ctx, &oid);224 if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false; 225 if (!asn1_read_OID(asn1, mem_ctx, &oid)) return false; 206 226 token->supportedMech = oid; 207 asn1_end_tag(asn1);227 if (!asn1_end_tag(asn1)) return false; 208 228 break; 209 229 case ASN1_CONTEXT(2): 210 asn1_start_tag(asn1, ASN1_CONTEXT(2));211 asn1_read_OctetString(asn1, mem_ctx, &token->responseToken);212 asn1_end_tag(asn1);230 if (!asn1_start_tag(asn1, ASN1_CONTEXT(2))) return false; 231 if (!asn1_read_OctetString(asn1, mem_ctx, &token->responseToken)) return false; 232 if (!asn1_end_tag(asn1)) return false; 213 233 break; 214 234 case ASN1_CONTEXT(3): 215 asn1_start_tag(asn1, ASN1_CONTEXT(3));216 asn1_read_OctetString(asn1, mem_ctx, &token->mechListMIC);217 asn1_end_tag(asn1);235 if (!asn1_start_tag(asn1, ASN1_CONTEXT(3))) return false; 236 if (!asn1_read_OctetString(asn1, mem_ctx, &token->mechListMIC)) return false; 237 if (!asn1_end_tag(asn1)) return false; 218 238 break; 219 239 default: 220 asn1 ->has_error = true;221 break; 222 } 223 } 224 225 asn1_end_tag(asn1);226 asn1_end_tag(asn1);227 228 return !asn1 ->has_error;240 asn1_set_error(asn1); 241 break; 242 } 243 } 244 245 if (!asn1_end_tag(asn1)) return false; 246 if (!asn1_end_tag(asn1)) return false; 247 248 return !asn1_has_error(asn1); 229 249 } 230 250 231 251 static bool write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token) 232 252 { 233 asn1_push_tag(asn1, ASN1_CONTEXT(1));234 asn1_push_tag(asn1, ASN1_SEQUENCE(0));253 if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false; 254 if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false; 235 255 236 256 if (token->negResult != SPNEGO_NONE_RESULT) { 237 asn1_push_tag(asn1, ASN1_CONTEXT(0));238 asn1_write_enumerated(asn1, token->negResult);239 asn1_pop_tag(asn1);257 if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false; 258 if (!asn1_write_enumerated(asn1, token->negResult)) return false; 259 if (!asn1_pop_tag(asn1)) return false; 240 260 } 241 261 242 262 if (token->supportedMech) { 243 asn1_push_tag(asn1, ASN1_CONTEXT(1));244 asn1_write_OID(asn1, token->supportedMech);245 asn1_pop_tag(asn1);263 if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false; 264 if (!asn1_write_OID(asn1, token->supportedMech)) return false; 265 if (!asn1_pop_tag(asn1)) return false; 246 266 } 247 267 248 268 if (token->responseToken.data) { 249 asn1_push_tag(asn1, ASN1_CONTEXT(2));250 asn1_write_OctetString(asn1, token->responseToken.data,251 token->responseToken.length) ;252 asn1_pop_tag(asn1);269 if (!asn1_push_tag(asn1, ASN1_CONTEXT(2))) return false; 270 if (!asn1_write_OctetString(asn1, token->responseToken.data, 271 token->responseToken.length)) return false; 272 if (!asn1_pop_tag(asn1)) return false; 253 273 } 254 274 255 275 if (token->mechListMIC.data) { 256 asn1_push_tag(asn1, ASN1_CONTEXT(3));257 asn1_write_OctetString(asn1, token->mechListMIC.data,258 token->mechListMIC.length) ;259 asn1_pop_tag(asn1);260 } 261 262 asn1_pop_tag(asn1);263 asn1_pop_tag(asn1);264 265 return !asn1 ->has_error;276 if (!asn1_push_tag(asn1, ASN1_CONTEXT(3))) return false; 277 if (!asn1_write_OctetString(asn1, token->mechListMIC.data, 278 token->mechListMIC.length)) return false; 279 if (!asn1_pop_tag(asn1)) return false; 280 } 281 282 if (!asn1_pop_tag(asn1)) return false; 283 if (!asn1_pop_tag(asn1)) return false; 284 285 return !asn1_has_error(asn1); 266 286 } 267 287 … … 283 303 } 284 304 285 asn1_load(asn1, data);305 if (!asn1_load(asn1, data)) goto err; 286 306 287 307 if (!asn1_peek_uint8(asn1, &context)) { 288 asn1 ->has_error = true;308 asn1_set_error(asn1); 289 309 } else { 290 310 switch (context) { 291 311 case ASN1_APPLICATION(0): 292 asn1_start_tag(asn1, ASN1_APPLICATION(0));293 asn1_check_OID(asn1, OID_SPNEGO);312 if (!asn1_start_tag(asn1, ASN1_APPLICATION(0))) goto err; 313 if (!asn1_check_OID(asn1, OID_SPNEGO)) goto err; 294 314 if (read_negTokenInit(asn1, mem_ctx, &token->negTokenInit)) { 295 315 token->type = SPNEGO_NEG_TOKEN_INIT; 296 316 } 297 asn1_end_tag(asn1);317 if (!asn1_end_tag(asn1)) goto err; 298 318 break; 299 319 case ASN1_CONTEXT(1): … … 303 323 break; 304 324 default: 305 asn1->has_error = true; 306 break; 307 } 308 } 309 310 if (!asn1->has_error) ret = asn1->ofs; 325 asn1_set_error(asn1); 326 break; 327 } 328 } 329 330 if (!asn1_has_error(asn1)) { 331 ret = asn1_current_ofs(asn1); 332 } 333 334 err: 335 311 336 asn1_free(asn1); 312 337 … … 325 350 switch (spnego->type) { 326 351 case SPNEGO_NEG_TOKEN_INIT: 327 asn1_push_tag(asn1, ASN1_APPLICATION(0));328 asn1_write_OID(asn1, OID_SPNEGO);329 write_negTokenInit(asn1, &spnego->negTokenInit);330 asn1_pop_tag(asn1);352 if (!asn1_push_tag(asn1, ASN1_APPLICATION(0))) goto err; 353 if (!asn1_write_OID(asn1, OID_SPNEGO)) goto err; 354 if (!write_negTokenInit(asn1, &spnego->negTokenInit)) goto err; 355 if (!asn1_pop_tag(asn1)) goto err; 331 356 break; 332 357 case SPNEGO_NEG_TOKEN_TARG: … … 334 359 break; 335 360 default: 336 asn1->has_error = true; 337 break; 338 } 339 340 if (!asn1->has_error) { 341 *blob = data_blob_talloc(mem_ctx, asn1->data, asn1->length); 342 ret = asn1->ofs; 343 } 361 asn1_set_error(asn1); 362 break; 363 } 364 365 if (!asn1_extract_blob(asn1, mem_ctx, blob)) { 366 goto err; 367 } 368 369 ret = asn1_current_ofs(asn1); 370 371 err: 372 344 373 asn1_free(asn1); 345 374 … … 356 385 case SPNEGO_NEG_TOKEN_INIT: 357 386 if (spnego->negTokenInit.mechTypes) { 358 talloc_free( spnego->negTokenInit.mechTypes);387 talloc_free(discard_const(spnego->negTokenInit.mechTypes)); 359 388 } 360 389 data_blob_free(&spnego->negTokenInit.reqFlags); … … 380 409 381 410 bool spnego_write_mech_types(TALLOC_CTX *mem_ctx, 382 const char * *mech_types,411 const char * const *mech_types, 383 412 DATA_BLOB *blob) 384 413 { 414 bool ret = false; 385 415 struct asn1_data *asn1 = asn1_init(mem_ctx); 386 416 … … 393 423 int i; 394 424 395 asn1_push_tag(asn1, ASN1_SEQUENCE(0));425 if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) goto err; 396 426 for (i = 0; mech_types[i]; i++) { 397 asn1_write_OID(asn1, mech_types[i]); 398 } 399 asn1_pop_tag(asn1); 400 } 401 402 if (asn1->has_error) { 403 asn1_free(asn1); 404 return false; 405 } 406 407 *blob = data_blob_talloc(mem_ctx, asn1->data, asn1->length); 408 if (blob->length != asn1->length) { 409 asn1_free(asn1); 410 return false; 411 } 427 if (!asn1_write_OID(asn1, mech_types[i])) goto err; 428 } 429 if (!asn1_pop_tag(asn1)) goto err; 430 } 431 432 if (asn1_has_error(asn1)) { 433 goto err; 434 } 435 436 if (!asn1_extract_blob(asn1, mem_ctx, blob)) { 437 goto err; 438 } 439 440 ret = true; 441 442 err: 412 443 413 444 asn1_free(asn1); 414 445 415 return true;416 } 446 return ret; 447 } -
vendor/current/libcli/auth/spnego_proto.h
r414 r988 25 25 bool spnego_free_data(struct spnego_data *spnego); 26 26 bool spnego_write_mech_types(TALLOC_CTX *mem_ctx, 27 const char * *mech_types,27 const char * const *mech_types, 28 28 DATA_BLOB *blob); -
vendor/current/libcli/auth/wscript_build
r919 r988 3 3 bld.SAMBA_LIBRARY('cliauth', 4 4 source='', 5 deps=' NTLMSSP_COMMON MSRPC_PARSE LIBCLI_AUTH COMMON_SCHANNEL PAM_ERRORS SPNEGO_PARSE',5 deps='MSRPC_PARSE LIBCLI_AUTH COMMON_SCHANNEL PAM_ERRORS SPNEGO_PARSE krb5samba samba-errors NTLM_CHECK UTIL_LSARPC', 6 6 private_library=True, 7 7 grouping_library=True) 8 9 bld.SAMBA_SUBSYSTEM('NTLMSSP_COMMON',10 source='ntlmssp.c ntlmssp_ndr.c ntlmssp_server.c ntlmssp_sign.c ntlm_check.c',11 deps='samba-util NDR_NTLMSSP MSRPC_PARSE')12 13 8 14 9 bld.SAMBA_SUBSYSTEM('MSRPC_PARSE', … … 17 12 ) 18 13 14 bld.SAMBA_SUBSYSTEM('NTLM_CHECK', 15 source='ntlm_check.c', 16 deps = 'talloc' 17 ) 19 18 20 19 bld.SAMBA_SUBSYSTEM('LIBCLI_AUTH', 21 20 source='credentials.c session.c smbencrypt.c smbdes.c', 22 public_deps='MSRPC_PARSE NDR_NTLMSSP',21 public_deps='MSRPC_PARSE', 23 22 public_headers='credentials.h:domain_credentials.h' 24 23 ) … … 26 25 27 26 bld.SAMBA_SUBSYSTEM('COMMON_SCHANNEL', 28 source='schannel_state_tdb.c schannel_sign.c',29 deps=' tdb-wrap UTIL_TDB'27 source='schannel_state_tdb.c', 28 deps='dbwrap util_tdb samba-hostconfig NDR_NETLOGON' 30 29 ) 31 30 31 bld.SAMBA_SUBSYSTEM('NETLOGON_CREDS_CLI', 32 source='netlogon_creds_cli.c', 33 deps='dbwrap util_tdb tevent-util samba-hostconfig RPC_NDR_NETLOGON NDR_NETLOGON' 34 ) 32 35 33 36 bld.SAMBA_SUBSYSTEM('PAM_ERRORS', … … 38 41 bld.SAMBA_SUBSYSTEM('SPNEGO_PARSE', 39 42 source='spnego_parse.c', 40 deps=' ASN1_UTIL')43 deps='asn1util')
Note:
See TracChangeset
for help on using the changeset viewer.