Print this page
NEX-16824 SMB client connection setup rework
NEX-17232 SMB client reconnect failures
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
and: (improve debug)
SUP-513 Unable to join AD domain (fix lint)
SUP-513 Unable to join AD domain (with NtlmMinSeverSec set in the registry)
 Implement "Extended Session Security" and "Key Exchange" in NTLMSSP
re #12739 rb4271 AD join with lmauth_level=2 fails
re #12394 rb3934 Even NULL sessions should use SPNEGO

Split Close
Expand all
Collapse all
          --- old/usr/src/lib/libsmbfs/smb/ntlmssp.c
          +++ new/usr/src/lib/libsmbfs/smb/ntlmssp.c
↓ open down ↓ 13 lines elided ↑ open up ↑
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  24      - * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
       24 + * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  25   25   */
  26   26  
  27   27  /*
  28   28   * NT Lan Manager Security Support Provider (NTLMSSP)
  29   29   *
  30   30   * Based on information from the "Davenport NTLM" page:
  31   31   * http://davenport.sourceforge.net/ntlm.html
  32   32   */
  33   33  
  34   34  
↓ open down ↓ 25 lines elided ↑ open up ↑
  60   60  #include "private.h"
  61   61  #include "charsets.h"
  62   62  #include "smb_crypt.h"
  63   63  #include "spnego.h"
  64   64  #include "derparse.h"
  65   65  #include "ssp.h"
  66   66  #include "ntlm.h"
  67   67  #include "ntlmssp.h"
  68   68  
  69   69  /* A shorter alias for a crazy long name from [MS-NLMP] */
  70      -#define NTLMSSP_NEGOTIATE_NTLM2 \
       70 +#define NTLMSSP_NEGOTIATE_ESS \
  71   71          NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
  72   72  
  73   73  typedef struct ntlmssp_state {
  74   74          uint32_t ss_flags;
  75   75          char *ss_target_name;   /* Primary domain or server name */
  76   76          struct mbuf *ss_target_info;
       77 +        uchar_t ss_ssnkey[NTLM_HASH_SZ];
  77   78          uchar_t ss_kxkey[NTLM_HASH_SZ];
  78   79  } ntlmssp_state_t;
  79   80  
  80   81  /*
  81   82   * So called "security buffer".
  82   83   * A lot like an RPC string.
  83   84   */
  84   85  struct sec_buf {
  85   86          uint16_t sb_length;
  86   87          uint16_t sb_maxlen;
  87   88          uint32_t sb_offset;
  88   89  };
  89   90  #define ID_SZ 8
  90   91  static const char ntlmssp_id[ID_SZ] = "NTLMSSP";
  91   92  
  92   93  static int
  93      -ntlm_rand_ssn_key(struct smb_ctx *ctx,
  94      -        ntlmssp_state_t *ssp_st, struct mbdata *ek_mbp);
       94 +ntlm_rand_ssn_key(ntlmssp_state_t *ssp_st, struct mbdata *ek_mbp);
  95   95  
  96   96  /*
  97   97   * Get a "security buffer" (header part)
  98   98   */
  99   99  static int
 100  100  md_get_sb_hdr(struct mbdata *mbp, struct sec_buf *sb)
 101  101  {
 102  102          int err;
 103  103  
 104  104          (void) md_get_uint16le(mbp, &sb->sb_length);
↓ open down ↓ 137 lines elided ↑ open up ↑
 242  242           * See: [MS-NLMP 2.2.2.5 NEGOTIATE]
 243  243           */
 244  244          ssp_st->ss_flags =
 245  245              NTLMSSP_NEGOTIATE_UNICODE |
 246  246              NTLMSSP_NEGOTIATE_OEM |
 247  247              NTLMSSP_REQUEST_TARGET |
 248  248              NTLMSSP_NEGOTIATE_SIGN |
 249  249              NTLMSSP_NEGOTIATE_SEAL |
 250  250              /* NTLMSSP_NEGOTIATE_LM_KEY (never) */
 251  251              NTLMSSP_NEGOTIATE_NTLM |
 252      -            /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN (set below) */
 253      -            NTLMSSP_NEGOTIATE_NTLM2 |
      252 +            NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
      253 +            NTLMSSP_NEGOTIATE_ESS |
 254  254              NTLMSSP_NEGOTIATE_128 |
 255  255              NTLMSSP_NEGOTIATE_KEY_EXCH |
 256  256              NTLMSSP_NEGOTIATE_56;
 257  257  
 258      -        if (ctx->ct_vcflags & SMBV_WILL_SIGN) {
 259      -                ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
 260      -                ctx->ct_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE;
 261      -        }
      258 +        if ((ctx->ct_vopt & SMBVOPT_SIGNING_ENABLED) == 0)
      259 +                ssp_st->ss_flags &= ~NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
 262  260  
 263  261          bcopy(ntlmssp_id, &hdr.h_id, ID_SZ);
 264  262          hdr.h_type = NTLMSSP_MSGTYPE_NEGOTIATE;
 265  263          hdr.h_flags = ssp_st->ss_flags;
 266  264  
 267  265          /*
 268  266           * We could put the client domain, client name strings
 269  267           * here, (always in OEM format, upper-case), and set
 270  268           * NTLMSSP_NEGOTIATE_OEM_..._SUPPLIED, but Windows
 271  269           * leaves these NULL so let's do the same.
↓ open down ↓ 168 lines elided ↑ open up ↑
 440  438          hdr.h_flags = ssp_st->ss_flags;
 441  439  
 442  440          /*
 443  441           * Put the NTLMv2/LMv2 or NTLM/LM (v1) responses,
 444  442           * and compute the session key, etc.
 445  443           */
 446  444          if (ctx->ct_authflags & SMB_AT_ANON) {
 447  445                  /*
 448  446                   * We're setting up a NULL session, meaning
 449  447                   * the lm_mbc, nt_mbc parts remain empty.
 450      -                 * Let's add the "anon" flag (hint).
 451      -                 * As there is no session key, disable the
 452      -                 * fancy session key stuff.
      448 +                 * Let's add the "anon" flag (hint), and
      449 +                 * as we have no OWF hashes, we can't use
      450 +                 * "extended session security" (_ESS).
      451 +                 * The SessionBaseKey is all zeros, so
      452 +                 * the KeyExchangeKey is too.  Otherwise
      453 +                 * this is like NTLMv2/LMv2
 453  454                   */
 454      -                hdr.h_flags |= NTLMSSP_NEGOTIATE_NULL_SESSION;
 455      -                ssp_st->ss_flags &= ~(
 456      -                    NTLMSSP_NEGOTIATE_NTLM2 |
 457      -                    NTLMSSP_NEGOTIATE_KEY_EXCH);
      455 +                ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_NULL_SESSION;
      456 +                ssp_st->ss_flags &= ~NTLMSSP_NEGOTIATE_ESS;
      457 +                hdr.h_flags = ssp_st->ss_flags;
 458  458                  err = 0;
      459 +                /* KeyExchangeKey = SessionBaseKey = (zeros) */
      460 +                memset(ssp_st->ss_ssnkey, 0, NTLM_HASH_SZ);
      461 +                memset(ssp_st->ss_kxkey, 0, NTLM_HASH_SZ);
 459  462          } else if (ctx->ct_authflags & SMB_AT_NTLM2) {
 460  463                  /*
 461  464                   * Doing NTLMv2/LMv2
 462  465                   */
 463  466                  err = ntlm_build_target_info(ctx,
 464  467                      ssp_st->ss_target_info, &ti_mbc);
 465  468                  if (err)
 466  469                          goto out;
 467  470                  err = ntlm_put_v2_responses(ctx, &ti_mbc,
 468      -                    &lm_mbc, &nt_mbc);
      471 +                    &lm_mbc, &nt_mbc, ssp_st->ss_ssnkey);
 469  472                  if (err)
 470  473                          goto out;
 471      -                /* The "key exg. key" is the session base key */
 472      -                memcpy(ssp_st->ss_kxkey, ctx->ct_ssn_key, NTLM_HASH_SZ);
 473      -
 474      -        } else if (ssp_st->ss_flags & NTLMSSP_NEGOTIATE_NTLM2) {
      474 +                /* KeyExchangeKey = SessionBaseKey (v2) */
      475 +                memcpy(ssp_st->ss_kxkey, ssp_st->ss_ssnkey, NTLM_HASH_SZ);
      476 +        } else if (ssp_st->ss_flags & NTLMSSP_NEGOTIATE_ESS) {
 475  477                  /*
 476  478                   * Doing NTLM ("v1x") which is NTLM with
 477  479                   * "Extended Session Security"
 478  480                   */
 479  481                  err = ntlm_put_v1x_responses(ctx,
 480      -                    &lm_mbc, &nt_mbc);
      482 +                    &lm_mbc, &nt_mbc, ssp_st->ss_ssnkey);
 481  483                  if (err)
 482  484                          goto out;
 483      -                /* Compute the "Key exchange key". */
 484      -                ntlm2_kxkey(ctx, &lm_mbc, ssp_st->ss_kxkey);
      485 +                /*
      486 +                 * "v1x computes the KeyExchangeKey from both the
      487 +                 * server and client nonce and (v1) SessionBaseKey.
      488 +                 */
      489 +                ntlm2_kxkey(ctx, &lm_mbc, ssp_st->ss_ssnkey,
      490 +                    ssp_st->ss_kxkey);
 485  491          } else {
 486  492                  /*
 487  493                   * Doing plain old NTLM (and LM if enabled)
 488  494                   */
 489  495                  err = ntlm_put_v1_responses(ctx,
 490      -                    &lm_mbc, &nt_mbc);
      496 +                    &lm_mbc, &nt_mbc, ssp_st->ss_ssnkey);
 491  497                  if (err)
 492  498                          goto out;
 493      -                /* The "key exg. key" is the session base key */
 494      -                memcpy(ssp_st->ss_kxkey, ctx->ct_ssn_key, NTLM_HASH_SZ);
      499 +                /* KeyExchangeKey = SessionBaseKey (v1) */
      500 +                memcpy(ssp_st->ss_kxkey, ssp_st->ss_ssnkey, NTLM_HASH_SZ);
 495  501          }
 496  502  
 497  503          /*
 498      -         * Compute the "Exported Session Key" and (possibly)
 499      -         * the "Encrypted Random Sesion Key".
 500      -         * [MS-NLMP 3.1.5.1.2]
      504 +         * Compute the "ExportedSessionKey" and (possibly) the
      505 +         * "EncryptedRandomSesionKey". [MS-NLMP 3.1.5.1.2]
 501  506           */
 502  507          if (ssp_st->ss_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
 503      -                err = ntlm_rand_ssn_key(ctx, ssp_st, &ek_mbc);
      508 +                err = ntlm_rand_ssn_key(ssp_st, &ek_mbc);
 504  509                  if (err)
 505  510                          goto out;
 506  511          } else {
 507  512                  /* ExportedSessionKey is the KeyExchangeKey */
 508      -                memcpy(ctx->ct_ssn_key, ssp_st->ss_kxkey, NTLM_HASH_SZ);
      513 +                memcpy(ssp_st->ss_ssnkey, ssp_st->ss_kxkey, NTLM_HASH_SZ);
 509  514                  /* EncryptedRandomSessionKey remains NULL */
 510  515          }
 511  516  
 512  517          err = mb_put_sb_data(&mb2, &hdr.h_lm_resp, lm_mbc.mb_top);
 513  518          lm_mbc.mb_top = NULL; /* consumed */
 514  519          if (err)
 515  520                  goto out;
 516  521          err = mb_put_sb_data(&mb2, &hdr.h_nt_resp, nt_mbc.mb_top);
 517  522          nt_mbc.mb_top = NULL; /* consumed */
 518  523          if (err)
