1

I want to let my user edit a configuration file in /etc from my graphical C application. One method is:

fprintf( fopen("/tmp/tmpfile", "w+") , "content\n"); // Write to a tmpfile
system("pkexec mv /tmp/tmpfile /etc/myapp.conf");    // Use pkexec to move that tmpfile to our file

However, I'd like to implement a polkit client (as what I have to do is a little more complicated than a single write).This will cause the desktop's polkit agent to pop-up asking to authenticate as the administrator (root). Once authenticated, it will perform the action, then return to unprivileged use.

I've implemented client reference API and added /usr/share/polkit-1/actions up until the point where this function (simplified version here) returns TRUE;

int IsAuthorized(const char* action) {
    PolkutAuthorizationResult* r = polkit_authority_check_authorization_sync(
        polkit_authority_get_sync(NULL, &error),
        polkit_unix_process_new_for_owner( getpid(), 0, getuid() ),
        "org.myapp.editconfig",
        NULL, /* details */
        POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
        NULL, /* cancellable */
        &error
    );
    return polkit_authorization_result_get_is_authorized( r );
}

This authorizes this action:

<action id="org.myapp.editconfig">
  <description>Edit myapp config</description>
  <message>Authentication is required to edit this system-wide config file</message>
  <defaults>
    <allow_any>auth_admin</allow_any>
    <allow_inactive>auth_admin</allow_inactive>
    <allow_active>auth_admin</allow_active>
  </defaults>
</action>

But my question is: now what? I'm not sure what "being authorized" did for me. If I try to open a root:root file for writing, I get permission denied. If I create a new file, it's owned by the user, not root.

Looking into the sources of pkexec I see it changes the uid/gid of itself. Does authentication simply give CAP_SETUID and CAP_SETGID to the process?

Assuming this is true, I tried to use setreuid() and setregid():

int uid = getuid();    int euid = geteuid();
int gid = getgid();    int egid = getegid();

setregid(0,0);  // Switch to root
setreuid(0,0);

fprintf( fopen("/etc/myapp.conf", "w+"), "content\n");  // Write the file

setregid(gid,egid);  // Switch back
setreuid(uid,euid);

I find that setreuid(0,0) fails and I never switch users like pkexec does. Note I skipped a few things pkexec does such as set the environment, and set_close_exec for all file descriptors.

I must be missing something.

1 Answer 1

3

polkit_authority_check_authorization_sync simply checks whether the caller is authorized to perform the action based on the polkit rules and that's it. This usually means an application/daemon that is already running as root wants to check whether the caller is actually allowed to perform certain action and then performs it on the behalf of the caller. Polkit itself doesn't grant any extra permission, your application already needs to be able to perform the action. pkexec <command> works because pkexec is a setuid binary so the process runs as root and only checks if you are authorized to run the command you are trying to run.

2
  • Wow. I totally misunderstood polkit, but that makes sense. I completely missed the setuid bit. It looks like I'll need to put my logic into a smaller binary or script and call pkexec on it. Reading [man pkexec(1)](https://manpages.debian.org/experimental/policykit-1/pkexec.1.en.html) I see that I can still add custom actions so I can customize the message to the user when the polkit-agent appears.
    – Stewart
    Commented Feb 5, 2021 at 15:36
  • You can also simply run the entire app as root with pkexec, it's not recommended but if it's something small, it could be the easiest way. GParted does this -- gparted binary is just a small bash script that basically runs pkexec gpartedbin and has its own simple polkit rule to allow that. Commented Feb 5, 2021 at 15:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .