Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP4:GA
emacs.34545
01a4035c.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 01a4035c.patch of Package emacs.34545
From e339926272a598bd9ee7e02989c1662b89e64cf0 Mon Sep 17 00:00:00 2001 From: lu4nx <lx@shellcodes.org> Date: Tue, 6 Dec 2022 15:42:40 +0800 Subject: [PATCH] Fix etags local command injection vulnerability * lib-src/etags.c: (escape_shell_arg_string): New function. (process_file_name): Use it to quote file names passed to the shell. (Bug#59817) (cherry picked from commit 01a4035c869b91c153af9a9132c87adb7669ea1c) --- lib-src/etags.c | 241 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 185 insertions(+), 56 deletions(-) --- lib-src/etags.c +++ lib-src/etags.c 2023-02-21 11:12:06.012397649 +0000 @@ -142,6 +142,7 @@ char pot_etags_version[] = "@(#) pot rev #include <stdio.h> #include <ctype.h> #include <errno.h> +#include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <c-strcase.h> @@ -370,6 +371,7 @@ static void invalidate_nodes (fdesc *, n static void put_entries (node *); static void clean_matched_file_tag (char const * const, char const * const); +static char *escape_shell_arg_string (char *); static void do_move_file (const char *, const char *); static char *concat (const char *, const char *, const char *); static char *skip_spaces (char *); @@ -384,6 +386,7 @@ static char *absolute_filename (char *, static char *absolute_dirname (char *, char *); static bool filename_is_absolute (char *f); static void canonicalize_filename (char *); +static char *etags_mktmp (void); static void linebuffer_init (linebuffer *); static void linebuffer_setlen (linebuffer *, int); static void *xmalloc (size_t); @@ -1526,12 +1529,11 @@ get_language_from_filename (char *file, static void process_file_name (char *file, language *lang) { - struct stat stat_buf; FILE *inf; fdesc *fdp; compressor *compr; char *compressed_name, *uncompressed_name; - char *ext, *real_name; + char *ext, *real_name, *tmp_name; int retval; canonicalize_filename (file); @@ -1540,15 +1542,16 @@ process_file_name (char *file, language error ("skipping inclusion of %s in self.", file); return; } - if ((compr = get_compressor_from_suffix (file, &ext)) == NULL) + compr = get_compressor_from_suffix (file, &ext); + if (compr) { - compressed_name = NULL; - real_name = uncompressed_name = savestr (file); + compressed_name = file; + uncompressed_name = savenstr (file, ext - file); } else { - real_name = compressed_name = savestr (file); - uncompressed_name = savenstr (file, ext - file); + compressed_name = NULL; + uncompressed_name = file; } /* If the canonicalized uncompressed name @@ -1560,86 +1563,117 @@ process_file_name (char *file, language goto cleanup; } - if (stat (real_name, &stat_buf) != 0) + inf = fopen (file, "r"); + if (inf) + real_name = file; + else { - /* Reset real_name and try with a different name. */ - real_name = NULL; - if (compressed_name != NULL) /* try with the given suffix */ + int file_errno = errno; + if (compressed_name) { - if (stat (uncompressed_name, &stat_buf) == 0) + /* Try with the given suffix. */ + inf = fopen (uncompressed_name, "r"); + if (inf) real_name = uncompressed_name; } - else /* try all possible suffixes */ + else { + /* Try all possible suffixes. */ for (compr = compressors; compr->suffix != NULL; compr++) { compressed_name = concat (file, ".", compr->suffix); - if (stat (compressed_name, &stat_buf) != 0) + inf = fopen (compressed_name, "r"); + if (inf) + { + real_name = compressed_name; + break; + } + if (MSDOS) { - if (MSDOS) + char *suf = compressed_name + strlen (file); + size_t suflen = strlen (compr->suffix) + 1; + for ( ; suf[1]; suf++, suflen--) { - char *suf = compressed_name + strlen (file); - size_t suflen = strlen (compr->suffix) + 1; - for ( ; suf[1]; suf++, suflen--) + memmove (suf, suf + 1, suflen); + inf = fopen (compressed_name, "r"); + if (inf) { - memmove (suf, suf + 1, suflen); - if (stat (compressed_name, &stat_buf) == 0) - { - real_name = compressed_name; - break; - } + real_name = compressed_name; + break; } - if (real_name != NULL) - break; - } /* MSDOS */ - free (compressed_name); - compressed_name = NULL; - } - else - { - real_name = compressed_name; - break; + } + if (inf) + break; } + free (compressed_name); + compressed_name = NULL; } } - if (real_name == NULL) + if (! inf) { + errno = file_errno; perror (file); goto cleanup; } - } /* try with a different name */ - - if (!S_ISREG (stat_buf.st_mode)) - { - error ("skipping %s: it is not a regular file.", real_name); - goto cleanup; } + if (real_name == compressed_name) { - char *cmd = concat (compr->command, " ", real_name); - inf = (FILE *) popen (cmd, "r"); - free (cmd); - } - else - inf = fopen (real_name, "r"); - if (inf == NULL) - { - perror (real_name); - goto cleanup; + fclose (inf); + tmp_name = etags_mktmp (); + if (!tmp_name) + inf = NULL; + else + { +#if MSDOS || defined (DOS_NT) + int buf_len = strlen (compr->command) + strlen (" \"\" > \"\"") + strlen (real_name) + strlen (tmp_name) + 1; + char *cmd = xmalloc (buf_len); + snprintf (cmd, buf_len, "%s \"%s\" > \"%s\"", compr->command, real_name, tmp_name); +#else + char *new_real_name = escape_shell_arg_string (real_name); + char *new_tmp_name = escape_shell_arg_string (tmp_name); + int buf_len = strlen (compr->command) + strlen (" > ") + strlen (new_real_name) + strlen (new_tmp_name) + 1; + char *cmd = xmalloc (buf_len); + snprintf (cmd, buf_len, "%s %s > %s", compr->command, new_real_name, new_tmp_name); +#endif + int tmp_errno; + if (system (cmd) == -1) + { + inf = NULL; + tmp_errno = EINVAL; + } + else + { + inf = fopen (tmp_name, "r"); + tmp_errno = errno; + } + free (cmd); + errno = tmp_errno; + } + + if (!inf) + { + perror (real_name); + goto cleanup; + } } process_file (inf, uncompressed_name, lang); + retval = fclose (inf); if (real_name == compressed_name) - retval = pclose (inf); - else - retval = fclose (inf); + { + remove (tmp_name); + free (tmp_name); + } if (retval < 0) pfatal (file); cleanup: - free (compressed_name); - free (uncompressed_name); + if (compressed_name != file) + free (compressed_name); + if (uncompressed_name != file) + free (uncompressed_name); last_node = NULL; curfdp = NULL; return; @@ -6454,6 +6488,101 @@ etags_getcwd (void) #endif /* not HAVE_GETCWD */ } +/* Return a newly allocated string containing a name of a temporary file. */ +static char * +etags_mktmp (void) +{ + const char *tmpdir = getenv ("TMPDIR"); + const char *slash = "/"; + +#if MSDOS || defined (DOS_NT) + if (!tmpdir) + tmpdir = getenv ("TEMP"); + if (!tmpdir) + tmpdir = getenv ("TMP"); + if (!tmpdir) + tmpdir = "."; + if (tmpdir[strlen (tmpdir) - 1] == '/' + || tmpdir[strlen (tmpdir) - 1] == '\\') + slash = ""; +#else + if (!tmpdir) + tmpdir = "/tmp"; + if (tmpdir[strlen (tmpdir) - 1] == '/') + slash = ""; +#endif + + char *templt = concat (tmpdir, slash, "etXXXXXX"); + int fd = mkostemp (templt, O_CLOEXEC); + if (fd < 0 || close (fd) != 0) + { + int temp_errno = errno; + free (templt); + errno = temp_errno; + templt = NULL; + } + +#if defined (DOS_NT) + /* The file name will be used in shell redirection, so it needs to have + DOS-style backslashes, or else the Windows shell will barf. */ + char *p; + for (p = templt; *p; p++) + if (*p == '/') + *p = '\\'; +#endif + + return templt; +} + +/* + * Adds single quotes around a string, if found single quotes, escaped it. + * Return a newly-allocated string. + * + * For example: + * escape_shell_arg_string("test.txt") => 'test.txt' + * escape_shell_arg_string("'test.txt") => ''\''test.txt' + */ +static char * +escape_shell_arg_string (char *str) +{ + char *p = str; + int need_space = 2; /* ' at begin and end */ + + while (*p != '\0') + { + if (*p == '\'') + need_space += 4; /* ' to '\'', length is 4 */ + else + need_space++; + + p++; + } + + char *new_str = xnew (need_space + 1, char); + new_str[0] = '\''; + new_str[need_space-1] = '\''; + + int i = 1; /* skip first byte */ + p = str; + while (*p != '\0') + { + new_str[i] = *p; + if (*p == '\'') + { + new_str[i+1] = '\\'; + new_str[i+2] = '\''; + new_str[i+3] = '\''; + i += 3; + } + + i++; + p++; + } + + new_str[need_space] = '\0'; + return new_str; +} + static void do_move_file(const char *src_file, const char *dst_file) {
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor