diff --git a/debian/changelog b/debian/changelog index 9d284a0..209ee9b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +fsq (0.9.6) unstable; urgency=low + + * Path comparison now using strncmp instead of for-loop. + + -- Samuel Hasert Mon, 08 Dec 2025 13:27:20 +0200 + +fsq (0.9.5) unstable; urgency=low + + * Absolute file path resolution to prevent path traversal exploit. + + -- Samuel Hasert Tue, 21 Oct 2025 16:25:15 +0200 + fsq (0.9.4) unstable; urgency=medium * Support for IPv6. diff --git a/rpm/fsq.spec b/rpm/fsq.spec index 0a316ad..02a844f 100644 --- a/rpm/fsq.spec +++ b/rpm/fsq.spec @@ -62,6 +62,12 @@ rm -rf %{buildroot} %changelog +* Mon 08 Dec 2025 Samuel Hasert 0.9.6 +- Path comparison now using strncmp instead of for-loop. + +* Tue 21 Oct 2025 Samuel Hasert 0.9.5 +- Absolute file path resolution to prevent path traversal exploit. + * Fri 2 Aug 2024 Thomas Stibor 0.9.4-1 - Support for IPv6. diff --git a/src/fsqd.c b/src/fsqd.c index 035a194..4fdf824 100644 --- a/src/fsqd.c +++ b/src/fsqd.c @@ -700,21 +700,39 @@ static int write_access(struct fsq_session_t *fsq_session, char *lustre_dpath) return rc; } - /* File path name and Lustre directory name are matching - up to the end of the Lustre directory name. */ + /* Resolve path to absolute path */ + char resolved[PATH_MAX]; + char *output = realpath(fpath, resolved); + const size_t L_resolved = strlen(resolved); + LOG_DEBUG("fpath: '%s', realpath output: '%s', resolved: '%s'", + fpath, output, resolved); + + /* Resolved file path name and Lustre directory name are + matching up to the end of the Lustre directory name. */ LOG_DEBUG("verify lustre_dpath '%s' is a strict prefix of fpath '%s'", lustre_dpath, fpath); - for (size_t l = 0; l < L_lustre_dpath; l++) { - if (fpath[l] != lustre_dpath[l]) { - int rc = -EACCES; - FSQ_ERROR(*fsq_session, rc, - "lustre_dpath '%s' is not a strict prefix of fpath '%s'", - lustre_dpath, fpath); - return rc; - } - } - return 0; + /* Make sure lustre_dpath is shorter */ + if (L_resolved <= L_lustre_dpath) { + int rc = -EACCES; + FSQ_ERROR(*fsq_session, rc, + "lustre_dpath '%s' is not a strict prefix of fpath '%s'", + lustre_dpath, fpath); + return rc; + + } + + /* Compare paths to make sure resolved path (resolved) includes + lustre_dpath. */ + if (strncmp(resolved, lustre_dpath, L_lustre_dpath) != 0) { + int rc = -EACCES; + FSQ_ERROR(*fsq_session, rc, + "lustre_dpath '%s' is not a strict prefix of fpath '%s'", + lustre_dpath, fpath); + return rc; + } + + return 0; } static int init_fsq_dev_null(char *fpath_local, int *fd_local, @@ -1008,19 +1026,19 @@ static void *thread_sock_client(void *arg) if (fsq_session.fsq_packet.state & FSQ_DISCONNECT) goto out; - rc = init_fsq_storage(fpath_local, &fd_local, &fsq_session); + rc = write_access(&fsq_session, lustre_dpath); if (rc) { - FSQ_ERROR(fsq_session, rc, - "init_fsq_storage failed '%s'", - FSQ_STORAGE_DEST_STR( - fsq_session.fsq_packet.fsq_info.fsq_storage_dest)); + /* FSQ error field is filled inside write_access function. */ rc = fsq_send(&fsq_session, FSQ_ERROR | FSQ_REPLY); goto out; } - rc = write_access(&fsq_session, lustre_dpath); + rc = init_fsq_storage(fpath_local, &fd_local, &fsq_session); if (rc) { - /* FSQ error field is filled inside write_access function. */ + FSQ_ERROR(fsq_session, rc, + "init_fsq_storage failed '%s'", + FSQ_STORAGE_DEST_STR( + fsq_session.fsq_packet.fsq_info.fsq_storage_dest)); rc = fsq_send(&fsq_session, FSQ_ERROR | FSQ_REPLY); goto out; }