Skip to content

Commit

Permalink
Rewrite dpkginfo probe without using APT
Browse files Browse the repository at this point in the history
This change rewrites the dpkginfo probe without using the
APT library.

The dpkginfo now parses the list of installed package
(/var/lib/dpkg/status) directly, instead of relying on
the APT library.

This prevents loading the full list of packages in memory
and various issues related to the use of the APT library.

The dpkginfo probe is now stateless and doesn't require
init and fini functions. Also, the dpkginfo_get_by_name
function can now be called from multiple threads without
having to be protected by a lock.

The dependency on the APT library has been removed from OpenSCAP.
  • Loading branch information
0intro committed Nov 8, 2023
1 parent 727256c commit 557ddee
Show file tree
Hide file tree
Showing 13 changed files with 188 additions and 255 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- name: Install Deps
run: |
sudo apt-get update
sudo apt-get -y install lcov swig xsltproc rpm-common lua5.3 libpcre2-dev libyaml-dev libapt-pkg-dev libdbus-1-dev libdbus-glib-1-dev libcurl4-openssl-dev libgcrypt-dev libselinux1-dev libgconf2-dev libacl1-dev libblkid-dev libcap-dev libxml2-dev libxslt1-dev libxml-parser-perl libxml-xpath-perl libperl-dev librpm-dev librtmp-dev libxmlsec1-dev libxmlsec1-openssl python3-dbusmock python3-pytest
sudo apt-get -y install lcov swig xsltproc rpm-common lua5.3 libpcre2-dev libyaml-dev libdbus-1-dev libdbus-glib-1-dev libcurl4-openssl-dev libgcrypt-dev libselinux1-dev libgconf2-dev libacl1-dev libblkid-dev libcap-dev libxml2-dev libxslt1-dev libxml-parser-perl libxml-xpath-perl libperl-dev librpm-dev librtmp-dev libxmlsec1-dev libxmlsec1-openssl python3-dbusmock python3-pytest
sudo apt-get -y remove rpm
# Runs a set of commands using the runners shell
Expand All @@ -57,7 +57,7 @@ jobs:
image: fedora:latest
steps:
- name: Install Deps
run: dnf install -y cmake git dbus-devel GConf2-devel libacl-devel libblkid-devel libcap-devel libcurl-devel libgcrypt-devel libselinux-devel libxml2-devel libxslt-devel libattr-devel make openldap-devel pcre2-devel perl-XML-Parser perl-XML-XPath perl-devel python3-devel python3-dbusmock rpm-devel swig bzip2-devel gcc-c++ libyaml-devel xmlsec1-devel xmlsec1-openssl-devel hostname bzip2 lua rpm-build which strace apt-devel python3-pytest
run: dnf install -y cmake git dbus-devel GConf2-devel libacl-devel libblkid-devel libcap-devel libcurl-devel libgcrypt-devel libselinux-devel libxml2-devel libxslt-devel libattr-devel make openldap-devel pcre2-devel perl-XML-Parser perl-XML-XPath perl-devel python3-devel python3-dbusmock rpm-devel swig bzip2-devel gcc-c++ libyaml-devel xmlsec1-devel xmlsec1-openssl-devel hostname bzip2 lua rpm-build which strace python3-pytest
- name: Checkout
uses: actions/checkout@v3
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
- name: Install Deps
run: |
sudo apt-get update
sudo apt-get -y install lcov swig xsltproc rpm-common lua5.3 libyaml-dev libapt-pkg-dev libdbus-1-dev libdbus-glib-1-dev libcurl4-openssl-dev libgcrypt-dev libselinux1-dev libgconf2-dev libacl1-dev libblkid-dev libcap-dev libxml2-dev libxslt1-dev libxml-parser-perl libxml-xpath-perl libperl-dev librpm-dev librtmp-dev libxmlsec1-dev libxmlsec1-openssl python3-dbusmock
sudo apt-get -y install lcov swig xsltproc rpm-common lua5.3 libyaml-dev libdbus-1-dev libdbus-glib-1-dev libcurl4-openssl-dev libgcrypt-dev libselinux1-dev libgconf2-dev libacl1-dev libblkid-dev libcap-dev libxml2-dev libxslt1-dev libxml-parser-perl libxml-xpath-perl libperl-dev librpm-dev librtmp-dev libxmlsec1-dev libxmlsec1-openssl python3-dbusmock
sudo apt-get -y remove rpm
# Initializes the CodeQL tools for scanning.
Expand Down
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ cmake_dependent_option(OPENSCAP_PROBE_UNIX_UNAME "Unix uname probe" ON "ENABLE_P
cmake_dependent_option(OPENSCAP_PROBE_UNIX_XINETD "Unix xinetd probe" ON "ENABLE_PROBES_UNIX" OFF)

# LINUX PROBES
cmake_dependent_option(OPENSCAP_PROBE_LINUX_DPKGINFO "Linux dpkginfo probe" ON "ENABLE_PROBES_LINUX; APTPKG_FOUND" OFF)
cmake_dependent_option(OPENSCAP_PROBE_LINUX_DPKGINFO "Linux dpkginfo probe" ON "ENABLE_PROBES_LINUX" OFF)
cmake_dependent_option(OPENSCAP_PROBE_LINUX_IFLISTENERS "Linux iflisteners probe" ON "ENABLE_PROBES_LINUX" OFF)
cmake_dependent_option(OPENSCAP_PROBE_LINUX_INETLISTENINGSERVERS "Linux inetlisteningservers probe" ON "ENABLE_PROBES_LINUX" OFF)
cmake_dependent_option(OPENSCAP_PROBE_LINUX_PARTITION "Linux partition probe" ON "ENABLE_PROBES_LINUX; BLKID_FOUND" OFF)
Expand Down Expand Up @@ -433,7 +433,7 @@ message(STATUS " Unix xinetd probe: ${OPENSCAP_PROBE_UNIX_XINETD}")
message(STATUS " ")

message(STATUS "Linux probes: ${ENABLE_PROBES_LINUX}")
message(STATUS " Linux dpkginfo probe (depends on aptpkg): ${OPENSCAP_PROBE_LINUX_DPKGINFO}")
message(STATUS " Linux dpkginfo probe: ${OPENSCAP_PROBE_LINUX_DPKGINFO}")
message(STATUS " Linux iflisteners probe: ${OPENSCAP_PROBE_LINUX_IFLISTENERS}")
message(STATUS " Linux inetlisteningservers probe: ${OPENSCAP_PROBE_LINUX_INETLISTENINGSERVERS}")
message(STATUS " Linux partition probe (depends on blkid): ${OPENSCAP_PROBE_LINUX_PARTITION}")
Expand Down
30 changes: 0 additions & 30 deletions cmake/FindAptPkg.cmake

This file was deleted.

4 changes: 2 additions & 2 deletions docs/developer/developer.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ sudo dnf install \
cmake dbus-devel GConf2-devel libacl-devel libblkid-devel libcap-devel libcurl-devel \
libgcrypt-devel libselinux-devel libxml2-devel libxslt-devel libattr-devel make openldap-devel \
pcre-devel perl-XML-Parser perl-XML-XPath perl-devel python3-devel python3-dbusmock rpm-devel swig \
bzip2-devel gcc-c++ libyaml-devel xmlsec1-devel xmlsec1-openssl-devel apt-devel
bzip2-devel gcc-c++ libyaml-devel xmlsec1-devel xmlsec1-openssl-devel
----

On RHEL 8+ / CentOS 8+, the command to install the build dependencies is:
Expand All @@ -85,7 +85,7 @@ On Ubuntu 16.04, Debian 8 or Debian 9, the command to install the build dependen
sudo apt-get install -y cmake libdbus-1-dev libdbus-glib-1-dev libcurl4-openssl-dev \
libgcrypt20-dev libselinux1-dev libxslt1-dev libgconf2-dev libacl1-dev libblkid-dev \
libcap-dev libxml2-dev libldap2-dev libpcre3-dev python-dev swig libxml-parser-perl \
libxml-xpath-perl libperl-dev libbz2-dev librpm-dev g++ libapt-pkg-dev libyaml-dev \
libxml-xpath-perl libperl-dev libbz2-dev librpm-dev g++ libyaml-dev \
libxmlsec1-dev libxmlsec1-openssl
----

Expand Down
6 changes: 0 additions & 6 deletions openscap.spec
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ BuildRequires: cmake >= 2.6
BuildRequires: gcc
BuildRequires: gcc-c++
BuildRequires: swig libxml2-devel libxslt-devel perl-generators perl-XML-Parser
%if 0%{?fedora}
BuildRequires: apt-devel
%endif
BuildRequires: rpm-devel
BuildRequires: libgcrypt-devel
%if 0%{?fedora}
Expand Down Expand Up @@ -47,9 +44,6 @@ Requires: libacl
Requires: libblkid
Requires: libcap
Requires: libselinux
%if 0%{?fedora}
Requires: apt-libs
%endif
Requires: openldap
Requires: popt
# Fedora has procps-ng, which provides procps
Expand Down
3 changes: 0 additions & 3 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,6 @@ endif()
if(DBUS_FOUND)
target_link_libraries(openscap ${DBUS_LIBRARIES})
endif()
if(APTPKG_FOUND)
target_link_libraries(openscap ${APTPKG_LIBRARIES})
endif()
if(ACL_FOUND)
target_link_libraries(openscap ${ACL_LIBRARY})
endif()
Expand Down
2 changes: 1 addition & 1 deletion src/OVAL/probes/probe-table.c
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ static const probe_table_entry_t probe_table[] = {
{OVAL_INDEPENDENT_YAML_FILE_CONTENT, NULL, yamlfilecontent_probe_main, NULL, yamlfilecontent_probe_offline_mode_supported},
#endif
#ifdef OPENSCAP_PROBE_LINUX_DPKGINFO
{OVAL_LINUX_DPKG_INFO, dpkginfo_probe_init, dpkginfo_probe_main, dpkginfo_probe_fini, dpkginfo_probe_offline_mode_supported},
{OVAL_LINUX_DPKG_INFO, NULL, dpkginfo_probe_main, NULL, dpkginfo_probe_offline_mode_supported},
#endif
#ifdef OPENSCAP_PROBE_LINUX_IFLISTENERS
{OVAL_LINUX_IFLISTENERS, NULL, iflisteners_probe_main, NULL, NULL},
Expand Down
5 changes: 1 addition & 4 deletions src/OVAL/probes/unix/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
if(OPENSCAP_PROBE_LINUX_DPKGINFO)
list(APPEND LINUX_PROBES_SOURCES
"dpkginfo-helper.cxx"
"dpkginfo-helper.c"
"dpkginfo-helper.h"
"dpkginfo_probe.c"
"dpkginfo_probe.h"
)
list(APPEND LINUX_PROBES_INCLUDE_DIRECTORIES
${APTPKG_INCLUDE_DIR}
)
endif()

if(OPENSCAP_PROBE_LINUX_IFLISTENERS)
Expand Down
179 changes: 179 additions & 0 deletions src/OVAL/probes/unix/linux/dpkginfo-helper.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>

#include "debug_priv.h"
#include "dpkginfo-helper.h"

#define DPKG_STATUS_BUFFER_SIZE 4096

static char* trimleft(char *str)
{
while (isspace((unsigned char)*str))
str++;

if (*str == 0)
return str;

return str;
}

static int version(struct dpkginfo_reply_t *reply)
{
char *evr, *epoch, *version, *release;

if (reply->evr == NULL)
return -1;

evr = strdup(reply->evr);
if (evr == NULL)
return -1;

if ((epoch = strchr(evr, ':')) != NULL) {
*epoch++ = '\0';
reply->epoch = strdup(evr);
if (reply->epoch == NULL)
goto err;
} else {
reply->epoch = strdup("0");
if (reply->epoch == NULL)
goto err;
epoch = evr;
}

version = epoch;
if ((release = strchr(version, '-')) != NULL) {
*release++ = '\0';
reply->release = strdup(release);
if (reply->release == NULL)
goto err;
}
reply->version = strdup(version);
if (reply->version == NULL)
goto err;

free(evr);
return 0;
err:
free(evr);
return -1;
}

struct dpkginfo_reply_t* dpkginfo_get_by_name(const char *name, int *err)
{
FILE *f;
char buf[DPKG_STATUS_BUFFER_SIZE], path[PATH_MAX], *root, *key, *value;
struct dpkginfo_reply_t *reply;

*err = 0;
reply = NULL;

root = getenv("OSCAP_PROBE_ROOT");
if (root != NULL)
snprintf(path, PATH_MAX, "%s/var/lib/dpkg/status", root);
else
snprintf(path, PATH_MAX, "/var/lib/dpkg/status");

f = fopen(path, "r");

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This argument to a file access function is derived from
user input (an environment variable)
and then passed to fopen(__filename).
if (f == NULL) {
dW("%s not found.", path);
*err = -1;
return NULL;
}

dD("Searching package \"%s\".", name);

while (fgets(buf, DPKG_STATUS_BUFFER_SIZE, f)) {
if (buf[0] == '\n') {
// New package entry.
if (reply != NULL) {
// Package found.
goto out;
}
continue;
}
if (isspace(buf[0])) {
// Ignore line beginning by a space.
continue;
}
buf[strcspn(buf, "\n")] = 0;
key = buf;
value = strchr(buf, ':');
if (value == NULL) {
// Ignore truncated line.
continue;
}
*value++ = '\0';
value = trimleft(value);
// Package should be the first line.
if (strcmp(key, "Package") == 0) {
if (strcmp(value, name) == 0) {
if (reply != NULL)
continue;
reply = calloc(1, sizeof(*reply));
if (reply == NULL)
goto err;
reply->name = strdup(value);
if (reply->name == NULL)
goto err;
}
} else if (reply != NULL) {
if (strcmp(key, "Status") == 0) {
if (strcmp(value, "install") != 0) {
// Package deinstalled.
dD("Package \"%s\" has been deinstalled.", name);
dpkginfo_free_reply(reply);
reply = NULL;
continue;
}
} else if (strcmp(key, "Architecture") == 0) {
reply->arch = strdup(value);
if (reply->arch == NULL)
goto err;
} else if (strcmp(key, "Version") == 0) {
reply->evr = strdup(value);
if (reply->evr == NULL)
goto err;
if (version(reply) < 0)
goto err;
}
}
}

// Reached end of file.

out:
if (reply != NULL) {
// Package found.
dD("Package \"%s\" found (arch=%s evr=%s epoch=%s version=%s release=%s).",
name, reply->arch, reply->evr, reply->epoch, reply->version, reply->release);
*err = 1;
}
fclose(f);
return reply;
err:
dW("Insufficient memory available to allocate duplicate string.");
fclose(f);
dpkginfo_free_reply(reply);
*err = -1;
return NULL;
}

void dpkginfo_free_reply(struct dpkginfo_reply_t *reply)
{
if (reply) {
free(reply->name);
free(reply->arch);
free(reply->epoch);
free(reply->release);
free(reply->version);
free(reply->evr);
free(reply);
}
}
Loading

0 comments on commit 557ddee

Please sign in to comment.