Much tasks can be executed with the regular user privileges, but nevertheless, there are some tasks that require the user to provide access to system files and utilities that require the user to allow the application to execute under a privileged user rights. Linux provides several ways to solve this issue in graphical mode:

  • Setting a sticky bit and root privileges for group
  • Configuring PAM authorization
  • Using gksu, ksu or any other graphical shells
  • Passing a password to the su or sudo utility

As you can see, only one option can be used by not pre-installed software, - passing a password to the system authorization utility. Following code shows how to execute user command with root privileges by providing login information from regular user account:

/*
 * The Open Source Unix Installer
 *
 * The MIT License
 *
 * Copyright 2011-2013 Andrey Pudov.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/*
 * The shell representation allows to execute shell utilities.
 *
 * @author  Andrey Pudov    <andrey@andreypudov.com>
 * @version 0.00.00
 * @name    shell.h
 * @date:   Feb 22, 2013
 */

#ifndef INSTALLER_UTIL_SHELL_H_
#define INSTALLER_UTIL_SHELL_H_

#define _BSD_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <pty.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/wait.h>

#include "core.h"

#ifdef __cplusplus
extern "C" {
#endif

extern const int AUTH_SUCCESS;
extern const int AUTH_FAILURE;

typedef struct _Shell Shell;

struct _Shell {
    /* the file descriptor for the attached pty */
    int pty;
};

/* creates a new property list */
Shell* installer_shell_new();
void installer_shell_delete(Shell *shell);

/* execute shell command */
int installer_shell_execute(Shell *shell, const char *login, 
                             const char *password, const char *command);

#ifdef __cplusplus
}
#endif

#endif /* INSTALLER_UTIL_SHELL_H_ */
          

Here is the code of installer_shell_execute() function. You can invoke this function by providing newly created Shell object, login name, password and required command to execute. Function will return one of the execution status code: AUTH_SUCCESS in case of successful execution and AUTH_FAILURE in case of authorization or execution issue.

/*
 * The Open Source Unix Installer
 *
 * The MIT License
 *
 * Copyright 2011-2013 Andrey Pudov.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/*
 * @author  Andrey Pudov    <andrey@andreypudov.com>
 * @version 0.00.00
 * @name    shell.c
 * @date:   Feb 22, 2013
 */

#include "shell.h"

/* private code declaration */
const int AUTH_SUCCESS = 0;
const int AUTH_FAILURE = 1;

/* public code definition */
Shell* installer_shell_new() {
    Logger *logger = installer_core_get_logger();
    
    Shell *shell = (Shell*) malloc(sizeof(Shell));
    if (shell == NULL) {
        installer_logger_log(logger, SEVERE, "shell", 
                              "Can't create new shell");
        exit(EXIT_FAILURE);
    }
    
    shell->pty = 0;
    
    return shell;
}

void installer_shell_delete(Shell *shell) {
    if (shell != NULL) {
        free(shell);
    }
}

int installer_shell_execute(Shell *shell, const char *login, 
                             const char *password, const char *command) {
    Logger *logger = installer_core_get_logger();

    char  buffer[128] = {0};

    /* p - do not reset environment variables */
    /* c - pass a single command to the shell */
    const char *command_list[] = {"/bin/su", login, "-p", "-c", 
                                  command, (char*) NULL};

    int   status = 1;
    pid_t cpid   = 0;

    fd_set set;

    const struct timespec limit = {.tv_sec = 0, .tv_nsec = 10000};

    if ((shell == NULL) || (login == NULL) || (password == NULL) 
        || (command == NULL)) {
            installer_logger_log(logger, SEVERE, "shell", 
                                 "Invalid arguments exception");
            return (AUTH_FAILURE);
    }

    /* create new terminal */
    if ((cpid = forkpty(&(shell->pty), NULL, NULL, NULL)) < 0) {
        installer_logger_log(logger, SEVERE, "shell", 
                             "Can't create new tty");
        return (AUTH_FAILURE);
    }

    if (cpid == 0) {
        /* run a program in a new session */
        execv(command_list[0], (char**) command_list);

        installer_logger_log(logger, SEVERE, "shell", 
                             "Can't create new session");
        return (AUTH_FAILURE);
    }

    for (int index = 16; index && status; --index) {
        FD_ZERO(&set);
        FD_SET(shell->pty, &set);

        /* select ready descriptor */
        if (pselect(shell->pty + 1, &set, NULL, NULL, &limit, NULL) < 0) {
            {installer_logger_log(logger, SEVERE, "shell", 
                                  "Can't select descriptor");
            exit(EXIT_FAILURE);}
        }

        if (FD_ISSET(shell->pty, &set)) {
            read(shell->pty, buffer, sizeof(buffer) - 1);
            status = 0;
        }

        /* sleep a specified number of microsecondses */
        usleep(1000);
    }

    /* check if timout is riched */
    if (status) {
        kill(cpid, SIGKILL);

        installer_logger_log(logger, SEVERE, "shell", 
                             "Login shell is not riched");
        return (AUTH_FAILURE);
    }

    /* pass the password */
    snprintf(buffer, 64, "%s\n", password);
    write(shell->pty, buffer, strlen(buffer));

    while (!waitpid(cpid, &status, WNOHANG)) {
        FD_ZERO(&set);
        FD_SET(shell->pty, &set);
        FD_SET(STDIN_FILENO, &set);

        if (pselect(((shell->pty > STDIN_FILENO) 
                ? shell->pty : STDIN_FILENO) + 1,
                &set, NULL, NULL, &limit, NULL) < 0) {
            installer_logger_log(logger, SEVERE, "shell", 
                                 "Can't select terminal description");
            return (AUTH_FAILURE);
        }

        if (FD_ISSET(shell->pty, &set)) {
            status = read(shell->pty, buffer, sizeof(buffer) - 1);
            write(STDOUT_FILENO, buffer, status);
        } else if (FD_ISSET(STDIN_FILENO, &set)) {
            status = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
            write(shell->pty, buffer, status);
        }

        usleep(100);
    }

    /* send EOF to the child process */
    close(shell->pty);

    /* exit status of a child process */
    if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) {
        sprintf(buffer, "Child process exited abnormally (exit code = %d)", 
                status);
        installer_logger_log(logger, SEVERE, "shell", buffer);

        return (AUTH_FAILURE);
    }

    return (AUTH_SUCCESS);
}
          

The following code shows a simple example of using described authorization technique. You have to change a password and sample command to try it by yourself.

Shell *shell = installer_shell_new();
    
const char *login    = "root";
const char *password = "password";
const char *command  = "echo $(whoami) > /tmp/status";

int  unit = 0;

installer_shell_execute(shell, login, password, command);
unit = installer_shell_execute(shell, login, password, command);
if ((unit != AUTH_SUCCESS) || (errno != 0)) {
    fprintf(stderr, 
            "installer_shell_execute: execution or authorization failure.");
        
    exit(EXIT_FAILURE);
}