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 2014 Nexenta Systems, Inc.  All rights reserved.
  14  */
  15 
  16 /*
  17  * Dispatch function for SMB2_SESSION_SETUP
  18  *
  19  * Note that the Capabilities supplied in this request are an inferior
  20  * subset of those given to us previously in the SMB2 Negotiate request.
  21  * We need to remember the full set of capabilities from SMB2 Negotiate,
  22  * and therefore ignore the subset of capabilities supplied here.
  23  */
  24 
  25 #include <smbsrv/smb2_kproto.h>
  26 
  27 static void smb2_ss_adjust_credits(smb_request_t *);
  28 
  29 smb_sdrc_t
  30 smb2_session_setup(smb_request_t *sr)
  31 {
  32         smb_arg_sessionsetup_t  *sinfo;
  33         uint16_t StructureSize;
  34         uint8_t  Flags;
  35         uint8_t  SecurityMode;
  36         uint32_t Capabilities;  /* ignored - see above */
  37         uint32_t Channel;
  38         uint16_t SecBufOffset;
  39         uint16_t SecBufLength;
  40         uint64_t PrevSessionId;
  41         uint16_t SessionFlags;
  42         uint32_t status;
  43         int skip;
  44         int rc = 0;
  45 
  46         sinfo = smb_srm_zalloc(sr, sizeof (smb_arg_sessionsetup_t));
  47         sr->sr_ssetup = sinfo;
  48 
  49         rc = smb_mbc_decodef(
  50             &sr->smb_data, "wbbllwwq",
  51             &StructureSize, /* w */
  52             &Flags,         /* b */
  53             &SecurityMode,  /* b */
  54             &Capabilities,  /* l */
  55             &Channel,               /* l */
  56             &SecBufOffset,  /* w */
  57             &SecBufLength,  /* w */
  58             &PrevSessionId);        /* q */
  59         if (rc)
  60                 return (SDRC_ERROR);
  61 
  62         /*
  63          * We're normally positioned at the security buffer now,
  64          * but there could be some padding before it.
  65          */
  66         skip = (SecBufOffset + sr->smb2_cmd_hdr) -
  67             sr->smb_data.chain_offset;
  68         if (skip < 0)
  69                 return (SDRC_ERROR);
  70         if (skip > 0)
  71                 (void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
  72 
  73         /*
  74          * Get the security buffer
  75          */
  76         sinfo->ssi_iseclen = SecBufLength;
  77         sinfo->ssi_isecblob = smb_srm_zalloc(sr, sinfo->ssi_iseclen);
  78         rc = smb_mbc_decodef(&sr->smb_data, "#c",
  79             sinfo->ssi_iseclen, sinfo->ssi_isecblob);
  80         if (rc)
  81                 return (SDRC_ERROR);
  82 
  83         /*
  84          * The real auth. work happens in here.
  85          */
  86         status = smb_authenticate_ext(sr);
  87 
  88         SecBufOffset = SMB2_HDR_SIZE + 8;
  89         SecBufLength = sinfo->ssi_oseclen;
  90         SessionFlags = 0;
  91 
  92         switch (status) {
  93 
  94         case NT_STATUS_SUCCESS: /* Authenticated */
  95                 if (sr->uid_user->u_flags & SMB_USER_FLAG_GUEST)
  96                         SessionFlags |= SMB2_SESSION_FLAG_IS_GUEST;
  97                 if (sr->uid_user->u_flags & SMB_USER_FLAG_ANON)
  98                         SessionFlags |= SMB2_SESSION_FLAG_IS_NULL;
  99                 smb2_ss_adjust_credits(sr);
 100                 break;
 101 
 102         /*
 103          * This is not really an error, but tells the client
 104          * it should send another session setup request.
 105          * Not smb2_put_error because we send a payload.
 106          */
 107         case NT_STATUS_MORE_PROCESSING_REQUIRED:
 108                 sr->smb2_status = status;
 109                 break;
 110 
 111         default:
 112                 SecBufLength = 0;
 113                 sr->smb2_status = status;
 114                 break;
 115         }
 116 
 117         /*
 118          * SMB2 Session Setup reply
 119          */
 120 
 121         rc = smb_mbc_encodef(
 122             &sr->reply,
 123             "wwww#c",
 124             9,  /* StructSize */        /* w */
 125             SessionFlags,               /* w */
 126             SecBufOffset,               /* w */
 127             SecBufLength,               /* w */
 128             SecBufLength,               /* # */
 129             sinfo->ssi_osecblob);    /* c */
 130         if (rc)
 131                 return (SDRC_ERROR);
 132 
 133         return (SDRC_SUCCESS);
 134 }
 135 
 136 /*
 137  * After a successful authentication, raise s_max_credits up to the
 138  * normal maximum that clients are allowed to request.  Also, if we
 139  * haven't yet given them their initial credits, do that now.
 140  *
 141  * Normally, clients will request some credits with session setup,
 142  * but in case they don't request enough to raise s_cur_credits
 143  * up to the configured initial_credits, increase the requested
 144  * credits of this SR sufficiently to make that happen.  The actual
 145  * increase happens in the dispatch code after we return.
 146  */
 147 static void
 148 smb2_ss_adjust_credits(smb_request_t *sr)
 149 {
 150         smb_session_t *s = sr->session;
 151 
 152         mutex_enter(&s->s_credits_mutex);
 153         s->s_max_credits = s->s_cfg.skc_maximum_credits;
 154 
 155         if (s->s_cur_credits < s->s_cfg.skc_initial_credits) {
 156                 uint16_t grant;
 157 
 158                 /* How many credits we want to grant with this SR. */
 159                 grant = s->s_cfg.skc_initial_credits - s->s_cur_credits;
 160 
 161                 /*
 162                  * Do we need to increase the smb2_credit_request?
 163                  * One might prefer to read this expression as:
 164                  *      ((credit_request - credit_charge) < grant)
 165                  * but we know credit_charge == 1 and would rather not
 166                  * deal with a possibly negative value on the left,
 167                  * so adding credit_charge to both sides...
 168                  */
 169                 if (sr->smb2_credit_request < (grant + 1)) {
 170                         sr->smb2_credit_request = (grant + 1);
 171                 }
 172         }
 173 
 174         mutex_exit(&s->s_credits_mutex);
 175 }