I reviewed upki 1.0.0~beta.3-0ubuntu1 as checked into stonking. This shouldn't
be considered a full audit but rather a quick gauge of maintainability.

upki is a privacy-preserving certificate revocation system from the rustls
project, built on Mozilla's crlite-clubcard construction. It determines whether
an end-entity certificate is revoked by consulting a locally cached,
cryptographically-filtered dataset rather than polling a server per check (no
OCSP, no per-cert telemetry). The crate is a Rust workspace: the `upki` core
library (offline revocation index + C API), `upki-cli` (the `upki` command),
`upki-openssl` (an `SSL_verify_cb` callback), `rustls-upki` (a rustls
`ServerCertVerifier`) and `upki-mirror` (tooling to host Mozilla CRLite files).
The `upki-fetch.service` systemd unit runs `upki fetch` unprivileged as the
`upki` user on a 2-hour persistent timer; it downloads `manifest.json`, diffs it
against the local cache and atomically fetches only the changed CRLite
`.filter`/`.delta` files over TLS using the system CA store. A check hashes the
certificate's issuer SPKI and serial into a CRLite lookup key, seeks the rebuilt
`index.bin` to the covering filter and CT-timestamp window and returns one of
three outcomes: `NotRevoked`, `CertainlyRevoked`, or
`NotCoveredByRevocationData`. Consumers must already have validated the chain -
all four interfaces (CLI, C-FFI, OpenSSL callback, rustls verifier) only report
revocation status, never perform their own chain validation. Packaged as `upki`,
`libupki1` (+`-dev`), `libupki-openssl1` (+`-dev`) and `upki-mirror`

- CVE History
  - No CVEs are recorded for the `upki` package itself. Transitive dependencies
    are current: rustls 0.23.42, rustls-webpki 0.103.13, aws-lc-rs 1.17.1,
    reqwest 0.13.0. The recent rustls-webpki advisories (RUSTSEC-2026-0049,
    RUSTSEC-2026-0104) concern CRL parsing/authority semantics that the crlite
    path does not exercise: upki parses the end-entity certificate with webpki
    but performs revocation via `clubcard_crlite` not via webpki CRLs.
    `bincode` 1.3.3 is flagged by RUSTSEC-2025-0141 (unmaintained) and is
    explicitly acknowledged in `deny.toml` [advisories] -- retained only for
    on-disk `index.bin` format compatibility - it is not used as a
    network-facing parser.
- Build-Depends
  - The build uses a native Rust toolchain (Rust 1.97) via dh-cargo with full
    vendoring (`rust-vendor/`). Dependencies are appropriate:
    `ca-certificates` (system CA store for the fetch path), `libssl-dev` (for
    the `upki-openssl` FFI crate and the `fetch-native-tls` TLS backend),
    `cargo-c` (shared-library install), `help2man`, `pkgconf`. No unusual crypto
    or networking libraries beyond these. The CLI is deliberately built
    `--no-default-features --features fetch-native-tls`, so fetching is done
    against system OpenSSL rather than the bundled `aws-lc-rs`/rustls path; the
    offline `libupki1` library links no TLS.
- pre/post inst/rm scripts
  - Standard maintainer scripts generated by dh_installsysusers /
    dh_installtmpfiles / dh_installsystemd. `postinst` (configure) creates the
    `upki` system user (`systemd-sysusers upki.conf`), the cache directories
    (`systemd-tmpfiles --create upki.conf`, provisioning `/var/cache/upki` and
    `/var/cache/upki/revocation` both 0755 upki:upki), unmask+enables the timer,
    reloads the daemon and starts `upki-fetch.service`/`.timer`. `prerm`
    (remove) stops the service and timer. `postrm` (remove) reloads the daemon;
    (purge) purges the timer. Cleanup is symmetric with installation. The cache
    path is consistent: `revocation_cache_dir()` returns
    `cache_dir.join("revocation")`, i.e. `/var/cache/upki/revocation`, matching
    both the tmpfiles entry and the unit's `CacheDirectory=upki/revocation`. No
    custom logic beyond standard debhelper generated scripts.
- init scripts
  - None.
