mirror of
https://git.proxmox.com/git/fwupd
synced 2025-04-30 18:31:48 +00:00
277 lines
6.5 KiB
C
277 lines
6.5 KiB
C
/*
|
|
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#define G_LOG_DOMAIN "FuCommon"
|
|
|
|
#include "config.h"
|
|
|
|
#include <errno.h>
|
|
#include <glib/gstdio.h>
|
|
#ifdef HAVE_UTSNAME_H
|
|
#include <sys/utsname.h>
|
|
#endif
|
|
|
|
#include "fu-common.h"
|
|
#include "fu-kernel.h"
|
|
#include "fu-path.h"
|
|
#include "fu-string.h"
|
|
#include "fu-version-common.h"
|
|
|
|
/**
|
|
* fu_kernel_locked_down:
|
|
*
|
|
* Determines if kernel lockdown in effect
|
|
*
|
|
* Since: 1.8.2
|
|
**/
|
|
gboolean
|
|
fu_kernel_locked_down(void)
|
|
{
|
|
#ifdef __linux__
|
|
gsize len = 0;
|
|
g_autofree gchar *dir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_SECURITY);
|
|
g_autofree gchar *fname = g_build_filename(dir, "lockdown", NULL);
|
|
g_autofree gchar *data = NULL;
|
|
g_auto(GStrv) options = NULL;
|
|
|
|
if (!g_file_test(fname, G_FILE_TEST_EXISTS))
|
|
return FALSE;
|
|
if (!g_file_get_contents(fname, &data, &len, NULL))
|
|
return FALSE;
|
|
if (len < 1)
|
|
return FALSE;
|
|
options = g_strsplit(data, " ", -1);
|
|
for (guint i = 0; options[i] != NULL; i++) {
|
|
if (g_strcmp0(options[i], "[none]") == 0)
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
#else
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* fu_kernel_check_version:
|
|
* @minimum_kernel: (not nullable): The minimum kernel version to check against
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Determines if the system is running at least a certain required kernel version
|
|
*
|
|
* Since: 1.8.2
|
|
**/
|
|
gboolean
|
|
fu_kernel_check_version(const gchar *minimum_kernel, GError **error)
|
|
{
|
|
#ifdef HAVE_UTSNAME_H
|
|
struct utsname name_tmp;
|
|
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
g_return_val_if_fail(minimum_kernel != NULL, FALSE);
|
|
|
|
memset(&name_tmp, 0, sizeof(struct utsname));
|
|
if (uname(&name_tmp) < 0) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"failed to read kernel version");
|
|
return FALSE;
|
|
}
|
|
if (fu_version_compare(name_tmp.release, minimum_kernel, FWUPD_VERSION_FORMAT_TRIPLET) <
|
|
0) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"kernel %s doesn't meet minimum %s",
|
|
name_tmp.release,
|
|
minimum_kernel);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
#else
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"platform doesn't support checking for minimum Linux kernel");
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* fu_kernel_get_firmware_search_path:
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Reads the FU_PATH_KIND_FIRMWARE_SEARCH and
|
|
* returns its contents
|
|
*
|
|
* Returns: a pointer to a gchar array
|
|
*
|
|
* Since: 1.8.2
|
|
**/
|
|
gchar *
|
|
fu_kernel_get_firmware_search_path(GError **error)
|
|
{
|
|
gsize sz = 0;
|
|
g_autofree gchar *sys_fw_search_path = NULL;
|
|
g_autofree gchar *contents = NULL;
|
|
|
|
sys_fw_search_path = fu_path_from_kind(FU_PATH_KIND_FIRMWARE_SEARCH);
|
|
if (!g_file_get_contents(sys_fw_search_path, &contents, &sz, error))
|
|
return NULL;
|
|
|
|
/* sanity check */
|
|
if (contents == NULL || sz == 0) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"failed to get firmware search path from %s",
|
|
sys_fw_search_path);
|
|
return NULL;
|
|
}
|
|
|
|
/* remove newline character */
|
|
if (contents[sz - 1] == '\n')
|
|
contents[sz - 1] = 0;
|
|
|
|
g_debug("read firmware search path (%" G_GSIZE_FORMAT "): %s", sz, contents);
|
|
|
|
return g_steal_pointer(&contents);
|
|
}
|
|
|
|
/**
|
|
* fu_kernel_set_firmware_search_path:
|
|
* @path: NUL-terminated string
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Writes path to the FU_PATH_KIND_FIRMWARE_SEARCH
|
|
*
|
|
* Returns: %TRUE if successful
|
|
*
|
|
* Since: 1.8.2
|
|
**/
|
|
gboolean
|
|
fu_kernel_set_firmware_search_path(const gchar *path, GError **error)
|
|
{
|
|
#if GLIB_CHECK_VERSION(2, 66, 0)
|
|
g_autofree gchar *sys_fw_search_path_prm = NULL;
|
|
|
|
g_return_val_if_fail(path != NULL, FALSE);
|
|
g_return_val_if_fail(strlen(path) < PATH_MAX, FALSE);
|
|
|
|
sys_fw_search_path_prm = fu_path_from_kind(FU_PATH_KIND_FIRMWARE_SEARCH);
|
|
|
|
g_debug("writing firmware search path (%" G_GSIZE_FORMAT "): %s", strlen(path), path);
|
|
|
|
return g_file_set_contents_full(sys_fw_search_path_prm,
|
|
path,
|
|
strlen(path),
|
|
G_FILE_SET_CONTENTS_NONE,
|
|
0644,
|
|
error);
|
|
#else
|
|
FILE *fd;
|
|
gsize res;
|
|
g_autofree gchar *sys_fw_search_path_prm = NULL;
|
|
|
|
g_return_val_if_fail(path != NULL, FALSE);
|
|
g_return_val_if_fail(strlen(path) < PATH_MAX, FALSE);
|
|
|
|
sys_fw_search_path_prm = fu_path_from_kind(FU_PATH_KIND_FIRMWARE_SEARCH);
|
|
/* g_file_set_contents will try to create backup files in sysfs, so use fopen here */
|
|
fd = fopen(sys_fw_search_path_prm, "w");
|
|
if (fd == NULL) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_PERMISSION_DENIED,
|
|
"Failed to open %s: %s",
|
|
sys_fw_search_path_prm,
|
|
g_strerror(errno));
|
|
return FALSE;
|
|
}
|
|
|
|
g_debug("writing firmware search path (%" G_GSIZE_FORMAT "): %s", strlen(path), path);
|
|
|
|
res = fwrite(path, sizeof(gchar), strlen(path), fd);
|
|
|
|
fclose(fd);
|
|
|
|
if (res != strlen(path)) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_WRITE,
|
|
"Failed to write firmware search path: %s",
|
|
g_strerror(errno));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* fu_kernel_reset_firmware_search_path:
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Resets the FU_PATH_KIND_FIRMWARE_SEARCH to an empty string
|
|
*
|
|
* Returns: %TRUE if successful
|
|
*
|
|
* Since: 1.8.2
|
|
**/
|
|
gboolean
|
|
fu_kernel_reset_firmware_search_path(GError **error)
|
|
{
|
|
const gchar *contents = " ";
|
|
|
|
return fu_kernel_set_firmware_search_path(contents, error);
|
|
}
|
|
|
|
/**
|
|
* fu_kernel_get_cmdline:
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Loads all the kernel /proc/cmdline key/values into a hash table.
|
|
*
|
|
* Returns: (transfer container) (element-type utf8 utf8): keys from the kernel command line
|
|
*
|
|
* Since: 1.8.5
|
|
**/
|
|
GHashTable *
|
|
fu_kernel_get_cmdline(GError **error)
|
|
{
|
|
#ifdef __linux__
|
|
gsize bufsz = 0;
|
|
g_autofree gchar *buf = NULL;
|
|
g_autoptr(GHashTable) hash = NULL;
|
|
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
|
|
if (!g_file_get_contents("/proc/cmdline", &buf, &bufsz, error))
|
|
return NULL;
|
|
hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
|
|
if (bufsz > 0) {
|
|
g_auto(GStrv) tokens = fu_strsplit(buf, bufsz - 1, " ", -1);
|
|
for (guint i = 0; tokens[i] != NULL; i++) {
|
|
g_auto(GStrv) kv = NULL;
|
|
if (strlen(tokens[i]) == 0)
|
|
continue;
|
|
kv = g_strsplit(tokens[i], "=", 2);
|
|
g_hash_table_insert(hash, g_strdup(kv[0]), g_strdup(kv[1]));
|
|
}
|
|
}
|
|
|
|
/* success */
|
|
return g_steal_pointer(&hash);
|
|
#else
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"platform does not support getting the kernel cmdline");
|
|
return NULL;
|
|
#endif
|
|
}
|