root/usr/src/cmd/pfexec/pfexec.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2014 Gary Mills
 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * New implementation of pfexec(1) and all of the profile shells.
 *
 * The algorithm is as follows:
 *      first try to derive the shell's path from getexecname();
 *      note that this requires a *hard* link to the program, so
 *      if we find that we are actually executing pfexec, we start
 *      looking at argv[0].
 *      argv[0] is also our fallback in case getexecname doesn't find it.
 */
#include <sys/param.h>
#include <alloca.h>
#include <errno.h>
#include <locale.h>
#include <priv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define PFEXEC  "pfexec"
#ifndef TEXT_DOMAIN
#define TEXT_DOMAIN     "SYS_TEST"
#endif

#define RES_PFEXEC      1
#define RES_OK          0
#define RES_FAILURE     -1

/*
 * Return the shellname
 */
int
shellname(const char *name, char buf[MAXPATHLEN])
{
        const char *cmd = strrchr(name, '/');

        if (cmd == NULL)
                cmd = name;
        else
                cmd++;

        if (strncmp(cmd, "pf", 2) != 0)
                return (RES_FAILURE);

        if (strcmp(cmd, PFEXEC) == 0)
                return (RES_PFEXEC);

        if (strlen(name) >= MAXPATHLEN)
                return (RES_FAILURE);

        if (cmd == name) {
                (void) strlcpy(buf, cmd + 2, MAXPATHLEN);
        } else {
                (void) strncpy(buf, name, cmd - name);
                (void) strcpy(buf + (cmd - name), cmd + 2);
        }
        return (RES_OK);

}

static void
usage(void)
{
        (void) fprintf(stderr, gettext("pfexec [-P privset] cmd [arg ..]\n"));
        exit(EXIT_FAILURE);
}

int
main(int argc, char **argv)
{
        char *cmd;
        char *pset = NULL;
        char pathbuf[MAXPATHLEN];
        int c;
        priv_set_t *wanted;
        int oflag;

        oflag = getpflags(PRIV_PFEXEC);
        if (setpflags(PRIV_PFEXEC, 1) != 0) {
                (void) fprintf(stderr,
                    gettext("pfexec: unable to set PFEXEC flag: %s\n"),
                    strerror(errno));
                exit(1);
        }

        if (*argv[0] == '-')
                cmd = argv[0] + 1;
        else
                cmd = argv[0];

        /* Strip "pf" from argv[0], it confuses some shells. */
        if (strncmp(cmd, "pf", 2) == 0) {
                argv[0] += 2;
                /* argv[0] will need to start with '-' again. */
                if (argv[0][-2] == '-')
                        *argv[0] = '-';
        }

        /* If this fails, we just continue with plan B */
        if (shellname(getexecname(), pathbuf) == RES_OK)
                (void) execv(pathbuf, argv);

        switch (shellname(cmd, pathbuf)) {
        case RES_OK:
                (void) execv(pathbuf, argv);
                (void) fprintf(stderr,
                    gettext("pfexec: unable to execute %s: %s\n"),
                    pathbuf, strerror(errno));
                return (1);
        case RES_PFEXEC:
        case RES_FAILURE:
                while ((c = getopt(argc, argv, "P:")) != EOF) {
                        switch (c) {
                        case 'P':
                                if (pset == NULL) {
                                        pset = optarg;
                                        break;
                                }
                                /* FALLTHROUGH */
                        default:
                                usage();
                        }
                }
                argc -= optind;
                argv += optind;
                if (argc < 1)
                        usage();

                if (pset != NULL) {
                        if ((wanted = priv_str_to_set(pset, ",", NULL)) ==
                            NULL) {
                                (void) fprintf(stderr,
                                    gettext("pfexec: error parsing "
                                    "privileges: %s\n"), strerror(errno));
                                exit(EXIT_FAILURE);
                        }
                        if (setppriv(PRIV_ON, PRIV_INHERITABLE, wanted) != 0) {
                                (void) fprintf(stderr,
                                    gettext("pfexec: error setting "
                                    "privileges: %s\n"), strerror(errno));
                                exit(EXIT_FAILURE);
                        }
                        (void) setpflags(PRIV_PFEXEC, oflag);
                }

                (void) execvp(argv[0], argv);
                (void) fprintf(stderr,
                    gettext("pfexec: unable to execute %s: %s\n"),
                    argv[0], strerror(errno));
                return (1);
        }
        return (1);
}