- systemd units
  - Two units, both installed for the `upki` package:
  - `upki-fetch.service`: `Type=oneshot`, `ExecStart=/usr/bin/upki fetch`,
    running as `User=upki`/`Group=upki` (unprivileged, no root daemon).
    Extensively hardened: `NoNewPrivileges=true`, `PrivateTmp=true`,
    `PrivateDevices=true`, `ProtectSystem=strict`, `ProtectHome`,
    `ProtectClock/Hostname/KernelLogs/ Modules/Tunables`,
    `ProtectControlGroups`, `RestrictNamespaces/Realtime/ SUIDSGID`,
    `MemoryDenyWriteExecute=true`, `LockPersonality`, `RemoveIPC`,
    `ProcSubset=pid`, `UMask=0022`, empty
    `CapabilityBoundingSet`/`AmbientCapabilities`,
    `RestrictAddressFamilies=AF_INET AF_INET6`,
    `SystemCallArchitectures=native`, `SystemCallFilter=@system-service` with
    `@privileged` and `@resources` excluded, `StateDirectory`/`CacheDirectory`
    mode 0755.
  - `upki-fetch.timer`: `OnBootSec=10min`, `OnUnitActiveSec=2h`,
    `Persistent=true`. The unit confines the fetcher well and exposes no
    listening socket, the only networking is outbound HTTPS from an unprivileged
    account.
- dbus services
  - None.
- setuid binaries
  - None.
- binaries in PATH
  - One executable is installed under `/usr/bin`: the `upki` CLI
    (`/usr/bin/upki`). The mirror helpers `mozilla-crlite` and `intermediates`
    are installed under `/usr/libexec/upki-mirror/` (not in `$PATH`)
- sudo fragments
  - None.
- polkit files
  - None.
- udev rules
  - None.
- unit tests / autopkgtests
  - Package sets `Testsuite: autopkgtest` with a `python3` trigger. Rust unit
    tests exist in `upki/tests`, `upki-cli/tests/integration.rs`, `upki-openssl`
    and `revoke-test/tests/system_tests.rs` (including a dedicated
    `Index::check` test module covering invalid/truncated/oversorted index
    inputs). However, `debian/rules` overrides `override_dh_auto_test` to
    comment out `cargo test`, so the Rust test suite is NOT executed during the
    package build (the build log confirms no test phase). `dh-cargo-built-using`
    is also stubbed with a comment that it is "horribly broken" for the embedded
    `aws-lc` library. The autopkgtest `debian/tests/upki-fetch` does start a
    local HTTP server, repoints `fetch-url` at it and verifies
    `manifest.json`/`filter1.filter` land in the cache. Recommendation: have CI
    run the upstream `cargo test` suite rather than leaving it disabled in the
    build.
- cron jobs None; periodic fetching is handled by the systemd timer
  (appropriate).
- Build logs
  - The only lines matching `error:` are
    `[eyre 0.6.12] error[E0407]/E0554: `#![feature]` may not be used on the 
stable release channel`
    -- this is the `eyre` build-script toolchain probe (it compiles a
    `#![feature]` snippet to detect `backtrace` support) and is expected
    `eyre` then compiles cleanly (42 warnings). There are routine
    dependency warnings (clippy `unexpected cfg` values, elided lifetimes) and
    `dpkg-gencontrol` warnings that `${cargo:Depends/Recommends/...}` are
    undefined (a known `dh-cargo`/built-using limitation with the embedded
    aws-lc)

- Processes spawned
  - Only two hits in the whole scan, both in test code
  (`upki-cli/tests/integration.rs` and `revoke-test/tests/system_tests.rs`),
  each spawning `cargo` to drive the build. No process spawning in any
  production path (CLI, library, mirror, callbacks). Single-process design.
- Memory management
  - Rust source; no manual `malloc`/`free`/unbounded copies. The only
    raw-pointer/`unsafe` work is in the C-FFI (`upki/src/ffi.rs`) and the
    OpenSSL FFI (`upki-openssl/src/lib.rs`): FFI slices are reconstructed with
    `slice::from_raw_parts` after null/length checks and the OpenSSL `SSL_CTX`
    config is tracked via a statically-registered index
    (`CRYPTO_get_ex_new_index`) with explicit free-on-override and
    free-on-config-drop.
- File IO
  - Config-driven paths: `/etc/upki/config.toml` (config) and
    `/var/cache/upki/revocation` (cache) on Linux, with XDG/home fallbacks on
    desktop platforms. Downloads are written via `atomic_write` (the `tempfile`
    crate, random names, mode 0644, atomic rename) and immediately hash-checked
    against the manifest SHA-256 before being kept. The `index.bin` reads use
    bounds-checked `split_first_chunk`/`read_be` calls and are validated on load
    magic/version, header counts bounds-checked before table allocation.
    `umask` is 0022 via the unit. No predictable temp-file names. No unsafe file
    permission handling.
- Logging
  - Fetch progress uses `tracing` (`info!`/`debug!`); the CLI revocation status
    is `println!`ed to stdout. All logging interpolates owned/format-safe values
    (`format!`, `{...}`) -- no user-controlled format strings, no fixed-buffer
    concatenation.
