Message ID | 20230517185820.1264368-1-h3xrabbit@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | ksmbd: fix multiple out-of-bounds read during context decoding | expand |
On Wed, May 17, 2023 at 06:58:20PM +0000, HexRabbit wrote: >From: Kuan-Ting Chen <h3xrabbit@gmail.com> > >Ensure the context's length is valid (excluding VLAs) before casting the >pointer to the corresponding structure pointer type, also removed >redundant check on `len_of_ctxts`. > >Signed-off-by: Kuan-Ting Chen <h3xrabbit@gmail.com> >--- > fs/ksmbd/smb2pdu.c | 23 +++++++++++++++++------ > 1 file changed, 17 insertions(+), 6 deletions(-) > >diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c >index 972176bff..83b877254 100644 >--- a/fs/ksmbd/smb2pdu.c >+++ b/fs/ksmbd/smb2pdu.c >@@ -969,18 +969,16 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, > len_of_ctxts = len_of_smb - offset; > > while (i++ < neg_ctxt_cnt) { >- int clen; >- >- /* check that offset is not beyond end of SMB */ >- if (len_of_ctxts == 0) >- break; >+ int clen, ctxt_len; Just a drive-by comment here (haven't had time to look at the underlying code). Should lengths in protocol parsing *ever* be defined at 'int' ? IMHO no, never. That's a disaster waiting to happen as int overflow driven by the peer can often cause integer wrap to negative, leaving all the nice "not greater than packet length" to fail horribly. We excised all 'int' length representations from the Samba parser a long time ago.
On (23/05/18 03:28), Hex Rabbit wrote: > > Maybe it might be worth addressing these issues in the upcoming patches. > I guess that would be nice to see
On Thu, May 18, 2023 at 09:37:48AM +0900, Sergey Senozhatsky wrote: >On (23/05/18 03:28), Hex Rabbit wrote: >> >> Maybe it might be worth addressing these issues in the upcoming patches. >> > >I guess that would be nice to see More than nice. Essential IMHO.
2023-05-18 3:58 GMT+09:00, HexRabbit <h3xrabbit@gmail.com>: > From: Kuan-Ting Chen <h3xrabbit@gmail.com> > > Ensure the context's length is valid (excluding VLAs) before casting the > pointer to the corresponding structure pointer type, also removed > redundant check on `len_of_ctxts`. > > Signed-off-by: Kuan-Ting Chen <h3xrabbit@gmail.com> > --- > fs/ksmbd/smb2pdu.c | 23 +++++++++++++++++------ > 1 file changed, 17 insertions(+), 6 deletions(-) > > diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c > index 972176bff..83b877254 100644 > --- a/fs/ksmbd/smb2pdu.c > +++ b/fs/ksmbd/smb2pdu.c > @@ -969,18 +969,16 @@ static __le32 deassemble_neg_contexts(struct > ksmbd_conn *conn, > len_of_ctxts = len_of_smb - offset; > > while (i++ < neg_ctxt_cnt) { > - int clen; > - > - /* check that offset is not beyond end of SMB */ > - if (len_of_ctxts == 0) > - break; > + int clen, ctxt_len; > > if (len_of_ctxts < sizeof(struct smb2_neg_context)) > break; > > pctx = (struct smb2_neg_context *)((char *)pctx + offset); > clen = le16_to_cpu(pctx->DataLength); > - if (clen + sizeof(struct smb2_neg_context) > len_of_ctxts) > + ctxt_len = clen + sizeof(struct smb2_neg_context); > + > + if (ctxt_len > len_of_ctxts) > break; > > if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) { > @@ -989,6 +987,9 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn > *conn, > if (conn->preauth_info->Preauth_HashId) > break; > > + if (ctxt_len < sizeof(struct smb2_preauth_neg_context)) > + break; > + > status = decode_preauth_ctxt(conn, > (struct smb2_preauth_neg_context *)pctx, > len_of_ctxts); > @@ -1000,6 +1001,9 @@ static __le32 deassemble_neg_contexts(struct > ksmbd_conn *conn, > if (conn->cipher_type) > break; > > + if (ctxt_len < sizeof(struct smb2_encryption_neg_context)) You need to consider Ciphers flex-array size to validate ctxt_len. we can get its size using CipherCount in smb2_encryption_neg_context. > + break; > + > decode_encrypt_ctxt(conn, > (struct smb2_encryption_neg_context *)pctx, > len_of_ctxts); > @@ -1009,6 +1013,9 @@ static __le32 deassemble_neg_contexts(struct > ksmbd_conn *conn, > if (conn->compress_algorithm) > break; > > + if (ctxt_len < sizeof(struct smb2_compression_capabilities_context)) Ditto. > + break; > + > decode_compress_ctxt(conn, > (struct smb2_compression_capabilities_context *)pctx); > } else if (pctx->ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) { > @@ -1021,6 +1028,10 @@ static __le32 deassemble_neg_contexts(struct > ksmbd_conn *conn, > } else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) { > ksmbd_debug(SMB, > "deassemble SMB2_SIGNING_CAPABILITIES context\n"); > + > + if (ctxt_len < sizeof(struct smb2_signing_capabilities)) Ditto. Thanks. > + break; > + > decode_sign_cap_ctxt(conn, > (struct smb2_signing_capabilities *)pctx, > len_of_ctxts); > -- > 2.25.1 > >
> You need to consider Ciphers flex-array size to validate ctxt_len. we > can get its size using CipherCount in smb2_encryption_neg_context. I'm not checking the flex-array size since both `decode_sign_cap_ctxt()` and `decode_encrypt_ctxt()` have done it, or should I move it out? ``` if (sizeof(struct smb2_encryption_neg_context) + cphs_size > len_of_ctxts) { pr_err("Invalid cipher count(%d)\n", cph_cnt); return; } ``` ``` if (sizeof(struct smb2_signing_capabilities) + sign_alos_size > len_of_ctxts) { pr_err("Invalid signing algorithm count(%d)\n", sign_algo_cnt); return; } ```
2023-05-18 15:30 GMT+09:00, Hex Rabbit <h3xrabbit@gmail.com>: >> You need to consider Ciphers flex-array size to validate ctxt_len. we >> can get its size using CipherCount in smb2_encryption_neg_context. > > I'm not checking the flex-array size since both `decode_sign_cap_ctxt()` > and `decode_encrypt_ctxt()` have done it, or should I move it out? Yes, We can move it out. Thanks. > > ``` > if (sizeof(struct smb2_encryption_neg_context) + cphs_size > > len_of_ctxts) { > pr_err("Invalid cipher count(%d)\n", cph_cnt); > return; > } > ``` > > ``` > if (sizeof(struct smb2_signing_capabilities) + sign_alos_size > > len_of_ctxts) { > pr_err("Invalid signing algorithm count(%d)\n", sign_algo_cnt); > return; > } > ``` >
I have decided to leave the modifications within the function that handles the corresponding context. The reason for this is that I believe consolidating the checks together can improve readability, also, moving them out would require us to read the size of the flex-sized array again in the corresponding function. What do you think? below is the modified patch: diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 972176bff..0285c3f9e 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -849,13 +849,13 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, struct smb2_preauth_neg_context *pneg_ctxt, - int len_of_ctxts) + int ctxt_len) { /* * sizeof(smb2_preauth_neg_context) assumes SMB311_SALT_SIZE Salt, * which may not be present. Only check for used HashAlgorithms[1]. */ - if (len_of_ctxts < MIN_PREAUTH_CTXT_DATA_LEN) + if (ctxt_len < MIN_PREAUTH_CTXT_DATA_LEN) return STATUS_INVALID_PARAMETER; if (pneg_ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512) @@ -867,15 +867,23 @@ static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, static void decode_encrypt_ctxt(struct ksmbd_conn *conn, struct smb2_encryption_neg_context *pneg_ctxt, - int len_of_ctxts) + int ctxt_len) { - int cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount); - int i, cphs_size = cph_cnt * sizeof(__le16); + int cph_cnt; + int i, cphs_size; + + if (sizeof(struct smb2_encryption_neg_context) > ctxt_len) { + pr_err("Invalid SMB2_ENCRYPTION_CAPABILITIES context size\n"); + return; + } conn->cipher_type = 0; + cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount); + cphs_size = cph_cnt * sizeof(__le16); + if (sizeof(struct smb2_encryption_neg_context) + cphs_size > - len_of_ctxts) { + ctxt_len) { pr_err("Invalid cipher count(%d)\n", cph_cnt); return; } @@ -923,15 +931,22 @@ static void decode_compress_ctxt(struct ksmbd_conn *conn, static void decode_sign_cap_ctxt(struct ksmbd_conn *conn, struct smb2_signing_capabilities *pneg_ctxt, - int len_of_ctxts) + int ctxt_len) { - int sign_algo_cnt = le16_to_cpu(pneg_ctxt->SigningAlgorithmCount); - int i, sign_alos_size = sign_algo_cnt * sizeof(__le16); + int sign_algo_cnt; + int i, sign_alos_size; + + if (sizeof(struct smb2_signing_capabilities) > ctxt_len) { + pr_err("Invalid SMB2_SIGNING_CAPABILITIES context length\n"); + return; + } conn->signing_negotiated = false; + sign_algo_cnt = le16_to_cpu(pneg_ctxt->SigningAlgorithmCount); + sign_alos_size = sign_algo_cnt * sizeof(__le16); if (sizeof(struct smb2_signing_capabilities) + sign_alos_size > - len_of_ctxts) { + ctxt_len) { pr_err("Invalid signing algorithm count(%d)\n", sign_algo_cnt); return; } @@ -969,18 +984,16 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, len_of_ctxts = len_of_smb - offset; while (i++ < neg_ctxt_cnt) { - int clen; - - /* check that offset is not beyond end of SMB */ - if (len_of_ctxts == 0) - break; + int clen, ctxt_len; if (len_of_ctxts < sizeof(struct smb2_neg_context)) break; pctx = (struct smb2_neg_context *)((char *)pctx + offset); clen = le16_to_cpu(pctx->DataLength); - if (clen + sizeof(struct smb2_neg_context) > len_of_ctxts) + ctxt_len = clen + sizeof(struct smb2_neg_context); + + if (ctxt_len > len_of_ctxts) break; if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) { @@ -991,7 +1004,7 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, status = decode_preauth_ctxt(conn, (struct smb2_preauth_neg_context *)pctx, - len_of_ctxts); + ctxt_len); if (status != STATUS_SUCCESS) break; } else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) { @@ -1002,7 +1015,7 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, decode_encrypt_ctxt(conn, (struct smb2_encryption_neg_context *)pctx, - len_of_ctxts); + ctxt_len); } else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) { ksmbd_debug(SMB, "deassemble SMB2_COMPRESSION_CAPABILITIES context\n"); @@ -1021,9 +1034,10 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, } else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) { ksmbd_debug(SMB, "deassemble SMB2_SIGNING_CAPABILITIES context\n"); + decode_sign_cap_ctxt(conn, (struct smb2_signing_capabilities *)pctx, - len_of_ctxts); + ctxt_len); } /* offsets must be 8 byte aligned */ --- Namjae Jeon <linkinjeon@kernel.org> 於 2023年5月18日 週四 下午2:36寫道: > > 2023-05-18 15:30 GMT+09:00, Hex Rabbit <h3xrabbit@gmail.com>: > >> You need to consider Ciphers flex-array size to validate ctxt_len. we > >> can get its size using CipherCount in smb2_encryption_neg_context. > > > > I'm not checking the flex-array size since both `decode_sign_cap_ctxt()` > > and `decode_encrypt_ctxt()` have done it, or should I move it out? > Yes, We can move it out. Thanks. > > > > ``` > > if (sizeof(struct smb2_encryption_neg_context) + cphs_size > > > len_of_ctxts) { > > pr_err("Invalid cipher count(%d)\n", cph_cnt); > > return; > > } > > ``` > > > > ``` > > if (sizeof(struct smb2_signing_capabilities) + sign_alos_size > > > len_of_ctxts) { > > pr_err("Invalid signing algorithm count(%d)\n", sign_algo_cnt); > > return; > > } > > ``` > >
2023-05-18 17:11 GMT+09:00, Hex Rabbit <h3xrabbit@gmail.com>: > I have decided to leave the modifications within the function that handles > the > corresponding context. The reason for this is that I believe consolidating > the > checks together can improve readability, also, moving them out would > require > us to read the size of the flex-sized array again in the corresponding > function. > > What do you think? Looks okay. Please send the patch to test to the list. Thanks. > > below is the modified patch: > > diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c > index 972176bff..0285c3f9e 100644 > --- a/fs/ksmbd/smb2pdu.c > +++ b/fs/ksmbd/smb2pdu.c > @@ -849,13 +849,13 @@ static void assemble_neg_contexts(struct ksmbd_conn > *conn, > > static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, > struct smb2_preauth_neg_context *pneg_ctxt, > - int len_of_ctxts) > + int ctxt_len) > { > /* > * sizeof(smb2_preauth_neg_context) assumes SMB311_SALT_SIZE Salt, > * which may not be present. Only check for used HashAlgorithms[1]. > */ > - if (len_of_ctxts < MIN_PREAUTH_CTXT_DATA_LEN) > + if (ctxt_len < MIN_PREAUTH_CTXT_DATA_LEN) > return STATUS_INVALID_PARAMETER; > > if (pneg_ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512) > @@ -867,15 +867,23 @@ static __le32 decode_preauth_ctxt(struct ksmbd_conn > *conn, > > static void decode_encrypt_ctxt(struct ksmbd_conn *conn, > struct smb2_encryption_neg_context *pneg_ctxt, > - int len_of_ctxts) > + int ctxt_len) > { > - int cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount); > - int i, cphs_size = cph_cnt * sizeof(__le16); > + int cph_cnt; > + int i, cphs_size; > + > + if (sizeof(struct smb2_encryption_neg_context) > ctxt_len) { > + pr_err("Invalid SMB2_ENCRYPTION_CAPABILITIES context size\n"); > + return; > + } > > conn->cipher_type = 0; > > + cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount); > + cphs_size = cph_cnt * sizeof(__le16); > + > if (sizeof(struct smb2_encryption_neg_context) + cphs_size > > - len_of_ctxts) { > + ctxt_len) { > pr_err("Invalid cipher count(%d)\n", cph_cnt); > return; > } > @@ -923,15 +931,22 @@ static void decode_compress_ctxt(struct ksmbd_conn > *conn, > > static void decode_sign_cap_ctxt(struct ksmbd_conn *conn, > struct smb2_signing_capabilities *pneg_ctxt, > - int len_of_ctxts) > + int ctxt_len) > { > - int sign_algo_cnt = le16_to_cpu(pneg_ctxt->SigningAlgorithmCount); > - int i, sign_alos_size = sign_algo_cnt * sizeof(__le16); > + int sign_algo_cnt; > + int i, sign_alos_size; > + > + if (sizeof(struct smb2_signing_capabilities) > ctxt_len) { > + pr_err("Invalid SMB2_SIGNING_CAPABILITIES context length\n"); > + return; > + } > > conn->signing_negotiated = false; > + sign_algo_cnt = le16_to_cpu(pneg_ctxt->SigningAlgorithmCount); > + sign_alos_size = sign_algo_cnt * sizeof(__le16); > > if (sizeof(struct smb2_signing_capabilities) + sign_alos_size > > - len_of_ctxts) { > + ctxt_len) { > pr_err("Invalid signing algorithm count(%d)\n", sign_algo_cnt); > return; > } > @@ -969,18 +984,16 @@ static __le32 deassemble_neg_contexts(struct > ksmbd_conn *conn, > len_of_ctxts = len_of_smb - offset; > > while (i++ < neg_ctxt_cnt) { > - int clen; > - > - /* check that offset is not beyond end of SMB */ > - if (len_of_ctxts == 0) > - break; > + int clen, ctxt_len; > > if (len_of_ctxts < sizeof(struct smb2_neg_context)) > break; > > pctx = (struct smb2_neg_context *)((char *)pctx + offset); > clen = le16_to_cpu(pctx->DataLength); > - if (clen + sizeof(struct smb2_neg_context) > len_of_ctxts) > + ctxt_len = clen + sizeof(struct smb2_neg_context); > + > + if (ctxt_len > len_of_ctxts) > break; > > if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) { > @@ -991,7 +1004,7 @@ static __le32 deassemble_neg_contexts(struct > ksmbd_conn *conn, > > status = decode_preauth_ctxt(conn, > (struct smb2_preauth_neg_context *)pctx, > - len_of_ctxts); > + ctxt_len); > if (status != STATUS_SUCCESS) > break; > } else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) { > @@ -1002,7 +1015,7 @@ static __le32 deassemble_neg_contexts(struct > ksmbd_conn *conn, > > decode_encrypt_ctxt(conn, > (struct smb2_encryption_neg_context *)pctx, > - len_of_ctxts); > + ctxt_len); > } else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) { > ksmbd_debug(SMB, > "deassemble SMB2_COMPRESSION_CAPABILITIES context\n"); > @@ -1021,9 +1034,10 @@ static __le32 deassemble_neg_contexts(struct > ksmbd_conn *conn, > } else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) { > ksmbd_debug(SMB, > "deassemble SMB2_SIGNING_CAPABILITIES context\n"); > + > decode_sign_cap_ctxt(conn, > (struct smb2_signing_capabilities *)pctx, > - len_of_ctxts); > + ctxt_len); > } > > /* offsets must be 8 byte aligned */ > --- > > Namjae Jeon <linkinjeon@kernel.org> 於 2023年5月18日 週四 下午2:36寫道: >> >> 2023-05-18 15:30 GMT+09:00, Hex Rabbit <h3xrabbit@gmail.com>: >> >> You need to consider Ciphers flex-array size to validate ctxt_len. we >> >> can get its size using CipherCount in smb2_encryption_neg_context. >> > >> > I'm not checking the flex-array size since both >> > `decode_sign_cap_ctxt()` >> > and `decode_encrypt_ctxt()` have done it, or should I move it out? >> Yes, We can move it out. Thanks. >> > >> > ``` >> > if (sizeof(struct smb2_encryption_neg_context) + cphs_size > >> > len_of_ctxts) { >> > pr_err("Invalid cipher count(%d)\n", cph_cnt); >> > return; >> > } >> > ``` >> > >> > ``` >> > if (sizeof(struct smb2_signing_capabilities) + sign_alos_size > >> > len_of_ctxts) { >> > pr_err("Invalid signing algorithm count(%d)\n", sign_algo_cnt); >> > return; >> > } >> > ``` >> > >
diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 972176bff..83b877254 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -969,18 +969,16 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, len_of_ctxts = len_of_smb - offset; while (i++ < neg_ctxt_cnt) { - int clen; - - /* check that offset is not beyond end of SMB */ - if (len_of_ctxts == 0) - break; + int clen, ctxt_len; if (len_of_ctxts < sizeof(struct smb2_neg_context)) break; pctx = (struct smb2_neg_context *)((char *)pctx + offset); clen = le16_to_cpu(pctx->DataLength); - if (clen + sizeof(struct smb2_neg_context) > len_of_ctxts) + ctxt_len = clen + sizeof(struct smb2_neg_context); + + if (ctxt_len > len_of_ctxts) break; if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) { @@ -989,6 +987,9 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, if (conn->preauth_info->Preauth_HashId) break; + if (ctxt_len < sizeof(struct smb2_preauth_neg_context)) + break; + status = decode_preauth_ctxt(conn, (struct smb2_preauth_neg_context *)pctx, len_of_ctxts); @@ -1000,6 +1001,9 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, if (conn->cipher_type) break; + if (ctxt_len < sizeof(struct smb2_encryption_neg_context)) + break; + decode_encrypt_ctxt(conn, (struct smb2_encryption_neg_context *)pctx, len_of_ctxts); @@ -1009,6 +1013,9 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, if (conn->compress_algorithm) break; + if (ctxt_len < sizeof(struct smb2_compression_capabilities_context)) + break; + decode_compress_ctxt(conn, (struct smb2_compression_capabilities_context *)pctx); } else if (pctx->ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) { @@ -1021,6 +1028,10 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, } else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) { ksmbd_debug(SMB, "deassemble SMB2_SIGNING_CAPABILITIES context\n"); + + if (ctxt_len < sizeof(struct smb2_signing_capabilities)) + break; + decode_sign_cap_ctxt(conn, (struct smb2_signing_capabilities *)pctx, len_of_ctxts);