1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  14  */
  15 
  16 /*
  17  * SPNEGO back-end for NTLMSSP.  See [MS-NLMP]
  18  */
  19 
  20 #include <sys/types.h>
  21 #include <sys/byteorder.h>
  22 #include <strings.h>
  23 #include "smbd.h"
  24 #include "smbd_authsvc.h"
  25 #include "netsmb/ntlmssp.h"
  26 #include <assert.h>
  27 
  28 /* A shorter alias for a crazy long name from [MS-NLMP] */
  29 #define NTLMSSP_NEGOTIATE_NTLM2 \
  30         NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
  31 
  32 /* Need this in a header somewhere */
  33 #ifdef _LITTLE_ENDIAN
  34 /* little-endian values on little-endian */
  35 #define htolel(x)       ((uint32_t)(x))
  36 #define letohl(x)       ((uint32_t)(x))
  37 #else   /* (BYTE_ORDER == LITTLE_ENDIAN) */
  38 /* little-endian values on big-endian (swap) */
  39 #define letohl(x)       BSWAP_32(x)
  40 #define htolel(x)       BSWAP_32(x)
  41 #endif  /* (BYTE_ORDER == LITTLE_ENDIAN) */
  42 
  43 typedef struct ntlmssp_backend {
  44         uint32_t expect_type;
  45         uint32_t clnt_flags;
  46         uint32_t srv_flags;
  47         char srv_challenge[8];
  48 } ntlmssp_backend_t;
  49 
  50 struct genhdr {
  51         char h_id[8];   /* "NTLMSSP" */
  52         uint32_t h_type;
  53 };
  54 
  55 struct sec_buf {
  56         uint16_t sb_length;
  57         uint16_t sb_maxlen;
  58         uint32_t sb_offset;
  59 };
  60 
  61 struct nego_hdr {
  62         char h_id[8];
  63         uint32_t h_type;
  64         uint32_t h_flags;
  65         /* workstation domain, name (place holders) */
  66         uint16_t ws_dom[4];
  67         uint16_t ws_name[4];
  68 };
  69 
  70 struct auth_hdr {
  71         char h_id[8];
  72         uint32_t h_type;
  73         struct sec_buf h_lm_resp;
  74         struct sec_buf h_nt_resp;
  75         struct sec_buf h_domain;
  76         struct sec_buf h_user;
  77         struct sec_buf h_wksta;
  78         struct sec_buf h_essn_key; /* encrypted session key */
  79         uint32_t h_flags;
  80         /* Version struct (optional) */
  81         /* MIC hash (optional) */
  82 };
  83 
  84 /* Allow turning these off for debugging, etc. */
  85 int smbd_signing_enabled = 1;
  86 
  87 int smbd_constant_challenge = 0;
  88 static uint8_t constant_chal[8] = {
  89     0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 };
  90 
  91 static int smbd_ntlmssp_negotiate(authsvc_context_t *);
  92 static int smbd_ntlmssp_authenticate(authsvc_context_t *);
  93 static int encode_avpair_str(smb_msgbuf_t *, uint16_t, char *);
  94 static int decode_secbuf_bin(smb_msgbuf_t *, struct sec_buf *, void **);
  95 static int decode_secbuf_str(smb_msgbuf_t *, struct sec_buf *, char **);
  96 
  97 /*
  98  * Initialize this context for NTLMSSP, if possible.
  99  */
 100 int
 101 smbd_ntlmssp_init(authsvc_context_t *ctx)
 102 {
 103         ntlmssp_backend_t *be;
 104 
 105         be = malloc(sizeof (*be));
 106         if (be == 0)
 107                 return (NT_STATUS_NO_MEMORY);
 108         bzero(be, sizeof (*be));
 109         be->expect_type = NTLMSSP_MSGTYPE_NEGOTIATE;
 110         ctx->ctx_backend = be;
 111 
 112         return (0);
 113 }
 114 
 115 void
 116 smbd_ntlmssp_fini(authsvc_context_t *ctx)
 117 {
 118         free(ctx->ctx_backend);
 119 }
 120 
 121 /*
 122  * Handle an auth message
 123  */
 124 int
 125 smbd_ntlmssp_work(authsvc_context_t *ctx)
 126 {
 127         struct genhdr *ihdr = ctx->ctx_ibodybuf;
 128         ntlmssp_backend_t *be = ctx->ctx_backend;
 129         uint32_t mtype;
 130         int rc;
 131 
 132         if (ctx->ctx_ibodylen < sizeof (*ihdr))
 133                 return (NT_STATUS_INVALID_PARAMETER);
 134 
 135         if (bcmp(ihdr->h_id, "NTLMSSP", 8))
 136                 return (NT_STATUS_INVALID_PARAMETER);
 137         mtype = letohl(ihdr->h_type);
 138         if (mtype != be->expect_type)
 139                 return (NT_STATUS_INVALID_PARAMETER);
 140 
 141         switch (mtype) {
 142         case NTLMSSP_MSGTYPE_NEGOTIATE:
 143                 ctx->ctx_orawtype = LSA_MTYPE_ES_CONT;
 144                 rc = smbd_ntlmssp_negotiate(ctx);
 145                 break;
 146         case NTLMSSP_MSGTYPE_AUTHENTICATE:
 147                 ctx->ctx_orawtype = LSA_MTYPE_ES_DONE;
 148                 rc = smbd_ntlmssp_authenticate(ctx);
 149                 break;
 150 
 151         default:
 152         case NTLMSSP_MSGTYPE_CHALLENGE:
 153                 /* Sent by servers, not received. */
 154                 rc = NT_STATUS_INVALID_PARAMETER;
 155                 break;
 156         }
 157 
 158         return (rc);
 159 }
 160 
 161 #if (MAXHOSTNAMELEN < NETBIOS_NAME_SZ)
 162 #error "MAXHOSTNAMELEN < NETBIOS_NAME_SZ"
 163 #endif
 164 
 165 /*
 166  * Handle an NTLMSSP_MSGTYPE_NEGOTIATE message, and reply
 167  * with an NTLMSSP_MSGTYPE_CHALLENGE message.
 168  * See: [MS-NLMP] 2.2.1.1, 3.2.5.1.1
 169  */
 170 static int
 171 smbd_ntlmssp_negotiate(authsvc_context_t *ctx)
 172 {
 173         char tmp_name[MAXHOSTNAMELEN];
 174         ntlmssp_backend_t *be = ctx->ctx_backend;
 175         struct nego_hdr *ihdr = ctx->ctx_ibodybuf;
 176         smb_msgbuf_t mb;
 177         uint8_t *save_scan;
 178         int secmode;
 179         int mbflags;
 180         int rc;
 181         size_t var_start, var_end;
 182         uint16_t var_size;
 183 
 184         if (ctx->ctx_ibodylen < sizeof (*ihdr))
 185                 return (NT_STATUS_INVALID_PARAMETER);
 186         be->clnt_flags = letohl(ihdr->h_flags);
 187 
 188         /*
 189          * Looks like we can ignore ws_dom, ws_name.
 190          * Otherwise would parse those here.
 191          */
 192 
 193         secmode = smb_config_get_secmode();
 194         if (smbd_constant_challenge) {
 195                 (void) memcpy(be->srv_challenge, constant_chal,
 196                     sizeof (be->srv_challenge));
 197         } else {
 198                 randomize(be->srv_challenge, sizeof (be->srv_challenge));
 199         }
 200 
 201         /*
 202          * Compute srv_flags
 203          */
 204         be->srv_flags =
 205             NTLMSSP_REQUEST_TARGET |
 206             NTLMSSP_NEGOTIATE_NTLM |
 207             NTLMSSP_NEGOTIATE_TARGET_INFO;
 208         be->srv_flags |= be->clnt_flags & (
 209             NTLMSSP_NEGOTIATE_NTLM2 |
 210             NTLMSSP_NEGOTIATE_128 |
 211             NTLMSSP_NEGOTIATE_KEY_EXCH |
 212             NTLMSSP_NEGOTIATE_56);
 213 
 214         if (smbd_signing_enabled) {
 215                 be->srv_flags |= be->clnt_flags & (
 216                     NTLMSSP_NEGOTIATE_SIGN |
 217                     NTLMSSP_NEGOTIATE_SEAL |
 218                     NTLMSSP_NEGOTIATE_ALWAYS_SIGN);
 219         }
 220 
 221         if (be->clnt_flags & NTLMSSP_NEGOTIATE_UNICODE)
 222                 be->srv_flags |= NTLMSSP_NEGOTIATE_UNICODE;
 223         else if (be->clnt_flags & NTLMSSP_NEGOTIATE_OEM)
 224                 be->srv_flags |= NTLMSSP_NEGOTIATE_OEM;
 225 
 226         /* LM Key is mutually exclusive with NTLM2 */
 227         if ((be->srv_flags & NTLMSSP_NEGOTIATE_NTLM2) == 0 &&
 228             (be->clnt_flags & NTLMSSP_NEGOTIATE_LM_KEY) != 0)
 229                 be->srv_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
 230 
 231         /* Get our "target name" */
 232         if (secmode == SMB_SECMODE_DOMAIN) {
 233                 be->srv_flags |= NTLMSSP_TARGET_TYPE_DOMAIN;
 234                 rc = smb_getdomainname(tmp_name, NETBIOS_NAME_SZ);
 235         } else {
 236                 be->srv_flags |= NTLMSSP_TARGET_TYPE_SERVER;
 237                 rc = smb_getnetbiosname(tmp_name, NETBIOS_NAME_SZ);
 238         }
 239         if (rc)
 240                 goto errout;
 241 
 242         /*
 243          * Build the NTLMSSP_MSGTYPE_CHALLENGE message.
 244          */
 245         mbflags = SMB_MSGBUF_NOTERM;
 246         if (be->srv_flags & NTLMSSP_NEGOTIATE_UNICODE)
 247                 mbflags |= SMB_MSGBUF_UNICODE;
 248         smb_msgbuf_init(&mb, ctx->ctx_obodybuf, ctx->ctx_obodylen, mbflags);
 249 
 250         /*
 251          * Fixed size parts
 252          */
 253         rc = smb_msgbuf_encode(
 254             &mb, "8clwwll8cllwwl",  /* offset, name (fmt) */
 255             "NTLMSSP",                  /* 0: signature (8c) */
 256             NTLMSSP_MSGTYPE_CHALLENGE,  /* 8: type      (l) */
 257             0, 0, 0,    /* filled later:   12: target name (wwl) */
 258             be->srv_flags,           /* 20: flags    (l) */
 259             be->srv_challenge,               /* 24:          (8c) */
 260             0, 0,                       /* 32: reserved (ll) */
 261             0, 0, 0);   /* filled later:   40: target info (wwl) */
 262 #define TARGET_NAME_OFFSET      12
 263 #define TARGET_INFO_OFFSET      40
 264         if (rc < 0)
 265                 goto errout;
 266 
 267         /*
 268          * Variable length parts.
 269          *
 270          * Target name
 271          */
 272         var_start = smb_msgbuf_used(&mb);
 273         rc = smb_msgbuf_encode(&mb, "u", tmp_name);
 274         var_end = smb_msgbuf_used(&mb);
 275         var_size = (uint16_t)(var_end - var_start);
 276         if (rc < 0)
 277                 goto errout;
 278 
 279         /* overwrite target name offset+lengths */
 280         save_scan = mb.scan;
 281         mb.scan = mb.base + TARGET_NAME_OFFSET;
 282         (void) smb_msgbuf_encode(&mb, "wwl", var_size, var_size, var_start);
 283         mb.scan = save_scan;
 284 
 285         /*
 286          * Target info (AvPairList)
 287          *
 288          * These AV pairs are like our name/value pairs, but have
 289          * numeric identifiers instead of names.  There are many
 290          * of these, but we put only the four expected by Windows:
 291          *      NetBIOS computer name
 292          *      NetBIOS domain name
 293          *      DNS computer name
 294          *      DNS domain name
 295          * Note that "domain" above (even "DNS domain") refers to
 296          * the AD domain of which we're a member, which may be
 297          * _different_ from the configured DNS domain.
 298          *
 299          * Also note that in "workgroup" mode (not a domain member)
 300          * all "domain" fields should be set to the same values as
 301          * the "computer" fields ("bare" host name, not FQDN).
 302          */
 303         var_start = smb_msgbuf_used(&mb);
 304 
 305         /* NetBIOS Computer Name */
 306         if (smb_getnetbiosname(tmp_name, NETBIOS_NAME_SZ))
 307                 goto errout;
 308         if (encode_avpair_str(&mb, MsvAvNbComputerName, tmp_name) < 0)
 309                 goto errout;
 310 
 311         if (secmode != SMB_SECMODE_DOMAIN) {
 312                 /*
 313                  * Workgroup mode.  Set all to hostname.
 314                  * tmp_name = netbios hostname from above.
 315                  */
 316                 if (encode_avpair_str(&mb, MsvAvNbDomainName, tmp_name) < 0)
 317                         goto errout;
 318                 /*
 319                  * Want the bare computer name here (not FQDN).
 320                  */
 321                 if (smb_gethostname(tmp_name, MAXHOSTNAMELEN, SMB_CASE_LOWER))
 322                         goto errout;
 323                 if (encode_avpair_str(&mb, MsvAvDnsComputerName, tmp_name) < 0)
 324                         goto errout;
 325                 if (encode_avpair_str(&mb, MsvAvDnsDomainName, tmp_name) < 0)
 326                         goto errout;
 327         } else {
 328                 /*
 329                  * Domain mode.  Use real host and domain values.
 330                  */
 331 
 332                 /* NetBIOS Domain Name */
 333                 if (smb_getdomainname(tmp_name, NETBIOS_NAME_SZ))
 334                         goto errout;
 335                 if (encode_avpair_str(&mb, MsvAvNbDomainName, tmp_name) < 0)
 336                         goto errout;
 337 
 338                 /* DNS Computer Name */
 339                 if (smb_getfqhostname(tmp_name, MAXHOSTNAMELEN))
 340                         goto errout;
 341                 if (encode_avpair_str(&mb, MsvAvDnsComputerName, tmp_name) < 0)
 342                         goto errout;
 343 
 344                 /* DNS Domain Name */
 345                 if (smb_getfqdomainname(tmp_name, MAXHOSTNAMELEN))
 346                         goto errout;
 347                 if (encode_avpair_str(&mb, MsvAvDnsDomainName, tmp_name) < 0)
 348                         goto errout;
 349         }
 350 
 351         /* End marker */
 352         if (smb_msgbuf_encode(&mb, "ww", MsvAvEOL, 0) < 0)
 353                 goto errout;
 354         var_end = smb_msgbuf_used(&mb);
 355         var_size = (uint16_t)(var_end - var_start);
 356 
 357         /* overwrite target  offset+lengths */
 358         save_scan = mb.scan;
 359         mb.scan = mb.base + TARGET_INFO_OFFSET;
 360         (void) smb_msgbuf_encode(&mb, "wwl", var_size, var_size, var_start);
 361         mb.scan = save_scan;
 362 
 363         ctx->ctx_obodylen = smb_msgbuf_used(&mb);
 364         smb_msgbuf_term(&mb);
 365 
 366         be->expect_type = NTLMSSP_MSGTYPE_AUTHENTICATE;
 367 
 368         return (0);
 369 
 370 errout:
 371         smb_msgbuf_term(&mb);
 372         return (NT_STATUS_INTERNAL_ERROR);
 373 }
 374 
 375 static int
 376 encode_avpair_str(smb_msgbuf_t *mb, uint16_t AvId, char *name)
 377 {
 378         int rc;
 379         uint16_t len;
 380 
 381         len = smb_wcequiv_strlen(name);
 382         rc = smb_msgbuf_encode(mb, "wwU", AvId, len, name);
 383         return (rc);
 384 }
 385 
 386 /*
 387  * Handle an NTLMSSP_MSGTYPE_AUTHENTICATE message.
 388  * See: [MS-NLMP] 2.2.1.3, 3.2.5.1.2
 389  */
 390 static int
 391 smbd_ntlmssp_authenticate(authsvc_context_t *ctx)
 392 {
 393         struct auth_hdr hdr;
 394         smb_msgbuf_t mb;
 395         smb_logon_t     user_info;
 396         smb_token_t     *token = NULL;
 397         ntlmssp_backend_t *be = ctx->ctx_backend;
 398         void *lm_resp;
 399         void *nt_resp;
 400         char *domain;
 401         char *user;
 402         char *wksta;
 403         void *essn_key; /* encrypted session key (optional) */
 404         int mbflags;
 405         uint_t status = NT_STATUS_INTERNAL_ERROR;
 406         char combined_challenge[SMBAUTH_CHAL_SZ];
 407         unsigned char kxkey[SMBAUTH_HASH_SZ];
 408         boolean_t ntlm_v1x = B_FALSE;
 409 
 410         bzero(&user_info, sizeof (user_info));
 411 
 412         /*
 413          * Parse the NTLMSSP_MSGTYPE_AUTHENTICATE message.
 414          */
 415         if (ctx->ctx_ibodylen < sizeof (hdr))
 416                 return (NT_STATUS_INVALID_PARAMETER);
 417         mbflags = SMB_MSGBUF_NOTERM;
 418         if (be->srv_flags & NTLMSSP_NEGOTIATE_UNICODE)
 419                 mbflags |= SMB_MSGBUF_UNICODE;
 420         smb_msgbuf_init(&mb, ctx->ctx_ibodybuf, ctx->ctx_ibodylen, mbflags);
 421         bzero(&hdr, sizeof (hdr));
 422 
 423         if (smb_msgbuf_decode(&mb, "12.") < 0)
 424                 goto errout;
 425         if (decode_secbuf_bin(&mb, &hdr.h_lm_resp, &lm_resp) < 0)
 426                 goto errout;
 427         if (decode_secbuf_bin(&mb, &hdr.h_nt_resp, &nt_resp) < 0)
 428                 goto errout;
 429         if (decode_secbuf_str(&mb, &hdr.h_domain, &domain) < 0)
 430                 goto errout;
 431         if (decode_secbuf_str(&mb, &hdr.h_user, &user) < 0)
 432                 goto errout;
 433         if (decode_secbuf_str(&mb, &hdr.h_wksta, &wksta) < 0)
 434                 goto errout;
 435         if (decode_secbuf_bin(&mb, &hdr.h_essn_key, &essn_key) < 0)
 436                 goto errout;
 437         if (smb_msgbuf_decode(&mb, "l", &be->clnt_flags) < 0)
 438                 goto errout;
 439 
 440         if (be->clnt_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
 441                 if (hdr.h_essn_key.sb_length < 16 || essn_key == NULL)
 442                         goto errout;
 443         }
 444 
 445         user_info.lg_level = NETR_NETWORK_LOGON;
 446         user_info.lg_flags = 0;
 447 
 448         user_info.lg_ntlm_flags = be->clnt_flags;
 449         user_info.lg_username = (user) ? user : "";
 450         user_info.lg_domain = (domain) ? domain : "";
 451         user_info.lg_workstation = (wksta) ? wksta : "";
 452 
 453         user_info.lg_clnt_ipaddr =
 454             ctx->ctx_clinfo.lci_clnt_ipaddr;
 455         user_info.lg_local_port = 445;
 456 
 457         user_info.lg_challenge_key.len = SMBAUTH_CHAL_SZ;
 458         user_info.lg_challenge_key.val = (uint8_t *)be->srv_challenge;
 459 
 460         user_info.lg_nt_password.len = hdr.h_nt_resp.sb_length;
 461         user_info.lg_nt_password.val = nt_resp;
 462 
 463         user_info.lg_lm_password.len = hdr.h_lm_resp.sb_length;
 464         user_info.lg_lm_password.val = lm_resp;
 465 
 466         user_info.lg_native_os = ctx->ctx_clinfo.lci_native_os;
 467         user_info.lg_native_lm = ctx->ctx_clinfo.lci_native_lm;
 468 
 469         /*
 470          * If we're doing extended session security, the challenge
 471          * this OWF was computed with is different. [MS-NLMP 3.3.1]
 472          * It's: MD5(concat(ServerChallenge,ClientChallenge))
 473          * where the ClientChallenge is in the LM resp. field.
 474          */
 475         if (user_info.lg_nt_password.len == SMBAUTH_LM_RESP_SZ &&
 476             user_info.lg_lm_password.len >= SMBAUTH_CHAL_SZ &&
 477             (be->clnt_flags & NTLMSSP_NEGOTIATE_NTLM2) != 0) {
 478                 smb_auth_ntlm2_mkchallenge(combined_challenge,
 479                     be->srv_challenge, lm_resp);
 480                 user_info.lg_challenge_key.val =
 481                     (uint8_t *)combined_challenge;
 482                 user_info.lg_lm_password.len = 0;
 483                 ntlm_v1x = B_TRUE;
 484         }
 485 
 486         /*
 487          * This (indirectly) calls smb_auth_validate() to
 488          * check that the client gave us a valid hash.
 489          */
 490         token = smbd_user_auth_logon(&user_info);
 491         if (token == NULL) {
 492                 status = user_info.lg_status;
 493                 if (status == 0) /* should not happen */
 494                         status = NT_STATUS_INTERNAL_ERROR;
 495                 goto errout;
 496         }
 497 
 498         if (token->tkn_ssnkey.val != NULL &&
 499             token->tkn_ssnkey.len == SMBAUTH_HASH_SZ) {
 500 
 501                 /*
 502                  * At this point, token->tkn_session_key is the
 503                  * "Session Base Key" [MS-NLMP] 3.2.5.1.2
 504                  * Compute the final session key.  First need the
 505                  * "Key Exchange Key" [MS-NLMP] 3.4.5.1
 506                  */
 507                 if (ntlm_v1x) {
 508                         smb_auth_ntlm2_kxkey(kxkey,
 509                             be->srv_challenge, lm_resp,
 510                             token->tkn_ssnkey.val);
 511                 } else {
 512                         /* KXKEY is the Session Base Key. */
 513                         (void) memcpy(kxkey, token->tkn_ssnkey.val,
 514                             SMBAUTH_HASH_SZ);
 515                 }
 516 
 517                 /*
 518                  * If the client give us an encrypted session key,
 519                  * decrypt it (RC4) using the "key exchange key".
 520                  */
 521                 if (be->clnt_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
 522                         /* RC4 args: result, key, data */
 523                         (void) smb_auth_RC4(token->tkn_ssnkey.val,
 524                             SMBAUTH_HASH_SZ, kxkey, SMBAUTH_HASH_SZ,
 525                             essn_key, hdr.h_essn_key.sb_length);
 526                 } else {
 527                         /* Final key is the KXKEY */
 528                         (void) memcpy(token->tkn_ssnkey.val, kxkey,
 529                             SMBAUTH_HASH_SZ);
 530                 }
 531         }
 532 
 533         ctx->ctx_token = token;
 534         ctx->ctx_obodylen = 0;
 535 
 536         smb_msgbuf_term(&mb);
 537         return (0);
 538 
 539 errout:
 540         smb_msgbuf_term(&mb);
 541         return (status);
 542 }
 543 
 544 static int
 545 decode_secbuf_bin(smb_msgbuf_t *mb, struct sec_buf *sb, void **binp)
 546 {
 547         int rc;
 548 
 549         *binp = NULL;
 550         rc = smb_msgbuf_decode(
 551             mb, "wwl",
 552             &sb->sb_length,
 553             &sb->sb_maxlen,
 554             &sb->sb_offset);
 555         if (rc < 0)
 556                 return (rc);
 557 
 558         if (sb->sb_offset > mb->max)
 559                 return (SMB_MSGBUF_UNDERFLOW);
 560         if (sb->sb_length > (mb->max - sb->sb_offset))
 561                 return (SMB_MSGBUF_UNDERFLOW);
 562         if (sb->sb_length == 0)
 563                 return (rc);
 564 
 565         *binp = mb->base + sb->sb_offset;
 566         return (0);
 567 }
 568 
 569 static int
 570 decode_secbuf_str(smb_msgbuf_t *mb, struct sec_buf *sb, char **cpp)
 571 {
 572         uint8_t *save_scan;
 573         int rc;
 574 
 575         *cpp = NULL;
 576         rc = smb_msgbuf_decode(
 577             mb, "wwl",
 578             &sb->sb_length,
 579             &sb->sb_maxlen,
 580             &sb->sb_offset);
 581         if (rc < 0)
 582                 return (rc);
 583 
 584         if (sb->sb_offset > mb->max)
 585                 return (SMB_MSGBUF_UNDERFLOW);
 586         if (sb->sb_length > (mb->max - sb->sb_offset))
 587                 return (SMB_MSGBUF_UNDERFLOW);
 588         if (sb->sb_length == 0)
 589                 return (rc);
 590 
 591         save_scan = mb->scan;
 592         mb->scan = mb->base + sb->sb_offset;
 593         rc = smb_msgbuf_decode(mb, "#u", (int)sb->sb_length, cpp);
 594         mb->scan = save_scan;
 595 
 596         return (rc);
 597 }