1 /*
   2  * Copyright (c) 2011  Apple Inc. All rights reserved.
   3  *
   4  * @APPLE_LICENSE_HEADER_START@
   5  *
   6  * This file contains Original Code and/or Modifications of Original Code
   7  * as defined in and that are subject to the Apple Public Source License
   8  * Version 2.0 (the 'License'). You may not use this file except in
   9  * compliance with the License. Please obtain a copy of the License at
  10  * http://www.opensource.apple.com/apsl/ and read it before using this
  11  * file.
  12  *
  13  * The Original Code and all software distributed under the License are
  14  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  15  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  16  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
  17  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  18  * Please see the License for the specific language governing rights and
  19  * limitations under the License.
  20  *
  21  * @APPLE_LICENSE_HEADER_END@
  22  */
  23 
  24 /*
  25  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  26  */
  27 
  28 #include <sys/param.h>
  29 #include <sys/systm.h>
  30 #include <sys/time.h>
  31 #include <sys/kmem.h>
  32 #include <sys/proc.h>
  33 #include <sys/lock.h>
  34 #include <sys/socket.h>
  35 #include <sys/mount.h>
  36 #include <sys/sunddi.h>
  37 #include <sys/cmn_err.h>
  38 #include <sys/atomic.h>
  39 #include <sys/sdt.h>
  40 
  41 #include <netsmb/smb_osdep.h>
  42 
  43 #include <netsmb/smb.h>
  44 #include <netsmb/smb2.h>
  45 #include <netsmb/smb_conn.h>
  46 #include <netsmb/smb_subr.h>
  47 #include <netsmb/smb_tran.h>
  48 #include <netsmb/smb_rq.h>
  49 #include <netsmb/smb2_rq.h>
  50 
  51 static const uint8_t SMB2_SIGNATURE[4] = SMB2_PROTOCOL_ID;
  52 
  53 static int  smb2_rq_enqueue(struct smb_rq *rqp);
  54 static int  smb2_rq_reply(struct smb_rq *rqp);
  55 
  56 /*
  57  * Given a request with it's body already composed,
  58  * rewind to the start and fill in the SMB2 header.
  59  * This is called when the request is enqueued,
  60  * so we have the final message ID etc.
  61  */
  62 void
  63 smb2_rq_fillhdr(struct smb_rq *rqp)
  64 {
  65         struct mbchain mbtmp, *mbp = &mbtmp;
  66         uint16_t creditcharge, creditrequest;
  67         size_t len;
  68         mblk_t *m;
  69 
  70         ASSERT((rqp->sr2_nextcmd & 7) == 0);
  71         if (rqp->sr2_nextcmd != 0) {
  72                 len = msgdsize(rqp->sr_rq.mb_top);
  73                 ASSERT((len & 7) == 0);
  74         }
  75 
  76         /*
  77          * When sending negotiate, we don't technically know yet
  78          * if the server handles SMB 2.1 or later and credits.
  79          * Negotiate is supposed to set these to zero.
  80          */
  81         if (rqp->sr2_command == SMB2_NEGOTIATE) {
  82                 creditcharge = creditrequest = 0;
  83         } else {
  84                 creditcharge = rqp->sr2_creditcharge;
  85                 creditrequest = rqp->sr2_creditsrequested;
  86         }
  87 
  88         /*
  89          * Fill in the SMB2 header using a dup of the first mblk,
  90          * which points at the same data but has its own wptr,
  91          * so we can rewind without trashing the message.
  92          */
  93         m = dupb(rqp->sr_rq.mb_top);
  94         m->b_wptr = m->b_rptr;    /* rewind */
  95         mb_initm(mbp, m);
  96 
  97         mb_put_mem(mbp, SMB2_SIGNATURE, 4, MB_MSYSTEM);
  98         mb_put_uint16le(mbp, SMB2_HDR_SIZE);            /* Struct Size */
  99         mb_put_uint16le(mbp, creditcharge);
 100         mb_put_uint32le(mbp, 0);        /* Status */
 101         mb_put_uint16le(mbp, rqp->sr2_command);
 102         mb_put_uint16le(mbp, creditrequest);
 103         mb_put_uint32le(mbp, rqp->sr2_rqflags);
 104         mb_put_uint32le(mbp, rqp->sr2_nextcmd);
 105         mb_put_uint64le(mbp, rqp->sr2_messageid);
 106 
 107         mb_put_uint32le(mbp, rqp->sr_pid);           /* Process ID */
 108         mb_put_uint32le(mbp, rqp->sr2_rqtreeid);     /* Tree ID */
 109         mb_put_uint64le(mbp, rqp->sr2_rqsessionid);  /* Session ID */
 110         /* The MAC signature is filled in by smb2_vc_sign() */
 111 
 112         /* This will free the mblk from dupb. */
 113         mb_done(mbp);
 114 }
 115 
 116 int
 117 smb2_rq_simple(struct smb_rq *rqp)
 118 {
 119         return (smb2_rq_simple_timed(rqp, smb2_timo_default));
 120 }
 121 
 122 /*
 123  * Simple request-reply exchange
 124  */
 125 int
 126 smb2_rq_simple_timed(struct smb_rq *rqp, int timeout)
 127 {
 128         int error;
 129 
 130         rqp->sr_flags &= ~SMBR_RESTART;
 131         rqp->sr_timo = timeout;      /* in seconds */
 132         rqp->sr_state = SMBRQ_NOTSENT;
 133 
 134         error = smb2_rq_enqueue(rqp);
 135         if (error == 0)
 136                 error = smb2_rq_reply(rqp);
 137 
 138         return (error);
 139 }
 140 
 141 
 142 static int
 143 smb2_rq_enqueue(struct smb_rq *rqp)
 144 {
 145         struct smb_vc *vcp = rqp->sr_vc;
 146         struct smb_share *ssp = rqp->sr_share;
 147         int error = 0;
 148 
 149         ASSERT((vcp->vc_flags & SMBV_SMB2) != 0);
 150 
 151         /*
 152          * Normal requests may initiate a reconnect,
 153          * and/or wait for state changes to finish.
 154          * Some requests set the NORECONNECT flag
 155          * to avoid all that (i.e. tree discon)
 156          */
 157         if (rqp->sr_flags & SMBR_NORECONNECT) {
 158                 if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
 159                         SMBSDEBUG("bad vc_state=%d\n", vcp->vc_state);
 160                         return (ENOTCONN);
 161                 }
 162                 if (ssp != NULL &&
 163                     ((ssp->ss_flags & SMBS_CONNECTED) == 0))
 164                         return (ENOTCONN);
 165                 goto ok_out;
 166         }
 167 
 168         /*
 169          * If we're not connected, initiate a reconnect
 170          * and/or wait for an existing one to finish.
 171          */
 172         if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
 173                 error = smb_iod_reconnect(vcp);
 174                 if (error != 0)
 175                         return (error);
 176         }
 177 
 178         /*
 179          * If this request has a "share" object
 180          * that needs a tree connect, do it now.
 181          */
 182         if (ssp != NULL && (ssp->ss_flags & SMBS_CONNECTED) == 0) {
 183                 error = smb_share_tcon(ssp, rqp->sr_cred);
 184                 if (error)
 185                         return (error);
 186         }
 187 
 188         /*
 189          * We now know what UID + TID to use.
 190          * Store them in the request.
 191          */
 192 ok_out:
 193         rqp->sr2_rqsessionid = vcp->vc2_session_id;
 194         rqp->sr2_rqtreeid = ssp ? ssp->ss2_tree_id : SMB2_TID_UNKNOWN;
 195         error = smb2_iod_addrq(rqp);
 196 
 197         return (error);
 198 }
 199 
 200 /*
 201  * Used by the IOD thread during connection setup,
 202  * and for smb2_echo after network timeouts.  Note that
 203  * unlike smb2_rq_simple, callers must check sr_error.
 204  */
 205 int
 206 smb2_rq_internal(struct smb_rq *rqp, int timeout)
 207 {
 208         struct smb_vc *vcp = rqp->sr_vc;
 209         int error;
 210 
 211         ASSERT((vcp->vc_flags & SMBV_SMB2) != 0);
 212 
 213         rqp->sr_flags &= ~SMBR_RESTART;
 214         rqp->sr_timo = timeout;      /* in seconds */
 215         rqp->sr_state = SMBRQ_NOTSENT;
 216 
 217         /*
 218          * In-line smb2_rq_enqueue(rqp) here, as we don't want it
 219          * trying to reconnect etc. for an internal request.
 220          */
 221         rqp->sr2_rqsessionid = vcp->vc2_session_id;
 222         rqp->sr2_rqtreeid = SMB2_TID_UNKNOWN;
 223         rqp->sr_flags |= SMBR_INTERNAL;
 224         error = smb2_iod_addrq(rqp);
 225         if (error != 0)
 226                 return (error);
 227 
 228         /*
 229          * In-line a variant of smb2_rq_reply(rqp) here as we may
 230          * need to do custom parsing for SMB1-to-SMB2 negotiate.
 231          */
 232         if (rqp->sr_timo == SMBNOREPLYWAIT) {
 233                 smb_iod_removerq(rqp);
 234                 return (0);
 235         }
 236 
 237         error = smb_iod_waitrq_int(rqp);
 238         if (error)
 239                 return (error);
 240 
 241         /*
 242          * If the request was signed, validate the
 243          * signature on the response.
 244          */
 245         if (rqp->sr2_rqflags & SMB2_FLAGS_SIGNED) {
 246                 error = smb2_rq_verify(rqp);
 247                 if (error)
 248                         return (error);
 249         }
 250 
 251         /*
 252          * Parse the SMB2 header.
 253          */
 254         error = smb2_rq_parsehdr(rqp);
 255 
 256         /*
 257          * Skip the error translation smb2_rq_reply does.
 258          * Callers of this expect "raw" NT status.
 259          */
 260 
 261         return (error);
 262 }
 263 
 264 /*
 265  * Wait for a reply to this request, then parse it.
 266  */
 267 static int
 268 smb2_rq_reply(struct smb_rq *rqp)
 269 {
 270         int error;
 271 
 272         if (rqp->sr_timo == SMBNOREPLYWAIT) {
 273                 smb_iod_removerq(rqp);
 274                 return (0);
 275         }
 276 
 277         error = smb_iod_waitrq(rqp);
 278         if (error)
 279                 return (error);
 280 
 281         /*
 282          * If the request was signed, validate the
 283          * signature on the response.
 284          */
 285         if (rqp->sr2_rqflags & SMB2_FLAGS_SIGNED) {
 286                 error = smb2_rq_verify(rqp);
 287                 if (error)
 288                         return (error);
 289         }
 290 
 291         /*
 292          * Parse the SMB2 header
 293          */
 294         error = smb2_rq_parsehdr(rqp);
 295         if (error != 0)
 296                 return (error);
 297 
 298         if (rqp->sr_error != 0) {
 299                 error = smb_maperr32(rqp->sr_error);
 300         }
 301 
 302         if (error != 0) {
 303                 /*
 304                  * Do a special check for STATUS_BUFFER_OVERFLOW;
 305                  * it's not an error.
 306                  */
 307                 if (rqp->sr_error == NT_STATUS_BUFFER_OVERFLOW) {
 308                         /*
 309                          * Don't report it as an error to our caller;
 310                          * they can look at rqp->sr_error if they
 311                          * need to know whether we got a
 312                          * STATUS_BUFFER_OVERFLOW.
 313                          */
 314                         rqp->sr_flags |= SMBR_MOREDATA;
 315                         error = 0;
 316                 }
 317         } else {
 318                 rqp->sr_flags &= ~SMBR_MOREDATA;
 319         }
 320 
 321         return (error);
 322 }
 323 
 324 /*
 325  * Parse the SMB 2+ Header
 326  */
 327 int
 328 smb2_rq_parsehdr(struct smb_rq *rqp)
 329 {
 330         struct mdchain *mdp = &rqp->sr_rp;
 331         uint32_t protocol_id;
 332         uint16_t length = 0;
 333         uint16_t credit_charge;
 334         uint16_t command;
 335         uint64_t message_id = 0;
 336         int error = 0;
 337 
 338         /* Get Protocol ID */
 339         md_get_uint32le(mdp, &protocol_id);
 340 
 341         /* Get/Check structure size is 64 */
 342         md_get_uint16le(mdp, &length);
 343         if (length != 64)
 344                 return (EBADRPC);
 345 
 346         md_get_uint16le(mdp, &credit_charge);
 347         md_get_uint32le(mdp, &rqp->sr_error);
 348         md_get_uint16le(mdp, &command);
 349         md_get_uint16le(mdp, &rqp->sr2_rspcreditsgranted);
 350         md_get_uint32le(mdp, &rqp->sr2_rspflags);
 351         md_get_uint32le(mdp, &rqp->sr2_rspnextcmd);
 352         md_get_uint64le(mdp, &message_id);
 353 
 354         if ((rqp->sr2_rspflags & SMB2_FLAGS_ASYNC_COMMAND) == 0) {
 355                 /*
 356                  * Sync Header
 357                  */
 358 
 359                 /* Get Process ID */
 360                 md_get_uint32le(mdp, &rqp->sr2_rsppid);
 361 
 362                 /* Get Tree ID */
 363                 md_get_uint32le(mdp, &rqp->sr2_rsptreeid);
 364         } else {
 365                 /*
 366                  * Async Header
 367                  */
 368 
 369                 /* Get Async ID */
 370                 md_get_uint64le(mdp, &rqp->sr2_rspasyncid);
 371         }
 372 
 373         /* Get Session ID */
 374         error = md_get_uint64le(mdp, &rqp->sr2_rspsessionid);
 375         if (error)
 376                 return (error);
 377 
 378         /* Skip MAC Signature */
 379         error = md_get_mem(mdp, NULL, 16, MB_MSYSTEM);
 380 
 381         return (error);
 382 }