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