↓ open down ↓ 64 lines elided ↑ open up ↑
 583  588  /*
 584  589   * Helper for ntlmssp_put_type3 when doing key exchange.
 585  590   *
 586  591   * "ExportedSessionKey" is what we give to the "application"
 587  592   * layer, which in here means the MAC key for SMB signing.
 588  593   * With "key exchange", we replace the ExportedSessionKey
 589  594   * with random data and send that (encrypted) to the peer.
 590  595   */
 591  596  static int
 592  597  ntlm_rand_ssn_key(
 593      -        struct smb_ctx *ctx,
 594  598          ntlmssp_state_t *ssp_st,
 595  599          struct mbdata *ek_mbp)
 596  600  {
 597  601  
 598  602          uchar_t *encr_ssn_key;
 599  603          int err;
 600  604  
 601  605          if ((err = mb_init(ek_mbp)) != 0)
 602  606                  return (err);
 603  607          encr_ssn_key = mb_reserve(ek_mbp, NTLM_HASH_SZ);
 604  608  
 605  609          /* Set "ExportedSessionKey to NONCE(16) */
 606      -        (void) smb_get_urandom(ctx->ct_ssn_key, NTLM_HASH_SZ);
      610 +        (void) smb_get_urandom(ssp_st->ss_ssnkey, NTLM_HASH_SZ);
 607  611  
 608  612          /* Set "EncryptedRandomSessionKey" to RC4(...) */
 609  613          err = smb_encrypt_RC4(encr_ssn_key, NTLM_HASH_SZ,
 610  614              ssp_st->ss_kxkey, NTLM_HASH_SZ,
 611      -            ctx->ct_ssn_key, NTLM_HASH_SZ);
      615 +            ssp_st->ss_ssnkey, NTLM_HASH_SZ);
 612  616  
 613  617          return (err);
 614  618  }
 615  619  
 616  620  /*
 617  621   * ntlmssp_final
 618  622   *
 619  623   * Called after successful authentication.
 620      - * Setup the MAC key for signing.
      624 + * Save the session key.
 621  625   */
 622  626  int
 623  627  ntlmssp_final(struct ssp_ctx *sp)
 624  628  {
 625  629          struct smb_ctx *ctx = sp->smb_ctx;
      630 +        ntlmssp_state_t *ssp_st = sp->sp_private;
 626  631          int err = 0;
 627  632  
 628  633          /*
 629      -         * MAC_key is just the session key, but
 630      -         * Only on the first successful auth.
      634 +         * Update/save the session key.
 631  635           */
 632      -        if ((ctx->ct_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) &&
 633      -            (ctx->ct_mackey == NULL)) {
 634      -                ctx->ct_mackeylen = NTLM_HASH_SZ;
 635      -                ctx->ct_mackey = malloc(ctx->ct_mackeylen);
 636      -                if (ctx->ct_mackey == NULL) {
 637      -                        ctx->ct_mackeylen = 0;
 638      -                        err = ENOMEM;
 639      -                        goto out;
 640      -                }
 641      -                memcpy(ctx->ct_mackey, ctx->ct_ssn_key, NTLM_HASH_SZ);
 642      -                /*
 643      -                 * Apparently, the server used seq. no. zero
 644      -                 * for our previous message, so next is two.
 645      -                 */
 646      -                ctx->ct_mac_seqno = 2;
      636 +        if (ctx->ct_ssnkey_buf != NULL) {
      637 +                free(ctx->ct_ssnkey_buf);
      638 +                ctx->ct_ssnkey_buf = NULL;
 647  639          }
      640 +        ctx->ct_ssnkey_buf = malloc(NTLM_HASH_SZ);
      641 +        if (ctx->ct_ssnkey_buf == NULL) {
      642 +                err = ENOMEM;
      643 +                goto out;
      644 +        }
      645 +        ctx->ct_ssnkey_len = NTLM_HASH_SZ;
      646 +        memcpy(ctx->ct_ssnkey_buf, ssp_st->ss_ssnkey, NTLM_HASH_SZ);
 648  647  
 649  648  out:
 650  649          return (err);
 651  650  }
 652  651  
 653  652  /*
 654  653   * ntlmssp_next_token
 655  654   *
 656  655   * See ssp.c: ssp_ctx_next_token
 657  656   */
↓ open down ↓ 63 lines elided ↑ open up ↑
 721  720  
 722  721  /*
 723  722   * ntlmssp_init_clnt
 724  723   *
 725  724   * Initialize a new NTLMSSP client context.
 726  725   */
 727  726  int
 728  727  ntlmssp_init_client(struct ssp_ctx *sp)
 729  728  {
 730  729          ntlmssp_state_t *ssp_st;
      730 +        smb_ctx_t *ctx = sp->smb_ctx;
 731  731  
 732      -        if ((sp->smb_ctx->ct_authflags &
      732 +        if ((ctx->ct_authflags &
 733  733              (SMB_AT_NTLM2 | SMB_AT_NTLM1 | SMB_AT_ANON)) == 0) {
 734  734                  DPRINT("No NTLM authflags");
 735  735                  return (EINVAL);
 736  736          }
 737  737  
      738 +        /* Get the client nonce. */
      739 +        (void) smb_get_urandom(ctx->ct_clnonce, NTLM_CHAL_SZ);
      740 +
 738  741          ssp_st = calloc(1, sizeof (*ssp_st));
 739  742          if (ssp_st == NULL)
 740  743                  return (ENOMEM);
 741  744  
 742  745          sp->sp_nexttok = ntlmssp_next_token;
 743  746          sp->sp_destroy = ntlmssp_destroy;
 744  747          sp->sp_private = ssp_st;
 745  748  
 746  749          return (0);
 747  750  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX