/*
 * 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);
}