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 2015 Nexenta Systems, Inc. All rights reserved.
14 */
15
16 /*
17 * Dispatch function for SMB2_CREATE
18 * [MS-SMB2] 2.2.13
19 */
20
21 #include <smbsrv/smb2_kproto.h>
22 #include <smbsrv/smb_fsops.h>
23
24 /*
25 * Some flags used locally to keep track of which Create Context
26 * names have been provided and/or requested.
27 */
28 #define CCTX_EA_BUFFER 1
29 #define CCTX_SD_BUFFER 2
30 #define CCTX_DH_REQUEST 4
31 #define CCTX_DH_RECONNECT 8
32 #define CCTX_ALLOCATION_SIZE 0x10
33 #define CCTX_QUERY_MAX_ACCESS 0x20
34 #define CCTX_TIMEWARP_TOKEN 0x40
35 #define CCTX_QUERY_ON_DISK_ID 0x80
36 #define CCTX_REQUEST_LEASE 0x100
37
38
39 typedef struct smb2_create_ctx_elem {
40 uint32_t cce_len;
41 mbuf_chain_t cce_mbc;
42 } smb2_create_ctx_elem_t;
43
44 typedef struct smb2_create_ctx {
45 uint_t cc_in_flags; /* CCTX_... */
46 uint_t cc_out_flags; /* CCTX_... */
47 /* Elements we may see in the request. */
48 smb2_create_ctx_elem_t cc_in_ext_attr;
49 smb2_create_ctx_elem_t cc_in_sec_desc;
50 smb2_create_ctx_elem_t cc_in_dh_request;
51 smb2_create_ctx_elem_t cc_in_dh_reconnect;
52 smb2_create_ctx_elem_t cc_in_alloc_size;
53 smb2_create_ctx_elem_t cc_in_time_warp;
54 smb2_create_ctx_elem_t cc_in_req_lease;
55 /* Elements we my place in the response */
56 smb2_create_ctx_elem_t cc_out_max_access;
57 smb2_create_ctx_elem_t cc_out_file_id;
58 } smb2_create_ctx_t;
59
60 static uint32_t smb2_decode_create_ctx(
61 mbuf_chain_t *, smb2_create_ctx_t *);
62 static uint32_t smb2_encode_create_ctx(
63 mbuf_chain_t *, smb2_create_ctx_t *);
64 static int smb2_encode_create_ctx_elem(
65 mbuf_chain_t *, smb2_create_ctx_elem_t *, uint32_t);
66 static void smb2_free_create_ctx(smb2_create_ctx_t *);
67
68 smb_sdrc_t
69 smb2_create(smb_request_t *sr)
70 {
71 smb_attr_t *attr;
72 smb2_create_ctx_elem_t *cce;
73 smb2_create_ctx_t cctx;
74 mbuf_chain_t cc_mbc;
75 smb_arg_open_t *op = &sr->arg.open;
76 smb_ofile_t *of = NULL;
77 uint16_t StructSize;
78 uint8_t SecurityFlags;
79 uint8_t OplockLevel;
80 uint32_t ImpersonationLevel;
81 uint64_t SmbCreateFlags;
82 uint64_t Reserved4;
83 uint16_t NameOffset;
84 uint16_t NameLength;
85 uint32_t CreateCtxOffset;
86 uint32_t CreateCtxLength;
87 smb2fid_t smb2fid;
88 uint32_t status;
89 int skip;
90 int rc = 0;
91
92 bzero(&cctx, sizeof (cctx));
93 bzero(&cc_mbc, sizeof (cc_mbc));
94
95 /*
96 * Paranoia. This will set sr->fid_ofile, so
97 * if we already have one, release it now.
98 */
99 if (sr->fid_ofile != NULL) {
100 smb_ofile_request_complete(sr->fid_ofile);
101 smb_ofile_release(sr->fid_ofile);
102 sr->fid_ofile = NULL;
103 }
104
105 /*
106 * SMB2 Create request
107 */
108 rc = smb_mbc_decodef(
109 &sr->smb_data, "wbblqqlllllwwll",
110 &StructSize, /* w */
111 &SecurityFlags, /* b */
112 &OplockLevel, /* b */
113 &ImpersonationLevel, /* l */
114 &SmbCreateFlags, /* q */
115 &Reserved4, /* q */
116 &op->desired_access, /* l */
117 &op->dattr, /* l */
118 &op->share_access, /* l */
119 &op->create_disposition, /* l */
120 &op->create_options, /* l */
121 &NameOffset, /* w */
122 &NameLength, /* w */
123 &CreateCtxOffset, /* l */
124 &CreateCtxLength); /* l */
125 if (rc != 0 || StructSize != 57)
126 return (SDRC_ERROR);
127
128 /*
129 * We're normally positioned at the path name now,
130 * but there could be some padding before it.
131 */
132 skip = (NameOffset + sr->smb2_cmd_hdr) -
133 sr->smb_data.chain_offset;
134 if (skip < 0) {
135 status = NT_STATUS_OBJECT_PATH_INVALID;
136 goto errout;
137 }
138 if (skip > 0)
139 (void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
140
141 /*
142 * Get the path name
143 */
144 if (NameLength >= SMB_MAXPATHLEN) {
145 status = NT_STATUS_OBJECT_PATH_INVALID;
146 goto errout;
147 }
148 if (NameLength == 0) {
149 op->fqi.fq_path.pn_path = "\\";
150 } else {
151 rc = smb_mbc_decodef(&sr->smb_data, "%#U", sr,
152 NameLength, &op->fqi.fq_path.pn_path);
153 if (rc) {
154 status = NT_STATUS_OBJECT_PATH_INVALID;
155 goto errout;
156 }
157 }
158 op->fqi.fq_dnode = sr->tid_tree->t_snode;
159
160 switch (OplockLevel) {
161 case SMB2_OPLOCK_LEVEL_NONE:
162 op->op_oplock_level = SMB_OPLOCK_NONE;
163 break;
164 case SMB2_OPLOCK_LEVEL_II:
165 op->op_oplock_level = SMB_OPLOCK_LEVEL_II;
166 break;
167 case SMB2_OPLOCK_LEVEL_EXCLUSIVE:
168 op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE;
169 break;
170 case SMB2_OPLOCK_LEVEL_BATCH:
171 op->op_oplock_level = SMB_OPLOCK_BATCH;
172 break;
173 case SMB2_OPLOCK_LEVEL_LEASE:
174 status = NT_STATUS_INVALID_PARAMETER;
175 goto errout;
176 }
177 op->op_oplock_levelII = B_TRUE;
178
179 /*
180 * ImpersonationLevel (spec. says ignore)
181 * SmbCreateFlags (spec. says ignore)
182 */
183
184 if ((op->create_options & FILE_DELETE_ON_CLOSE) &&
185 !(op->desired_access & DELETE)) {
186 status = NT_STATUS_INVALID_PARAMETER;
187 goto errout;
188 }
189 if (op->create_disposition > FILE_MAXIMUM_DISPOSITION) {
190 status = NT_STATUS_INVALID_PARAMETER;
191 goto errout;
192 }
193
194 if (op->dattr & FILE_FLAG_WRITE_THROUGH)
195 op->create_options |= FILE_WRITE_THROUGH;
196 if (op->dattr & FILE_FLAG_DELETE_ON_CLOSE)
197 op->create_options |= FILE_DELETE_ON_CLOSE;
198 if (op->dattr & FILE_FLAG_BACKUP_SEMANTICS)
199 op->create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
200 if (op->create_options & FILE_OPEN_FOR_BACKUP_INTENT)
201 sr->user_cr = smb_user_getprivcred(sr->uid_user);
202
203 /*
204 * If there is a "Create Context" payload, decode it.
205 * This may carry things like a security descriptor,
206 * extended attributes, etc. to be used in create.
207 *
208 * The create ctx buffer must start after the headers
209 * and file name, and must be 8-byte aligned.
210 */
211 if (CreateCtxLength != 0) {
212 if ((CreateCtxOffset & 7) != 0 ||
213 (CreateCtxOffset + sr->smb2_cmd_hdr) <
214 sr->smb_data.chain_offset) {
215 status = NT_STATUS_INVALID_PARAMETER;
216 goto errout;
217 }
218
219 rc = MBC_SHADOW_CHAIN(&cc_mbc, &sr->smb_data,
220 sr->smb2_cmd_hdr + CreateCtxOffset, CreateCtxLength);
221 if (rc) {
222 status = NT_STATUS_INVALID_PARAMETER;
223 goto errout;
224 }
225 status = smb2_decode_create_ctx(&cc_mbc, &cctx);
226 if (status)
227 goto errout;
228
229 if (cctx.cc_in_flags & CCTX_EA_BUFFER) {
230 status = NT_STATUS_EAS_NOT_SUPPORTED;
231 goto errout;
232 }
233
234 if (cctx.cc_in_flags & CCTX_SD_BUFFER) {
235 smb_sd_t sd;
236 cce = &cctx.cc_in_sec_desc;
237 status = smb_decode_sd(
238 &cce->cce_mbc, &sd);
239 if (status)
240 goto errout;
241 op->sd = kmem_alloc(sizeof (sd), KM_SLEEP);
242 *op->sd = sd;
243 }
244
245 if (cctx.cc_in_flags & CCTX_ALLOCATION_SIZE) {
246 cce = &cctx.cc_in_alloc_size;
247 rc = smb_mbc_decodef(&cce->cce_mbc, "q", &op->dsize);
248 if (rc) {
249 status = NT_STATUS_INVALID_PARAMETER;
250 goto errout;
251 }
252 }
253
254 /*
255 * Support for opening "Previous Versions".
256 * [MS-SMB2] 2.2.13.2.7 Data is an NT time.
257 */
258 if (cctx.cc_in_flags & CCTX_TIMEWARP_TOKEN) {
259 uint64_t timewarp;
260 cce = &cctx.cc_in_time_warp;
261 status = smb_mbc_decodef(&cce->cce_mbc,
262 "q", &timewarp);
263 if (status)
264 goto errout;
265 smb_time_nt_to_unix(timewarp, &op->timewarp);
266 op->create_timewarp = B_TRUE;
267 }
268 }
269
270 /*
271 * The real open call. Note: this gets attributes into
272 * op->fqi.fq_fattr (SMB_AT_ALL). We need those below.
273 */
274 status = smb_common_open(sr);
275 if (status != NT_STATUS_SUCCESS)
276 goto errout;
277 attr = &op->fqi.fq_fattr;
278
279 /*
280 * Convert the negotiate Oplock level back into
281 * SMB2 encoding form.
282 */
283 switch (op->op_oplock_level) {
284 default:
285 case SMB_OPLOCK_NONE:
286 OplockLevel = SMB2_OPLOCK_LEVEL_NONE;
287 break;
288 case SMB_OPLOCK_LEVEL_II:
289 OplockLevel = SMB2_OPLOCK_LEVEL_II;
290 break;
291 case SMB_OPLOCK_EXCLUSIVE:
292 OplockLevel = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
293 break;
294 case SMB_OPLOCK_BATCH:
295 OplockLevel = SMB2_OPLOCK_LEVEL_BATCH;
296 break;
297 }
298
299 /*
300 * NB: after the above smb_common_open() success,
301 * we have a handle allocated (sr->fid_ofile).
302 * If we don't return success, we must close it.
303 *
304 * Using sr->smb_fid as the file handle for now,
305 * though it could later be something larger,
306 * (16 bytes) similar to an NFSv4 open handle.
307 */
308 of = sr->fid_ofile;
309 smb2fid.persistent = 0;
310 smb2fid.temporal = sr->smb_fid;
311
312 switch (sr->tid_tree->t_res_type & STYPE_MASK) {
313 case STYPE_DISKTREE:
314 case STYPE_PRINTQ:
315 if (op->create_options & FILE_DELETE_ON_CLOSE)
316 smb_ofile_set_delete_on_close(of);
317 break;
318 }
319
320 /*
321 * Build the Create Context to return; first the
322 * per-element parts, then the aggregated buffer.
323 *
324 * No response for these:
325 * CCTX_EA_BUFFER
326 * CCTX_SD_BUFFER
327 * CCTX_ALLOCATION_SIZE
328 * CCTX_TIMEWARP_TOKEN
329 *
330 * We don't handle these yet.
331 * CCTX_DH_REQUEST
332 * CCTX_DH_RECONNECT
333 * CCTX_REQUEST_LEASE
334 */
335 if (cctx.cc_in_flags & CCTX_QUERY_MAX_ACCESS) {
336 cce = &cctx.cc_out_max_access;
337 uint32_t MaxAccess = 0;
338 if (of->f_node != NULL) {
339 smb_fsop_eaccess(sr, of->f_cr, of->f_node, &MaxAccess);
340 }
341 MaxAccess |= of->f_granted_access;
342 cce->cce_len = 8;
343 cce->cce_mbc.max_bytes = 8;
344 (void) smb_mbc_encodef(&cce->cce_mbc,
345 "ll", 0, MaxAccess);
346 cctx.cc_out_flags |= CCTX_QUERY_MAX_ACCESS;
347 }
348 if ((cctx.cc_in_flags & CCTX_QUERY_ON_DISK_ID) != 0 &&
349 of->f_node != NULL) {
350 cce = &cctx.cc_out_file_id;
351 fsid_t fsid;
352
353 fsid = SMB_NODE_FSID(of->f_node);
354
355 cce->cce_len = 32;
356 cce->cce_mbc.max_bytes = 32;
357 (void) smb_mbc_encodef(
358 &cce->cce_mbc, "qll.15.",
359 op->fileid, /* q */
360 fsid.val[0], /* l */
361 fsid.val[1]); /* l */
362 /* reserved (16 bytes) .15. */
363 cctx.cc_out_flags |= CCTX_QUERY_ON_DISK_ID;
364 }
365 if (cctx.cc_out_flags) {
366 sr->raw_data.max_bytes = smb2_max_trans;
367 status = smb2_encode_create_ctx(&sr->raw_data, &cctx);
368 if (status)
369 goto errout;
370 }
371
372 /*
373 * SMB2 Create reply
374 */
375 rc = smb_mbc_encodef(
376 &sr->reply,
377 "wb.lTTTTqqllqqll",
378 89, /* StructSize */ /* w */
379 OplockLevel, /* b */
380 op->action_taken, /* l */
381 &attr->sa_crtime, /* T */
382 &attr->sa_vattr.va_atime, /* T */
383 &attr->sa_vattr.va_mtime, /* T */
384 &attr->sa_vattr.va_ctime, /* T */
385 attr->sa_allocsz, /* q */
386 attr->sa_vattr.va_size, /* q */
387 attr->sa_dosattr, /* l */
388 0, /* reserved2 */ /* l */
389 smb2fid.persistent, /* q */
390 smb2fid.temporal, /* q */
391 0, /* CreateCtxOffset l */
392 0); /* CreateCtxLength l */
393 if (rc != 0) {
394 status = NT_STATUS_UNSUCCESSFUL;
395 goto errout;
396 }
397
398 CreateCtxOffset = sr->reply.chain_offset - sr->smb2_reply_hdr;
399 CreateCtxLength = MBC_LENGTH(&sr->raw_data);
400 if (CreateCtxLength != 0) {
401 /*
402 * Overwrite CreateCtxOffset, CreateCtxLength, pad
403 */
404 sr->reply.chain_offset -= 8;
405 rc = smb_mbc_encodef(
406 &sr->reply,
407 "ll#C",
408 CreateCtxOffset, /* l */
409 CreateCtxLength, /* l */
410 CreateCtxLength, /* # */
411 &sr->raw_data); /* C */
412 if (rc != 0) {
413 status = NT_STATUS_UNSUCCESSFUL;
414 goto errout;
415 }
416 } else {
417 (void) smb_mbc_encodef(&sr->reply, ".");
418 }
419 return (SDRC_SUCCESS);
420
421 errout:
422 if (of != NULL)
423 smb_ofile_close(of, 0);
424 if (cctx.cc_out_flags)
425 smb2_free_create_ctx(&cctx);
426 smb2sr_put_error(sr, status);
427 return (SDRC_SUCCESS);
428 }
429
430 /*
431 * Decode an SMB2 Create Context buffer into our internal form.
432 * No policy decisions about what's supported here, just decode.
433 */
434 static uint32_t
435 smb2_decode_create_ctx(mbuf_chain_t *in_mbc, smb2_create_ctx_t *cc)
436 {
437 smb2_create_ctx_elem_t *cce;
438 mbuf_chain_t name_mbc;
439 union {
440 uint32_t i;
441 char ch[4];
442 } cc_name;
443 uint32_t status;
444 int32_t next_off;
445 uint32_t data_len;
446 uint16_t data_off;
447 uint16_t name_off;
448 uint16_t name_len;
449 int top_offset;
450 int rc;
451
452 status = NT_STATUS_INVALID_PARAMETER;
453 for (;;) {
454 cce = NULL;
455 top_offset = in_mbc->chain_offset;
456 rc = smb_mbc_decodef(
457 in_mbc,
458 "lww..wl",
459 &next_off, /* l */
460 &name_off, /* w */
461 &name_len, /* w */
462 /* reserved .. */
463 &data_off, /* w */
464 &data_len); /* l */
465 if (rc)
466 break;
467
468 /*
469 * The Create Context "name", per [MS-SMB] 2.2.13.2
470 * They're defined as network-order integers for our
471 * switch below. We don't have routines to decode
503 case SMB2_CREATE_ALLOCATION_SIZE: /* ("AISi") */
504 cc->cc_in_flags |= CCTX_ALLOCATION_SIZE;
505 cce = &cc->cc_in_alloc_size;
506 break;
507 case SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQ: /* ("MxAc") */
508 cc->cc_in_flags |= CCTX_QUERY_MAX_ACCESS;
509 /* no input data for this */
510 break;
511 case SMB2_CREATE_TIMEWARP_TOKEN: /* ("TWrp") */
512 cc->cc_in_flags |= CCTX_TIMEWARP_TOKEN;
513 cce = &cc->cc_in_time_warp;
514 break;
515 case SMB2_CREATE_QUERY_ON_DISK_ID: /* ("QFid") */
516 cc->cc_in_flags |= CCTX_QUERY_ON_DISK_ID;
517 /* no input data for this */
518 break;
519 case SMB2_CREATE_REQUEST_LEASE: /* ("RqLs") */
520 cc->cc_in_flags |= CCTX_REQUEST_LEASE;
521 cce = &cc->cc_in_req_lease;
522 break;
523 default:
524 /*
525 * Unknown create context values are normal, and
526 * should be ignored. However, in debug mode,
527 * let's log them so we know which ones we're
528 * not handling (and may want to add).
529 */
530 #ifdef DEBUG
531 cmn_err(CE_NOTE, "unknown create context ID 0x%x",
532 cc_name.i);
533 #endif
534 cce = NULL;
535 break;
536 }
537
538 if (cce != NULL && data_len != 0) {
539 if ((data_off & 7) != 0)
540 break;
541 if ((top_offset + data_off) < in_mbc->chain_offset)
542 break;
543 rc = MBC_SHADOW_CHAIN(&cce->cce_mbc, in_mbc,
544 top_offset + data_off, data_len);
545 if (rc)
546 break;
547 cce->cce_len = data_len;
548 }
549
550 if (next_off == 0) {
551 /* Normal loop termination */
552 status = 0;
553 break;
554 }
555
556 if ((next_off & 7) != 0)
557 break;
558 if ((top_offset + next_off) < in_mbc->chain_offset)
559 break;
560 if ((top_offset + next_off) > in_mbc->max_bytes)
561 break;
562 in_mbc->chain_offset = top_offset + next_off;
563 }
564
565 return (status);
566 }
567
568 /*
569 * Encode an SMB2 Create Context buffer from our internal form.
570 */
571 /* ARGSUSED */
572 static uint32_t
573 smb2_encode_create_ctx(mbuf_chain_t *mbc, smb2_create_ctx_t *cc)
574 {
575 smb2_create_ctx_elem_t *cce;
576 int last_top = -1;
577 int rc;
578
579 if (cc->cc_out_flags & CCTX_QUERY_MAX_ACCESS) {
580 cce = &cc->cc_out_max_access;
581 last_top = mbc->chain_offset;
582 rc = smb2_encode_create_ctx_elem(mbc, cce,
583 SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQ);
584 if (rc)
585 return (NT_STATUS_INTERNAL_ERROR);
586 (void) smb_mbc_poke(mbc, last_top, "l",
587 mbc->chain_offset - last_top);
588 }
589
590 if (cc->cc_out_flags & CCTX_QUERY_ON_DISK_ID) {
591 cce = &cc->cc_out_file_id;
592 last_top = mbc->chain_offset;
593 rc = smb2_encode_create_ctx_elem(mbc, cce,
594 SMB2_CREATE_QUERY_ON_DISK_ID);
595 if (rc)
596 return (NT_STATUS_INTERNAL_ERROR);
597 (void) smb_mbc_poke(mbc, last_top, "l",
598 mbc->chain_offset - last_top);
599 }
600
601 if (last_top >= 0)
602 (void) smb_mbc_poke(mbc, last_top, "l", 0);
603
604 return (0);
605 }
606
607 static int
608 smb2_encode_create_ctx_elem(mbuf_chain_t *out_mbc,
609 smb2_create_ctx_elem_t *cce, uint32_t id)
610 {
611 union {
612 uint32_t i;
613 char ch[4];
614 } cc_name;
615 int rc;
616
617 /* as above */
618 cc_name.i = htonl(id);
619
620 /*
621 * This is the header, per [MS-SMB2] 2.2.13.2
622 * Sorry about the fixed offsets. We know we'll
623 * layout the data part as [name, payload] and
624 * name is a fixed length, so this easy.
625 * The final layout looks like this:
626 * a: this header (16 bytes)
627 * b: the name (4 bytes, 4 pad)
628 * c: the payload (variable)
629 *
630 * Note that "Next elem." is filled in later.
631 */
632 rc = smb_mbc_encodef(
633 out_mbc, "lwwwwl",
634 0, /* Next offset l */
635 16, /* NameOffset w */
636 4, /* NameLength w */
637 0, /* Reserved w */
638 24, /* DataOffset w */
639 cce->cce_len); /* l */
640 if (rc)
641 return (rc);
642
643 /*
644 * Now the "name" and payload.
645 */
646 rc = smb_mbc_encodef(
647 out_mbc, "4c4.#C",
648 cc_name.ch, /* 4c4. */
649 cce->cce_len, /* # */
650 &cce->cce_mbc); /* C */
651
652 return (rc);
653 }
654
655 static void
656 smb2_free_create_ctx(smb2_create_ctx_t *cc)
657 {
658 smb2_create_ctx_elem_t *cce;
659
660 if (cc->cc_out_flags & CCTX_QUERY_MAX_ACCESS) {
661 cce = &cc->cc_out_max_access;
662 MBC_FLUSH(&cce->cce_mbc);
663 }
664 if (cc->cc_out_flags & CCTX_QUERY_ON_DISK_ID) {
665 cce = &cc->cc_out_file_id;
666 MBC_FLUSH(&cce->cce_mbc);
667 }
668 }
|
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 2018 Nexenta Systems, Inc. All rights reserved.
14 */
15
16 /*
17 * Dispatch function for SMB2_CREATE
18 * [MS-SMB2] 2.2.13
19 */
20
21 #include <smbsrv/smb2_kproto.h>
22 #include <smbsrv/smb_fsops.h>
23
24 #define DH_PERSISTENT SMB2_DHANDLE_FLAG_PERSISTENT
25
26 /*
27 * Compile-time check that the SMB2_LEASE_... definitions
28 * match the (internal) equivalents from ntifs.h
29 */
30 #if SMB2_LEASE_NONE != OPLOCK_LEVEL_NONE
31 #error "SMB2_LEASE_NONE"
32 #endif
33 #if SMB2_LEASE_READ_CACHING != OPLOCK_LEVEL_CACHE_READ
34 #error "SMB2_LEASE_READ_CACHING"
35 #endif
36 #if SMB2_LEASE_HANDLE_CACHING != OPLOCK_LEVEL_CACHE_HANDLE
37 #error "SMB2_LEASE_HANDLE_CACHING"
38 #endif
39 #if SMB2_LEASE_WRITE_CACHING != OPLOCK_LEVEL_CACHE_WRITE
40 #error "SMB2_LEASE_WRITE_CACHING"
41 #endif
42
43 /*
44 * Some flags used locally to keep track of which Create Context
45 * names have been provided and/or requested.
46 */
47 #define CCTX_EA_BUFFER 1
48 #define CCTX_SD_BUFFER 2
49 #define CCTX_DH_REQUEST 4
50 #define CCTX_DH_RECONNECT 8
51 #define CCTX_ALLOCATION_SIZE 0x10
52 #define CCTX_QUERY_MAX_ACCESS 0x20
53 #define CCTX_TIMEWARP_TOKEN 0x40
54 #define CCTX_QUERY_ON_DISK_ID 0x80
55 #define CCTX_REQUEST_LEASE 0x100
56 #define CCTX_AAPL_EXT 0x200
57 #define CCTX_DH_REQUEST_V2 0x400
58 #define CCTX_DH_RECONNECT_V2 0x800
59
60 typedef struct smb2_create_ctx_elem {
61 uint32_t cce_len;
62 mbuf_chain_t cce_mbc;
63 } smb2_create_ctx_elem_t;
64
65 typedef struct smb2_create_ctx {
66 mbuf_chain_t cc_in_mbc;
67 uint_t cc_in_flags; /* CCTX_... */
68 uint_t cc_out_flags; /* CCTX_... */
69 /* Elements we may see in the request. */
70 smb2_create_ctx_elem_t cc_in_ext_attr;
71 smb2_create_ctx_elem_t cc_in_sec_desc;
72 smb2_create_ctx_elem_t cc_in_dh_request;
73 smb2_create_ctx_elem_t cc_in_dh_reconnect;
74 smb2_create_ctx_elem_t cc_in_alloc_size;
75 smb2_create_ctx_elem_t cc_in_time_warp;
76 smb2_create_ctx_elem_t cc_in_req_lease;
77 smb2_create_ctx_elem_t cc_in_aapl;
78 smb2_create_ctx_elem_t cc_in_dh_request_v2;
79 smb2_create_ctx_elem_t cc_in_dh_reconnect_v2;
80 /* Elements we my place in the response */
81 smb2_create_ctx_elem_t cc_out_max_access;
82 smb2_create_ctx_elem_t cc_out_file_id;
83 smb2_create_ctx_elem_t cc_out_aapl;
84 smb2_create_ctx_elem_t cc_out_req_lease;
85 smb2_create_ctx_elem_t cc_out_dh_request;
86 smb2_create_ctx_elem_t cc_out_dh_request_v2;
87 } smb2_create_ctx_t;
88
89 static uint32_t smb2_decode_create_ctx(
90 smb_request_t *, smb2_create_ctx_t *);
91 static uint32_t smb2_encode_create_ctx(
92 smb_request_t *, smb2_create_ctx_t *);
93 static int smb2_encode_create_ctx_elem(
94 mbuf_chain_t *, smb2_create_ctx_elem_t *, uint32_t);
95 static void smb2_free_create_ctx(smb2_create_ctx_t *);
96
97 int smb2_enable_dh = 1;
98
99 smb_sdrc_t
100 smb2_create(smb_request_t *sr)
101 {
102 smb_attr_t *attr;
103 smb2_create_ctx_elem_t *cce;
104 smb2_create_ctx_t cctx;
105 smb_arg_open_t *op = &sr->arg.open;
106 smb_ofile_t *of = NULL;
107 uint16_t StructSize;
108 uint8_t SecurityFlags;
109 uint32_t ImpersonationLevel;
110 uint64_t SmbCreateFlags;
111 uint64_t Reserved4;
112 uint16_t NameOffset;
113 uint16_t NameLength;
114 uint32_t CreateCtxOffset;
115 uint32_t CreateCtxLength;
116 smb2fid_t smb2fid = { 0, 0 };
117 uint32_t status;
118 int dh_flags;
119 int skip;
120 int rc = 0;
121
122 bzero(&cctx, sizeof (cctx));
123 op->create_ctx = &cctx; /* for debugging */
124
125 /*
126 * Paranoia. This will set sr->fid_ofile, so
127 * if we already have one, release it now.
128 */
129 if (sr->fid_ofile != NULL) {
130 smb_ofile_release(sr->fid_ofile);
131 sr->fid_ofile = NULL;
132 }
133
134 /*
135 * Decode the SMB2 Create request
136 *
137 * Most decode errors return SDRC_ERROR, but
138 * for some we give a more specific error.
139 *
140 * In the "decode section" (starts here) any
141 * errors should either return SDRC_ERROR, or
142 * if any cleanup is needed, goto errout.
143 */
144 rc = smb_mbc_decodef(
145 &sr->smb_data, "wbblqqlllllwwll",
146 &StructSize, /* w */
147 &SecurityFlags, /* b */
148 &op->op_oplock_level, /* b */
149 &ImpersonationLevel, /* l */
150 &SmbCreateFlags, /* q */
151 &Reserved4, /* q */
152 &op->desired_access, /* l */
153 &op->dattr, /* l */
154 &op->share_access, /* l */
155 &op->create_disposition, /* l */
156 &op->create_options, /* l */
157 &NameOffset, /* w */
158 &NameLength, /* w */
159 &CreateCtxOffset, /* l */
160 &CreateCtxLength); /* l */
161 if (rc != 0 || StructSize != 57)
162 return (SDRC_ERROR);
163
164 /*
165 * We're normally positioned at the path name now,
166 * but there could be some padding before it.
167 */
168 skip = (NameOffset + sr->smb2_cmd_hdr) -
169 sr->smb_data.chain_offset;
170 if (skip < 0)
171 return (SDRC_ERROR);
172 if (skip > 0)
173 (void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
174
175 /*
176 * Get the path name
177 *
178 * Name too long is not technically a decode error,
179 * but it's very rare, so we'll just skip the
180 * dtrace probes for this error case.
181 */
182 if (NameLength >= SMB_MAXPATHLEN) {
183 status = NT_STATUS_OBJECT_PATH_INVALID;
184 goto errout;
185 }
186 if (NameLength == 0) {
187 op->fqi.fq_path.pn_path = "\\";
188 } else {
189 rc = smb_mbc_decodef(&sr->smb_data, "%#U", sr,
190 NameLength, &op->fqi.fq_path.pn_path);
191 if (rc) {
192 status = NT_STATUS_OBJECT_PATH_INVALID;
193 goto errout;
194 }
195 }
196 op->fqi.fq_dnode = sr->tid_tree->t_snode;
197
198 /*
199 * If there is a "Create Context" payload, decode it.
200 * This may carry things like a security descriptor,
201 * extended attributes, etc. to be used in create.
202 *
203 * The create ctx buffer must start after the headers
204 * and file name, and must be 8-byte aligned.
205 */
206 if (CreateCtxLength != 0) {
207 if ((CreateCtxOffset & 7) != 0 ||
208 (CreateCtxOffset + sr->smb2_cmd_hdr) <
209 sr->smb_data.chain_offset) {
210 status = NT_STATUS_INVALID_PARAMETER;
211 goto errout;
212 }
213
214 rc = MBC_SHADOW_CHAIN(&cctx.cc_in_mbc, &sr->smb_data,
215 sr->smb2_cmd_hdr + CreateCtxOffset, CreateCtxLength);
216 if (rc) {
217 status = NT_STATUS_INVALID_PARAMETER;
218 goto errout;
219 }
220 status = smb2_decode_create_ctx(sr, &cctx);
221 if (status)
222 goto errout;
223 }
224
225 /*
226 * Everything is decoded into some internal form, so
227 * in this probe one can look at sr->arg.open etc.
228 *
229 * This marks the end of the "decode" section and the
230 * beginning of the "body" section. Any errors in
231 * this section should use: goto cmd_done (which is
232 * just before the dtrace "done" probe).
233 */
234 DTRACE_SMB2_START(op__Create, smb_request_t *, sr); /* arg.open */
235
236 /*
237 * Process the incoming create contexts (already decoded),
238 * that need action before the open, starting with the
239 * Durable Handle ones, which may override others.
240 */
241
242 /*
243 * Only disk trees get durable handles.
244 */
245 if (smb2_enable_dh == 0 ||
246 (sr->tid_tree->t_res_type & STYPE_MASK) != STYPE_DISKTREE) {
247 cctx.cc_in_flags &=
248 ~(CCTX_DH_REQUEST | CCTX_DH_REQUEST_V2 |
249 CCTX_DH_RECONNECT | CCTX_DH_RECONNECT_V2);
250 }
251
252 /*
253 * DH v2 is only valid in SMB3.0 and later.
254 * If seen in earlier dialects, ignore.
255 */
256 if (sr->session->dialect < SMB_VERS_3_0) {
257 cctx.cc_in_flags &=
258 ~(CCTX_DH_REQUEST_V2|CCTX_DH_RECONNECT_V2);
259 }
260
261 /*
262 * It is an error to specify more than one Durable Handle
263 * operation in a single create, except when only the v1
264 * REQUEST and RECONNECT operations are specified. In that
265 * case, the v1 REQUEST is ignored.
266 */
267 dh_flags = cctx.cc_in_flags &
268 (CCTX_DH_REQUEST | CCTX_DH_REQUEST_V2 |
269 CCTX_DH_RECONNECT | CCTX_DH_RECONNECT_V2);
270 if ((dh_flags & (dh_flags - 1)) != 0 &&
271 dh_flags != (CCTX_DH_REQUEST|CCTX_DH_RECONNECT)) {
272 status = NT_STATUS_INVALID_PARAMETER;
273 goto cmd_done;
274 }
275
276 /*
277 * Reconnect is special in MANY ways, including the
278 * somewhat surprising (specified) behavior that
279 * most other creat parameters are ignored, and
280 * many create context types are ignored too.
281 */
282 op->dh_vers = SMB2_NOT_DURABLE;
283 if ((cctx.cc_in_flags &
284 (CCTX_DH_RECONNECT|CCTX_DH_RECONNECT_V2)) != 0) {
285
286 if ((cctx.cc_in_flags & CCTX_DH_RECONNECT_V2) != 0)
287 op->dh_vers = SMB2_DURABLE_V2;
288 else
289 op->dh_vers = SMB2_DURABLE_V1;
290
291 /* Ignore these create contexts. */
292 cctx.cc_in_flags &=
293 ~(CCTX_DH_REQUEST |
294 CCTX_DH_REQUEST_V2 |
295 CCTX_EA_BUFFER |
296 CCTX_SD_BUFFER |
297 CCTX_ALLOCATION_SIZE |
298 CCTX_TIMEWARP_TOKEN |
299 CCTX_QUERY_ON_DISK_ID);
300
301 /*
302 * Reconnect check needs to know if a lease was requested.
303 * The requested oplock level is ignored in reconnect, so
304 * using op_oplock_level to convey this info.
305 */
306 if (cctx.cc_in_flags & CCTX_REQUEST_LEASE)
307 op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
308 else
309 op->op_oplock_level = 0;
310
311 status = smb2_dh_reconnect(sr);
312 if (status != NT_STATUS_SUCCESS)
313 goto cmd_done;
314
315 /*
316 * Skip most open execution during reconnect,
317 * but need (reclaimed) oplock state in *op.
318 */
319 of = sr->fid_ofile;
320
321 op->op_oplock_state = of->f_oplock.og_state;
322 if (of->f_lease != NULL) {
323 smb_lease_t *ls = of->f_lease;
324
325 op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
326 op->lease_state = ls->ls_state &
327 OPLOCK_LEVEL_CACHE_MASK;
328 op->lease_flags = (ls->ls_breaking != 0) ?
329 SMB2_LEASE_FLAG_BREAK_IN_PROGRESS : 0;
330 op->lease_epoch = ls->ls_epoch;
331 op->lease_version = ls->ls_version;
332 } else {
333 switch (op->op_oplock_state & OPLOCK_LEVEL_TYPE_MASK) {
334 default:
335 case OPLOCK_LEVEL_NONE:
336 op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
337 break;
338 case OPLOCK_LEVEL_TWO:
339 op->op_oplock_level = SMB2_OPLOCK_LEVEL_II;
340 break;
341 case OPLOCK_LEVEL_ONE:
342 op->op_oplock_level =
343 SMB2_OPLOCK_LEVEL_EXCLUSIVE;
344 break;
345 case OPLOCK_LEVEL_BATCH:
346 op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
347 break;
348 }
349 }
350
351 goto reconnect_done;
352 }
353
354 /*
355 * Real create (of a new handle, not reconnect)
356 */
357
358 /*
359 * Validate the requested oplock level.
360 * Conversion to internal form is in smb2_oplock_acquire()
361 */
362 switch (op->op_oplock_level) {
363 case SMB2_OPLOCK_LEVEL_NONE: /* OPLOCK_LEVEL_NONE */
364 case SMB2_OPLOCK_LEVEL_II: /* OPLOCK_LEVEL_TWO */
365 case SMB2_OPLOCK_LEVEL_EXCLUSIVE: /* OPLOCK_LEVEL_ONE */
366 case SMB2_OPLOCK_LEVEL_BATCH: /* OPLOCK_LEVEL_BATCH */
367 /*
368 * Ignore lease create context (if any)
369 */
370 cctx.cc_in_flags &= ~CCTX_REQUEST_LEASE;
371 break;
372
373 case SMB2_OPLOCK_LEVEL_LEASE: /* OPLOCK_LEVEL_GRANULAR */
374 /*
375 * Require a lease create context.
376 */
377 if ((cctx.cc_in_flags & CCTX_REQUEST_LEASE) == 0) {
378 cmn_err(CE_NOTE, "smb2:create, oplock=ff and no lease");
379 status = NT_STATUS_INVALID_PARAMETER;
380 goto cmd_done;
381 }
382
383 /*
384 * Validate lease request state
385 * Only a few valid combinations.
386 */
387 switch (op->lease_state) {
388 case SMB2_LEASE_NONE:
389 case SMB2_LEASE_READ_CACHING:
390 case SMB2_LEASE_READ_CACHING | SMB2_LEASE_HANDLE_CACHING:
391 case SMB2_LEASE_READ_CACHING | SMB2_LEASE_WRITE_CACHING:
392 case SMB2_LEASE_READ_CACHING | SMB2_LEASE_WRITE_CACHING |
393 SMB2_LEASE_HANDLE_CACHING:
394 break;
395
396 default:
397 /*
398 * Invalid lease state flags
399 * Just force to "none".
400 */
401 op->lease_state = SMB2_LEASE_NONE;
402 break;
403 }
404 break;
405
406 default:
407 /* Unknown SMB2 oplock level. */
408 status = NT_STATUS_INVALID_PARAMETER;
409 goto cmd_done;
410 }
411
412 /*
413 * Only disk trees get oplocks or leases.
414 */
415 if ((sr->tid_tree->t_res_type & STYPE_MASK) != STYPE_DISKTREE) {
416 op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
417 cctx.cc_in_flags &= ~CCTX_REQUEST_LEASE;
418 }
419
420 if ((sr->tid_tree->t_flags & SMB_TREE_CA) == 0)
421 op->dh_v2_flags &= ~DH_PERSISTENT;
422
423 if ((cctx.cc_in_flags &
424 (CCTX_DH_REQUEST|CCTX_DH_REQUEST_V2)) != 0) {
425 if ((cctx.cc_in_flags & CCTX_DH_REQUEST_V2) != 0)
426 op->dh_vers = SMB2_DURABLE_V2;
427 else
428 op->dh_vers = SMB2_DURABLE_V1;
429 }
430
431 if (cctx.cc_in_flags & CCTX_EA_BUFFER) {
432 status = NT_STATUS_EAS_NOT_SUPPORTED;
433 goto cmd_done;
434 }
435
436 /*
437 * ImpersonationLevel (spec. says validate + ignore)
438 * SmbCreateFlags (spec. says ignore)
439 */
440
441 if ((op->create_options & FILE_DELETE_ON_CLOSE) &&
442 !(op->desired_access & DELETE)) {
443 status = NT_STATUS_INVALID_PARAMETER;
444 goto cmd_done;
445 }
446
447 if (op->dattr & FILE_FLAG_WRITE_THROUGH)
448 op->create_options |= FILE_WRITE_THROUGH;
449 if (op->dattr & FILE_FLAG_DELETE_ON_CLOSE)
450 op->create_options |= FILE_DELETE_ON_CLOSE;
451 if (op->dattr & FILE_FLAG_BACKUP_SEMANTICS)
452 op->create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
453 if (op->create_options & FILE_OPEN_FOR_BACKUP_INTENT)
454 sr->user_cr = smb_user_getprivcred(sr->uid_user);
455 if (op->create_disposition > FILE_MAXIMUM_DISPOSITION) {
456 status = NT_STATUS_INVALID_PARAMETER;
457 goto cmd_done;
458 }
459
460 /*
461 * The real open call. Note: this gets attributes into
462 * op->fqi.fq_fattr (SMB_AT_ALL). We need those below.
463 * When of != NULL, goto errout closes it.
464 */
465 status = smb_common_open(sr);
466 if (status != NT_STATUS_SUCCESS)
467 goto cmd_done;
468 of = sr->fid_ofile;
469
470 /*
471 * Set the "persistent" part of the file ID
472 * (only for DISK shares). Need this even for
473 * non-durable handles in case we get the ioctl
474 * to set "resiliency" on this handle.
475 */
476 if (of->f_ftype == SMB_FTYPE_DISK) {
477 if ((op->dh_v2_flags & DH_PERSISTENT) != 0)
478 smb_ofile_set_persistid_ph(of);
479 else
480 smb_ofile_set_persistid_dh(of);
481 }
482
483 /*
484 * [MS-SMB2] 3.3.5.9.8
485 * Handling the SMB2_CREATE_REQUEST_LEASE Create Context
486 */
487 if ((cctx.cc_in_flags & CCTX_REQUEST_LEASE) != 0) {
488 status = smb2_lease_create(sr, sr->session->clnt_uuid);
489 if (status != NT_STATUS_SUCCESS) {
490 if (op->action_taken == SMB_OACT_CREATED) {
491 smb_ofile_set_delete_on_close(sr, of);
492 }
493 goto cmd_done;
494 }
495 }
496 if (op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE) {
497 smb2_lease_acquire(sr);
498 } else if (op->op_oplock_level != SMB2_OPLOCK_LEVEL_NONE) {
499 smb2_oplock_acquire(sr);
500 }
501
502 /*
503 * Make this a durable open, but only if:
504 * (durable handle requested and...)
505 *
506 * 1. op_oplock_level == SMB2_OPLOCK_LEVEL_BATCH
507 * 2. A lease is requested with handle caching
508 * - for v1, the lease must not be on a directory
509 * 3. For v2, flags has "persistent" && tree->is_CA
510 * (when tree not CA, turned off persist above)
511 *
512 * Otherwise, DH requests are ignored, so we set
513 * dh_vers = not durable
514 */
515 if ((cctx.cc_in_flags &
516 (CCTX_DH_REQUEST|CCTX_DH_REQUEST_V2)) != 0 &&
517 smb_node_is_file(of->f_node) &&
518 ((op->dh_v2_flags & DH_PERSISTENT) != 0 ||
519 (op->op_oplock_level == SMB2_OPLOCK_LEVEL_BATCH) ||
520 (op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE &&
521 (op->lease_state & OPLOCK_LEVEL_CACHE_HANDLE) != 0))) {
522 /*
523 * OK, make this handle "durable"
524 */
525 if (op->dh_vers == SMB2_DURABLE_V2) {
526 (void) memcpy(of->dh_create_guid,
527 op->create_guid, UUID_LEN);
528
529 if ((op->dh_v2_flags & DH_PERSISTENT) != 0) {
530 if (smb2_dh_make_persistent(sr, of) == 0) {
531 of->dh_persist = B_TRUE;
532 } else {
533 op->dh_v2_flags = 0;
534 }
535 }
536 }
537 if (op->dh_vers != SMB2_NOT_DURABLE) {
538 uint32_t msto;
539
540 of->dh_vers = op->dh_vers;
541 of->dh_expire_time = 0;
542
543 /*
544 * Client may provide timeout=0 to request
545 * the default timeout (in mSec.)
546 */
547 msto = op->dh_timeout;
548 if (msto == 0) {
549 msto = (of->dh_persist) ?
550 smb2_persist_timeout :
551 smb2_dh_def_timeout;
552 }
553 if (msto > smb2_dh_max_timeout)
554 msto = smb2_dh_max_timeout;
555 op->dh_timeout = msto;
556 of->dh_timeout_offset = MSEC2NSEC(msto);
557 }
558 } else {
559 op->dh_vers = SMB2_NOT_DURABLE;
560 op->dh_v2_flags = 0;
561 }
562
563 /*
564 * NB: after the above smb_common_open() success,
565 * we have a handle allocated (sr->fid_ofile).
566 * If we don't return success, we must close it.
567 *
568 * Using sr->smb_fid as the file handle for now,
569 * though it could later be something larger,
570 * (16 bytes) similar to an NFSv4 open handle.
571 */
572 reconnect_done:
573 smb2fid.persistent = of->f_persistid;
574 smb2fid.temporal = sr->smb_fid;
575
576 switch (sr->tid_tree->t_res_type & STYPE_MASK) {
577 case STYPE_DISKTREE:
578 case STYPE_PRINTQ:
579 if (op->create_options & FILE_DELETE_ON_CLOSE)
580 smb_ofile_set_delete_on_close(sr, of);
581 break;
582 }
583
584 /*
585 * Process any outgoing create contexts that need work
586 * after the open succeeds. Encode happens later.
587 */
588 if (cctx.cc_in_flags & CCTX_QUERY_MAX_ACCESS) {
589 op->maximum_access = 0;
590 if (of->f_node != NULL) {
591 smb_fsop_eaccess(sr, of->f_cr, of->f_node,
592 &op->maximum_access);
593 }
594 op->maximum_access |= of->f_granted_access;
595 cctx.cc_out_flags |= CCTX_QUERY_MAX_ACCESS;
596 }
597
598 if ((cctx.cc_in_flags & CCTX_QUERY_ON_DISK_ID) != 0 &&
599 of->f_node != NULL) {
600 op->op_fsid = SMB_NODE_FSID(of->f_node);
601 cctx.cc_out_flags |= CCTX_QUERY_ON_DISK_ID;
602 }
603
604 if ((cctx.cc_in_flags & CCTX_AAPL_EXT) != 0) {
605 cce = &cctx.cc_out_aapl;
606 /*
607 * smb2_aapl_crctx has a variable response depending on
608 * what the incoming context looks like, so it does all
609 * the work of building cc_out_aapl, including setting
610 * cce_len, cce_mbc.max_bytes, and smb_mbc_encode.
611 * If we see errors getting this, simply omit it from
612 * the collection of returned create contexts.
613 */
614 status = smb2_aapl_crctx(sr,
615 &cctx.cc_in_aapl.cce_mbc, &cce->cce_mbc);
616 if (status == 0) {
617 cce->cce_len = cce->cce_mbc.chain_offset;
618 cctx.cc_out_flags |= CCTX_AAPL_EXT;
619 }
620 status = 0;
621 }
622
623 /*
624 * If a lease was requested, and we got one...
625 */
626 if ((cctx.cc_in_flags & CCTX_REQUEST_LEASE) != 0 &&
627 op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE)
628 cctx.cc_out_flags |= CCTX_REQUEST_LEASE;
629
630 /*
631 * If a durable handle was requested and we got one...
632 */
633 if ((cctx.cc_in_flags & CCTX_DH_REQUEST) != 0 &&
634 of->dh_vers == SMB2_DURABLE_V1) {
635 cctx.cc_out_flags |= CCTX_DH_REQUEST;
636 }
637 if ((cctx.cc_in_flags & CCTX_DH_REQUEST_V2) != 0 &&
638 of->dh_vers == SMB2_DURABLE_V2) {
639 cctx.cc_out_flags |= CCTX_DH_REQUEST_V2;
640 }
641
642 /*
643 * This marks the end of the "body" section and the
644 * beginning of the "encode" section. Any errors
645 * encoding the response should use: goto errout
646 */
647 cmd_done:
648 /* Want status visible in the done probe. */
649 sr->smb2_status = status;
650 DTRACE_SMB2_DONE(op__Create, smb_request_t *, sr);
651 if (status != NT_STATUS_SUCCESS)
652 goto errout;
653
654 /*
655 * Encode all the create contexts to return.
656 */
657 if (cctx.cc_out_flags) {
658 sr->raw_data.max_bytes = smb2_max_trans;
659 status = smb2_encode_create_ctx(sr, &cctx);
660 if (status)
661 goto errout;
662 }
663
664 /*
665 * Encode the SMB2 Create reply
666 */
667 attr = &op->fqi.fq_fattr;
668 rc = smb_mbc_encodef(
669 &sr->reply,
670 "wb.lTTTTqqllqqll",
671 89, /* StructSize */ /* w */
672 op->op_oplock_level, /* b */
673 op->action_taken, /* l */
674 &attr->sa_crtime, /* T */
675 &attr->sa_vattr.va_atime, /* T */
676 &attr->sa_vattr.va_mtime, /* T */
677 &attr->sa_vattr.va_ctime, /* T */
678 attr->sa_allocsz, /* q */
679 attr->sa_vattr.va_size, /* q */
680 attr->sa_dosattr, /* l */
681 0, /* reserved2 */ /* l */
682 smb2fid.persistent, /* q */
683 smb2fid.temporal, /* q */
684 0, /* CreateCtxOffset l */
685 0); /* CreateCtxLength l */
686 if (rc != 0) {
687 status = NT_STATUS_UNSUCCESSFUL;
688 goto errout;
689 }
690
691 CreateCtxOffset = sr->reply.chain_offset - sr->smb2_reply_hdr;
692 CreateCtxLength = MBC_LENGTH(&sr->raw_data);
693 if (CreateCtxLength != 0) {
694 /*
695 * Overwrite CreateCtxOffset, CreateCtxLength, pad
696 */
697 sr->reply.chain_offset -= 8;
698 rc = smb_mbc_encodef(
699 &sr->reply,
700 "ll#C",
701 CreateCtxOffset, /* l */
702 CreateCtxLength, /* l */
703 CreateCtxLength, /* # */
704 &sr->raw_data); /* C */
705 if (rc != 0) {
706 status = NT_STATUS_UNSUCCESSFUL;
707 goto errout;
708 }
709 } else {
710 (void) smb_mbc_encodef(&sr->reply, ".");
711 }
712
713 if (status != 0) {
714 errout:
715 if (of != NULL)
716 smb_ofile_close(of, 0);
717 smb2sr_put_error(sr, status);
718 }
719 if (op->sd != NULL) {
720 smb_sd_term(op->sd);
721 kmem_free(op->sd, sizeof (*op->sd));
722 }
723 if (cctx.cc_out_flags)
724 smb2_free_create_ctx(&cctx);
725
726 return (SDRC_SUCCESS);
727 }
728
729 /*
730 * Decode an SMB2 Create Context buffer into our internal form.
731 * Avoid policy decisions about what's supported here, just decode.
732 */
733 static uint32_t
734 smb2_decode_create_ctx(smb_request_t *sr, smb2_create_ctx_t *cc)
735 {
736 smb_arg_open_t *op = &sr->arg.open;
737 smb2_create_ctx_elem_t *cce;
738 mbuf_chain_t *in_mbc = &cc->cc_in_mbc;
739 mbuf_chain_t name_mbc;
740 union {
741 uint32_t i;
742 char ch[4];
743 } cc_name;
744 uint32_t status;
745 int32_t next_off;
746 uint32_t data_len;
747 uint16_t data_off;
748 uint16_t name_off;
749 uint16_t name_len;
750 int top_offset;
751 int rc;
752
753 /*
754 * Any break from the loop below before we've decoded
755 * the entire create context means it was malformatted,
756 * so we should return INVALID_PARAMETER.
757 */
758 status = NT_STATUS_INVALID_PARAMETER;
759 for (;;) {
760 cce = NULL;
761 top_offset = in_mbc->chain_offset;
762 rc = smb_mbc_decodef(
763 in_mbc,
764 "lww..wl",
765 &next_off, /* l */
766 &name_off, /* w */
767 &name_len, /* w */
768 /* reserved .. */
769 &data_off, /* w */
770 &data_len); /* l */
771 if (rc)
772 break;
773
774 /*
775 * The Create Context "name", per [MS-SMB] 2.2.13.2
776 * They're defined as network-order integers for our
777 * switch below. We don't have routines to decode
809 case SMB2_CREATE_ALLOCATION_SIZE: /* ("AISi") */
810 cc->cc_in_flags |= CCTX_ALLOCATION_SIZE;
811 cce = &cc->cc_in_alloc_size;
812 break;
813 case SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQ: /* ("MxAc") */
814 cc->cc_in_flags |= CCTX_QUERY_MAX_ACCESS;
815 /* no input data for this */
816 break;
817 case SMB2_CREATE_TIMEWARP_TOKEN: /* ("TWrp") */
818 cc->cc_in_flags |= CCTX_TIMEWARP_TOKEN;
819 cce = &cc->cc_in_time_warp;
820 break;
821 case SMB2_CREATE_QUERY_ON_DISK_ID: /* ("QFid") */
822 cc->cc_in_flags |= CCTX_QUERY_ON_DISK_ID;
823 /* no input data for this */
824 break;
825 case SMB2_CREATE_REQUEST_LEASE: /* ("RqLs") */
826 cc->cc_in_flags |= CCTX_REQUEST_LEASE;
827 cce = &cc->cc_in_req_lease;
828 break;
829 case SMB2_CREATE_CTX_AAPL: /* ("AAPL") */
830 cc->cc_in_flags |= CCTX_AAPL_EXT;
831 cce = &cc->cc_in_aapl;
832 break;
833 case SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2: /* ("DH2Q") */
834 cc->cc_in_flags |= CCTX_DH_REQUEST_V2;
835 cce = &cc->cc_in_dh_request_v2;
836 break;
837 case SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2: /* ("DH2C") */
838 cc->cc_in_flags |= CCTX_DH_RECONNECT_V2;
839 cce = &cc->cc_in_dh_reconnect_v2;
840 break;
841 case 0x9ccbcf9e: /* SVHDX_OPEN_DEVICE_CONTEXT */
842 /* 9ccbcf9e 04c1e643 980e158d a1f6ec83 */
843 /* silently ignore */
844 break;
845 default:
846 /*
847 * Unknown create context values are normal, and
848 * should be ignored. However, in debug mode,
849 * let's log them so we know which ones we're
850 * not handling (and may want to add).
851 */
852 #ifdef DEBUG
853 cmn_err(CE_NOTE, "unknown create context ID 0x%x",
854 cc_name.i);
855 #endif
856 cce = NULL;
857 break;
858 }
859
860 if (cce == NULL || data_len == 0)
861 goto next_cc;
862
863 if ((data_off & 7) != 0)
864 break;
865 if ((top_offset + data_off) < in_mbc->chain_offset)
866 break;
867 rc = MBC_SHADOW_CHAIN(&cce->cce_mbc, in_mbc,
868 top_offset + data_off, data_len);
869 if (rc)
870 break;
871 cce->cce_len = data_len;
872
873 /*
874 * Additonal decoding for some create contexts.
875 */
876 switch (cc_name.i) {
877 uint64_t nttime;
878
879 case SMB2_CREATE_SD_BUFFER: /* ("SecD") */
880 op->sd = kmem_alloc(sizeof (smb_sd_t), KM_SLEEP);
881 if (smb_decode_sd(&cce->cce_mbc, op->sd) != 0)
882 goto errout;
883 break;
884
885 case SMB2_CREATE_ALLOCATION_SIZE: /* ("AISi") */
886 rc = smb_mbc_decodef(&cce->cce_mbc, "q", &op->dsize);
887 if (rc != 0)
888 goto errout;
889 break;
890
891 case SMB2_CREATE_TIMEWARP_TOKEN: /* ("TWrp") */
892 /*
893 * Support for opening "Previous Versions".
894 * [MS-SMB2] 2.2.13.2.7 Data is an NT time.
895 */
896 rc = smb_mbc_decodef(&cce->cce_mbc,
897 "q", &nttime);
898 if (rc != 0)
899 goto errout;
900 smb_time_nt_to_unix(nttime, &op->timewarp);
901 op->create_timewarp = B_TRUE;
902 break;
903
904 /*
905 * Note: This handles both V1 and V2 leases,
906 * which differ only by their length.
907 */
908 case SMB2_CREATE_REQUEST_LEASE: /* ("RqLs") */
909 if (data_len == 52) {
910 op->lease_version = 2;
911 } else if (data_len == 32) {
912 op->lease_version = 1;
913 } else {
914 cmn_err(CE_NOTE, "Cctx RqLs bad len=0x%x",
915 data_len);
916 }
917 rc = smb_mbc_decodef(&cce->cce_mbc, "#cllq",
918 UUID_LEN, /* # */
919 op->lease_key, /* c */
920 &op->lease_state, /* l */
921 &op->lease_flags, /* l */
922 &nttime); /* (ignored) q */
923 if (rc != 0)
924 goto errout;
925 if (op->lease_version == 2) {
926 rc = smb_mbc_decodef(&cce->cce_mbc,
927 "#cw..",
928 UUID_LEN,
929 op->parent_lease_key,
930 &op->lease_epoch);
931 if (rc != 0)
932 goto errout;
933 } else {
934 bzero(op->parent_lease_key, UUID_LEN);
935 }
936 break;
937
938 case SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2: /* ("DH2C") */
939 rc = smb_mbc_decodef(&cce->cce_mbc, "qq#cl",
940 &op->dh_fileid.persistent, /* q */
941 &op->dh_fileid.temporal, /* q */
942 UUID_LEN, /* # */
943 op->create_guid, /* c */
944 &op->dh_v2_flags); /* l */
945 if (rc != 0)
946 goto errout;
947 break;
948
949 case SMB2_CREATE_DURABLE_HANDLE_RECONNECT: /* ("DHnC") */
950 rc = smb_mbc_decodef(&cce->cce_mbc, "qq",
951 &op->dh_fileid.persistent, /* q */
952 &op->dh_fileid.temporal); /* q */
953 if (rc != 0)
954 goto errout;
955 bzero(op->create_guid, UUID_LEN);
956 op->dh_v2_flags = 0;
957 break;
958
959 case SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2: /* ("DH2Q") */
960 rc = smb_mbc_decodef(&cce->cce_mbc,
961 "ll8.#c",
962 &op->dh_timeout, /* l */
963 &op->dh_v2_flags, /* l */
964 /* reserved */ /* 8. */
965 UUID_LEN, /* # */
966 op->create_guid); /* c */
967 if (rc != 0)
968 goto errout;
969 break;
970
971 case SMB2_CREATE_DURABLE_HANDLE_REQUEST: /* ("DHnQ") */
972 rc = smb_mbc_decodef(&cce->cce_mbc,
973 "16."); /* reserved */
974 if (rc != 0)
975 goto errout;
976 op->dh_timeout = 0; /* default */
977 op->dh_v2_flags = 0;
978 break;
979 }
980
981 next_cc:
982 if (next_off == 0) {
983 /* Normal loop termination */
984 status = 0;
985 break;
986 }
987
988 if ((next_off & 7) != 0)
989 break;
990 if ((top_offset + next_off) < in_mbc->chain_offset)
991 break;
992 if ((top_offset + next_off) > in_mbc->max_bytes)
993 break;
994 in_mbc->chain_offset = top_offset + next_off;
995 }
996
997 errout:
998 return (status);
999 }
1000
1001 /*
1002 * Encode an SMB2 Create Context buffer from our internal form.
1003 *
1004 * Build the Create Context to return; first the
1005 * per-element parts, then the aggregated buffer.
1006 *
1007 * No response for these:
1008 * CCTX_EA_BUFFER
1009 * CCTX_SD_BUFFER
1010 * CCTX_ALLOCATION_SIZE
1011 * CCTX_TIMEWARP_TOKEN
1012 *
1013 * Remember to add code sections to smb2_free_create_ctx()
1014 * for each section here that encodes a context element.
1015 */
1016 static uint32_t
1017 smb2_encode_create_ctx(smb_request_t *sr, smb2_create_ctx_t *cc)
1018 {
1019 smb_arg_open_t *op = &sr->arg.open;
1020 smb2_create_ctx_elem_t *cce;
1021 mbuf_chain_t *mbc = &sr->raw_data;
1022 int last_top = -1;
1023 int rc;
1024
1025 if (cc->cc_out_flags & CCTX_QUERY_MAX_ACCESS) {
1026 cce = &cc->cc_out_max_access;
1027
1028 cce->cce_mbc.max_bytes = cce->cce_len = 8;
1029 (void) smb_mbc_encodef(&cce->cce_mbc,
1030 "ll", 0, op->maximum_access);
1031
1032 last_top = mbc->chain_offset;
1033 rc = smb2_encode_create_ctx_elem(mbc, cce,
1034 SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQ);
1035 if (rc)
1036 return (NT_STATUS_INTERNAL_ERROR);
1037 (void) smb_mbc_poke(mbc, last_top, "l",
1038 mbc->chain_offset - last_top);
1039 }
1040
1041 if (cc->cc_out_flags & CCTX_QUERY_ON_DISK_ID) {
1042 cce = &cc->cc_out_file_id;
1043
1044 cce->cce_mbc.max_bytes = cce->cce_len = 32;
1045 (void) smb_mbc_encodef(
1046 &cce->cce_mbc, "qll.15.",
1047 op->fileid, /* q */
1048 op->op_fsid.val[0], /* l */
1049 op->op_fsid.val[1]); /* l */
1050 /* reserved (16 bytes) .15. */
1051
1052 last_top = mbc->chain_offset;
1053 rc = smb2_encode_create_ctx_elem(mbc, cce,
1054 SMB2_CREATE_QUERY_ON_DISK_ID);
1055 if (rc)
1056 return (NT_STATUS_INTERNAL_ERROR);
1057 (void) smb_mbc_poke(mbc, last_top, "l",
1058 mbc->chain_offset - last_top);
1059 }
1060
1061 if (cc->cc_out_flags & CCTX_AAPL_EXT) {
1062 cce = &cc->cc_out_aapl;
1063 /* cc_out_aapl already encoded */
1064
1065 last_top = mbc->chain_offset;
1066 rc = smb2_encode_create_ctx_elem(mbc, cce,
1067 SMB2_CREATE_CTX_AAPL);
1068 if (rc)
1069 return (NT_STATUS_INTERNAL_ERROR);
1070 (void) smb_mbc_poke(mbc, last_top, "l",
1071 mbc->chain_offset - last_top);
1072 }
1073
1074 if (cc->cc_out_flags & CCTX_REQUEST_LEASE) {
1075 cce = &cc->cc_out_req_lease;
1076
1077 cce->cce_mbc.max_bytes = cce->cce_len = 32;
1078 (void) smb_mbc_encodef(&cce->cce_mbc, "#cllq",
1079 UUID_LEN, /* # */
1080 op->lease_key, /* c */
1081 op->lease_state, /* l */
1082 op->lease_flags, /* l */
1083 0LL); /* q */
1084 if (op->lease_version == 2) {
1085 cce->cce_mbc.max_bytes = cce->cce_len = 52;
1086 (void) smb_mbc_encodef(&cce->cce_mbc,
1087 "#cw..",
1088 UUID_LEN,
1089 op->parent_lease_key,
1090 op->lease_epoch);
1091 }
1092
1093 last_top = mbc->chain_offset;
1094 rc = smb2_encode_create_ctx_elem(mbc, cce,
1095 SMB2_CREATE_REQUEST_LEASE);
1096 if (rc)
1097 return (NT_STATUS_INTERNAL_ERROR);
1098 (void) smb_mbc_poke(mbc, last_top, "l",
1099 mbc->chain_offset - last_top);
1100 }
1101
1102 if (cc->cc_out_flags & CCTX_DH_REQUEST) {
1103 cce = &cc->cc_out_dh_request;
1104
1105 cce->cce_mbc.max_bytes = cce->cce_len = 8;
1106 (void) smb_mbc_encodef(&cce->cce_mbc, "q", 0LL);
1107
1108 last_top = mbc->chain_offset;
1109 rc = smb2_encode_create_ctx_elem(mbc, cce,
1110 SMB2_CREATE_DURABLE_HANDLE_REQUEST);
1111 if (rc)
1112 return (NT_STATUS_INTERNAL_ERROR);
1113 (void) smb_mbc_poke(mbc, last_top, "l",
1114 mbc->chain_offset - last_top);
1115 }
1116
1117 if (cc->cc_out_flags & CCTX_DH_REQUEST_V2) {
1118 cce = &cc->cc_out_dh_request_v2;
1119
1120 cce->cce_mbc.max_bytes = cce->cce_len = 8;
1121 (void) smb_mbc_encodef(&cce->cce_mbc, "ll",
1122 op->dh_timeout, op->dh_v2_flags);
1123
1124 last_top = mbc->chain_offset;
1125 rc = smb2_encode_create_ctx_elem(mbc, cce,
1126 SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2);
1127 if (rc)
1128 return (NT_STATUS_INTERNAL_ERROR);
1129 (void) smb_mbc_poke(mbc, last_top, "l",
1130 mbc->chain_offset - last_top);
1131 }
1132
1133 if (last_top >= 0)
1134 (void) smb_mbc_poke(mbc, last_top, "l", 0);
1135
1136 return (0);
1137 }
1138
1139 static int
1140 smb2_encode_create_ctx_elem(mbuf_chain_t *out_mbc,
1141 smb2_create_ctx_elem_t *cce, uint32_t id)
1142 {
1143 union {
1144 uint32_t i;
1145 char ch[4];
1146 } cc_name;
1147 int rc;
1148
1149 /* as above */
1150 cc_name.i = htonl(id);
1151
1152 /*
1153 * This is the header, per [MS-SMB2] 2.2.13.2
1154 * Sorry about the fixed offsets. We know we'll
1155 * layout the data part as [name, payload] and
1156 * name is a fixed length, so this easy.
1157 * The final layout looks like this:
1158 * a: this header (16 bytes)
1159 * b: the name (4 bytes, 4 pad)
1160 * c: the payload (variable)
1161 * d: padding (to align 8)
1162 *
1163 * Note that "Next elem." is filled in later.
1164 */
1165 rc = smb_mbc_encodef(
1166 out_mbc, "lwwwwl",
1167 0, /* Next offset l */
1168 16, /* NameOffset w */
1169 4, /* NameLength w */
1170 0, /* Reserved w */
1171 24, /* DataOffset w */
1172 cce->cce_len); /* DataLen l */
1173 if (rc)
1174 return (rc);
1175
1176 /*
1177 * Now the "name" and payload.
1178 */
1179 rc = smb_mbc_encodef(
1180 out_mbc, "4c4.#C",
1181 cc_name.ch, /* 4c4. */
1182 cce->cce_len, /* # */
1183 &cce->cce_mbc); /* C */
1184
1185 (void) smb_mbc_put_align(out_mbc, 8);
1186
1187 return (rc);
1188 }
1189
1190 static void
1191 smb2_free_create_ctx(smb2_create_ctx_t *cc)
1192 {
1193 smb2_create_ctx_elem_t *cce;
1194
1195 if (cc->cc_out_flags & CCTX_QUERY_MAX_ACCESS) {
1196 cce = &cc->cc_out_max_access;
1197 MBC_FLUSH(&cce->cce_mbc);
1198 }
1199 if (cc->cc_out_flags & CCTX_QUERY_ON_DISK_ID) {
1200 cce = &cc->cc_out_file_id;
1201 MBC_FLUSH(&cce->cce_mbc);
1202 }
1203 if (cc->cc_out_flags & CCTX_AAPL_EXT) {
1204 cce = &cc->cc_out_aapl;
1205 MBC_FLUSH(&cce->cce_mbc);
1206 }
1207 if (cc->cc_out_flags & CCTX_REQUEST_LEASE) {
1208 cce = &cc->cc_out_req_lease;
1209 MBC_FLUSH(&cce->cce_mbc);
1210 }
1211 if (cc->cc_out_flags & CCTX_DH_REQUEST) {
1212 cce = &cc->cc_out_dh_request;
1213 MBC_FLUSH(&cce->cce_mbc);
1214 }
1215 if (cc->cc_out_flags & CCTX_DH_REQUEST_V2) {
1216 cce = &cc->cc_out_dh_request_v2;
1217 MBC_FLUSH(&cce->cce_mbc);
1218 }
1219 }
|