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 6, 2023
1 parent c26da2a commit b7088b9
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 244 deletions.
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.

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 b7088b9

Please sign in to comment.