Welcome to little lamb

Code » slicd » master » tree

[master] / src / extra / setuid.c

/*
 * slicd - Copyright (C) 2016 Olivier Brunel
 *
 * setuid.c
 * Copyright (C) 2016 Olivier Brunel <jjk@jjacky.com>
 *
 * This file is part of slicd.
 *
 * slicd is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * slicd is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * slicd. If not, see http://www.gnu.org/licenses/
 */

#define _BSD_SOURCE

#include "slicd/config.h"

#include <errno.h>
#include <getopt.h>
#include <sys/types.h>
#include <limits.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <skalibs/djbunix.h>
#include <skalibs/bytestr.h>
#include <skalibs/exec.h>
#include <skalibs/strerr2.h>
#include <slicd/die.h>

enum
{
    RC_OK = 0,
    RC_SYNTAX = 131,
    RC_IO,
    RC_MEMORY,
    RC_UNKNOWN,
    RC_OTHER
};

static void
dieusage (int rc)
{
    slicd_die_usage (rc, "USERNAME PROG...",
            " -h, --help                    Show this help screen and exit\n"
            " -V, --version                 Show version information and exit\n"
            );
}

int
main (int argc, char * const argv[])
{
    PROG = "setuid";
    struct passwd *pw;

    for (;;)
    {
        struct option longopts[] = {
            { "help",               no_argument,        NULL,   'h' },
            { "version",            no_argument,        NULL,   'V' },
            { NULL, 0, 0, 0 }
        };
        int c;

        c = getopt_long (argc, argv, "+hV", longopts, NULL);
        if (c == -1)
            break;
        switch (c)
        {
            case 'h':
                dieusage (RC_OK);

            case 'V':
                slicd_die_version ();

            default:
                dieusage (RC_SYNTAX);
        }
    }
    argc -= optind;
    argv += optind;

    if (argc < 2)
        dieusage (RC_SYNTAX);

    pw = getpwnam (argv[0]);
    if (!pw)
        strerr_dief2x (RC_UNKNOWN, "unknown user: ", argv[0]);

    {
        gid_t gids[NGROUPS_MAX];
        int n = 0;

        for (;;)
        {
            struct group *gr;
            register char **member;

            errno = 0;
            gr = getgrent ();
            if (!gr)
                break;
            for (member = gr->gr_mem; *member; ++member)
                if (str_equal (*member, argv[0]))
                    break;
            if (*member)
                gids[n++] = gr->gr_gid;
        }
        endgrent ();
        if (errno != 0)
            strerr_diefu2sys (RC_OTHER, "get supplementary groups for ", argv[0]);
        if (setgroups (n, gids) < 0)
            strerr_diefu2sys (RC_OTHER, "set supplementary groups for ", argv[0]);
    }
    if (setgid (pw->pw_gid) < 0)
        strerr_diefu1sys (RC_OTHER, "setgid");
    if (setuid (pw->pw_uid) < 0)
        strerr_diefu1sys (RC_OTHER, "setuid");

    ++argv;
    mexec ((char const * const *) argv);
    strerr_dieexec (RC_IO, argv[0]);
}