Comment 43 for bug 1854362

Revision history for this message
Chris Coulson (chrisccoulson) wrote :

I reviewed tcmu 1.5.2-5build1 as checked into focal. This shouldn't be
considered a full audit but rather a quick gauge of maintainability.

tcmu is the userspace side of the kernel's LIO TCM in userspace backstore,
which allows backstores for LIO (the kernel's SCSI target) to live outside
of the kernel and to be implemented in userspace. It consists of a daemon
(tcmu-runner) which communicates with the kernel side via the TCM-USER
generic netlink family. Handlers for SCSI commands are implemented as modules
that run inside tcmu-runner - currently built and insalled are "File-backed
optical", "ZBC emulation", "QCOW image file" and "Ceph RBD" handlers. Handlers
process SCSI commands from a ring buffer shared between the kernel and
userspace.

- Some limited CVE history - a mixture of DoS and information leaks, all fixed
  in the current version:
  - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-1000198
  - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-1000199
  - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-1000200
  - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-1000201
- Upstream is active, and we had a positive interaction with them whilst I was
  working on this MIR (see https://github.com/open-iscsi/tcmu-runner/issues/613).
- Build-depends all in main (cmake, kmod, glib2.0, libnl3, ceph, pkg-config,
  zlib)
  - It build-depends on libkmod-dev because it consumes symbols from libkmod2,
    but what does it need the kmod binaries for at build time?
- tcmu-runner has postinst/prerm/postrm scripts just containing autogenerated
  snippets from dh_installinit and dh_installsystemd.
  - It appears to be missing cleanup hooks for /etc/tcmu/tcmu.conf though.
- Contains an init script and systemd unit that starts the tcmu-runner daemon,
  which is started by default as part of the multi-user target.
  - The daemon runs as root.
  - Doesn't listen on any ports.
- Also installs a dbus service to activate the tcmu-runner daemon.
- It exposes a couple of dbus interfaces:
  - org.kernel.TCMUService1 which provides a single CheckConfig method and is
    exported for each registered type (with a type specific object path). None
    of the internal plugins appear to implement check_config, so I'd imagine it
    just always returns success for these.
  - org.kernel.TCMUService1.HandlerManager1 which allows external handlers for
    a type to be registered (via the RegisterHandler method). Calls to
    org.kernel.TCMUService1.CheckConfig are then proxied to the external handler
    process, which owns a type-specific well-known name on the system bus.
  - The dbus policy allows all users to call
    org.kernel.TCMUService1.HandlerManager1.RegisterHandler, which doesn't seem
    desirable. I don't think there is a direct security impact from this, as
    external handlers need to be privileged in order to own the type-specific
    well-known name on the system bus, and the call will return an error if
    called before that name is owned. But I think this should only be callable
    as the root user.
- No setuid binaries.
- One binary in PATH (/usr/bin/tcmu-runner).
- No sudo fragments.
- No polkit files.
- No udev rules.
- There don't appear to be any unit tests or autopkgtests.
- No cron jobs.
- Build logs appear to be mostly clean.
  - Note, there is a patch that removes -Werror, which appears to be unnecessary.
  - There are some dpkg-shlibdeps warnings because the modules don't link
    against libtcmu2.
  - tcmu-runner has a "dbus-policy-without-send-destination" lintian warning which
    should probably be fixed.
- No subprocesses spawned.
- Memory management seems to generally be cautious - it takes care to handle
  heap allocation failures, and makes use of calloc to avoid arithmetic overflow
  issues in a lot of places.
- Seems to handle errors when performing file I/O operations. It uses pread
  and pwrite quite a bit and in most cases checks that the expected number of
  bytes are read / written.
  - There is a usage of pwritev in qcow_pwritev that checks for an error
    condition (ret < 0), but doesn't appear to check that the correct number of
    bytes are written or expose the number of bytes written for callers to
    handle.
- It appears to be fairly defensive when reading metadata from files on disk.
- Has some logging helpers, which log to a file and syslog.
  - Writing to the syslog happens on a dedicated thread.
  - There is some locking which allows the logging functions to be used from
    multiple threads - tcmu-runner has a dedicated thread for each internal
    device handler, which is why this is necessary.
  - Log levels are set by a config file, and the default is "info (3)". The
    highest log level (5) logs SCSI command sequences, but only to the
    dedicated log file rather than syslog. I didn't find any indication that
    sensitive data might be logged, although perhaps SCSI command sequences
    could be considered sensitive at the highest log level.
  - I didn't find any format string issues.
  - The file logging has proper EINTR handling around write().
- The only environment usage is reading the default log file directory
  (TCMU_LOGDIR).
- No use of privileged functions.
- No crypto.
- No tmpfile usage.
- No direct networking usage.
- No webkit.
- No policykit usage.
- All toolchain hardening measures are on.
- Coverity found what look like a couple of genuine infinite loops in qcow.c.
  - It also found a couple of TOCTOU issues (one in fbo_open and one in
    zbc_open_backstore, but these don't look security relevant).
- cppcheck found one uninitialized variable issue, but it looks spurious.
- No relevant shellcheck issues.

Tentative security team ACK for promoting tcmu to main. I'd like to see the
"dbus-policy-without-send-destination" lintian warning fixed, and the dbus
policy tightened to only allow calls to
org.kernel.TCMUService1.HandlerManager1.RegisterHandler method from the root
user, unless there is a need for that to be callable by unprivileged users.