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  * Dispatch function for SMB2_LOCK
  18  */
  19 
  20 #include <smbsrv/smb2_kproto.h>
  21 
  22 /*
  23  * [MS-SMB2] 2.2.26 LockSequenceIndex, LockSequenceNumber.
  24  */
  25 #define SMB2_LSN_SHIFT  4
  26 #define SMB2_LSN_MASK   0xf
  27 
  28 typedef struct SMB2_LOCK_ELEMENT {
  29         uint64_t Offset;
  30         uint64_t Length;
  31         uint32_t Flags;
  32         uint32_t reserved;
  33 } lock_elem_t;
  34 
  35 static uint32_t smb2_unlock(smb_request_t *);
  36 static uint32_t smb2__lock(smb_request_t *);
  37 static uint32_t smb2_lock_blocking(smb_request_t *);
  38 
  39 static boolean_t smb2_lock_chk_lockseq(smb_ofile_t *, uint32_t);
  40 static void smb2_lock_set_lockseq(smb_ofile_t *, uint32_t);
  41 
  42 /*
  43  * This is a somewhat arbitrary sanity limit on the length of the
  44  * SMB2_LOCK_ELEMENT array.  It usually has length one or two.
  45  */
  46 int smb2_lock_max_elem = 1024;
  47 
  48 smb_sdrc_t
  49 smb2_lock(smb_request_t *sr)
  50 {
  51         lock_elem_t *lvec, *lk;
  52         smb2fid_t smb2fid;
  53         uint32_t LockSequence;
  54         uint32_t status;
  55         uint16_t StructSize;
  56         uint16_t LockCount;
  57         uint16_t i;
  58         int rc;
  59 
  60         /*
  61          * Decode SMB2 Lock request
  62          */
  63         rc = smb_mbc_decodef(
  64             &sr->smb_data, "wwlqq",
  65             &StructSize,            /* w */
  66             &LockCount,                     /* w */
  67             &LockSequence,          /* l */
  68             &smb2fid.persistent,    /* q */
  69             &smb2fid.temporal);             /* q */
  70         if (rc || StructSize != 48)
  71                 return (SDRC_ERROR);
  72 
  73         /*
  74          * Want FID lookup before the start probe.
  75          */
  76         status = smb2sr_lookup_fid(sr, &smb2fid);
  77         DTRACE_SMB2_START(op__Lock, smb_request_t *, sr);
  78 
  79         if (status)
  80                 goto errout; /* Bad FID */
  81         if (sr->fid_ofile->f_node == NULL || LockCount == 0) {
  82                 status = NT_STATUS_INVALID_PARAMETER;
  83                 goto errout;
  84         }
  85         if (LockCount > smb2_lock_max_elem) {
  86                 status = NT_STATUS_INSUFFICIENT_RESOURCES;
  87                 goto errout;
  88         }
  89 
  90         /*
  91          * Check the LockSequence to determine whether a previous
  92          * lock request succeeded, but the client disconnected
  93          * (retaining a durable or resilient handle).  If so, this
  94          * is a lock "replay".  We'll find the lock sequence here
  95          * and return success without processing the lock again.
  96          */
  97         if (sr->session->dialect < SMB_VERS_2_1)
  98                 LockSequence = 0;
  99         if ((sr->session->dialect == SMB_VERS_2_1) &&
 100             sr->fid_ofile->dh_vers != SMB2_RESILIENT)
 101                 LockSequence = 0;
 102         /* dialect 3.0 or later can always use LockSequence */
 103 
 104         if (LockSequence != 0 &&
 105             smb2_lock_chk_lockseq(sr->fid_ofile, LockSequence)) {
 106                 status = NT_STATUS_SUCCESS;
 107                 goto errout;
 108         }
 109 
 110         /*
 111          * Parse the array of SMB2_LOCK_ELEMENT structs.
 112          * This array is free'd in smb_srm_fini.
 113          */
 114         lvec = smb_srm_zalloc(sr, LockCount * sizeof (*lvec));
 115         for (i = 0; i < LockCount; i++) {
 116                 lk = &lvec[i];
 117                 rc = smb_mbc_decodef(
 118                     &sr->smb_data, "qqll",
 119                     &lk->Offset, /* q */
 120                     &lk->Length, /* q */
 121                     &lk->Flags,          /* l */
 122                     &lk->reserved);      /* l */
 123                 if (rc) {
 124                         status = NT_STATUS_INVALID_PARAMETER;
 125                         goto errout;
 126                 }
 127         }
 128 
 129         /*
 130          * [MS-SMB2] 3.3.5.14
 131          * If the flags of the [first element of] the Locks array
 132          * [has] SMB2_LOCKFLAG_UNLOCK set, the server MUST process
 133          * the lock array as a series of unlocks. Otherwise, it
 134          * MUST process the lock array as a series of lock requests.
 135          */
 136         sr->arg.lock.lvec = lvec;
 137         sr->arg.lock.lcnt = LockCount;
 138         sr->arg.lock.lseq = LockSequence;
 139         if (lvec[0].Flags & SMB2_LOCKFLAG_UNLOCK) {
 140                 status = smb2_unlock(sr);
 141         } else {
 142                 status = smb2__lock(sr);
 143         }
 144 
 145         if (sr->fid_ofile->dh_persist) {
 146                 smb2_dh_update_locks(sr, sr->fid_ofile);
 147         }
 148 
 149 errout:
 150         sr->smb2_status = status;
 151         DTRACE_SMB2_DONE(op__Lock, smb_request_t *, sr);
 152 
 153         if (status) {
 154                 smb2sr_put_error(sr, status);
 155                 return (SDRC_SUCCESS);
 156         }
 157 
 158         /*
 159          * Encode SMB2 Lock reply
 160          */
 161         (void) smb_mbc_encodef(
 162             &sr->reply, "w..",
 163             4); /* StructSize   w */
 164             /* reserved         .. */
 165         return (SDRC_SUCCESS);
 166 }
 167 
 168 /*
 169  * Process what should be an array of unlock requests.
 170  */
 171 static uint32_t
 172 smb2_unlock(smb_request_t *sr)
 173 {
 174         lock_elem_t *lk;
 175         lock_elem_t *lvec = sr->arg.lock.lvec;
 176         uint32_t LockCount = sr->arg.lock.lcnt;
 177         uint32_t LockSequence = sr->arg.lock.lseq;
 178         uint32_t status = 0;
 179         uint32_t pid = 0;       /* SMB2 ignores lock PIDs. */
 180         int i;
 181 
 182         for (i = 0; i < LockCount; i++) {
 183                 lk = &lvec[i];
 184 
 185                 if (lk->Flags != SMB2_LOCKFLAG_UNLOCK) {
 186                         status = NT_STATUS_INVALID_PARAMETER;
 187                         break;
 188                 }
 189 
 190                 status = smb_unlock_range(sr, lk->Offset, lk->Length, pid);
 191                 if (status != 0)
 192                         break;
 193         }
 194         if (status == 0 && LockSequence != 0) {
 195                 smb2_lock_set_lockseq(sr->fid_ofile, LockSequence);
 196         }
 197 
 198         return (status);
 199 }
 200 
 201 /*
 202  * Process what should be an array of lock requests.
 203  */
 204 static uint32_t
 205 smb2__lock(smb_request_t *sr)
 206 {
 207         lock_elem_t *lk;
 208         lock_elem_t *lvec = sr->arg.lock.lvec;
 209         uint32_t LockCount = sr->arg.lock.lcnt;
 210         uint32_t LockSequence = sr->arg.lock.lseq;
 211         uint32_t i;
 212         uint32_t ltype;
 213         uint32_t pid = 0;       /* SMB2 ignores lock PIDs */
 214         uint32_t timeout = 0;
 215         uint32_t status = 0;
 216 
 217         for (i = 0; i < LockCount; i++) {
 218                 lk = &lvec[i];
 219 
 220                 switch (lk->Flags) {
 221 
 222                 case SMB2_LOCKFLAG_SHARED_LOCK:
 223                 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK:
 224                         /*
 225                          * Blocking locks have special rules:
 226                          * Must be exactly one element, else
 227                          * invalid parameter.
 228                          */
 229                         if (i == 0 && LockCount == 1) {
 230                                 status = smb2_lock_blocking(sr);
 231                                 return (status);
 232                         }
 233                         /* FALLTHROUGH */
 234                 case SMB2_LOCKFLAG_UNLOCK:
 235                 default:
 236                         status = NT_STATUS_INVALID_PARAMETER;
 237                         goto end_loop;
 238 
 239                 /* BEGIN CSTYLED */
 240                 case SMB2_LOCKFLAG_SHARED_LOCK |
 241                      SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
 242                 /* END CSTYLED */
 243                         ltype = SMB_LOCK_TYPE_READONLY;
 244                         break;
 245 
 246                 /* BEGIN CSTYLED */
 247                 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK |
 248                      SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
 249                 /* END CSTYLED */
 250                         ltype = SMB_LOCK_TYPE_READWRITE;
 251                         break;
 252                 }
 253 
 254                 status = smb_lock_range(sr, lk->Offset, lk->Length, pid,
 255                     ltype, timeout);
 256                 if (status != 0) {
 257                         goto end_loop;
 258                 }
 259         }
 260 
 261 end_loop:
 262         if (status != 0) {
 263                 /*
 264                  * Oh... we have to rollback.
 265                  */
 266                 while (i > 0) {
 267                         --i;
 268                         lk = &lvec[i];
 269                         (void) smb_unlock_range(sr,
 270                             lk->Offset, lk->Length, pid);
 271                 }
 272         }
 273         if (status == 0 && LockSequence != 0)
 274                 smb2_lock_set_lockseq(sr->fid_ofile, LockSequence);
 275 
 276         return (status);
 277 }
 278 
 279 /*
 280  * Handler for blocking lock requests, which may "go async".
 281  * Always exactly one lock request here.
 282  */
 283 static uint32_t
 284 smb2_lock_blocking(smb_request_t *sr)
 285 {
 286         lock_elem_t *lk = sr->arg.lock.lvec;
 287         uint32_t LockCount = sr->arg.lock.lcnt;
 288         uint32_t LockSequence = sr->arg.lock.lseq;
 289         uint32_t status;
 290         uint32_t ltype;
 291         uint32_t pid = 0;       /* SMB2 ignores lock PIDs */
 292         uint32_t timeout = UINT_MAX;
 293 
 294         ASSERT(sr->fid_ofile->f_node != NULL);
 295         ASSERT(LockCount == 1);
 296 
 297         switch (lk->Flags) {
 298         case SMB2_LOCKFLAG_SHARED_LOCK:
 299                 ltype = SMB_LOCK_TYPE_READONLY;
 300                 break;
 301 
 302         case SMB2_LOCKFLAG_EXCLUSIVE_LOCK:
 303                 ltype = SMB_LOCK_TYPE_READWRITE;
 304                 break;
 305 
 306         default:
 307                 ASSERT(0);
 308                 return (NT_STATUS_INTERNAL_ERROR);
 309         }
 310 
 311         /*
 312          * Try the lock first with timeout=0 as we can often
 313          * get a lock without going async and avoid an extra
 314          * round trip with the client.  Also, only go async
 315          * for status returns that mean we will block.
 316          */
 317         status = smb_lock_range(sr, lk->Offset, lk->Length, pid, ltype, 0);
 318         if (status == NT_STATUS_LOCK_NOT_GRANTED ||
 319             status == NT_STATUS_FILE_LOCK_CONFLICT) {
 320                 status = smb2sr_go_async(sr);
 321                 if (status != 0)
 322                         return (status);
 323                 status = smb_lock_range(sr, lk->Offset, lk->Length,
 324                     pid, ltype, timeout);
 325         }
 326 
 327         if (status == 0 && LockSequence != 0)
 328                 smb2_lock_set_lockseq(sr->fid_ofile, LockSequence);
 329 
 330         return (status);
 331 }
 332 
 333 /*
 334  * Check whether we've stored a given LockSequence
 335  *
 336  * [MS-SMB2] 3.3.5.14
 337  *
 338  * The server verifies the LockSequence by performing the following steps:
 339  *
 340  * 1. The server MUST use LockSequenceIndex as an index into the
 341  * Open.LockSequenceArray in order to locate the sequence number entry.
 342  * If the index exceeds the maximum extent of the Open.LockSequenceArray,
 343  * or LockSequenceIndex is 0, or if the sequence number entry is empty,
 344  * the server MUST skip step 2 and continue lock/unlock processing.
 345  *
 346  * 2. The server MUST compare LockSequenceNumber to the SequenceNumber of
 347  * the entry located in step 1. If the sequence numbers are equal, the
 348  * server MUST complete the lock/unlock request with success. Otherwise,
 349  * the server MUST reset the entry value to empty and continue lock/unlock
 350  * processing.
 351  */
 352 boolean_t
 353 smb2_lock_chk_lockseq(smb_ofile_t *ofile, uint32_t lockseq)
 354 {
 355         uint32_t lsi;
 356         uint8_t lsn;
 357         boolean_t rv;
 358 
 359         /*
 360          * LockSequenceNumber is the low four bits.
 361          * LockSequenceIndex is the remaining 28 bits.
 362          * valid range is 1..64, which we convert to an
 363          * array index in the range 0..63
 364          */
 365         lsn = lockseq & SMB2_LSN_MASK;
 366         lsi = (lockseq >> SMB2_LSN_SHIFT);
 367         if (lsi == 0 || lsi > SMB_OFILE_LSEQ_MAX)
 368                 return (B_FALSE);
 369         --lsi;
 370 
 371         mutex_enter(&ofile->f_mutex);
 372 
 373         if (ofile->f_lock_seq[lsi] == lsn) {
 374                 rv = B_TRUE;
 375         } else {
 376                 ofile->f_lock_seq[lsi] = (uint8_t)-1;        /* "Empty" */
 377                 rv = B_FALSE;
 378         }
 379 
 380         mutex_exit(&ofile->f_mutex);
 381 
 382         return (rv);
 383 }
 384 
 385 static void
 386 smb2_lock_set_lockseq(smb_ofile_t *ofile, uint32_t lockseq)
 387 {
 388         uint32_t lsi;
 389         uint8_t lsn;
 390 
 391         /*
 392          * LockSequenceNumber is the low four bits.
 393          * LockSequenceIndex is the remaining 28 bits.
 394          * valid range is 1..64, which we convert to an
 395          * array index in the range 0..63
 396          */
 397         lsn = lockseq & SMB2_LSN_MASK;
 398         lsi = (lockseq >> SMB2_LSN_SHIFT);
 399         if (lsi == 0 || lsi > SMB_OFILE_LSEQ_MAX) {
 400                 cmn_err(CE_NOTE, "smb2_lock_set_lockseq, index=%u", lsi);
 401                 return;
 402         }
 403         --lsi;
 404 
 405         mutex_enter(&ofile->f_mutex);
 406 
 407         ofile->f_lock_seq[lsi] = lsn;
 408 
 409         mutex_exit(&ofile->f_mutex);
 410 }