- Environment variable usage
  - Effectively none at runtime on Linux. The only environment reads are
    `ProgramData` (Windows-only, `#[cfg(target_os = "windows")]`) and
    `CARGO_MANIFEST_DIR`/ `CARGO_PKG_*` at build/test time (`codegen.rs` tests).
    No runtime path, URL, or behaviour is controlled by caller-supplied
    environment variables.
- Use of privileged functions
  - None. The code performs no `setuid`/`setgid`/capability manipulation, opens
    no device nodes and the service runs as the unprivileged `upki` user.
    Privilege is not sought or used anywhere.
- Use of cryptography / random number sources etc
  - Cryptography is delegated: chain validation and hostname checking are done
    by `webpki` (in `rustls-upki`) / the system OpenSSL (`upki-openssl`), TLS by
    system OpenSSL (native-tls) or `aws-lc-rs`; revocation uses the
    `clubcard_crlite` filter and a package-local `sha256` implementation (the
    hash is only a lookup-key primitive -- the data itself is fetched over TLS
    and hash-verified). No key generation and no user-facing RNG. The verifier
    validates the full chain (`verify_for_usage` against the supplied roots) and
    the subject name BEFORE consulting revocation data and treats the presence
    of a valid CT timestamp window as required for a positive determination.
- Use of temp files
  - Temp files come solely from the `tempfile` crate (`tempfile_in(dir)`), which
    creates uniquely-named files in the target directory and persists them
    atomically; index building appends a `.new` suffix. These live under the
    systemd `CacheDirectory` (`/var/cache/upki/revocation`) with `PrivateTmp` in
    effect, so names are not predictable and are confined. No hand-rolled `/tmp`
    paths.
- Use of networking
  - The sole network activity is the `upki fetch` subcommand, using `reqwest`
    over `https://` only (the configured `fetch-url` is
    `https://upki.ubuntu.com/revocation/`). `native-tls` uses the system CA
    store (`ca-certificates`), with a 30 s timeout and `.error_for_status()`
    (non-2xx rejected). No `http://` appears in production code (only
    `127.0.0.1` test URLs). The `libupki1` library and the revocation check
    itself are fully offline. The `upki-mirror` tool also fetches from Mozilla
    over HTTPS. No inbound listeners, no open sockets beyond outbound TLS.
- Use of WebKit
  - None.
- Use of PolicyKit
  - None.

- Any significant cppcheck results
  - 49 cppcheck findings, ALL located under `rust-vendor/aws-lc-sys/aws-lc/`
    (the vendored AWS-LC C library). They are parser artifacts (`unknownMacro`,
    `preprocessorErrorDirective` -- e.g. `OPENSSL_STATIC_ASSERT` requires
    configuration that cppcheck is not given). Zero findings reference package
    code. Not applicable to `upki`.
- Any significant Coverity results
  - 57 Coverity defects, ALL under `rust-vendor/aws-lc-sys/` (vendored AWS-LC).
    Zero findings reference package code. Not applicable to `upki`.
- Any significant shellcheck results 105 shellcheck warnings, concentrated in
  `./debian/bin/dh-cargo-built-using` (a bash helper the package ships to work
  around a dh-cargo limitation) plus vendored CI scripts (`aws-lc-rs`,
  `tokio-native-tls`, etc.). They are style/portability items (`local` in sh
  mode, `SC2155`, unquoted expansions). No injection or execution risk and none
  in shipped maintainer scripts beyond the one documented helper.
- Any significant bandit results
  - None
- Any significant govulncheck results
  - None
- Any significant Semgrep results
  - None

The design is sound: a single unprivileged timer that only fetches
hash-verified, TLS-protected data into a confined cache, plus a fully offline
revocation library and verifier. Rust eliminates the memory-safety class of bugs
that a parallel C/C++ implementation of this component could carry; index
parsing is defensive and explicitly tested against corrupt/oversized inputs; the
systemd unit is among the most hardened seen for an MIR. The only fail-open path
(`NotCoveredByRevocationData`) is deliberate browser parity (a very new
certificate is allowed when the filter cannot yet cover it) and is documented.
The main items to watch are process-level: the fetch uses system OpenSSL
(native-tls), so an OS OpenSSL vulnerability directly affects the update path,
and the upstream Rust test suite is disabled in the build so regressions rely on
separate autopkgtests.

Security team ACK for promoting upki to main. ACK, with a soft
condition:

  Run the upstream `cargo test` suite in CI (currently disabled via
  `override_dh_auto_test`) so revocation regressions are caught at build time.

The package is sound for promotion to main.


** Changed in: upki (Ubuntu)
     Assignee: Ubuntu Security Team (ubuntu-security) => (unassigned)

** Changed in: upki (Ubuntu)
       Status: New => In Progress

-- 
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2166514

Title:
  [MIR] upki

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/upki/+bug/2166514/+subscriptions


-- 
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs

Reply via email to