From 573699ca2dd0276bc22cf60bbabd6b3a9b9661fbd23c5f5beed9d9db70953586 Mon Sep 17 00:00:00 2001 From: Martin Pluskal Date: Tue, 5 Nov 2019 07:29:44 +0000 Subject: [PATCH] Accepting request 745252 from home:kstreitova:branches:Archiving - add cpio-2.12-CVE-2019-14866.patch to fix a security issue where cpio does not properly validate the values written in the header of a TAR file through the to_oct() function [bsc#1155199] [CVE-2019-14866] OBS-URL: https://build.opensuse.org/request/show/745252 OBS-URL: https://build.opensuse.org/package/show/Archiving/cpio?expand=0&rev=77 --- cpio-2.12-CVE-2019-14866.patch | 317 +++++++++++++++++++++++++++++++++ cpio.changes | 8 + cpio.spec | 2 + 3 files changed, 327 insertions(+) create mode 100644 cpio-2.12-CVE-2019-14866.patch diff --git a/cpio-2.12-CVE-2019-14866.patch b/cpio-2.12-CVE-2019-14866.patch new file mode 100644 index 0000000..2012484 --- /dev/null +++ b/cpio-2.12-CVE-2019-14866.patch @@ -0,0 +1,317 @@ +From 7554e3e42cd72f6f8304410c47fe6f8918e9bfd7 Mon Sep 17 00:00:00 2001 +From: Sergey Poznyakoff +Date: Sun, 3 Nov 2019 23:59:39 +0200 +Subject: Fix CVE-2019-14866 + +* src/copyout.c (to_ascii): Additional argument nul controls whether +to add the terminating nul character. +(field_width_error): Improve diagnostics: print the actual and the +maximum allowed field value. +* src/extern.h (to_ascii, field_width_error): New prototypes. +* src/tar.c (to_oct): Remove. +(to_oct_or_error): New function. +(TO_OCT): New macro. +(write_out_tar_header): Use TO_OCT and to_ascii. Return 0 on +success, 1 on error. +--- + src/copyout.c | 49 ++++++++++++++++++++++++---------------- + src/extern.h | 15 +++++++++++-- + src/tar.c | 72 +++++++++++++++++++++++++++-------------------------------- + 3 files changed, 76 insertions(+), 60 deletions(-) + +Index: cpio-2.12/src/copyout.c +=================================================================== +--- cpio-2.12.orig/src/copyout.c ++++ cpio-2.12/src/copyout.c +@@ -269,26 +269,32 @@ writeout_final_defers (int out_des) + so it should be moved to paxutils too. + Allowed values for logbase are: 1 (binary), 2, 3 (octal), 4 (hex) */ + int +-to_ascii (char *where, uintmax_t v, size_t digits, unsigned logbase) ++to_ascii (char *where, uintmax_t v, size_t digits, unsigned logbase, bool nul) + { + static char codetab[] = "0123456789ABCDEF"; +- int i = digits; +- +- do ++ ++ if (nul) ++ where[--digits] = 0; ++ while (digits > 0) + { +- where[--i] = codetab[(v & ((1 << logbase) - 1))]; ++ where[--digits] = codetab[(v & ((1 << logbase) - 1))]; + v >>= logbase; + } +- while (i); + + return v != 0; + } + +-static void +-field_width_error (const char *filename, const char *fieldname) ++void ++field_width_error (const char *filename, const char *fieldname, ++ uintmax_t value, size_t width, bool nul) + { +- error (0, 0, _("%s: field width not sufficient for storing %s"), +- filename, fieldname); ++ char valbuf[UINTMAX_STRSIZE_BOUND + 1]; ++ char maxbuf[UINTMAX_STRSIZE_BOUND + 1]; ++ error (0, 0, _("%s: value %s %s out of allowed range 0..%s"), ++ filename, fieldname, ++ STRINGIFY_BIGINT (value, valbuf), ++ STRINGIFY_BIGINT (MAX_VAL_WITH_DIGITS (width - nul, LG_8), ++ maxbuf)); + } + + static void +@@ -303,7 +309,7 @@ to_ascii_or_warn (char *where, uintmax_t + unsigned logbase, + const char *filename, const char *fieldname) + { +- if (to_ascii (where, n, digits, logbase)) ++ if (to_ascii (where, n, digits, logbase, false)) + field_width_warning (filename, fieldname); + } + +@@ -312,9 +318,9 @@ to_ascii_or_error (char *where, uintmax_ + unsigned logbase, + const char *filename, const char *fieldname) + { +- if (to_ascii (where, n, digits, logbase)) ++ if (to_ascii (where, n, digits, logbase, false)) + { +- field_width_error (filename, fieldname); ++ field_width_error (filename, fieldname, n, digits, false); + return 1; + } + return 0; +@@ -371,7 +377,7 @@ write_out_new_ascii_header (const char * + _("name size"))) + return 1; + p += 8; +- to_ascii (p, file_hdr->c_chksum & 0xffffffff, 8, LG_16); ++ to_ascii (p, file_hdr->c_chksum & 0xffffffff, 8, LG_16, false); + + tape_buffered_write (ascii_header, out_des, sizeof ascii_header); + +@@ -388,7 +394,7 @@ write_out_old_ascii_header (dev_t dev, d + char ascii_header[76]; + char *p = ascii_header; + +- to_ascii (p, file_hdr->c_magic, 6, LG_8); ++ to_ascii (p, file_hdr->c_magic, 6, LG_8, false); + p += 6; + to_ascii_or_warn (p, dev, 6, LG_8, file_hdr->c_name, _("device number")); + p += 6; +@@ -492,7 +498,10 @@ write_out_binary_header (dev_t rdev, + short_hdr.c_namesize = file_hdr->c_namesize & 0xFFFF; + if (short_hdr.c_namesize != file_hdr->c_namesize) + { +- field_width_error (file_hdr->c_name, _("name size")); ++ char maxbuf[UINTMAX_STRSIZE_BOUND + 1]; ++ error (0, 0, _("%s: value %s %s out of allowed range 0..%u"), ++ file_hdr->c_name, _("name size"), ++ STRINGIFY_BIGINT (file_hdr->c_namesize, maxbuf), 0xFFFFu); + return 1; + } + +@@ -502,7 +511,10 @@ write_out_binary_header (dev_t rdev, + if (((off_t)short_hdr.c_filesizes[0] << 16) + short_hdr.c_filesizes[1] + != file_hdr->c_filesize) + { +- field_width_error (file_hdr->c_name, _("file size")); ++ char maxbuf[UINTMAX_STRSIZE_BOUND + 1]; ++ error (0, 0, _("%s: value %s %s out of allowed range 0..%lu"), ++ file_hdr->c_name, _("file size"), ++ STRINGIFY_BIGINT (file_hdr->c_namesize, maxbuf), 0xFFFFFFFFlu); + return 1; + } + +@@ -552,8 +564,7 @@ write_out_header (struct cpio_file_stat + error (0, 0, _("%s: file name too long"), file_hdr->c_name); + return 1; + } +- write_out_tar_header (file_hdr, out_des); /* FIXME: No error checking */ +- return 0; ++ return write_out_tar_header (file_hdr, out_des); + + case arf_binary: + return write_out_binary_header (makedev (file_hdr->c_rdev_maj, +Index: cpio-2.12/src/extern.h +=================================================================== +--- cpio-2.12.orig/src/extern.h ++++ cpio-2.12/src/extern.h +@@ -118,6 +118,10 @@ void print_name_with_quoting (char *p); + /* copyout.c */ + int write_out_header (struct cpio_file_stat *file_hdr, int out_des); + void process_copy_out (void); ++int to_ascii (char *where, uintmax_t v, size_t digits, unsigned logbase, ++ bool nul); ++void field_width_error (const char *filename, const char *fieldname, ++ uintmax_t value, size_t width, bool nul); + + /* copypass.c */ + void process_copy_pass (void); +@@ -146,7 +150,7 @@ int make_path (char *argpath, uid_t owne + const char *verbose_fmt_string); + + /* tar.c */ +-void write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des); ++int write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des); + int null_block (long *block, int size); + void read_in_tar_header (struct cpio_file_stat *file_hdr, int in_des); + int otoa (char *s, unsigned long *n); +@@ -205,9 +209,16 @@ void cpio_safer_name_suffix (char *name, + int cpio_create_dir (struct cpio_file_stat *file_hdr, int existing_dir); + void change_dir (void); + +-/* FIXME: These two defines should be defined in paxutils */ ++/* FIXME: The following three should be defined in paxutils */ + #define LG_8 3 + #define LG_16 4 ++/* The maximum uintmax_t value that can be represented with DIGITS digits, ++ assuming that each digit is BITS_PER_DIGIT wide. */ ++#define MAX_VAL_WITH_DIGITS(digits, bits_per_digit) \ ++ ((digits) * (bits_per_digit) < sizeof (uintmax_t) * CHAR_BIT \ ++ ? ((uintmax_t) 1 << ((digits) * (bits_per_digit))) - 1 \ ++ : (uintmax_t) -1) ++ + + uintmax_t from_ascii (char const *where, size_t digs, unsigned logbase); + +Index: cpio-2.12/src/tar.c +=================================================================== +--- cpio-2.12.orig/src/tar.c ++++ cpio-2.12/src/tar.c +@@ -1,6 +1,5 @@ + /* tar.c - read in write tar headers for cpio +- Copyright (C) 1992, 2001, 2004, 2006-2007, 2010, 2014-2015 Free +- Software Foundation, Inc. ++ Copyright (C) 1992-2019 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by +@@ -79,36 +78,17 @@ stash_tar_filename (char *prefix, char * + return hold_tar_filename; + } + +-/* Convert a number into a string of octal digits. +- Convert long VALUE into a DIGITS-digit field at WHERE, +- including a trailing space and room for a NUL. DIGITS==3 means +- 1 digit, a space, and room for a NUL. +- +- We assume the trailing NUL is already there and don't fill it in. +- This fact is used by start_header and finish_header, so don't change it! +- +- This is be equivalent to: +- sprintf (where, "%*lo ", digits - 2, value); +- except that sprintf fills in the trailing NUL and we don't. */ +- +-static void +-to_oct (register long value, register int digits, register char *where) ++static int ++to_oct_or_error (uintmax_t value, size_t digits, char *where, char const *field, ++ char const *file) + { +- --digits; /* Leave the trailing NUL slot alone. */ +- +- /* Produce the digits -- at least one. */ +- do ++ if (to_ascii (where, value, digits, LG_8, true)) + { +- where[--digits] = '0' + (char) (value & 7); /* One octal digit. */ +- value >>= 3; ++ field_width_error (file, field, value, digits, true); ++ return 1; + } +- while (digits > 0 && value != 0); +- +- /* Add leading zeroes, if necessary. */ +- while (digits > 0) +- where[--digits] = '0'; ++ return 0; + } +- + + + /* Compute and return a checksum for TAR_HDR, +@@ -134,10 +114,22 @@ tar_checksum (struct tar_header *tar_hdr + return sum; + } + ++#define TO_OCT(file_hdr, c_fld, digits, tar_hdr, tar_field) \ ++ do \ ++ { \ ++ if (to_oct_or_error (file_hdr -> c_fld, \ ++ digits, \ ++ tar_hdr -> tar_field, \ ++ #tar_field, \ ++ file_hdr->c_name)) \ ++ return 1; \ ++ } \ ++ while (0) ++ + /* Write out header FILE_HDR, including the file name, to file + descriptor OUT_DES. */ + +-void ++int + write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des) + { + int name_len; +@@ -166,11 +158,11 @@ write_out_tar_header (struct cpio_file_s + + /* Ustar standard (POSIX.1-1988) requires the mode to contain only 3 octal + digits */ +- to_oct (file_hdr->c_mode & MODE_ALL, 8, tar_hdr->mode); +- to_oct (file_hdr->c_uid, 8, tar_hdr->uid); +- to_oct (file_hdr->c_gid, 8, tar_hdr->gid); +- to_oct (file_hdr->c_filesize, 12, tar_hdr->size); +- to_oct (file_hdr->c_mtime, 12, tar_hdr->mtime); ++ TO_OCT (file_hdr, c_mode & MODE_ALL, 8, tar_hdr, mode); ++ TO_OCT (file_hdr, c_uid, 8, tar_hdr, uid); ++ TO_OCT (file_hdr, c_gid, 8, tar_hdr, gid); ++ TO_OCT (file_hdr, c_filesize, 12, tar_hdr, size); ++ TO_OCT (file_hdr, c_mtime, 12, tar_hdr, mtime); + + switch (file_hdr->c_mode & CP_IFMT) + { +@@ -182,7 +174,7 @@ write_out_tar_header (struct cpio_file_s + strncpy (tar_hdr->linkname, file_hdr->c_tar_linkname, + TARLINKNAMESIZE); + tar_hdr->typeflag = LNKTYPE; +- to_oct (0, 12, tar_hdr->size); ++ to_ascii (tar_hdr->size, 0, 12, LG_8, true); + } + else + tar_hdr->typeflag = REGTYPE; +@@ -208,7 +200,7 @@ write_out_tar_header (struct cpio_file_s + than TARLINKNAMESIZE. */ + strncpy (tar_hdr->linkname, file_hdr->c_tar_linkname, + TARLINKNAMESIZE); +- to_oct (0, 12, tar_hdr->size); ++ to_ascii (tar_hdr->size, 0, 12, LG_8, true); + break; + #endif /* CP_IFLNK */ + } +@@ -227,13 +219,15 @@ write_out_tar_header (struct cpio_file_s + if (name) + strcpy (tar_hdr->gname, name); + +- to_oct (file_hdr->c_rdev_maj, 8, tar_hdr->devmajor); +- to_oct (file_hdr->c_rdev_min, 8, tar_hdr->devminor); ++ TO_OCT (file_hdr, c_rdev_maj, 8, tar_hdr, devmajor); ++ TO_OCT (file_hdr, c_rdev_min, 8, tar_hdr, devminor); + } + +- to_oct (tar_checksum (tar_hdr), 8, tar_hdr->chksum); ++ to_ascii (tar_hdr->chksum, tar_checksum (tar_hdr), 8, LG_8, true); + + tape_buffered_write ((char *) &tar_rec, out_des, TARRECORDSIZE); ++ ++ return 0; + } + + /* Return nonzero iff all the bytes in BLOCK are NUL. diff --git a/cpio.changes b/cpio.changes index 2bbf708..dda2ceb 100644 --- a/cpio.changes +++ b/cpio.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Mon Nov 4 15:53:41 UTC 2019 - Kristyna Streitova + +- add cpio-2.12-CVE-2019-14866.patch to fix a security issue where + cpio does not properly validate the values written in the header + of a TAR file through the to_oct() function [bsc#1155199] + [CVE-2019-14866] + ------------------------------------------------------------------- Thu Sep 19 11:50:42 UTC 2019 - Ludwig Nussel diff --git a/cpio.spec b/cpio.spec index 17fd8d3..e2c00f5 100644 --- a/cpio.spec +++ b/cpio.spec @@ -44,6 +44,7 @@ Patch24: cpio-check_for_symlinks.patch Patch25: cpio-fix_truncation_check.patch Patch26: cpio-2.12-util.c_no_return_in_nonvoid_fnc.patch Patch27: cpio-2.12-out_of_bounds_write.patch +Patch28: cpio-2.12-CVE-2019-14866.patch BuildRequires: autoconf BuildRequires: automake Requires(post): %{install_info_prereq} @@ -86,6 +87,7 @@ This package includes the 'mt', a local tape drive control program. %patch25 -p1 %patch26 -p1 %patch27 -p1 +%patch28 -p1 %build gettextize -f --no-changelog