ksmbd: add buffer validation in session setup

Make sure the security buffer's length/offset are valid with regards to
the packet length.

Acked-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Marios Makassikis <mmakassikis@freebox.fr>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by Marios Makassikis and committed by Steve French 0d994cd4 621be84a

+41 -28
+9 -7
fs/ksmbd/auth.c
··· 298 298 int blob_len, struct ksmbd_session *sess) 299 299 { 300 300 char *domain_name; 301 - unsigned int lm_off, nt_off; 302 - unsigned short nt_len; 301 + unsigned int nt_off, dn_off; 302 + unsigned short nt_len, dn_len; 303 303 int ret; 304 304 305 305 if (blob_len < sizeof(struct authenticate_message)) { ··· 314 314 return -EINVAL; 315 315 } 316 316 317 - lm_off = le32_to_cpu(authblob->LmChallengeResponse.BufferOffset); 318 317 nt_off = le32_to_cpu(authblob->NtChallengeResponse.BufferOffset); 319 318 nt_len = le16_to_cpu(authblob->NtChallengeResponse.Length); 319 + dn_off = le32_to_cpu(authblob->DomainName.BufferOffset); 320 + dn_len = le16_to_cpu(authblob->DomainName.Length); 321 + 322 + if (blob_len < (u64)dn_off + dn_len || blob_len < (u64)nt_off + nt_len) 323 + return -EINVAL; 320 324 321 325 /* TODO : use domain name that imported from configuration file */ 322 - domain_name = smb_strndup_from_utf16((const char *)authblob + 323 - le32_to_cpu(authblob->DomainName.BufferOffset), 324 - le16_to_cpu(authblob->DomainName.Length), true, 325 - sess->conn->local_nls); 326 + domain_name = smb_strndup_from_utf16((const char *)authblob + dn_off, 327 + dn_len, true, sess->conn->local_nls); 326 328 if (IS_ERR(domain_name)) 327 329 return PTR_ERR(domain_name); 328 330
+32 -21
fs/ksmbd/smb2pdu.c
··· 1257 1257 return 0; 1258 1258 } 1259 1259 1260 - static int decode_negotiation_token(struct ksmbd_work *work, 1261 - struct negotiate_message *negblob) 1260 + static int decode_negotiation_token(struct ksmbd_conn *conn, 1261 + struct negotiate_message *negblob, 1262 + size_t sz) 1262 1263 { 1263 - struct ksmbd_conn *conn = work->conn; 1264 - struct smb2_sess_setup_req *req; 1265 - int sz; 1266 - 1267 1264 if (!conn->use_spnego) 1268 1265 return -EINVAL; 1269 - 1270 - req = work->request_buf; 1271 - sz = le16_to_cpu(req->SecurityBufferLength); 1272 1266 1273 1267 if (ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) { 1274 1268 if (ksmbd_decode_negTokenTarg((char *)negblob, sz, conn)) { ··· 1275 1281 } 1276 1282 1277 1283 static int ntlm_negotiate(struct ksmbd_work *work, 1278 - struct negotiate_message *negblob) 1284 + struct negotiate_message *negblob, 1285 + size_t negblob_len) 1279 1286 { 1280 - struct smb2_sess_setup_req *req = work->request_buf; 1281 1287 struct smb2_sess_setup_rsp *rsp = work->response_buf; 1282 1288 struct challenge_message *chgblob; 1283 1289 unsigned char *spnego_blob = NULL; ··· 1286 1292 int sz, rc; 1287 1293 1288 1294 ksmbd_debug(SMB, "negotiate phase\n"); 1289 - sz = le16_to_cpu(req->SecurityBufferLength); 1290 - rc = ksmbd_decode_ntlmssp_neg_blob(negblob, sz, work->sess); 1295 + rc = ksmbd_decode_ntlmssp_neg_blob(negblob, negblob_len, work->sess); 1291 1296 if (rc) 1292 1297 return rc; 1293 1298 ··· 1354 1361 struct authenticate_message *authblob; 1355 1362 struct ksmbd_user *user; 1356 1363 char *name; 1357 - int sz; 1364 + unsigned int auth_msg_len, name_off, name_len, secbuf_len; 1358 1365 1366 + secbuf_len = le16_to_cpu(req->SecurityBufferLength); 1367 + if (secbuf_len < sizeof(struct authenticate_message)) { 1368 + ksmbd_debug(SMB, "blob len %d too small\n", secbuf_len); 1369 + return NULL; 1370 + } 1359 1371 authblob = user_authblob(conn, req); 1360 - sz = le32_to_cpu(authblob->UserName.BufferOffset); 1361 - name = smb_strndup_from_utf16((const char *)authblob + sz, 1362 - le16_to_cpu(authblob->UserName.Length), 1372 + name_off = le32_to_cpu(authblob->UserName.BufferOffset); 1373 + name_len = le16_to_cpu(authblob->UserName.Length); 1374 + auth_msg_len = le16_to_cpu(req->SecurityBufferOffset) + secbuf_len; 1375 + 1376 + if (auth_msg_len < (u64)name_off + name_len) 1377 + return NULL; 1378 + 1379 + name = smb_strndup_from_utf16((const char *)authblob + name_off, 1380 + name_len, 1363 1381 true, 1364 1382 conn->local_nls); 1365 1383 if (IS_ERR(name)) { ··· 1616 1612 struct smb2_sess_setup_rsp *rsp = work->response_buf; 1617 1613 struct ksmbd_session *sess; 1618 1614 struct negotiate_message *negblob; 1615 + unsigned int negblob_len, negblob_off; 1619 1616 int rc = 0; 1620 1617 1621 1618 ksmbd_debug(SMB, "Received request for session setup\n"); ··· 1697 1692 if (sess->state == SMB2_SESSION_EXPIRED) 1698 1693 sess->state = SMB2_SESSION_IN_PROGRESS; 1699 1694 1700 - negblob = (struct negotiate_message *)((char *)&req->hdr.ProtocolId + 1701 - le16_to_cpu(req->SecurityBufferOffset)); 1695 + negblob_off = le16_to_cpu(req->SecurityBufferOffset); 1696 + negblob_len = le16_to_cpu(req->SecurityBufferLength); 1697 + if (negblob_off < (offsetof(struct smb2_sess_setup_req, Buffer) - 4) || 1698 + negblob_len < offsetof(struct negotiate_message, NegotiateFlags)) 1699 + return -EINVAL; 1702 1700 1703 - if (decode_negotiation_token(work, negblob) == 0) { 1701 + negblob = (struct negotiate_message *)((char *)&req->hdr.ProtocolId + 1702 + negblob_off); 1703 + 1704 + if (decode_negotiation_token(conn, negblob, negblob_len) == 0) { 1704 1705 if (conn->mechToken) 1705 1706 negblob = (struct negotiate_message *)conn->mechToken; 1706 1707 } ··· 1730 1719 sess->Preauth_HashValue = NULL; 1731 1720 } else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) { 1732 1721 if (negblob->MessageType == NtLmNegotiate) { 1733 - rc = ntlm_negotiate(work, negblob); 1722 + rc = ntlm_negotiate(work, negblob, negblob_len); 1734 1723 if (rc) 1735 1724 goto out_err; 1736 1725 rsp->hdr.Status =