1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2014 Gary Mills
23 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26/*
27 * New implementation of pfexec(1) and all of the profile shells.
28 *
29 * The algorithm is as follows:
30 * 	first try to derive the shell's path from getexecname();
31 *	note that this requires a *hard* link to the program, so
32 *	if we find that we are actually executing pfexec, we start
33 *	looking at argv[0].
34 *	argv[0] is also our fallback in case getexecname doesn't find it.
35 */
36#include <sys/param.h>
37#include <alloca.h>
38#include <errno.h>
39#include <locale.h>
40#include <priv.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <unistd.h>
45
46#define	PFEXEC	"pfexec"
47#ifndef TEXT_DOMAIN
48#define	TEXT_DOMAIN	"SYS_TEST"
49#endif
50
51#define	RES_PFEXEC	1
52#define	RES_OK		0
53#define	RES_FAILURE	-1
54
55/*
56 * Return the shellname
57 */
58int
59shellname(const char *name, char buf[MAXPATHLEN])
60{
61	const char *cmd = strrchr(name, '/');
62
63	if (cmd == NULL)
64		cmd = name;
65	else
66		cmd++;
67
68	if (strncmp(cmd, "pf", 2) != 0)
69		return (RES_FAILURE);
70
71	if (strcmp(cmd, PFEXEC) == 0)
72		return (RES_PFEXEC);
73
74	if (strlen(name) >= MAXPATHLEN)
75		return (RES_FAILURE);
76
77	if (cmd == name) {
78		(void) strlcpy(buf, cmd + 2, MAXPATHLEN);
79	} else {
80		(void) strncpy(buf, name, cmd - name);
81		(void) strcpy(buf + (cmd - name), cmd + 2);
82	}
83	return (RES_OK);
84
85}
86
87static void
88usage(void)
89{
90	(void) fprintf(stderr, gettext("pfexec [-P privset] cmd [arg ..]\n"));
91	exit(EXIT_FAILURE);
92}
93
94int
95main(int argc, char **argv)
96{
97	char *cmd;
98	char *pset = NULL;
99	char pathbuf[MAXPATHLEN];
100	int c;
101	priv_set_t *wanted;
102	int oflag;
103
104	oflag = getpflags(PRIV_PFEXEC);
105	if (setpflags(PRIV_PFEXEC, 1) != 0) {
106		(void) fprintf(stderr,
107		    gettext("pfexec: unable to set PFEXEC flag: %s\n"),
108		    strerror(errno));
109		exit(1);
110	}
111
112	if (*argv[0] == '-')
113		cmd = argv[0] + 1;
114	else
115		cmd = argv[0];
116
117	/* Strip "pf" from argv[0], it confuses some shells. */
118	if (strncmp(cmd, "pf", 2) == 0) {
119		argv[0] += 2;
120		/* argv[0] will need to start with '-' again. */
121		if (argv[0][-2] == '-')
122			*argv[0] = '-';
123	}
124
125	/* If this fails, we just continue with plan B */
126	if (shellname(getexecname(), pathbuf) == RES_OK)
127		(void) execv(pathbuf, argv);
128
129	switch (shellname(cmd, pathbuf)) {
130	case RES_OK:
131		(void) execv(pathbuf, argv);
132		(void) fprintf(stderr,
133		    gettext("pfexec: unable to execute %s: %s\n"),
134		    pathbuf, strerror(errno));
135		return (1);
136	case RES_PFEXEC:
137	case RES_FAILURE:
138		while ((c = getopt(argc, argv, "P:")) != EOF) {
139			switch (c) {
140			case 'P':
141				if (pset == NULL) {
142					pset = optarg;
143					break;
144				}
145				/* FALLTHROUGH */
146			default:
147				usage();
148			}
149		}
150		argc -= optind;
151		argv += optind;
152		if (argc < 1)
153			usage();
154
155		if (pset != NULL) {
156			if ((wanted = priv_str_to_set(pset, ",", NULL)) ==
157			    NULL) {
158				(void) fprintf(stderr,
159				    gettext("pfexec: error parsing "
160				    "privileges: %s\n"), strerror(errno));
161				exit(EXIT_FAILURE);
162			}
163			if (setppriv(PRIV_ON, PRIV_INHERITABLE, wanted) != 0) {
164				(void) fprintf(stderr,
165				    gettext("pfexec: error setting "
166				    "privileges: %s\n"), strerror(errno));
167				exit(EXIT_FAILURE);
168			}
169			(void) setpflags(PRIV_PFEXEC, oflag);
170		}
171
172		(void) execvp(argv[0], argv);
173		(void) fprintf(stderr,
174		    gettext("pfexec: unable to execute %s: %s\n"),
175		    argv[0], strerror(errno));
176		return (1);
177	}
178	return (1);
179}
180