blob: 9cf76d8bb4d176b5abb1d27998100be31d9e669b [file] [log] [blame]
From db48acf51044715837b27061907d723c74ac62ea Mon Sep 17 00:00:00 2001
From: Hans Beckerus <hans.beckerus at gmail.com>
Date: Fri, 14 Aug 2020 01:56:30 +0200
Subject: [PATCH] Require password for encrypted RARs
This is a port of the following commits from master:
c3fd64f Make sure a bad password is detected for RAR4
19285d1 Fix yet another issue with RAR4 encrypted archives
7f1db90 Fix a problem in previous commit
611c987 Require password for encrypted archive mounts
When mounting an encrypted RAR, check if the provided password allows to
decrypt the archive.
If no password or an empty password is provided, the returned error is
ERAR_MISSING_PASSWORD.
If the password is invalid and non-empty, the returned error is either
ERAR_MISSING_PASSWORD (for RAR V5) or ERAR_BAD_DATA (for RAR V4).
---
src/rar2fs.c | 73 ++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 60 insertions(+), 13 deletions(-)
diff --git a/src/rar2fs.c b/src/rar2fs.c
index 08cd9cb..34b188a 100644
--- a/src/rar2fs.c
+++ b/src/rar2fs.c
@@ -186,6 +186,7 @@ struct eof_cb_arg {
struct extract_cb_arg {
char *arch;
void *arg;
+ int dry_run;
};
static int extract_index(const char *, const struct filecache_entry *, off_t);
@@ -714,12 +715,20 @@ static FILE *popen_(const struct filecache_entry *entry_p, pid_t *cpid)
pid = fork();
if (pid == 0) {
- int ret;
+ int ret = 0;
setpgid(getpid(), 0);
close(pfd[0]); /* Close unused read end */
- ret = extract_rar(entry_p->rar_p,
- entry_p->file_p,
- (void *)(uintptr_t) pfd[1]);
+ /* For folder mounts we need to perform an additional dummy
+ * extraction attempt to avoid feeding the file descriptor
+ * with garbage data in case of wrong password or CRC errors. */
+ if (mount_type == MOUNT_FOLDER)
+ ret = extract_rar(entry_p->rar_p,
+ entry_p->file_p,
+ NULL);
+ if (!ret || ret == ERAR_UNKNOWN)
+ ret = extract_rar(entry_p->rar_p,
+ entry_p->file_p,
+ (void *)(uintptr_t)pfd[1]);
close(pfd[1]);
_exit(ret);
} else if (pid < 0) {
@@ -1496,11 +1505,11 @@ check_idx:
if (sync_thread_read(op))
return -EIO;
/* If there is still no data assume something went wrong.
- * Also assume that, if the file is encrypted, the reason
- * for the error is a missing or invalid password!
+ * Most likely CRC errors or an invalid password in the case
+ * of encrypted aechives.
*/
if (!op->buf->offset)
- return op->entry_p->flags.encrypted ? -EPERM : -EIO;
+ return -EIO;
}
if ((off_t)(offset + size) > op->buf->offset) {
if (offset >= op->buf->offset) {
@@ -1720,6 +1729,7 @@ static void dump_stat(struct stat *stbuf)
static int collect_files(const char *arch, struct dir_entry_list *list)
{
RAROpenArchiveDataEx d;
+ int files;
memset(&d, 0, sizeof(RAROpenArchiveDataEx));
d.ArcName = (char *)arch; /* Horrible cast! But hey... it is the API! */
@@ -1730,15 +1740,40 @@ static int collect_files(const char *arch, struct dir_entry_list *list)
HANDLE h = RAROpenArchiveEx(&d);
/* Check for fault */
- const int err = d.OpenResult;
- if (err != ERAR_SUCCESS) {
+ if (d.OpenResult != ERAR_SUCCESS) {
if (h)
RARCloseArchive(h);
+ return -d.OpenResult;
+ }
- return -err;
+ RARArchiveDataEx *arc = NULL;
+ int dll_result = RARListArchiveEx(h, &arc);
+ if (dll_result && dll_result != ERAR_EOPEN) {
+ if (dll_result != ERAR_END_ARCHIVE) {
+ RARFreeArchiveDataEx(&arc);
+ RARCloseArchive(h);
+ return -dll_result;
+ }
}
- int files = 0;
+ /* Pointless to test for encrypted files if header is already encrypted
+ * and could be read. */
+ if (d.Flags & ROADF_ENCHEADERS)
+ goto skip_file_check;
+
+ if (arc->hdr.Flags & RHDF_ENCRYPTED) {
+ dll_result = extract_rar((char *)arch, arc->hdr.FileName, NULL);
+ if (dll_result && dll_result != ERAR_UNKNOWN) {
+ RARFreeArchiveDataEx(&arc);
+ RARCloseArchive(h);
+ return -dll_result;
+ }
+ }
+
+skip_file_check:
+ RARFreeArchiveDataEx(&arc);
+
+ files = 0;
if (d.Flags & ROADF_VOLUME) {
char *arch_ = strdup(arch);
int format = (d.Flags & ROADF_NEWNUMBERING) ? 0 : 1;
@@ -1758,6 +1793,8 @@ static int collect_files(const char *arch, struct dir_entry_list *list)
files = 1;
}
+ RARCloseArchive(h);
+
return files;
}
@@ -1893,13 +1930,22 @@ static int CALLBACK extract_callback(UINT msg, LPARAM UserData,
{
struct extract_cb_arg *cb_arg = (struct extract_cb_arg *)(UserData);
if (msg == UCM_PROCESSDATA) {
+ /* Handle the special case when asking for a quick "dry run"
+ * to test archive integrity. If all is well this will result
+ * in an ERAR_UNKNOWN error. */
+ if (!cb_arg->arg) {
+ if (!cb_arg->dry_run) {
+ cb_arg->dry_run = 1;
+ return 1;
+ }
+ return -1;
+ }
/*
* We do not need to handle the case that not all data is
* written after return from write() since the pipe is not
* opened using the O_NONBLOCK flag.
*/
- int fd = cb_arg->arg ? (LPARAM)cb_arg->arg : STDOUT_FILENO;
- if (write(fd, (void *)P1, P2) == -1) {
+ if (write((LPARAM)cb_arg->arg, (void *)P1, P2) == -1) {
/*
* Do not treat EPIPE as an error. It is the normal
* case when the process is terminted, ie. the pipe is
@@ -1942,6 +1988,7 @@ static int extract_rar(char *arch, const char *file, void *arg)
struct extract_cb_arg cb_arg;
cb_arg.arch = arch;
cb_arg.arg = arg;
+ cb_arg.dry_run = 0;
d.Callback = extract_callback;
d.UserData = (LPARAM)&cb_arg;
--
2.28.0.220.ged08abb693-goog