| 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 |
| |