Commit | Line | Data |
---|---|---|
e2f34481 NJ |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * Copyright (C) 2018 Samsung Electronics Co., Ltd. | |
4 | * Copyright (C) 2018 Namjae Jeon <linkinjeon@kernel.org> | |
5 | */ | |
6 | ||
7c88c1e0 MS |
7 | #include <linux/user_namespace.h> |
8 | ||
e2f34481 NJ |
9 | #include "smb_common.h" |
10 | #include "server.h" | |
11 | #include "misc.h" | |
12 | #include "smbstatus.h" | |
e2f34481 NJ |
13 | #include "connection.h" |
14 | #include "ksmbd_work.h" | |
15 | #include "mgmt/user_session.h" | |
16 | #include "mgmt/user_config.h" | |
17 | #include "mgmt/tree_connect.h" | |
18 | #include "mgmt/share_config.h" | |
19 | ||
20 | /*for shortname implementation */ | |
21 | static const char basechars[43] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%"; | |
64b39f4a | 22 | #define MANGLE_BASE (sizeof(basechars) / sizeof(char) - 1) |
e2f34481 NJ |
23 | #define MAGIC_CHAR '~' |
24 | #define PERIOD '.' | |
25 | #define mangle(V) ((char)(basechars[(V) % MANGLE_BASE])) | |
e2f34481 | 26 | |
e2f34481 NJ |
27 | struct smb_protocol { |
28 | int index; | |
29 | char *name; | |
30 | char *prot; | |
31 | __u16 prot_id; | |
32 | }; | |
33 | ||
eebff916 | 34 | static struct smb_protocol smb1_protos[] = { |
e2f34481 NJ |
35 | { |
36 | SMB21_PROT, | |
37 | "\2SMB 2.1", | |
38 | "SMB2_10", | |
39 | SMB21_PROT_ID | |
40 | }, | |
41 | { | |
42 | SMB2X_PROT, | |
43 | "\2SMB 2.???", | |
44 | "SMB2_22", | |
45 | SMB2X_PROT_ID | |
46 | }, | |
eebff916 MM |
47 | }; |
48 | ||
49 | static struct smb_protocol smb2_protos[] = { | |
50 | { | |
51 | SMB21_PROT, | |
52 | "\2SMB 2.1", | |
53 | "SMB2_10", | |
54 | SMB21_PROT_ID | |
55 | }, | |
e2f34481 NJ |
56 | { |
57 | SMB30_PROT, | |
58 | "\2SMB 3.0", | |
59 | "SMB3_00", | |
60 | SMB30_PROT_ID | |
61 | }, | |
62 | { | |
63 | SMB302_PROT, | |
64 | "\2SMB 3.02", | |
65 | "SMB3_02", | |
66 | SMB302_PROT_ID | |
67 | }, | |
68 | { | |
69 | SMB311_PROT, | |
70 | "\2SMB 3.1.1", | |
71 | "SMB3_11", | |
72 | SMB311_PROT_ID | |
73 | }, | |
74 | }; | |
75 | ||
76 | unsigned int ksmbd_server_side_copy_max_chunk_count(void) | |
77 | { | |
78 | return 256; | |
79 | } | |
80 | ||
81 | unsigned int ksmbd_server_side_copy_max_chunk_size(void) | |
82 | { | |
83 | return (2U << 30) - 1; | |
84 | } | |
85 | ||
86 | unsigned int ksmbd_server_side_copy_max_total_size(void) | |
87 | { | |
88 | return (2U << 30) - 1; | |
89 | } | |
90 | ||
91 | inline int ksmbd_min_protocol(void) | |
92 | { | |
51a13873 | 93 | return SMB21_PROT; |
e2f34481 NJ |
94 | } |
95 | ||
96 | inline int ksmbd_max_protocol(void) | |
97 | { | |
98 | return SMB311_PROT; | |
99 | } | |
100 | ||
101 | int ksmbd_lookup_protocol_idx(char *str) | |
102 | { | |
eebff916 | 103 | int offt = ARRAY_SIZE(smb1_protos) - 1; |
e2f34481 NJ |
104 | int len = strlen(str); |
105 | ||
106 | while (offt >= 0) { | |
eebff916 MM |
107 | if (!strncmp(str, smb1_protos[offt].prot, len)) { |
108 | ksmbd_debug(SMB, "selected %s dialect idx = %d\n", | |
109 | smb1_protos[offt].prot, offt); | |
110 | return smb1_protos[offt].index; | |
111 | } | |
112 | offt--; | |
113 | } | |
114 | ||
115 | offt = ARRAY_SIZE(smb2_protos) - 1; | |
116 | while (offt >= 0) { | |
117 | if (!strncmp(str, smb2_protos[offt].prot, len)) { | |
e2f34481 | 118 | ksmbd_debug(SMB, "selected %s dialect idx = %d\n", |
eebff916 MM |
119 | smb2_protos[offt].prot, offt); |
120 | return smb2_protos[offt].index; | |
e2f34481 NJ |
121 | } |
122 | offt--; | |
123 | } | |
124 | return -1; | |
125 | } | |
126 | ||
127 | /** | |
95fa1ce9 HL |
128 | * ksmbd_verify_smb_message() - check for valid smb2 request header |
129 | * @work: smb work | |
e2f34481 NJ |
130 | * |
131 | * check for valid smb signature and packet direction(request/response) | |
132 | * | |
18a015bc | 133 | * Return: 0 on success, otherwise -EINVAL |
e2f34481 NJ |
134 | */ |
135 | int ksmbd_verify_smb_message(struct ksmbd_work *work) | |
136 | { | |
a088ac85 | 137 | struct smb2_hdr *smb2_hdr = ksmbd_req_buf_next(work); |
18a015bc | 138 | struct smb_hdr *hdr; |
e2f34481 NJ |
139 | |
140 | if (smb2_hdr->ProtocolId == SMB2_PROTO_NUMBER) | |
141 | return ksmbd_smb2_check_message(work); | |
142 | ||
18a015bc NJ |
143 | hdr = work->request_buf; |
144 | if (*(__le32 *)hdr->Protocol == SMB1_PROTO_NUMBER && | |
376b9133 HL |
145 | hdr->Command == SMB_COM_NEGOTIATE) { |
146 | work->conn->outstanding_credits++; | |
18a015bc | 147 | return 0; |
376b9133 | 148 | } |
18a015bc NJ |
149 | |
150 | return -EINVAL; | |
e2f34481 NJ |
151 | } |
152 | ||
153 | /** | |
95fa1ce9 | 154 | * ksmbd_smb_request() - check for valid smb request type |
e2f34481 | 155 | * @conn: connection instance |
e2f34481 NJ |
156 | * |
157 | * Return: true on success, otherwise false | |
158 | */ | |
159 | bool ksmbd_smb_request(struct ksmbd_conn *conn) | |
160 | { | |
18d46769 | 161 | return conn->request_buf[0] == 0; |
e2f34481 NJ |
162 | } |
163 | ||
164 | static bool supported_protocol(int idx) | |
165 | { | |
166 | if (idx == SMB2X_PROT && | |
167 | (server_conf.min_protocol >= SMB21_PROT || | |
168 | server_conf.max_protocol <= SMB311_PROT)) | |
169 | return true; | |
170 | ||
171 | return (server_conf.min_protocol <= idx && | |
070fb21e | 172 | idx <= server_conf.max_protocol); |
e2f34481 NJ |
173 | } |
174 | ||
442ff9eb | 175 | static char *next_dialect(char *dialect, int *next_off, int bcount) |
e2f34481 NJ |
176 | { |
177 | dialect = dialect + *next_off; | |
442ff9eb NJ |
178 | *next_off = strnlen(dialect, bcount); |
179 | if (dialect[*next_off] != '\0') | |
180 | return NULL; | |
e2f34481 NJ |
181 | return dialect; |
182 | } | |
183 | ||
184 | static int ksmbd_lookup_dialect_by_name(char *cli_dialects, __le16 byte_count) | |
185 | { | |
186 | int i, seq_num, bcount, next; | |
187 | char *dialect; | |
188 | ||
eebff916 | 189 | for (i = ARRAY_SIZE(smb1_protos) - 1; i >= 0; i--) { |
e2f34481 NJ |
190 | seq_num = 0; |
191 | next = 0; | |
192 | dialect = cli_dialects; | |
193 | bcount = le16_to_cpu(byte_count); | |
194 | do { | |
442ff9eb NJ |
195 | dialect = next_dialect(dialect, &next, bcount); |
196 | if (!dialect) | |
197 | break; | |
e2f34481 | 198 | ksmbd_debug(SMB, "client requested dialect %s\n", |
070fb21e | 199 | dialect); |
eebff916 MM |
200 | if (!strcmp(dialect, smb1_protos[i].name)) { |
201 | if (supported_protocol(smb1_protos[i].index)) { | |
e2f34481 | 202 | ksmbd_debug(SMB, |
070fb21e | 203 | "selected %s dialect\n", |
eebff916 MM |
204 | smb1_protos[i].name); |
205 | if (smb1_protos[i].index == SMB1_PROT) | |
e2f34481 | 206 | return seq_num; |
eebff916 | 207 | return smb1_protos[i].prot_id; |
e2f34481 NJ |
208 | } |
209 | } | |
210 | seq_num++; | |
211 | bcount -= (++next); | |
212 | } while (bcount > 0); | |
213 | } | |
214 | ||
215 | return BAD_PROT_ID; | |
216 | } | |
217 | ||
218 | int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count) | |
219 | { | |
220 | int i; | |
221 | int count; | |
222 | ||
eebff916 | 223 | for (i = ARRAY_SIZE(smb2_protos) - 1; i >= 0; i--) { |
e2f34481 NJ |
224 | count = le16_to_cpu(dialects_count); |
225 | while (--count >= 0) { | |
226 | ksmbd_debug(SMB, "client requested dialect 0x%x\n", | |
070fb21e | 227 | le16_to_cpu(cli_dialects[count])); |
e2f34481 | 228 | if (le16_to_cpu(cli_dialects[count]) != |
eebff916 | 229 | smb2_protos[i].prot_id) |
e2f34481 NJ |
230 | continue; |
231 | ||
eebff916 | 232 | if (supported_protocol(smb2_protos[i].index)) { |
e2f34481 | 233 | ksmbd_debug(SMB, "selected %s dialect\n", |
eebff916 MM |
234 | smb2_protos[i].name); |
235 | return smb2_protos[i].prot_id; | |
e2f34481 NJ |
236 | } |
237 | } | |
238 | } | |
239 | ||
240 | return BAD_PROT_ID; | |
241 | } | |
242 | ||
eebff916 | 243 | static int ksmbd_negotiate_smb_dialect(void *buf) |
e2f34481 | 244 | { |
442ff9eb | 245 | int smb_buf_length = get_rfc1002_len(buf); |
cb451720 | 246 | __le32 proto = ((struct smb2_hdr *)smb2_get_msg(buf))->ProtocolId; |
e2f34481 | 247 | |
e2f34481 NJ |
248 | if (proto == SMB2_PROTO_NUMBER) { |
249 | struct smb2_negotiate_req *req; | |
442ff9eb | 250 | int smb2_neg_size = |
cb451720 | 251 | offsetof(struct smb2_negotiate_req, Dialects); |
e2f34481 | 252 | |
cb451720 | 253 | req = (struct smb2_negotiate_req *)smb2_get_msg(buf); |
442ff9eb NJ |
254 | if (smb2_neg_size > smb_buf_length) |
255 | goto err_out; | |
256 | ||
257 | if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > | |
258 | smb_buf_length) | |
259 | goto err_out; | |
260 | ||
e2f34481 NJ |
261 | return ksmbd_lookup_dialect_by_id(req->Dialects, |
262 | req->DialectCount); | |
263 | } | |
264 | ||
265 | proto = *(__le32 *)((struct smb_hdr *)buf)->Protocol; | |
266 | if (proto == SMB1_PROTO_NUMBER) { | |
267 | struct smb_negotiate_req *req; | |
268 | ||
269 | req = (struct smb_negotiate_req *)buf; | |
442ff9eb NJ |
270 | if (le16_to_cpu(req->ByteCount) < 2) |
271 | goto err_out; | |
272 | ||
273 | if (offsetof(struct smb_negotiate_req, DialectsArray) - 4 + | |
274 | le16_to_cpu(req->ByteCount) > smb_buf_length) { | |
275 | goto err_out; | |
276 | } | |
277 | ||
e2f34481 NJ |
278 | return ksmbd_lookup_dialect_by_name(req->DialectsArray, |
279 | req->ByteCount); | |
280 | } | |
281 | ||
442ff9eb | 282 | err_out: |
e2f34481 NJ |
283 | return BAD_PROT_ID; |
284 | } | |
285 | ||
dc8289f9 NJ |
286 | #define SMB_COM_NEGOTIATE_EX 0x0 |
287 | ||
288 | /** | |
289 | * get_smb1_cmd_val() - get smb command value from smb header | |
290 | * @work: smb work containing smb header | |
291 | * | |
292 | * Return: smb command value | |
293 | */ | |
294 | static u16 get_smb1_cmd_val(struct ksmbd_work *work) | |
e2f34481 | 295 | { |
dc8289f9 NJ |
296 | return SMB_COM_NEGOTIATE_EX; |
297 | } | |
e2f34481 | 298 | |
dc8289f9 NJ |
299 | /** |
300 | * init_smb1_rsp_hdr() - initialize smb negotiate response header | |
301 | * @work: smb work containing smb request | |
302 | * | |
303 | * Return: 0 on success, otherwise -EINVAL | |
304 | */ | |
305 | static int init_smb1_rsp_hdr(struct ksmbd_work *work) | |
306 | { | |
307 | struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; | |
308 | struct smb_hdr *rcv_hdr = (struct smb_hdr *)work->request_buf; | |
309 | ||
310 | /* | |
311 | * Remove 4 byte direct TCP header. | |
312 | */ | |
313 | *(__be32 *)work->response_buf = | |
314 | cpu_to_be32(sizeof(struct smb_hdr) - 4); | |
315 | ||
316 | rsp_hdr->Command = SMB_COM_NEGOTIATE; | |
317 | *(__le32 *)rsp_hdr->Protocol = SMB1_PROTO_NUMBER; | |
318 | rsp_hdr->Flags = SMBFLG_RESPONSE; | |
319 | rsp_hdr->Flags2 = SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS | | |
320 | SMBFLG2_EXT_SEC | SMBFLG2_IS_LONG_NAME; | |
321 | rsp_hdr->Pid = rcv_hdr->Pid; | |
322 | rsp_hdr->Mid = rcv_hdr->Mid; | |
323 | return 0; | |
324 | } | |
325 | ||
326 | /** | |
327 | * smb1_check_user_session() - check for valid session for a user | |
328 | * @work: smb work containing smb request buffer | |
329 | * | |
330 | * Return: 0 on success, otherwise error | |
331 | */ | |
332 | static int smb1_check_user_session(struct ksmbd_work *work) | |
333 | { | |
334 | unsigned int cmd = work->conn->ops->get_cmd_val(work); | |
335 | ||
336 | if (cmd == SMB_COM_NEGOTIATE_EX) | |
e2f34481 NJ |
337 | return 0; |
338 | ||
dc8289f9 NJ |
339 | return -EINVAL; |
340 | } | |
341 | ||
342 | /** | |
343 | * smb1_allocate_rsp_buf() - allocate response buffer for a command | |
344 | * @work: smb work containing smb request | |
345 | * | |
346 | * Return: 0 on success, otherwise -ENOMEM | |
347 | */ | |
348 | static int smb1_allocate_rsp_buf(struct ksmbd_work *work) | |
349 | { | |
350 | work->response_buf = kmalloc(MAX_CIFS_SMALL_BUFFER_SIZE, | |
351 | GFP_KERNEL | __GFP_ZERO); | |
352 | work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; | |
353 | ||
354 | if (!work->response_buf) { | |
355 | pr_err("Failed to allocate %u bytes buffer\n", | |
356 | MAX_CIFS_SMALL_BUFFER_SIZE); | |
357 | return -ENOMEM; | |
358 | } | |
e2f34481 | 359 | |
e2f34481 NJ |
360 | return 0; |
361 | } | |
362 | ||
dc8289f9 NJ |
363 | static struct smb_version_ops smb1_server_ops = { |
364 | .get_cmd_val = get_smb1_cmd_val, | |
365 | .init_rsp_hdr = init_smb1_rsp_hdr, | |
366 | .allocate_rsp_buf = smb1_allocate_rsp_buf, | |
367 | .check_user_session = smb1_check_user_session, | |
368 | }; | |
369 | ||
370 | static int smb1_negotiate(struct ksmbd_work *work) | |
371 | { | |
372 | return ksmbd_smb_negotiate_common(work, SMB_COM_NEGOTIATE); | |
373 | } | |
374 | ||
375 | static struct smb_version_cmds smb1_server_cmds[1] = { | |
376 | [SMB_COM_NEGOTIATE_EX] = { .proc = smb1_negotiate, }, | |
377 | }; | |
378 | ||
379 | static void init_smb1_server(struct ksmbd_conn *conn) | |
380 | { | |
381 | conn->ops = &smb1_server_ops; | |
382 | conn->cmds = smb1_server_cmds; | |
383 | conn->max_cmds = ARRAY_SIZE(smb1_server_cmds); | |
384 | } | |
385 | ||
386 | void ksmbd_init_smb_server(struct ksmbd_work *work) | |
387 | { | |
388 | struct ksmbd_conn *conn = work->conn; | |
389 | __le32 proto; | |
390 | ||
391 | if (conn->need_neg == false) | |
392 | return; | |
393 | ||
394 | proto = *(__le32 *)((struct smb_hdr *)work->request_buf)->Protocol; | |
395 | if (proto == SMB1_PROTO_NUMBER) | |
396 | init_smb1_server(conn); | |
397 | else | |
398 | init_smb3_11_server(conn); | |
399 | } | |
400 | ||
64b39f4a | 401 | int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, |
070fb21e NJ |
402 | struct ksmbd_file *dir, |
403 | struct ksmbd_dir_info *d_info, | |
404 | char *search_pattern, | |
405 | int (*fn)(struct ksmbd_conn *, int, | |
406 | struct ksmbd_dir_info *, | |
407 | struct ksmbd_kstat *)) | |
e2f34481 NJ |
408 | { |
409 | int i, rc = 0; | |
410 | struct ksmbd_conn *conn = work->conn; | |
b74d24f7 | 411 | struct mnt_idmap *idmap = file_mnt_idmap(dir->filp); |
e2f34481 NJ |
412 | |
413 | for (i = 0; i < 2; i++) { | |
414 | struct kstat kstat; | |
415 | struct ksmbd_kstat ksmbd_kstat; | |
97550c74 | 416 | struct dentry *dentry; |
e2f34481 NJ |
417 | |
418 | if (!dir->dot_dotdot[i]) { /* fill dot entry info */ | |
419 | if (i == 0) { | |
420 | d_info->name = "."; | |
421 | d_info->name_len = 1; | |
97550c74 | 422 | dentry = dir->filp->f_path.dentry; |
e2f34481 NJ |
423 | } else { |
424 | d_info->name = ".."; | |
425 | d_info->name_len = 2; | |
97550c74 | 426 | dentry = dir->filp->f_path.dentry->d_parent; |
e2f34481 NJ |
427 | } |
428 | ||
b24c9335 | 429 | if (!match_pattern(d_info->name, d_info->name_len, |
64b39f4a | 430 | search_pattern)) { |
e2f34481 NJ |
431 | dir->dot_dotdot[i] = 1; |
432 | continue; | |
433 | } | |
434 | ||
435 | ksmbd_kstat.kstat = &kstat; | |
436 | ksmbd_vfs_fill_dentry_attrs(work, | |
b74d24f7 | 437 | idmap, |
97550c74 | 438 | dentry, |
070fb21e | 439 | &ksmbd_kstat); |
475d6f98 | 440 | rc = fn(conn, info_level, d_info, &ksmbd_kstat); |
e2f34481 NJ |
441 | if (rc) |
442 | break; | |
443 | if (d_info->out_buf_len <= 0) | |
444 | break; | |
445 | ||
446 | dir->dot_dotdot[i] = 1; | |
447 | if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) { | |
448 | d_info->out_buf_len = 0; | |
449 | break; | |
450 | } | |
451 | } | |
452 | } | |
453 | ||
454 | return rc; | |
455 | } | |
456 | ||
457 | /** | |
458 | * ksmbd_extract_shortname() - get shortname from long filename | |
459 | * @conn: connection instance | |
460 | * @longname: source long filename | |
461 | * @shortname: destination short filename | |
462 | * | |
463 | * Return: shortname length or 0 when source long name is '.' or '..' | |
464 | * TODO: Though this function comforms the restriction of 8.3 Filename spec, | |
465 | * but the result is different with Windows 7's one. need to check. | |
466 | */ | |
64b39f4a | 467 | int ksmbd_extract_shortname(struct ksmbd_conn *conn, const char *longname, |
070fb21e | 468 | char *shortname) |
e2f34481 NJ |
469 | { |
470 | const char *p; | |
471 | char base[9], extension[4]; | |
472 | char out[13] = {0}; | |
473 | int baselen = 0; | |
474 | int extlen = 0, len = 0; | |
475 | unsigned int csum = 0; | |
476 | const unsigned char *ptr; | |
477 | bool dot_present = true; | |
478 | ||
479 | p = longname; | |
480 | if ((*p == '.') || (!(strcmp(p, "..")))) { | |
481 | /*no mangling required */ | |
482 | return 0; | |
483 | } | |
484 | ||
485 | p = strrchr(longname, '.'); | |
486 | if (p == longname) { /*name starts with a dot*/ | |
487 | strscpy(extension, "___", strlen("___")); | |
488 | } else { | |
64b39f4a | 489 | if (p) { |
e2f34481 NJ |
490 | p++; |
491 | while (*p && extlen < 3) { | |
492 | if (*p != '.') | |
493 | extension[extlen++] = toupper(*p); | |
494 | p++; | |
495 | } | |
496 | extension[extlen] = '\0'; | |
64b39f4a | 497 | } else { |
e2f34481 | 498 | dot_present = false; |
64b39f4a | 499 | } |
e2f34481 NJ |
500 | } |
501 | ||
502 | p = longname; | |
503 | if (*p == '.') { | |
504 | p++; | |
505 | longname++; | |
506 | } | |
507 | while (*p && (baselen < 5)) { | |
508 | if (*p != '.') | |
509 | base[baselen++] = toupper(*p); | |
510 | p++; | |
511 | } | |
512 | ||
513 | base[baselen] = MAGIC_CHAR; | |
64b39f4a | 514 | memcpy(out, base, baselen + 1); |
e2f34481 NJ |
515 | |
516 | ptr = longname; | |
517 | len = strlen(longname); | |
518 | for (; len > 0; len--, ptr++) | |
519 | csum += *ptr; | |
520 | ||
521 | csum = csum % (MANGLE_BASE * MANGLE_BASE); | |
64b39f4a NJ |
522 | out[baselen + 1] = mangle(csum / MANGLE_BASE); |
523 | out[baselen + 2] = mangle(csum); | |
524 | out[baselen + 3] = PERIOD; | |
e2f34481 NJ |
525 | |
526 | if (dot_present) | |
64b39f4a | 527 | memcpy(&out[baselen + 4], extension, 4); |
e2f34481 | 528 | else |
64b39f4a | 529 | out[baselen + 4] = '\0'; |
e2f34481 | 530 | smbConvertToUTF16((__le16 *)shortname, out, PATH_MAX, |
070fb21e | 531 | conn->local_nls, 0); |
e2f34481 NJ |
532 | len = strlen(out) * 2; |
533 | return len; | |
534 | } | |
535 | ||
536 | static int __smb2_negotiate(struct ksmbd_conn *conn) | |
537 | { | |
b53e8cfe | 538 | return (conn->dialect >= SMB20_PROT_ID && |
070fb21e | 539 | conn->dialect <= SMB311_PROT_ID); |
e2f34481 NJ |
540 | } |
541 | ||
542 | static int smb_handle_negotiate(struct ksmbd_work *work) | |
543 | { | |
e5066499 | 544 | struct smb_negotiate_rsp *neg_rsp = work->response_buf; |
e2f34481 | 545 | |
39b291b8 NJ |
546 | ksmbd_debug(SMB, "Unsupported SMB1 protocol\n"); |
547 | ||
dc8289f9 NJ |
548 | /* Add 2 byte bcc and 2 byte DialectIndex. */ |
549 | inc_rfc1001_len(work->response_buf, 4); | |
39b291b8 NJ |
550 | neg_rsp->hdr.Status.CifsError = STATUS_SUCCESS; |
551 | ||
39b291b8 NJ |
552 | neg_rsp->hdr.WordCount = 1; |
553 | neg_rsp->DialectIndex = cpu_to_le16(work->conn->dialect); | |
554 | neg_rsp->ByteCount = 0; | |
555 | return 0; | |
e2f34481 NJ |
556 | } |
557 | ||
558 | int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command) | |
559 | { | |
560 | struct ksmbd_conn *conn = work->conn; | |
561 | int ret; | |
562 | ||
cb451720 NJ |
563 | conn->dialect = |
564 | ksmbd_negotiate_smb_dialect(work->request_buf); | |
e2f34481 NJ |
565 | ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); |
566 | ||
b53e8cfe | 567 | if (command == SMB2_NEGOTIATE_HE) { |
e2f34481 | 568 | ret = smb2_handle_negotiate(work); |
e2f34481 NJ |
569 | return ret; |
570 | } | |
571 | ||
572 | if (command == SMB_COM_NEGOTIATE) { | |
573 | if (__smb2_negotiate(conn)) { | |
e2f34481 NJ |
574 | init_smb3_11_server(conn); |
575 | init_smb2_neg_rsp(work); | |
576 | ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n"); | |
577 | return 0; | |
578 | } | |
579 | return smb_handle_negotiate(work); | |
580 | } | |
581 | ||
bde1694a | 582 | pr_err("Unknown SMB negotiation command: %u\n", command); |
e2f34481 NJ |
583 | return -EINVAL; |
584 | } | |
585 | ||
586 | enum SHARED_MODE_ERRORS { | |
587 | SHARE_DELETE_ERROR, | |
588 | SHARE_READ_ERROR, | |
589 | SHARE_WRITE_ERROR, | |
590 | FILE_READ_ERROR, | |
591 | FILE_WRITE_ERROR, | |
592 | FILE_DELETE_ERROR, | |
593 | }; | |
594 | ||
595 | static const char * const shared_mode_errors[] = { | |
596 | "Current access mode does not permit SHARE_DELETE", | |
597 | "Current access mode does not permit SHARE_READ", | |
598 | "Current access mode does not permit SHARE_WRITE", | |
599 | "Desired access mode does not permit FILE_READ", | |
600 | "Desired access mode does not permit FILE_WRITE", | |
601 | "Desired access mode does not permit FILE_DELETE", | |
602 | }; | |
603 | ||
64b39f4a | 604 | static void smb_shared_mode_error(int error, struct ksmbd_file *prev_fp, |
070fb21e | 605 | struct ksmbd_file *curr_fp) |
e2f34481 NJ |
606 | { |
607 | ksmbd_debug(SMB, "%s\n", shared_mode_errors[error]); | |
608 | ksmbd_debug(SMB, "Current mode: 0x%x Desired mode: 0x%x\n", | |
070fb21e | 609 | prev_fp->saccess, curr_fp->daccess); |
e2f34481 NJ |
610 | } |
611 | ||
612 | int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) | |
613 | { | |
614 | int rc = 0; | |
615 | struct ksmbd_file *prev_fp; | |
e2f34481 NJ |
616 | |
617 | /* | |
618 | * Lookup fp in master fp list, and check desired access and | |
619 | * shared mode between previous open and current open. | |
620 | */ | |
621 | read_lock(&curr_fp->f_ci->m_lock); | |
6f3d5eee | 622 | list_for_each_entry(prev_fp, &curr_fp->f_ci->m_fp_list, node) { |
ab0b263b | 623 | if (file_inode(filp) != file_inode(prev_fp->filp)) |
e2f34481 NJ |
624 | continue; |
625 | ||
626 | if (filp == prev_fp->filp) | |
627 | continue; | |
628 | ||
629 | if (ksmbd_stream_fd(prev_fp) && ksmbd_stream_fd(curr_fp)) | |
630 | if (strcmp(prev_fp->stream.name, curr_fp->stream.name)) | |
631 | continue; | |
632 | ||
e2f34481 NJ |
633 | if (prev_fp->attrib_only != curr_fp->attrib_only) |
634 | continue; | |
635 | ||
636 | if (!(prev_fp->saccess & FILE_SHARE_DELETE_LE) && | |
64b39f4a | 637 | curr_fp->daccess & FILE_DELETE_LE) { |
e2f34481 NJ |
638 | smb_shared_mode_error(SHARE_DELETE_ERROR, |
639 | prev_fp, | |
640 | curr_fp); | |
641 | rc = -EPERM; | |
642 | break; | |
643 | } | |
644 | ||
645 | /* | |
646 | * Only check FILE_SHARE_DELETE if stream opened and | |
647 | * normal file opened. | |
648 | */ | |
649 | if (ksmbd_stream_fd(prev_fp) && !ksmbd_stream_fd(curr_fp)) | |
650 | continue; | |
651 | ||
652 | if (!(prev_fp->saccess & FILE_SHARE_READ_LE) && | |
64b39f4a | 653 | curr_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE)) { |
e2f34481 NJ |
654 | smb_shared_mode_error(SHARE_READ_ERROR, |
655 | prev_fp, | |
656 | curr_fp); | |
657 | rc = -EPERM; | |
658 | break; | |
659 | } | |
660 | ||
661 | if (!(prev_fp->saccess & FILE_SHARE_WRITE_LE) && | |
64b39f4a | 662 | curr_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE)) { |
e2f34481 NJ |
663 | smb_shared_mode_error(SHARE_WRITE_ERROR, |
664 | prev_fp, | |
665 | curr_fp); | |
666 | rc = -EPERM; | |
667 | break; | |
668 | } | |
669 | ||
64b39f4a NJ |
670 | if (prev_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE) && |
671 | !(curr_fp->saccess & FILE_SHARE_READ_LE)) { | |
e2f34481 NJ |
672 | smb_shared_mode_error(FILE_READ_ERROR, |
673 | prev_fp, | |
674 | curr_fp); | |
675 | rc = -EPERM; | |
676 | break; | |
677 | } | |
678 | ||
64b39f4a NJ |
679 | if (prev_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE) && |
680 | !(curr_fp->saccess & FILE_SHARE_WRITE_LE)) { | |
e2f34481 NJ |
681 | smb_shared_mode_error(FILE_WRITE_ERROR, |
682 | prev_fp, | |
683 | curr_fp); | |
684 | rc = -EPERM; | |
685 | break; | |
686 | } | |
687 | ||
688 | if (prev_fp->daccess & FILE_DELETE_LE && | |
64b39f4a | 689 | !(curr_fp->saccess & FILE_SHARE_DELETE_LE)) { |
e2f34481 NJ |
690 | smb_shared_mode_error(FILE_DELETE_ERROR, |
691 | prev_fp, | |
692 | curr_fp); | |
693 | rc = -EPERM; | |
694 | break; | |
695 | } | |
696 | } | |
697 | read_unlock(&curr_fp->f_ci->m_lock); | |
698 | ||
699 | return rc; | |
700 | } | |
701 | ||
702 | bool is_asterisk(char *p) | |
703 | { | |
704 | return p && p[0] == '*'; | |
705 | } | |
706 | ||
707 | int ksmbd_override_fsids(struct ksmbd_work *work) | |
708 | { | |
709 | struct ksmbd_session *sess = work->sess; | |
710 | struct ksmbd_share_config *share = work->tcon->share_conf; | |
711 | struct cred *cred; | |
712 | struct group_info *gi; | |
713 | unsigned int uid; | |
714 | unsigned int gid; | |
715 | ||
716 | uid = user_uid(sess->user); | |
717 | gid = user_gid(sess->user); | |
718 | if (share->force_uid != KSMBD_SHARE_INVALID_UID) | |
719 | uid = share->force_uid; | |
720 | if (share->force_gid != KSMBD_SHARE_INVALID_GID) | |
721 | gid = share->force_gid; | |
722 | ||
5a17f040 | 723 | cred = prepare_kernel_cred(&init_task); |
e2f34481 NJ |
724 | if (!cred) |
725 | return -ENOMEM; | |
726 | ||
7c88c1e0 MS |
727 | cred->fsuid = make_kuid(&init_user_ns, uid); |
728 | cred->fsgid = make_kgid(&init_user_ns, gid); | |
e2f34481 NJ |
729 | |
730 | gi = groups_alloc(0); | |
731 | if (!gi) { | |
732 | abort_creds(cred); | |
733 | return -ENOMEM; | |
734 | } | |
735 | set_groups(cred, gi); | |
736 | put_group_info(gi); | |
737 | ||
738 | if (!uid_eq(cred->fsuid, GLOBAL_ROOT_UID)) | |
739 | cred->cap_effective = cap_drop_fs_set(cred->cap_effective); | |
740 | ||
64b39f4a | 741 | WARN_ON(work->saved_cred); |
e2f34481 NJ |
742 | work->saved_cred = override_creds(cred); |
743 | if (!work->saved_cred) { | |
744 | abort_creds(cred); | |
745 | return -EINVAL; | |
746 | } | |
747 | return 0; | |
748 | } | |
749 | ||
750 | void ksmbd_revert_fsids(struct ksmbd_work *work) | |
751 | { | |
752 | const struct cred *cred; | |
753 | ||
64b39f4a | 754 | WARN_ON(!work->saved_cred); |
e2f34481 NJ |
755 | |
756 | cred = current_cred(); | |
757 | revert_creds(work->saved_cred); | |
758 | put_cred(cred); | |
759 | work->saved_cred = NULL; | |
760 | } | |
761 | ||
762 | __le32 smb_map_generic_desired_access(__le32 daccess) | |
763 | { | |
764 | if (daccess & FILE_GENERIC_READ_LE) { | |
765 | daccess |= cpu_to_le32(GENERIC_READ_FLAGS); | |
766 | daccess &= ~FILE_GENERIC_READ_LE; | |
767 | } | |
768 | ||
769 | if (daccess & FILE_GENERIC_WRITE_LE) { | |
770 | daccess |= cpu_to_le32(GENERIC_WRITE_FLAGS); | |
771 | daccess &= ~FILE_GENERIC_WRITE_LE; | |
772 | } | |
773 | ||
774 | if (daccess & FILE_GENERIC_EXECUTE_LE) { | |
775 | daccess |= cpu_to_le32(GENERIC_EXECUTE_FLAGS); | |
776 | daccess &= ~FILE_GENERIC_EXECUTE_LE; | |
777 | } | |
778 | ||
779 | if (daccess & FILE_GENERIC_ALL_LE) { | |
780 | daccess |= cpu_to_le32(GENERIC_ALL_FLAGS); | |
781 | daccess &= ~FILE_GENERIC_ALL_LE; | |
782 | } | |
783 | ||
784 | return daccess; | |
785 | } |