[RFC PATCH 02/11] smb/server: validate notify requests
ChenXiaoSong <[email protected]> Thu, 23 Jul 2026 03:16:30 +0000
| Newsgroups | org.kernel.vger.linux-cifs |
|---|---|
| Message-ID | <[email protected]> |
From: ChenXiaoSong <[email protected]> Validate the file ID, directory type, access rights and output buffer length before handling a change notify request. Signed-off-by: ChenXiaoSong <[email protected]> --- fs/smb/server/Makefile | 2 +- fs/smb/server/notify.c | 108 ++++++++++++++++++++++++++++++++++++++++ fs/smb/server/notify.h | 22 ++++++++ fs/smb/server/smb2pdu.c | 12 +++-- 4 files changed, 138 insertions(+), 6 deletions(-) create mode 100644 fs/smb/server/notify.c create mode 100644 fs/smb/server/notify.h diff --git a/fs/smb/server/Makefile b/fs/smb/server/Makefile index a3e9306055e8..51d8972a0942 100644 --- a/fs/smb/server/Makefile +++ b/fs/smb/server/Makefile @@ -9,7 +9,7 @@ ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o ndr.o \ mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \ mgmt/tree_connect.o mgmt/user_session.o smb_common.o \ transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \ - smb2ops.o smb2misc.o ksmbd_spnego_negtokeninit.asn1.o \ + smb2ops.o smb2misc.o notify.o ksmbd_spnego_negtokeninit.asn1.o \ ksmbd_spnego_negtokentarg.asn1.o asn1.o compress.o $(obj)/asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.h $(obj)/ksmbd_spnego_negtokentarg.asn1.h diff --git a/fs/smb/server/notify.c b/fs/smb/server/notify.c new file mode 100644 index 000000000000..ad812fde5879 --- /dev/null +++ b/fs/smb/server/notify.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * SMB2 CHANGE_NOTIFY + * + * Copyright (C) 2026 KylinSoft Co., Ltd. All rights reserved. + * Author(s): ChenXiaoSong <[email protected]> + * + */ + +#include "glob.h" +#include "../common/smb2status.h" +#include "connection.h" +#include "ksmbd_work.h" +#include "notify.h" +#include "smb_common.h" +#include "smb2pdu.h" +#include "vfs_cache.h" + +static struct ksmbd_file * +ksmbd_notify_validate_req(struct ksmbd_work *work, + struct smb2_change_notify_req *req, + struct smb2_change_notify_rsp *rsp) +{ + struct ksmbd_file *fp; + int err; + + fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, + req->PersistentFileId); + if (!fp) { + pr_err("Invalid file id for notify, fid %llu:%llu\n", + le64_to_cpu(req->PersistentFileId), + le64_to_cpu(req->VolatileFileId)); + rsp->hdr.Status = STATUS_FILE_CLOSED; + return ERR_PTR(-ENOENT); + } + + ksmbd_debug(NOTIFY, + "fid %llu:%llu, handle notify request, filter 0x%x, flags 0x%x\n", + fp->persistent_id, fp->volatile_id, + le32_to_cpu(req->CompletionFilter), le16_to_cpu(req->Flags)); + + if (!S_ISDIR(file_inode(fp->filp)->i_mode)) { + pr_err("Notify file id is not a directory, fid %llu:%llu\n", + fp->persistent_id, fp->volatile_id); + rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; + err = -ENOTDIR; + goto err_put_fp; + } + + if (!(fp->daccess & FILE_LIST_DIRECTORY_LE)) { + pr_err("No permission to monitor directory, fid %llu:%llu\n", + fp->persistent_id, fp->volatile_id); + rsp->hdr.Status = STATUS_ACCESS_DENIED; + err = -EACCES; + goto err_put_fp; + } + + if (le32_to_cpu(req->OutputBufferLength) > + work->conn->vals->max_trans_size) { + pr_err("Notify output buffer length %u exceeds maximum %u\n", + le32_to_cpu(req->OutputBufferLength), + work->conn->vals->max_trans_size); + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + err = -EINVAL; + goto err_put_fp; + } + + return fp; + +err_put_fp: + ksmbd_fd_put(work, fp); + return ERR_PTR(err); +} + +/** + * ksmbd_handle_notify() - handle an SMB2 change notify request + * @work: smb work containing notify command buffer + * @req: SMB2 change notify request + * @rsp: SMB2 change notify response + * + * Return: 0 on success, otherwise error + */ +int ksmbd_handle_notify(struct ksmbd_work *work, + struct smb2_change_notify_req *req, + struct smb2_change_notify_rsp *rsp) +{ + struct ksmbd_file *fp = NULL; + int err = 0; + + fp = ksmbd_notify_validate_req(work, req, rsp); + if (IS_ERR(fp)) { + err = PTR_ERR(fp); + goto out; + } + + ksmbd_fd_put(work, fp); + rsp->hdr.Status = STATUS_NOT_IMPLEMENTED; + err = -EOPNOTSUPP; + +out: + if (err) + pr_err("Failed to handle notify request: %d, status: 0x%x\n", + err, le32_to_cpu(rsp->hdr.Status)); + if (rsp->hdr.Status != STATUS_SUCCESS) + smb2_set_err_rsp(work); + return err; +} diff --git a/fs/smb/server/notify.h b/fs/smb/server/notify.h new file mode 100644 index 000000000000..3118ad09c841 --- /dev/null +++ b/fs/smb/server/notify.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * SMB2 CHANGE_NOTIFY + * + * Copyright (C) 2026 KylinSoft Co., Ltd. All rights reserved. + * Author(s): ChenXiaoSong <[email protected]> + * + */ + +#ifndef __SMB_SERVER_NOTIFY_H__ +#define __SMB_SERVER_NOTIFY_H__ + +struct ksmbd_work; +struct smb2_change_notify_req; +struct smb2_change_notify_rsp; + +int ksmbd_handle_notify(struct ksmbd_work *work, + struct smb2_change_notify_req *req, + struct smb2_change_notify_rsp *rsp); + +#endif /* __SMB_SERVER_NOTIFY_H__ */ diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 0680222a8da4..fe1b6c9f71c5 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -29,6 +29,7 @@ #include "vfs.h" #include "vfs_cache.h" #include "misc.h" +#include "notify.h" #include "server.h" #include "smb_common.h" @@ -10860,22 +10861,23 @@ int smb2_notify(struct ksmbd_work *work) struct smb2_change_notify_req *req; struct smb2_change_notify_rsp *rsp; - ksmbd_debug(SMB, "Received smb2 notify\n"); + ksmbd_debug(NOTIFY, "Received smb2 notify\n"); WORK_BUFFERS(work, req, rsp); - if (smb2_compound_has_failed(work, &rsp->hdr)) + if (smb2_compound_has_failed(work, &rsp->hdr)) { + pr_err("Failed compound notify request\n"); return -EACCES; + } if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) { + pr_err("Notify request is not the last compound command\n"); rsp->hdr.Status = STATUS_INTERNAL_ERROR; smb2_set_err_rsp(work); return -EIO; } - smb2_set_err_rsp(work); - rsp->hdr.Status = STATUS_NOT_IMPLEMENTED; - return -EOPNOTSUPP; + return ksmbd_handle_notify(work, req, rsp); } /** -- 2.54.0