From d55ed19fe1a8ac759dfda4f4dcce2debdd71d6a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20K=C3=A4llstr=C3=B6m?= Date: Sun, 28 Aug 2022 14:16:49 +0200 Subject: [PATCH] suid and apparmor support in linux The newer Ryzen controller does not like to be run as root (sandbox complain). Better run the application as a normal user and let the user space binary run as root if launched via the ryzen controller app and user bellongs to the ryzenadj group. --- README.md | 18 ++++++ debian/apparmor/usr.bin.ryzenadj | 49 ++++++++++++++++ debian/control | 2 + debian/rules | 5 ++ debian/ryzenadj.install | 1 + debian/ryzenadj.postinst | 14 +++++ debian/ryzenadj.preinst | 18 ++++++ main.c | 99 ++++++++++++++++++++++++++++++++ 8 files changed, 206 insertions(+) create mode 100644 debian/apparmor/usr.bin.ryzenadj create mode 100644 debian/ryzenadj.postinst create mode 100644 debian/ryzenadj.preinst diff --git a/README.md b/README.md index a931258e..32a51225 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,20 @@ The simplest way to build it: cmake -DCMAKE_BUILD_TYPE=Release .. make +modify permissions for executing as root when started from Ryzen controller +and user belongs to the ryzenadj group. + + sudo chow root:root ./ryzenadj + sudo chmod u+s ./ryzenadj + +add system group for RyzenAdj authentication + + getent group ryzenadj || sudo addgroup --quiet --system ryzenadj + +add allowed users to ryzenadj group + + sudo usermod -aG ryzenadj $USER + ### Package build On Debian/Ubuntu: @@ -135,6 +149,10 @@ On Debian/Ubuntu: dpkg-buildpackage -us -uc sudo dpkg -i ../ryzenadj_*.deb ../libryzenadj0_*.deb +add allowed users to ryzenadj group + + sudo usermod -aG ryzenadj $USER + ### Windows It can be built by Visual Studio + MSVC automaticaly, or Clang + Nmake in command line. diff --git a/debian/apparmor/usr.bin.ryzenadj b/debian/apparmor/usr.bin.ryzenadj new file mode 100644 index 00000000..c7ead37a --- /dev/null +++ b/debian/apparmor/usr.bin.ryzenadj @@ -0,0 +1,49 @@ +abi , + +include + +/usr/bin/ryzenadj flags=(enforce) { + include + include + include + include if exists + + capability sys_admin, + capability sys_rawio, + + # Needed for some files in /proc see + # https://gitlab.com/apparmor/apparmor/-/wikis/TechnicalDoc_Proc_and_ptrace#apparmor-3-with-ptrace-rules + # https://bugs.launchpad.net/ubuntu/+source/gpsd/+bug/1872175 + capability sys_ptrace, + ptrace read peer=unconfined, + + /etc/ld.so.cache r, + /lib/@{multiarch}/ld-*.so* mr, + /lib/@{multiarch}/libc-*.so* mr, + /lib/@{multiarch}/libgcc_s.so* mr, + /lib/@{multiarch}/libld-*.so* mr, + /lib/libgcc_s.so* mr, + /lib{,32,64}/ld-*.so* mr, + /lib{,32,64}/libc-*.so* mr, + /lib{,32,64}/libld-*.so* mr, + /usr/bin/ryzenadj mr, + /usr/lib/@{multiarch}/libpci.so* mr, + /usr/lib/@{multiarch}/libudev.so* mr, + /usr/lib/@{multiarch}/libz.so* mr, + /usr/lib/libpci.so* mr, + /usr/lib/libudev.so* mr, + /usr/lib/libz.so* mr, + @{PROC}/ r, + @{PROC}/[0-9]*/ r, + @{PROC}/[0-9]*/exe r, + + @{sys}/kernel/ryzen_smu_drv/pm_table r, + /dev/mem r, + + @{sys}/devices/pci0000:00/0000:00:00.0/class r, + @{sys}/devices/pci0000:00/0000:00:00.0/config rw, + @{sys}/devices/pci0000:00/0000:00:00.0/device r, + @{sys}/devices/pci0000:00/0000:00:00.0/resource r, + @{sys}/devices/pci0000:00/0000:00:00.0/vendor r, + +} diff --git a/debian/control b/debian/control index 2644538b..cb6f94fd 100644 --- a/debian/control +++ b/debian/control @@ -5,6 +5,7 @@ Maintainer: Johan Källström Uploaders: Johan Källström , Build-Depends: cmake (>= 3.0~), debhelper-compat (= 13), + dh-apparmor, libpci-dev Standards-Version: 4.6.0 Homepage: https://github.com/FlyGoat/RyzenAdj/ @@ -18,6 +19,7 @@ Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, libryzenadj0 (= ${binary:Version}), +Pre-Depends: adduser (>= 3.11), Description: Adjust power management settings for Ryzen Mobile Processors. This is a commandline tool that allows you to adjust power management settings for Ryzen Mobile Processors. diff --git a/debian/rules b/debian/rules index 067e6691..868d96f2 100755 --- a/debian/rules +++ b/debian/rules @@ -12,3 +12,8 @@ override_dh_auto_configure: dh_auto_configure -- \ -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) \ -DCMAKE_BUILD_TYPE=RelWithDebInfo + + +override_dh_install: + dh_install + dh_apparmor -p ryzenadj --profile-name=usr.bin.ryzenadj diff --git a/debian/ryzenadj.install b/debian/ryzenadj.install index dc921803..f1dbfe3e 100644 --- a/debian/ryzenadj.install +++ b/debian/ryzenadj.install @@ -1 +1,2 @@ usr/bin/ryzenadj +debian/apparmor/usr.bin.ryzenadj etc/apparmor.d diff --git a/debian/ryzenadj.postinst b/debian/ryzenadj.postinst new file mode 100644 index 00000000..f70bfe30 --- /dev/null +++ b/debian/ryzenadj.postinst @@ -0,0 +1,14 @@ +#!/bin/sh +# postinst script for #PACKAGE# + +case "$1" in + configure) + # Set setuid root on usr/bin/ryzenadj + chmod u+s /usr/bin/ryzenadj || ( echo 'Error: failed to setuid root on usr/bin/ryzenadj' >&2 ) + ;; + + *) + ;; +esac + +#DEBHELPER# diff --git a/debian/ryzenadj.preinst b/debian/ryzenadj.preinst new file mode 100644 index 00000000..e0f6197d --- /dev/null +++ b/debian/ryzenadj.preinst @@ -0,0 +1,18 @@ +#!/bin/sh +# preinst script for #PACKAGE# +set -e +RYZENADJ_GROUP=ryzenadj + +case "$1" in + install|upgrade) + # create group for authenticating users. + if ! getent group $RYZENADJ_GROUP 2>&1 >/dev/null ; then + echo "Adding group $RYZENADJ_GROUP.." + addgroup --quiet --system $RYZENADJ_GROUP + fi + ;; + *) + ;; +esac + +#DEBHELPER# diff --git a/main.c b/main.c index 25f48348..4de6fcb0 100644 --- a/main.c +++ b/main.c @@ -3,6 +3,13 @@ /* Ryzen NB SMU Service Request Tool */ #include + +#ifndef _WIN32 +#include +#include +#include +#endif + #include "lib/ryzenadj.h" #include "argparse.h" @@ -181,6 +188,90 @@ static void show_table_dump(ryzen_access ry, int any_adjust_applied) //don't free current_table_values because this would deinitialize our table } +// Check if launched from ryzen-controller +static int check_parent() { +#ifndef _WIN32 + pid_t ppid = getppid(); + char pathname[512]; + char buf[512]; + char gt[38] = "/opt/Ryzen Controller/ryzen-controller"; + int cx; + cx = snprintf(pathname, 512, "/proc/%d/exe", ppid); + + if (cx >= 0 && cx < 512) { + ssize_t l = readlink(pathname, buf, 512); + if (l > 0) { + if (strncmp(buf, gt, 38) == 0) { + return 1; + } + } + } +#endif + return 0; +} + +// Are we launched via a root user? +static int is_user_root() { +#ifndef _WIN32 + uid_t uid = getuid(); + return uid == 0; +#else + return 1; +#endif +} + +// Are we in ryzenadj group? +static int is_in_ryzenadj_group() { +#ifndef _WIN32 + int ngroups = 0; + uid_t uid; + struct passwd *pw; + struct group *gr_allow; + gr_allow = getgrnam("ryzenadj"); + + if (gr_allow == NULL) { + fprintf(stderr, "getgrnam(ryzenadj): group does not exsist\n"); + return 0; + } + + uid = getuid(); + pw = getpwuid(uid); + if (pw == NULL) { + fprintf(stderr, "getpwuid(uid): failed\n"); + exit(EXIT_FAILURE); + } + + /* Retrieve ngroups. */ + getgrouplist(pw->pw_name, pw->pw_gid, NULL, &ngroups); + + gid_t *groups = malloc(sizeof(*groups) * ngroups); + if (groups == NULL) { + perror("malloc"); + exit(EXIT_FAILURE); + } + + /* Retrieve group list. */ + if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups) == -1) { + fprintf(stderr, "getgrouplist() failed; ngroups = %d\n", ngroups); + exit(EXIT_FAILURE); + } + + /* Check if we are in allowed group */ + int found = 0; + for (int i = 0; i < ngroups; ++i) { + if (gr_allow->gr_gid == groups[i]) { + found = 1; + break; + } + } + free(groups); + return found; +#else + return 1; +#endif +} + + int main(int argc, const char **argv) { @@ -198,6 +289,14 @@ int main(int argc, const char **argv) uint32_t skin_temp_power_limit = -1; uint32_t gfx_clk = -1, oc_clk = -1, oc_volt = -1, coall = -1, coper = -1, cogfx = -1; + // Check if we run are lauched root or launched from ryzen-controller + // and the user is in the ryzenadj group. Exit if user is not not. + // As we want to limit the setuid bit capabilities. + if (!(is_user_root() || (check_parent() && is_in_ryzenadj_group()))) { + printf("Need root for ryzenadj\n"); + return -1; + } + //create structure for parseing struct argparse_option options[] = { OPT_HELP(),