[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#920376: lintian: Add a check for binaries using obsolete DES encryption



Package: lintian
Version: 2.5.123
Severity: wishlist
Tags: patch

The attached patch adds a Lintian check for binaries using the obsolete
DES encryption functions that were made inaccessible to newly linked
binaries in glibc 2.28.  See the patch's commit message for more detail.

-- System Information:
Debian Release: buster/sid
  APT prefers unstable
  APT policy: (500, 'unstable'), (500, 'stable'), (1, 'experimental')
Architecture: amd64 (x86_64)

Kernel: Linux 4.19.0-1-amd64 (SMP w/8 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE=en_US.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

Versions of packages lintian depends on:
ii  binutils                       2.31.1-11
ii  bzip2                          1.0.6-9
ii  diffstat                       1.62-1
ii  dpkg                           1.19.4
ii  dpkg-dev                       1.19.4
ii  file                           1:5.35-2
ii  gettext                        0.19.8.1-9
ii  gpg                            2.2.12-1
ii  intltool-debian                0.35.0+20060710.5
ii  libapt-pkg-perl                0.1.34+b1
ii  libarchive-zip-perl            1.64-1
ii  libcgi-pm-perl                 4.40-1
ii  libclass-accessor-perl         0.51-1
ii  libclone-perl                  0.41-1+b1
pn  libdigest-sha-perl             <none>
ii  libdpkg-perl                   1.19.4
ii  libemail-valid-perl            1.202-1
ii  libfile-basedir-perl           0.08-1
ii  libio-async-perl               0.72-1
ii  libipc-run-perl                20180523.0-1
ii  liblist-moreutils-perl         0.416-1+b4
ii  libparse-debianchangelog-perl  1.2.0-13
ii  libtext-levenshtein-perl       0.13-1
ii  libtimedate-perl               2.3000-2
ii  liburi-perl                    1.76-1
ii  libxml-simple-perl             2.25-1
ii  libyaml-libyaml-perl           0.76+repack-1
ii  man-db                         2.8.5-1
ii  patchutils                     0.3.4-2
ii  perl                           5.28.1-3
ii  t1utils                        1.41-3
ii  xz-utils                       5.2.2-1.3

Versions of packages lintian recommends:
pn  libperlio-gzip-perl  <none>

Versions of packages lintian suggests:
pn  binutils-multiarch     <none>
ii  libhtml-parser-perl    3.72-3+b3
ii  libtext-template-perl  1.54-1

-- no debconf information
>From d79acb92725ef2ddbc2b33eba62a4e7b7f361a3d Mon Sep 17 00:00:00 2001
From: Zack Weinberg <zackw@panix.com>
Date: Thu, 24 Jan 2019 15:40:26 -0500
Subject: [PATCH] Add a check for binaries using obsolete DES encryption.

libcrypt.so.1 (part of glibc) used to provide a set of functions that
allowed raw use of the DES block cipher.  Encryption with DES is now
inherently insecure (any use of single DES can be broken by brute
force) and therefore these functions were made inaccessible to newly
linked programs in glibc 2.28.

Any program in the archive that unconditionally uses one of these
functions will FTBFS with glibc 2.28.  Any program that conditionally
uses one of these functions may be falling back to an internal
implementation of DES and therefore have a security bug.

Therefore, I propose this new lintian check, which detects use of the
functions that were removed from libcrypt.so.1.  In sid it is arguably
redundant (since any program that would fail this check will FTBFS
anyway) but applying this check to the stable archive would detect
programs that were conditionally using these functions and need to be
audited for security bugs.
---
 checks/binaries.desc                          | 45 ++++++++++++++++
 checks/binaries.pm                            | 10 +++-
 data/binaries/obsolete-crypt-functions        | 13 +++++
 t/tests/binaries-obsolete-des/desc            |  7 +++
 t/tests/binaries-obsolete-des/orig/Makefile   | 51 +++++++++++++++++++
 t/tests/binaries-obsolete-des/orig/dummy.pod  | 12 +++++
 .../binaries-obsolete-des/orig/uses-encrypt.c | 30 +++++++++++
 .../orig/uses-encrypt_r.c                     | 33 ++++++++++++
 .../binaries-obsolete-des/orig/uses-fcrypt.c  | 21 ++++++++
 .../binaries-obsolete-des/orig/uses-setkey.c  | 45 ++++++++++++++++
 .../orig/uses-setkey_r.c                      | 48 +++++++++++++++++
 t/tests/binaries-obsolete-des/tags            |  5 ++
 12 files changed, 319 insertions(+), 1 deletion(-)
 create mode 100644 data/binaries/obsolete-crypt-functions
 create mode 100644 t/tests/binaries-obsolete-des/desc
 create mode 100644 t/tests/binaries-obsolete-des/orig/Makefile
 create mode 100644 t/tests/binaries-obsolete-des/orig/dummy.pod
 create mode 100644 t/tests/binaries-obsolete-des/orig/uses-encrypt.c
 create mode 100644 t/tests/binaries-obsolete-des/orig/uses-encrypt_r.c
 create mode 100644 t/tests/binaries-obsolete-des/orig/uses-fcrypt.c
 create mode 100644 t/tests/binaries-obsolete-des/orig/uses-setkey.c
 create mode 100644 t/tests/binaries-obsolete-des/orig/uses-setkey_r.c
 create mode 100644 t/tests/binaries-obsolete-des/tags

diff --git a/checks/binaries.desc b/checks/binaries.desc
index be00f37d7..09f0d3209 100644
--- a/checks/binaries.desc
+++ b/checks/binaries.desc
@@ -489,3 +489,48 @@ Info: This development package (ie. from the <tt>libdevel</tt> section of
  splitting architecture-independent development tools into a separate
  package that can be marked <tt>Multi-Arch: foreign</tt>.
 Ref: #794295, #794103
+
+Tag: obsolete-des-encryption
+Severity: important
+Certainty: possible
+Info: The listed ELF binary appears to use a C library function that
+  performs DES encryption and/or decryption (<tt>encrypt</tt>,
+  <tt>encrypt_r</tt>, <tt>setkey</tt>, and/or <tt>setkey_r</tt>).
+  The DES block cipher can be broken by brute force on modern hardware,
+  which makes any use of these functions insecure.  Also, programs that
+  use these functions cannot be linked against the <tt>libcrypt.so</tt>
+  provided by glibc 2.28 and higher.
+  .
+  The program will need to be revised to use modern cryptographic
+  primitives and protocols.  Depending on how the program uses these
+  functions, it may be necessary to continue using DES under some
+  circumstances (e.g. for protocol compatibility, or to retain the
+  ability to decrypt old data on disk) but this should be done using
+  the DES functions in a modern cryptographic <em>library</em>
+  (e.g. <tt>libgcrypt</tt>).
+  .
+  This is almost certainly an upstream bug, and should be addressed
+  in coordination with the upstream maintainers of the software.
+  .
+  A false positive for this check is possible if the binary expects the
+  definition of <tt>encrypt</tt>, <tt>encrypt_r</tt>, <tt>setkey</tt>,
+  and/or <tt>setkey_r</tt> to come from some shared library other than
+  <tt>libcrypt.so</tt>, <em>and</em> that shared library defines these
+  functions to do something other than perform DES encryption.  If this
+  is the case it is appropriate to override this tag.
+
+Tag: obsolete-crypt-alias
+Severity: important
+Certainty: possible
+Info: The listed ELF binary appears to use the C library function
+  <tt>fcrypt</tt>, which is a less-portable alias for <tt>crypt</tt>.
+  Programs that use this function cannot be linked against the
+  <tt>libcrypt.so</tt> provided by glibc 2.28 and higher.
+  .
+  The program should be changed to use <tt>crypt</tt> instead.
+  .
+  A false positive for this check is possible if the binary expects
+  the definition of <tt>fcrypt</tt> to come from some shared library
+  other than <tt>libcrypt.so</tt>, <em>and</em> that shared library
+  defines this function to do something other than hash passphrases.
+  If this is the case it is appropriate to override this tag.
diff --git a/checks/binaries.pm b/checks/binaries.pm
index fb6278ba7..196d87c1d 100644
--- a/checks/binaries.pm
+++ b/checks/binaries.pm
@@ -109,6 +109,8 @@ our $HARDENING= Lintian::Data->new('binaries/hardening-tags', qr/\s*\|\|\s*/o,
     \&_split_hash);
 our $HARDENED_FUNCTIONS = Lintian::Data->new('binaries/hardened-functions');
 our $LFS_SYMBOLS = Lintian::Data->new('binaries/lfs-symbols');
+our $OBSOLETE_CRYPT_FUNCTIONS
+  = Lintian::Data->new('binaries/obsolete-crypt-functions', qr/\s*\|\|\s*/o);
 
 our $ARCH_32_REGEX;
 
@@ -182,7 +184,6 @@ sub run {
                         $unharded_functions{$name} = 1;
                     }
                 }
-
             }
 
             unless (defined $has_lfs) {
@@ -193,6 +194,13 @@ sub run {
                     $has_lfs = 0;
                 }
             }
+
+            if ($foo eq 'UND' and $OBSOLETE_CRYPT_FUNCTIONS->known($sym)) {
+                # Using an obsolete DES encryption function.
+                my $tag = $OBSOLETE_CRYPT_FUNCTIONS->value($sym);
+                tag $tag, $file;
+            }
+
             next if $is_profiled;
             # According to the binutils documentation[1], the profiling symbol
             # can be named "mcount", "_mcount" or even "__mcount".
diff --git a/data/binaries/obsolete-crypt-functions b/data/binaries/obsolete-crypt-functions
new file mode 100644
index 000000000..6029f8a76
--- /dev/null
+++ b/data/binaries/obsolete-crypt-functions
@@ -0,0 +1,13 @@
+# Set of C functions defined by libcrypt.so.1 that should no longer be
+# used at all, either because they imply use of the obsolete DES
+# encryption algorithm, or because they are alternative, less-portable
+# names for 'crypt'.
+#
+# The list is manually updated, please keep it sorted by name.
+
+encrypt    || obsolete-des-encryption
+encrypt_r  || obsolete-des-encryption
+setkey     || obsolete-des-encryption
+setkey_r   || obsolete-des-encryption
+
+fcrypt     || obsolete-crypt-alias
diff --git a/t/tests/binaries-obsolete-des/desc b/t/tests/binaries-obsolete-des/desc
new file mode 100644
index 000000000..1ed9920e5
--- /dev/null
+++ b/t/tests/binaries-obsolete-des/desc
@@ -0,0 +1,7 @@
+Testname: binaries-obsolete-des
+Version: 1.0
+Description: Check detection of obsolete DES functions
+Package-Architecture: any
+Test-For:
+  obsolete-des-encryption
+  obsolete-crypt-alias
diff --git a/t/tests/binaries-obsolete-des/orig/Makefile b/t/tests/binaries-obsolete-des/orig/Makefile
new file mode 100644
index 000000000..7f93a235c
--- /dev/null
+++ b/t/tests/binaries-obsolete-des/orig/Makefile
@@ -0,0 +1,51 @@
+# Makefile for the obsolete-des tests.
+
+# glibc 2.28 and greater try to prevent new programs from being linked
+# against the obsolete functions we're testing for.  This can be worked
+# around, but we have to know the exact "symbol version" associated with
+# the obsolete functions, which has to be dug out of libcrypt.so with nm.
+
+LIBCRYPT_FILE := $(shell dpkg -L libc6-dev | grep 'libcrypt\.so$$')
+SYMVER := $(shell nm --dynamic --with-symbol-versions $(LIBCRYPT_FILE) | \
+    grep ' setkey@' | cut -d@ -f2)
+
+# The output of the above nm | grep | cut pipeline will be the empty string
+# if it is possible to link programs against 'setkey' without special magic,
+# and a nonempty string if special magic is required.  All five of the
+# functions we are testing were introduced in the same release of glibc and
+# disabled for new programs as a group in a single release of glibc, so it
+# is only necessary to check how one of them is handled.
+ifneq "$(SYMVER)" ""
+SYMVER_DEFINE := -DSYMVER=\"$(SYMVER)\"
+else
+SYMVER_DEFINE := -USYMVER
+endif
+
+CFLAGS   := $(shell dpkg-buildflags --get CFLAGS)
+CPPFLAGS := $(shell dpkg-buildflags --get CPPFLAGS) $(SYMVER_DEFINE)
+LDFLAGS  := $(shell dpkg-buildflags --get LDFLAGS)
+
+PROGRAMS := uses-fcrypt uses-encrypt uses-encrypt_r uses-setkey uses-setkey_r
+MANPAGES := $(PROGRAMS:=.1)
+
+all: $(PROGRAMS) $(MANPAGES)
+
+uses-%: uses-%.o
+	$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ -lcrypt
+
+%.1: dummy.pod
+	sed s/@NAME@/$(@F)/g < $< | \
+	   pod2man --name $(@F) --section 1 > $@
+
+install: all
+	$(INSTALL) -d $(DESTDIR)/usr/bin
+	$(INSTALL) -d $(DESTDIR)/usr/share/man/man1
+	$(INSTALL) -m 755 $(PROGRAMS) $(DESTDIR)/usr/bin
+	$(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)/usr/share/man/man1
+
+clean distclean:
+	-rm -f *.o $(PROGRAMS) $(MANPAGES)
+
+check test:
+
+.PRECIOUS: $(PROGRAMS:=.o)
diff --git a/t/tests/binaries-obsolete-des/orig/dummy.pod b/t/tests/binaries-obsolete-des/orig/dummy.pod
new file mode 100644
index 000000000..1e900d799
--- /dev/null
+++ b/t/tests/binaries-obsolete-des/orig/dummy.pod
@@ -0,0 +1,12 @@
+=head1 NAME
+
+@NAME@ -- binary that does something
+
+=head1 SYNOPSIS
+
+ @NAME@ [options]
+
+=head1 DESCRIPTION
+
+@NAME@ does something very useful.
+
diff --git a/t/tests/binaries-obsolete-des/orig/uses-encrypt.c b/t/tests/binaries-obsolete-des/orig/uses-encrypt.c
new file mode 100644
index 000000000..b6ca0624e
--- /dev/null
+++ b/t/tests/binaries-obsolete-des/orig/uses-encrypt.c
@@ -0,0 +1,30 @@
+/* This program uses the obsolete function 'encrypt', which performs
+   DES encryption.  */
+
+#define _GNU_SOURCE 1
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+/* The prototype of 'encrypt' may already have been removed from
+   unistd.h.  */
+extern void encrypt(char block[64], int edflag);
+
+/* It may already not be possible to link new programs that use
+   'encrypt' without special magic.  */
+#ifdef SYMVER
+__asm__ (".symver encrypt, encrypt@" SYMVER);
+#endif
+
+int
+main(void)
+{
+    char block[64];
+    memset(block, 0, sizeof block);
+    encrypt(block, 0);
+    for (size_t i = 0; i < sizeof block; i++) {
+        putchar(block[i] ? '1' : '0');
+    }
+    putchar('\n');
+    return 0;
+}
diff --git a/t/tests/binaries-obsolete-des/orig/uses-encrypt_r.c b/t/tests/binaries-obsolete-des/orig/uses-encrypt_r.c
new file mode 100644
index 000000000..a3826249c
--- /dev/null
+++ b/t/tests/binaries-obsolete-des/orig/uses-encrypt_r.c
@@ -0,0 +1,33 @@
+/* This program uses the obsolete function 'encrypt_r', which performs
+   DES encryption.  */
+
+#define _GNU_SOURCE 1
+#include <crypt.h>
+#include <string.h>
+#include <stdio.h>
+
+/* The prototype of 'encrypt_r' may already have been removed from
+   crypt.h.  */
+extern void encrypt_r(char block[64], int edflag, struct crypt_data *data);
+
+/* It may already not be possible to link new programs that use
+   'encrypt_r' without special magic.  */
+#ifdef SYMVER
+__asm__ (".symver encrypt_r, encrypt_r@" SYMVER);
+#endif
+
+int
+main(void)
+{
+    struct crypt_data data;
+    char block[64];
+
+    memset(&data, 0, sizeof data);
+    memset(block, 0, sizeof block);
+    encrypt_r(block, 0, &data);
+    for (size_t i = 0; i < sizeof block; i++) {
+        putchar(block[i] ? '1' : '0');
+    }
+    putchar('\n');
+    return 0;
+}
diff --git a/t/tests/binaries-obsolete-des/orig/uses-fcrypt.c b/t/tests/binaries-obsolete-des/orig/uses-fcrypt.c
new file mode 100644
index 000000000..d71a837a3
--- /dev/null
+++ b/t/tests/binaries-obsolete-des/orig/uses-fcrypt.c
@@ -0,0 +1,21 @@
+/* This program uses the obsolete function 'fcrypt',
+   which is an alias for 'crypt'.  */
+
+#include <crypt.h>
+#include <stdio.h>
+
+/* The prototype may already have been removed from crypt.h.  */
+extern char *fcrypt(const char *, const char *);
+
+/* It may already not be possible to link new programs that use
+   'fcrypt' without special magic.  */
+#ifdef SYMVER
+__asm__ (".symver fcrypt, fcrypt@" SYMVER);
+#endif
+
+int
+main(void)
+{
+    puts(fcrypt("password", "Dn"));
+    return 0;
+}
diff --git a/t/tests/binaries-obsolete-des/orig/uses-setkey.c b/t/tests/binaries-obsolete-des/orig/uses-setkey.c
new file mode 100644
index 000000000..bdf70b0c4
--- /dev/null
+++ b/t/tests/binaries-obsolete-des/orig/uses-setkey.c
@@ -0,0 +1,45 @@
+/* This program uses the obsolete function 'setkey', which sets a key for
+   DES encryption.  */
+
+#define _GNU_SOURCE 1
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+
+/* The prototype of 'setkey' may already have been removed from
+   stdlib.h.  */
+extern void setkey(const char *);
+
+
+/* It may already not be possible to link new programs that use
+   'setkey' without special magic.  */
+#ifdef SYMVER
+__asm__ (".symver setkey, setkey@" SYMVER);
+#endif
+
+/* setkey uses a 1-bit-per-byte representation of a DES key.
+   Yes, really.  */
+const char key[64] = {
+    0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+    0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+    0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+    0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+};
+
+int
+main(void)
+{
+    /* The primary effects of calling 'setkey' are only visible by
+       calling 'encrypt', and we don't want to call 'encrypt' in this
+       program because we want to make sure Lintian detects programs
+       that call 'setkey' but not 'encrypt', even though that doesn't
+       make a whole lot of sense.  So we just call it and then check
+       whether it changed errno, which is the documented way to check
+       whether it failed.  */
+    errno = 0;
+    setkey(key);
+    if (errno) {
+        perror("setkey");
+    }
+    return 0;
+}
diff --git a/t/tests/binaries-obsolete-des/orig/uses-setkey_r.c b/t/tests/binaries-obsolete-des/orig/uses-setkey_r.c
new file mode 100644
index 000000000..3ad3f3577
--- /dev/null
+++ b/t/tests/binaries-obsolete-des/orig/uses-setkey_r.c
@@ -0,0 +1,48 @@
+/* This program uses the obsolete function 'setkey_r', which sets a key for
+   DES encryption.  */
+
+#define _GNU_SOURCE 1
+#include <crypt.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+/* The prototype of 'setkey_r' may already have been removed from
+   crypt.h.  */
+extern void setkey_r(const char *, struct crypt_data *);
+
+/* It may already not be possible to link new programs that use
+   'setkey_r' without special magic.  */
+#ifdef SYMVER
+__asm__ (".symver setkey_r, setkey_r@" SYMVER);
+#endif
+
+/* setkey_r uses a 1-bit-per-byte representation of a DES key.
+   Yes, really.  */
+const char key[64] = {
+    0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+    0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+    0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+    0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+};
+
+int
+main(void)
+{
+    struct crypt_data data;
+    memset(&data, 0, sizeof data);
+
+    /* The primary effects of calling 'setkey_r' are only visible by
+       calling 'encrypt_r', and we don't want to call 'encrypt_r' in
+       this program because we want to make sure Lintian detects
+       programs that call 'setkey_r' but not 'encrypt_r', even though
+       that doesn't make a whole lot of sense.  So we just call it and
+       then check whether it changed errno, which is the documented
+       way to check whether it failed.  */
+    errno = 0;
+    setkey_r(key, &data);
+    if (errno) {
+        perror("setkey_r");
+    }
+    return 0;
+}
diff --git a/t/tests/binaries-obsolete-des/tags b/t/tests/binaries-obsolete-des/tags
new file mode 100644
index 000000000..d5a619861
--- /dev/null
+++ b/t/tests/binaries-obsolete-des/tags
@@ -0,0 +1,5 @@
+E: binaries-obsolete-des: obsolete-crypt-alias usr/bin/uses-fcrypt
+E: binaries-obsolete-des: obsolete-des-encryption usr/bin/uses-encrypt
+E: binaries-obsolete-des: obsolete-des-encryption usr/bin/uses-encrypt_r
+E: binaries-obsolete-des: obsolete-des-encryption usr/bin/uses-setkey
+E: binaries-obsolete-des: obsolete-des-encryption usr/bin/uses-setkey_r
-- 
2.20.1


Reply to: