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