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