1/*
2 * Copyright (c) 2006 Chad Mynhier.
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "config.h"
18#include "includes.h"
19
20#ifdef USE_SOLARIS_PROCESS_CONTRACTS
21
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <sys/param.h>
25
26#include <errno.h>
27#ifdef HAVE_FCNTL_H
28# include <fcntl.h>
29#endif
30#include <stdarg.h>
31#include <string.h>
32#include <unistd.h>
33
34#include <libcontract.h>
35#include <sys/contract/process.h>
36#include <sys/ctfs.h>
37
38#include "log.h"
39
40#define CT_TEMPLATE	CTFS_ROOT "/process/template"
41#define CT_LATEST	CTFS_ROOT "/process/latest"
42
43static int tmpl_fd = -1;
44
45/* Lookup the latest process contract */
46static ctid_t
47get_active_process_contract_id(void)
48{
49	int stat_fd;
50	ctid_t ctid = -1;
51	ct_stathdl_t stathdl;
52
53	if ((stat_fd = open64(CT_LATEST, O_RDONLY)) == -1) {
54		error("%s: Error opening 'latest' process "
55		    "contract: %s", __func__, strerror(errno));
56		return -1;
57	}
58	if (ct_status_read(stat_fd, CTD_COMMON, &stathdl) != 0) {
59		error("%s: Error reading process contract "
60		    "status: %s", __func__, strerror(errno));
61		goto out;
62	}
63	if ((ctid = ct_status_get_id(stathdl)) < 0) {
64		error("%s: Error getting process contract id: %s",
65		    __func__, strerror(errno));
66		goto out;
67	}
68
69	ct_status_free(stathdl);
70 out:
71	close(stat_fd);
72	return ctid;
73}
74
75void
76solaris_contract_pre_fork(void)
77{
78	if ((tmpl_fd = open64(CT_TEMPLATE, O_RDWR)) == -1) {
79		error("%s: open %s: %s", __func__,
80		    CT_TEMPLATE, strerror(errno));
81		return;
82	}
83
84	debug2("%s: setting up process contract template on fd %d",
85	    __func__, tmpl_fd);
86
87	/* First we set the template parameters and event sets. */
88	if (ct_pr_tmpl_set_param(tmpl_fd, CT_PR_PGRPONLY) != 0) {
89		error("%s: Error setting process contract parameter set "
90		    "(pgrponly): %s", __func__, strerror(errno));
91		goto fail;
92	}
93	if (ct_pr_tmpl_set_fatal(tmpl_fd, CT_PR_EV_HWERR) != 0) {
94		error("%s: Error setting process contract template "
95		    "fatal events: %s", __func__, strerror(errno));
96		goto fail;
97	}
98	if (ct_tmpl_set_critical(tmpl_fd, 0) != 0) {
99		error("%s: Error setting process contract template "
100		    "critical events: %s", __func__, strerror(errno));
101		goto fail;
102	}
103	if (ct_tmpl_set_informative(tmpl_fd, CT_PR_EV_HWERR) != 0) {
104		error("%s: Error setting process contract template "
105		    "informative events: %s", __func__, strerror(errno));
106		goto fail;
107	}
108
109	/* Now make this the active template for this process. */
110	if (ct_tmpl_activate(tmpl_fd) != 0) {
111		error("%s: Error activating process contract "
112		    "template: %s", __func__, strerror(errno));
113		goto fail;
114	}
115	return;
116
117 fail:
118	if (tmpl_fd != -1) {
119		close(tmpl_fd);
120		tmpl_fd = -1;
121	}
122}
123
124void
125solaris_contract_post_fork_child()
126{
127	debug2("%s: clearing process contract template on fd %d",
128	    __func__, tmpl_fd);
129
130	/* Clear the active template. */
131	if (ct_tmpl_clear(tmpl_fd) != 0)
132		error("%s: Error clearing active process contract "
133		    "template: %s", __func__, strerror(errno));
134
135	close(tmpl_fd);
136	tmpl_fd = -1;
137}
138
139void
140solaris_contract_post_fork_parent(pid_t pid)
141{
142	ctid_t ctid;
143	char ctl_path[256];
144	int r, ctl_fd = -1, stat_fd = -1;
145
146	debug2("%s: clearing template (fd %d)", __func__, tmpl_fd);
147
148	if (tmpl_fd == -1)
149		return;
150
151	/* First clear the active template. */
152	if ((r = ct_tmpl_clear(tmpl_fd)) != 0)
153		error("%s: Error clearing active process contract "
154		    "template: %s", __func__, strerror(errno));
155
156	close(tmpl_fd);
157	tmpl_fd = -1;
158
159	/*
160	 * If either the fork didn't succeed (pid < 0), or clearing
161	 * th active contract failed (r != 0), then we have nothing
162	 * more do.
163	 */
164	if (r != 0 || pid <= 0)
165		return;
166
167	/* Now lookup and abandon the contract we've created. */
168	ctid = get_active_process_contract_id();
169
170	debug2("%s: abandoning contract id %ld", __func__, ctid);
171
172	snprintf(ctl_path, sizeof(ctl_path),
173	    CTFS_ROOT "/process/%ld/ctl", ctid);
174	if ((ctl_fd = open64(ctl_path, O_WRONLY)) < 0) {
175		error("%s: Error opening process contract "
176		    "ctl file: %s", __func__, strerror(errno));
177		goto fail;
178	}
179	if (ct_ctl_abandon(ctl_fd) < 0) {
180		error("%s: Error abandoning process contract: %s",
181		    __func__, strerror(errno));
182		goto fail;
183	}
184	close(ctl_fd);
185	return;
186
187 fail:
188	if (tmpl_fd != -1) {
189		close(tmpl_fd);
190		tmpl_fd = -1;
191	}
192	if (stat_fd != -1)
193		close(stat_fd);
194	if (ctl_fd != -1)
195		close(ctl_fd);
196}
197#endif
198
199#ifdef USE_SOLARIS_PROJECTS
200#include <sys/task.h>
201#include <project.h>
202
203/*
204 * Get/set solaris default project.
205 * If we fail, just run along gracefully.
206 */
207void
208solaris_set_default_project(struct passwd *pw)
209{
210	struct project  *defaultproject;
211	struct project   tempproject;
212	char buf[1024];
213
214	/* get default project, if we fail just return gracefully  */
215	if ((defaultproject = getdefaultproj(pw->pw_name, &tempproject, &buf,
216	    sizeof(buf))) != NULL) {
217		/* set default project */
218		if (setproject(defaultproject->pj_name, pw->pw_name,
219		    TASK_NORMAL) != 0)
220			debug("setproject(%s): %s", defaultproject->pj_name,
221			    strerror(errno));
222	} else {
223		/* debug on getdefaultproj() error */
224		debug("getdefaultproj(%s): %s", pw->pw_name, strerror(errno));
225	}
226}
227#endif /* USE_SOLARIS_PROJECTS */
228
229#ifdef USE_SOLARIS_PRIVS
230# ifdef HAVE_PRIV_H
231#  include <priv.h>
232# endif
233
234priv_set_t *
235solaris_basic_privset(void)
236{
237	priv_set_t *pset;
238
239#ifdef HAVE_PRIV_BASICSET
240	if ((pset = priv_allocset()) == NULL) {
241		error("priv_allocset: %s", strerror(errno));
242		return NULL;
243	}
244	priv_basicset(pset);
245#else
246	if ((pset = priv_str_to_set("basic", ",", NULL)) == NULL) {
247		error("priv_str_to_set: %s", strerror(errno));
248		return NULL;
249	}
250#endif
251	return pset;
252}
253
254void
255solaris_drop_privs_pinfo_net_fork_exec(void)
256{
257	priv_set_t *pset = NULL, *npset = NULL;
258
259	/*
260	 * Note: this variant avoids dropping DAC filesystem rights, in case
261	 * the process calling it is running as root and should have the
262	 * ability to read/write/chown any file on the system.
263	 *
264	 * We start with the basic set, then *add* the DAC rights to it while
265	 * taking away other parts of BASIC we don't need. Then we intersect
266	 * this with our existing PERMITTED set. In this way we keep any
267	 * DAC rights we had before, while otherwise reducing ourselves to
268	 * the minimum set of privileges we need to proceed.
269	 *
270	 * This also means we drop any other parts of "root" that we don't
271	 * need (e.g. the ability to kill any process, create new device nodes
272	 * etc etc).
273	 */
274
275	if ((pset = priv_allocset()) == NULL)
276		fatal("priv_allocset: %s", strerror(errno));
277	if ((npset = solaris_basic_privset()) == NULL)
278		fatal("solaris_basic_privset: %s", strerror(errno));
279
280	if (priv_addset(npset, PRIV_FILE_CHOWN) != 0 ||
281	    priv_addset(npset, PRIV_FILE_DAC_READ) != 0 ||
282	    priv_addset(npset, PRIV_FILE_DAC_SEARCH) != 0 ||
283	    priv_addset(npset, PRIV_FILE_DAC_WRITE) != 0 ||
284	    priv_addset(npset, PRIV_FILE_OWNER) != 0)
285		fatal("priv_addset: %s", strerror(errno));
286
287	if (priv_delset(npset, PRIV_FILE_LINK_ANY) != 0 ||
288#ifdef PRIV_NET_ACCESS
289	    priv_delset(npset, PRIV_NET_ACCESS) != 0 ||
290#endif
291	    priv_delset(npset, PRIV_PROC_EXEC) != 0 ||
292	    priv_delset(npset, PRIV_PROC_FORK) != 0 ||
293	    priv_delset(npset, PRIV_PROC_INFO) != 0 ||
294	    priv_delset(npset, PRIV_PROC_SESSION) != 0)
295		fatal("priv_delset: %s", strerror(errno));
296
297	if (getppriv(PRIV_PERMITTED, pset) != 0)
298		fatal("getppriv: %s", strerror(errno));
299
300	priv_intersect(pset, npset);
301
302	if (setppriv(PRIV_SET, PRIV_PERMITTED, npset) != 0 ||
303	    setppriv(PRIV_SET, PRIV_LIMIT, npset) != 0 ||
304	    setppriv(PRIV_SET, PRIV_INHERITABLE, npset) != 0)
305		fatal("setppriv: %s", strerror(errno));
306
307	priv_freeset(pset);
308	priv_freeset(npset);
309}
310
311void
312solaris_drop_privs_root_pinfo_net(void)
313{
314	priv_set_t *pset = NULL;
315
316	/* Start with "basic" and drop everything we don't need. */
317	if ((pset = solaris_basic_privset()) == NULL)
318		fatal("solaris_basic_privset: %s", strerror(errno));
319
320	if (priv_delset(pset, PRIV_FILE_LINK_ANY) != 0 ||
321#ifdef PRIV_NET_ACCESS
322	    priv_delset(pset, PRIV_NET_ACCESS) != 0 ||
323#endif
324	    priv_delset(pset, PRIV_PROC_INFO) != 0 ||
325	    priv_delset(pset, PRIV_PROC_SESSION) != 0)
326		fatal("priv_delset: %s", strerror(errno));
327
328	if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) != 0 ||
329	    setppriv(PRIV_SET, PRIV_LIMIT, pset) != 0 ||
330	    setppriv(PRIV_SET, PRIV_INHERITABLE, pset) != 0)
331		fatal("setppriv: %s", strerror(errno));
332
333	priv_freeset(pset);
334}
335
336void
337solaris_drop_privs_root_pinfo_net_exec(void)
338{
339	priv_set_t *pset = NULL;
340
341
342	/* Start with "basic" and drop everything we don't need. */
343	if ((pset = solaris_basic_privset()) == NULL)
344		fatal("solaris_basic_privset: %s", strerror(errno));
345
346	if (priv_delset(pset, PRIV_FILE_LINK_ANY) != 0 ||
347#ifdef PRIV_NET_ACCESS
348	    priv_delset(pset, PRIV_NET_ACCESS) != 0 ||
349#endif
350	    priv_delset(pset, PRIV_PROC_EXEC) != 0 ||
351	    priv_delset(pset, PRIV_PROC_INFO) != 0 ||
352	    priv_delset(pset, PRIV_PROC_SESSION) != 0)
353		fatal("priv_delset: %s", strerror(errno));
354
355	if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) != 0 ||
356	    setppriv(PRIV_SET, PRIV_LIMIT, pset) != 0 ||
357	    setppriv(PRIV_SET, PRIV_INHERITABLE, pset) != 0)
358		fatal("setppriv: %s", strerror(errno));
359
360	priv_freeset(pset);
361}
362
363#endif
364