Pull smb server fixes from Steve French: "Five smb3 server fixes, all also for stable: - Fix four slab out of bounds warnings: improve checks for protocol id, and for small packet length, and for create context parsing, and for negotiate context parsing - Fix for incorrect dereferencing POSIX ACLs" * tag '6.4-rc5-smb3-server-fixes' of git://git.samba.org/ksmbd: ksmbd: validate smb request protocol id ksmbd: check the validation of pdu_size in ksmbd_conn_handler_loop ksmbd: fix posix_acls and acls dereferencing possible ERR_PTR() ksmbd: fix out-of-bound read in parse_lease_state() ksmbd: fix out-of-bound read in deassemble_neg_contexts()
Unified · Split
| @@ -294,6 +294,9 @@ bool ksmbd_conn_alive(struct ksmbd_conn *conn) · 293 unmodified lines | |||
| 294 | return true; | 294 | return true; |
| 295 | } | 295 | } |
| 296 | 296 | ||
| 297 | #define SMB1_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb_hdr)) | ||
| 298 | #define SMB2_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb2_hdr) + 4) | ||
| 299 | |||
| 297 | /** | 300 | /** |
| 298 | * ksmbd_conn_handler_loop() - session thread to listen on new smb requests | 301 | * ksmbd_conn_handler_loop() - session thread to listen on new smb requests |
| 299 | * @p: connection instance | 302 | * @p: connection instance |
| @@ -350,6 +353,9 @@ int ksmbd_conn_handler_loop(void *p) · 50 unmodified lines | |||
| 350 | if (pdu_size > MAX_STREAM_PROT_LEN) | 353 | if (pdu_size > MAX_STREAM_PROT_LEN) |
| 351 | break; | 354 | break; |
| 352 | 355 | ||
| 356 | if (pdu_size < SMB1_MIN_SUPPORTED_HEADER_SIZE) | ||
| 357 | break; | ||
| 358 | |||
| 353 | /* 4 for rfc1002 length field */ | 359 | /* 4 for rfc1002 length field */ |
| 354 | /* 1 for implied bcc[0] */ | 360 | /* 1 for implied bcc[0] */ |
| 355 | size = pdu_size + 4 + 1; | 361 | size = pdu_size + 4 + 1; |
| @@ -358,8 +364,6 @@ int ksmbd_conn_handler_loop(void *p) · 2 unmodified lines | |||
| 358 | break; | 364 | break; |
| 359 | 365 | ||
| 360 | memcpy(conn->request_buf, hdr_buf, sizeof(hdr_buf)); | 366 | memcpy(conn->request_buf, hdr_buf, sizeof(hdr_buf)); |
| 361 | if (!ksmbd_smb_request(conn)) | ||
| 362 | break; | ||
| 363 | 367 | ||
| 364 | /* | 368 | /* |
| 365 | * We already read 4 bytes to find out PDU size, now | 369 | * We already read 4 bytes to find out PDU size, now |
| @@ -377,6 +381,15 @@ int ksmbd_conn_handler_loop(void *p) · 11 unmodified lines | |||
| 377 | continue; | 381 | continue; |
| 378 | } | 382 | } |
| 379 | 383 | ||
| 384 | if (!ksmbd_smb_request(conn)) | ||
| 385 | break; | ||
| 386 | |||
| 387 | if (((struct smb2_hdr *)smb2_get_msg(conn->request_buf))->ProtocolId == | ||
| 388 | SMB2_PROTO_NUMBER) { | ||
| 389 | if (pdu_size < SMB2_MIN_SUPPORTED_HEADER_SIZE) | ||
| 390 | break; | ||
| 391 | } | ||
| 392 | |||
| 380 | if (!default_conn_ops.process_fn) { | 393 | if (!default_conn_ops.process_fn) { |
| 381 | pr_err("No connection request callback\n"); | 394 | pr_err("No connection request callback\n"); |
| 382 | break; | 395 | break; |
| @@ -1415,56 +1415,38 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) · 1414 unmodified lines | |||
| 1415 | */ | 1415 | */ |
| 1416 | struct lease_ctx_info *parse_lease_state(void *open_req) | 1416 | struct lease_ctx_info *parse_lease_state(void *open_req) |
| 1417 | { | 1417 | { |
| 1418 | char *data_offset; | ||
| 1419 | struct create_context *cc; | 1418 | struct create_context *cc; |
| 1420 | unsigned int next = 0; | ||
| 1421 | char *name; | ||
| 1422 | bool found = false; | ||
| 1423 | struct smb2_create_req *req = (struct smb2_create_req *)open_req; | 1419 | struct smb2_create_req *req = (struct smb2_create_req *)open_req; |
| 1424 | struct lease_ctx_info *lreq = kzalloc(sizeof(struct lease_ctx_info), | 1420 | struct lease_ctx_info *lreq; |
| 1425 | GFP_KERNEL); | 1421 | |
| 1422 | cc = smb2_find_context_vals(req, SMB2_CREATE_REQUEST_LEASE, 4); | ||
| 1423 | if (IS_ERR_OR_NULL(cc)) | ||
| 1424 | return NULL; | ||
| 1425 | |||
| 1426 | lreq = kzalloc(sizeof(struct lease_ctx_info), GFP_KERNEL); | ||
| 1426 | if (!lreq) | 1427 | if (!lreq) |
| 1427 | return NULL; | 1428 | return NULL; |
| 1428 | 1429 | ||
| 1429 | data_offset = (char *)req + le32_to_cpu(req->CreateContextsOffset); | 1430 | if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) { |
| 1430 | cc = (struct create_context *)data_offset; | 1431 | struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; |
| 1431 | do { | ||
| 1432 | cc = (struct create_context *)((char *)cc + next); | ||
| 1433 | name = le16_to_cpu(cc->NameOffset) + (char *)cc; | ||
| 1434 | if (le16_to_cpu(cc->NameLength) != 4 || | ||
| 1435 | strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4)) { | ||
| 1436 | next = le32_to_cpu(cc->Next); | ||
| 1437 | continue; | ||
| 1438 | } | ||
| 1439 | found = true; | ||
| 1440 | break; | ||
| 1441 | } while (next != 0); | ||
| 1442 | 1432 | ||
| 1443 | if (found) { | 1433 | memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); |
| 1444 | if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) { | 1434 | lreq->req_state = lc->lcontext.LeaseState; |
| 1445 | struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; | 1435 | lreq->flags = lc->lcontext.LeaseFlags; |
| 1446 | 1436 | lreq->duration = lc->lcontext.LeaseDuration; | |
| 1447 | memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); | 1437 | memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, |
| 1448 | lreq->req_state = lc->lcontext.LeaseState; | 1438 | SMB2_LEASE_KEY_SIZE); |
| 1449 | lreq->flags = lc->lcontext.LeaseFlags; | 1439 | lreq->version = 2; |
| 1450 | lreq->duration = lc->lcontext.LeaseDuration; | 1440 | } else { |
| 1451 | memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, | 1441 | struct create_lease *lc = (struct create_lease *)cc; |
| 1452 | SMB2_LEASE_KEY_SIZE); | ||
| 1453 | lreq->version = 2; | ||
| 1454 | } else { | ||
| 1455 | struct create_lease *lc = (struct create_lease *)cc; | ||
| 1456 | 1442 | ||
| 1457 | memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); | 1443 | memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); |
| 1458 | lreq->req_state = lc->lcontext.LeaseState; | 1444 | lreq->req_state = lc->lcontext.LeaseState; |
| 1459 | lreq->flags = lc->lcontext.LeaseFlags; | 1445 | lreq->flags = lc->lcontext.LeaseFlags; |
| 1460 | lreq->duration = lc->lcontext.LeaseDuration; | 1446 | lreq->duration = lc->lcontext.LeaseDuration; |
| 1461 | lreq->version = 1; | 1447 | lreq->version = 1; |
| 1462 | } | ||
| 1463 | return lreq; | ||
| 1464 | } | 1448 | } |
| 1465 | 1449 | return lreq; | |
| 1466 | kfree(lreq); | ||
| 1467 | return NULL; | ||
| 1468 | } | 1450 | } |
| 1469 | 1451 | ||
| 1470 | /** | 1452 | /** |
| @@ -963,13 +963,13 @@ static void decode_sign_cap_ctxt(struct ksmbd_conn *conn, · 962 unmodified lines | |||
| 963 | 963 | ||
| 964 | static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, | 964 | static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, |
| 965 | struct smb2_negotiate_req *req, | 965 | struct smb2_negotiate_req *req, |
| 966 | int len_of_smb) | 966 | unsigned int len_of_smb) |
| 967 | { | 967 | { |
| 968 | /* +4 is to account for the RFC1001 len field */ | 968 | /* +4 is to account for the RFC1001 len field */ |
| 969 | struct smb2_neg_context *pctx = (struct smb2_neg_context *)req; | 969 | struct smb2_neg_context *pctx = (struct smb2_neg_context *)req; |
| 970 | int i = 0, len_of_ctxts; | 970 | int i = 0, len_of_ctxts; |
| 971 | int offset = le32_to_cpu(req->NegotiateContextOffset); | 971 | unsigned int offset = le32_to_cpu(req->NegotiateContextOffset); |
| 972 | int neg_ctxt_cnt = le16_to_cpu(req->NegotiateContextCount); | 972 | unsigned int neg_ctxt_cnt = le16_to_cpu(req->NegotiateContextCount); |
| 973 | __le32 status = STATUS_INVALID_PARAMETER; | 973 | __le32 status = STATUS_INVALID_PARAMETER; |
| 974 | 974 | ||
| 975 | ksmbd_debug(SMB, "decoding %d negotiate contexts\n", neg_ctxt_cnt); | 975 | ksmbd_debug(SMB, "decoding %d negotiate contexts\n", neg_ctxt_cnt); |
| @@ -983,7 +983,7 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, · 7 unmodified lines | |||
| 983 | while (i++ < neg_ctxt_cnt) { | 983 | while (i++ < neg_ctxt_cnt) { |
| 984 | int clen, ctxt_len; | 984 | int clen, ctxt_len; |
| 985 | 985 | ||
| 986 | if (len_of_ctxts < sizeof(struct smb2_neg_context)) | 986 | if (len_of_ctxts < (int)sizeof(struct smb2_neg_context)) |
| 987 | break; | 987 | break; |
| 988 | 988 | ||
| 989 | pctx = (struct smb2_neg_context *)((char *)pctx + offset); | 989 | pctx = (struct smb2_neg_context *)((char *)pctx + offset); |
| @@ -1038,9 +1038,8 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, · 48 unmodified lines | |||
| 1038 | } | 1038 | } |
| 1039 | 1039 | ||
| 1040 | /* offsets must be 8 byte aligned */ | 1040 | /* offsets must be 8 byte aligned */ |
| 1041 | clen = (clen + 7) & ~0x7; | 1041 | offset = (ctxt_len + 7) & ~0x7; |
| 1042 | offset = clen + sizeof(struct smb2_neg_context); | 1042 | len_of_ctxts -= offset; |
| 1043 | len_of_ctxts -= clen + sizeof(struct smb2_neg_context); | ||
| 1044 | } | 1043 | } |
| 1045 | return status; | 1044 | return status; |
| 1046 | } | 1045 | } |
| @@ -158,7 +158,19 @@ int ksmbd_verify_smb_message(struct ksmbd_work *work) · 157 unmodified lines | |||
| 158 | */ | 158 | */ |
| 159 | bool ksmbd_smb_request(struct ksmbd_conn *conn) | 159 | bool ksmbd_smb_request(struct ksmbd_conn *conn) |
| 160 | { | 160 | { |
| 161 | return conn->request_buf[0] == 0; | 161 | __le32 *proto = (__le32 *)smb2_get_msg(conn->request_buf); |
| 162 | |||
| 163 | if (*proto == SMB2_COMPRESSION_TRANSFORM_ID) { | ||
| 164 | pr_err_ratelimited("smb2 compression not support yet"); | ||
| 165 | return false; | ||
| 166 | } | ||
| 167 | |||
| 168 | if (*proto != SMB1_PROTO_NUMBER && | ||
| 169 | *proto != SMB2_PROTO_NUMBER && | ||
| 170 | *proto != SMB2_TRANSFORM_PROTO_NUM) | ||
| 171 | return false; | ||
| 172 | |||
| 173 | return true; | ||
| 162 | } | 174 | } |
| 163 | 175 | ||
| 164 | static bool supported_protocol(int idx) | 176 | static bool supported_protocol(int idx) |
| @@ -1290,7 +1290,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, · 1289 unmodified lines | |||
| 1290 | 1290 | ||
| 1291 | if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { | 1291 | if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { |
| 1292 | posix_acls = get_inode_acl(d_inode(path->dentry), ACL_TYPE_ACCESS); | 1292 | posix_acls = get_inode_acl(d_inode(path->dentry), ACL_TYPE_ACCESS); |
| 1293 | if (posix_acls && !found) { | 1293 | if (!IS_ERR_OR_NULL(posix_acls) && !found) { |
| 1294 | unsigned int id = -1; | 1294 | unsigned int id = -1; |
| 1295 | 1295 | ||
| 1296 | pa_entry = posix_acls->a_entries; | 1296 | pa_entry = posix_acls->a_entries; |
| @@ -1314,7 +1314,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, · 17 unmodified lines | |||
| 1314 | } | 1314 | } |
| 1315 | } | 1315 | } |
| 1316 | } | 1316 | } |
| 1317 | if (posix_acls) | 1317 | if (!IS_ERR_OR_NULL(posix_acls)) |
| 1318 | posix_acl_release(posix_acls); | 1318 | posix_acl_release(posix_acls); |
| 1319 | } | 1319 | } |
| 1320 | 1320 | ||
| @@ -1321,7 +1321,7 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct mnt_idmap *id · 1320 unmodified lines | |||
| 1321 | return NULL; | 1321 | return NULL; |
| 1322 | 1322 | ||
| 1323 | posix_acls = get_inode_acl(inode, acl_type); | 1323 | posix_acls = get_inode_acl(inode, acl_type); |
| 1324 | if (!posix_acls) | 1324 | if (IS_ERR_OR_NULL(posix_acls)) |
| 1325 | return NULL; | 1325 | return NULL; |
| 1326 | 1326 | ||
| 1327 | smb_acl = kzalloc(sizeof(struct xattr_smb_acl) + | 1327 | smb_acl = kzalloc(sizeof(struct xattr_smb_acl) + |
| @@ -1830,7 +1830,7 @@ int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap, · 502 unmodified lines | |||
| 1830 | return -EOPNOTSUPP; | 1830 | return -EOPNOTSUPP; |
| 1831 | 1831 | ||
| 1832 | acls = get_inode_acl(parent_inode, ACL_TYPE_DEFAULT); | 1832 | acls = get_inode_acl(parent_inode, ACL_TYPE_DEFAULT); |
| 1833 | if (!acls) | 1833 | if (IS_ERR_OR_NULL(acls)) |
| 1834 | return -ENOENT; | 1834 | return -ENOENT; |
| 1835 | pace = acls->a_entries; | 1835 | pace = acls->a_entries; |
| 1836 | 1836 | ||