1221420Sdes/* $Id: port-solaris.c,v 1.4 2010/11/05 01:03:05 dtucker Exp $ */
2162852Sdes
3162852Sdes/*
4162852Sdes * Copyright (c) 2006 Chad Mynhier.
5162852Sdes *
6162852Sdes * Permission to use, copy, modify, and distribute this software for any
7162852Sdes * purpose with or without fee is hereby granted, provided that the above
8162852Sdes * copyright notice and this permission notice appear in all copies.
9162852Sdes *
10162852Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11162852Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12162852Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13162852Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14162852Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15162852Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16162852Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17162852Sdes */
18162852Sdes
19162852Sdes#include "config.h"
20162852Sdes#include "includes.h"
21162852Sdes
22162852Sdes#ifdef USE_SOLARIS_PROCESS_CONTRACTS
23162852Sdes
24162852Sdes#include <sys/types.h>
25162852Sdes#include <sys/stat.h>
26162852Sdes#include <sys/param.h>
27162852Sdes
28162852Sdes#include <errno.h>
29162852Sdes#ifdef HAVE_FCNTL_H
30162852Sdes# include <fcntl.h>
31162852Sdes#endif
32162852Sdes#include <stdarg.h>
33162852Sdes#include <string.h>
34162852Sdes#include <unistd.h>
35162852Sdes
36162852Sdes#include <libcontract.h>
37162852Sdes#include <sys/contract/process.h>
38162852Sdes#include <sys/ctfs.h>
39162852Sdes
40162852Sdes#include "log.h"
41162852Sdes
42162852Sdes#define CT_TEMPLATE	CTFS_ROOT "/process/template"
43162852Sdes#define CT_LATEST	CTFS_ROOT "/process/latest"
44162852Sdes
45162852Sdesstatic int tmpl_fd = -1;
46162852Sdes
47162852Sdes/* Lookup the latest process contract */
48162852Sdesstatic ctid_t
49162852Sdesget_active_process_contract_id(void)
50162852Sdes{
51162852Sdes	int stat_fd;
52162852Sdes	ctid_t ctid = -1;
53162852Sdes	ct_stathdl_t stathdl;
54162852Sdes
55162852Sdes	if ((stat_fd = open64(CT_LATEST, O_RDONLY)) == -1) {
56162852Sdes		error("%s: Error opening 'latest' process "
57162852Sdes		    "contract: %s", __func__, strerror(errno));
58162852Sdes		return -1;
59162852Sdes	}
60162852Sdes	if (ct_status_read(stat_fd, CTD_COMMON, &stathdl) != 0) {
61162852Sdes		error("%s: Error reading process contract "
62162852Sdes		    "status: %s", __func__, strerror(errno));
63162852Sdes		goto out;
64162852Sdes	}
65162852Sdes	if ((ctid = ct_status_get_id(stathdl)) < 0) {
66162852Sdes		error("%s: Error getting process contract id: %s",
67162852Sdes		    __func__, strerror(errno));
68162852Sdes		goto out;
69162852Sdes	}
70162852Sdes
71162852Sdes	ct_status_free(stathdl);
72162852Sdes out:
73162852Sdes	close(stat_fd);
74162852Sdes	return ctid;
75162852Sdes}
76162852Sdes
77162852Sdesvoid
78162852Sdessolaris_contract_pre_fork(void)
79162852Sdes{
80162852Sdes	if ((tmpl_fd = open64(CT_TEMPLATE, O_RDWR)) == -1) {
81162852Sdes		error("%s: open %s: %s", __func__,
82162852Sdes		    CT_TEMPLATE, strerror(errno));
83162852Sdes		return;
84162852Sdes	}
85162852Sdes
86162852Sdes	debug2("%s: setting up process contract template on fd %d",
87162852Sdes	    __func__, tmpl_fd);
88162852Sdes
89164146Sdes	/* First we set the template parameters and event sets. */
90164146Sdes	if (ct_pr_tmpl_set_param(tmpl_fd, CT_PR_PGRPONLY) != 0) {
91164146Sdes		error("%s: Error setting process contract parameter set "
92164146Sdes		    "(pgrponly): %s", __func__, strerror(errno));
93164146Sdes		goto fail;
94164146Sdes	}
95164146Sdes	if (ct_pr_tmpl_set_fatal(tmpl_fd, CT_PR_EV_HWERR) != 0) {
96162852Sdes		error("%s: Error setting process contract template "
97162852Sdes		    "fatal events: %s", __func__, strerror(errno));
98162852Sdes		goto fail;
99162852Sdes	}
100164146Sdes	if (ct_tmpl_set_critical(tmpl_fd, 0) != 0) {
101162852Sdes		error("%s: Error setting process contract template "
102162852Sdes		    "critical events: %s", __func__, strerror(errno));
103162852Sdes		goto fail;
104162852Sdes	}
105164146Sdes	if (ct_tmpl_set_informative(tmpl_fd, CT_PR_EV_HWERR) != 0) {
106164146Sdes		error("%s: Error setting process contract template "
107164146Sdes		    "informative events: %s", __func__, strerror(errno));
108164146Sdes		goto fail;
109164146Sdes	}
110162852Sdes
111162852Sdes	/* Now make this the active template for this process. */
112162852Sdes	if (ct_tmpl_activate(tmpl_fd) != 0) {
113162852Sdes		error("%s: Error activating process contract "
114162852Sdes		    "template: %s", __func__, strerror(errno));
115162852Sdes		goto fail;
116162852Sdes	}
117162852Sdes	return;
118162852Sdes
119162852Sdes fail:
120162852Sdes	if (tmpl_fd != -1) {
121162852Sdes		close(tmpl_fd);
122162852Sdes		tmpl_fd = -1;
123162852Sdes	}
124162852Sdes}
125162852Sdes
126162852Sdesvoid
127162852Sdessolaris_contract_post_fork_child()
128162852Sdes{
129162852Sdes	debug2("%s: clearing process contract template on fd %d",
130162852Sdes	    __func__, tmpl_fd);
131162852Sdes
132162852Sdes	/* Clear the active template. */
133162852Sdes	if (ct_tmpl_clear(tmpl_fd) != 0)
134162852Sdes		error("%s: Error clearing active process contract "
135162852Sdes		    "template: %s", __func__, strerror(errno));
136162852Sdes
137162852Sdes	close(tmpl_fd);
138162852Sdes	tmpl_fd = -1;
139162852Sdes}
140162852Sdes
141162852Sdesvoid
142162852Sdessolaris_contract_post_fork_parent(pid_t pid)
143162852Sdes{
144162852Sdes	ctid_t ctid;
145162852Sdes	char ctl_path[256];
146162852Sdes	int r, ctl_fd = -1, stat_fd = -1;
147162852Sdes
148162852Sdes	debug2("%s: clearing template (fd %d)", __func__, tmpl_fd);
149162852Sdes
150162852Sdes	if (tmpl_fd == -1)
151162852Sdes		return;
152162852Sdes
153162852Sdes	/* First clear the active template. */
154162852Sdes	if ((r = ct_tmpl_clear(tmpl_fd)) != 0)
155162852Sdes		error("%s: Error clearing active process contract "
156162852Sdes		    "template: %s", __func__, strerror(errno));
157162852Sdes
158162852Sdes	close(tmpl_fd);
159162852Sdes	tmpl_fd = -1;
160162852Sdes
161162852Sdes	/*
162162852Sdes	 * If either the fork didn't succeed (pid < 0), or clearing
163162852Sdes	 * th active contract failed (r != 0), then we have nothing
164162852Sdes	 * more do.
165162852Sdes	 */
166162852Sdes	if (r != 0 || pid <= 0)
167162852Sdes		return;
168162852Sdes
169162852Sdes	/* Now lookup and abandon the contract we've created. */
170162852Sdes	ctid = get_active_process_contract_id();
171162852Sdes
172162852Sdes	debug2("%s: abandoning contract id %ld", __func__, ctid);
173162852Sdes
174162852Sdes	snprintf(ctl_path, sizeof(ctl_path),
175162852Sdes	    CTFS_ROOT "/process/%ld/ctl", ctid);
176162852Sdes	if ((ctl_fd = open64(ctl_path, O_WRONLY)) < 0) {
177162852Sdes		error("%s: Error opening process contract "
178162852Sdes		    "ctl file: %s", __func__, strerror(errno));
179162852Sdes		goto fail;
180162852Sdes	}
181162852Sdes	if (ct_ctl_abandon(ctl_fd) < 0) {
182162852Sdes		error("%s: Error abandoning process contract: %s",
183162852Sdes		    __func__, strerror(errno));
184162852Sdes		goto fail;
185162852Sdes	}
186162852Sdes	close(ctl_fd);
187162852Sdes	return;
188162852Sdes
189162852Sdes fail:
190162852Sdes	if (tmpl_fd != -1) {
191162852Sdes		close(tmpl_fd);
192162852Sdes		tmpl_fd = -1;
193162852Sdes	}
194162852Sdes	if (stat_fd != -1)
195162852Sdes		close(stat_fd);
196162852Sdes	if (ctl_fd != -1)
197162852Sdes		close(ctl_fd);
198162852Sdes}
199162852Sdes#endif
200221420Sdes
201221420Sdes#ifdef USE_SOLARIS_PROJECTS
202221420Sdes#include <sys/task.h>
203221420Sdes#include <project.h>
204221420Sdes
205221420Sdes/*
206221420Sdes * Get/set solaris default project.
207221420Sdes * If we fail, just run along gracefully.
208221420Sdes */
209221420Sdesvoid
210221420Sdessolaris_set_default_project(struct passwd *pw)
211221420Sdes{
212221420Sdes	struct project  *defaultproject;
213221420Sdes	struct project   tempproject;
214221420Sdes	char buf[1024];
215221420Sdes
216221420Sdes	/* get default project, if we fail just return gracefully  */
217221420Sdes	if ((defaultproject = getdefaultproj(pw->pw_name, &tempproject, &buf,
218221420Sdes	    sizeof(buf))) > 0) {
219221420Sdes		/* set default project */
220221420Sdes		if (setproject(defaultproject->pj_name, pw->pw_name,
221221420Sdes		    TASK_NORMAL) != 0)
222221420Sdes			debug("setproject(%s): %s", defaultproject->pj_name,
223221420Sdes			    strerror(errno));
224221420Sdes	} else {
225221420Sdes		/* debug on getdefaultproj() error */
226221420Sdes		debug("getdefaultproj(%s): %s", pw->pw_name, strerror(errno));
227221420Sdes	}
228221420Sdes}
229221420Sdes#endif /* USE_SOLARIS_PROJECTS */
230296781Sdes
231296781Sdes#ifdef USE_SOLARIS_PRIVS
232296781Sdes# ifdef HAVE_PRIV_H
233296781Sdes#  include <priv.h>
234296781Sdes# endif
235296781Sdes
236296781Sdespriv_set_t *
237296781Sdessolaris_basic_privset(void)
238296781Sdes{
239296781Sdes	priv_set_t *pset;
240296781Sdes
241296781Sdes#ifdef HAVE_PRIV_BASICSET
242296781Sdes	if ((pset = priv_allocset()) == NULL) {
243296781Sdes		error("priv_allocset: %s", strerror(errno));
244296781Sdes		return NULL;
245296781Sdes	}
246296781Sdes	priv_basicset(pset);
247296781Sdes#else
248296781Sdes	if ((pset = priv_str_to_set("basic", ",", NULL)) == NULL) {
249296781Sdes		error("priv_str_to_set: %s", strerror(errno));
250296781Sdes		return NULL;
251296781Sdes	}
252296781Sdes#endif
253296781Sdes	return pset;
254296781Sdes}
255296781Sdes
256296781Sdesvoid
257296781Sdessolaris_drop_privs_pinfo_net_fork_exec(void)
258296781Sdes{
259296781Sdes	priv_set_t *pset = NULL, *npset = NULL;
260296781Sdes
261296781Sdes	/*
262296781Sdes	 * Note: this variant avoids dropping DAC filesystem rights, in case
263296781Sdes	 * the process calling it is running as root and should have the
264296781Sdes	 * ability to read/write/chown any file on the system.
265296781Sdes	 *
266296781Sdes	 * We start with the basic set, then *add* the DAC rights to it while
267296781Sdes	 * taking away other parts of BASIC we don't need. Then we intersect
268296781Sdes	 * this with our existing PERMITTED set. In this way we keep any
269296781Sdes	 * DAC rights we had before, while otherwise reducing ourselves to
270296781Sdes	 * the minimum set of privileges we need to proceed.
271296781Sdes	 *
272296781Sdes	 * This also means we drop any other parts of "root" that we don't
273296781Sdes	 * need (e.g. the ability to kill any process, create new device nodes
274296781Sdes	 * etc etc).
275296781Sdes	 */
276296781Sdes
277296781Sdes	if ((pset = priv_allocset()) == NULL)
278296781Sdes		fatal("priv_allocset: %s", strerror(errno));
279296781Sdes	if ((npset = solaris_basic_privset()) == NULL)
280296781Sdes		fatal("solaris_basic_privset: %s", strerror(errno));
281296781Sdes
282296781Sdes	if (priv_addset(npset, PRIV_FILE_CHOWN) != 0 ||
283296781Sdes	    priv_addset(npset, PRIV_FILE_DAC_READ) != 0 ||
284296781Sdes	    priv_addset(npset, PRIV_FILE_DAC_SEARCH) != 0 ||
285296781Sdes	    priv_addset(npset, PRIV_FILE_DAC_WRITE) != 0 ||
286296781Sdes	    priv_addset(npset, PRIV_FILE_OWNER) != 0)
287296781Sdes		fatal("priv_addset: %s", strerror(errno));
288296781Sdes
289296781Sdes	if (priv_delset(npset, PRIV_FILE_LINK_ANY) != 0 ||
290296781Sdes#ifdef PRIV_NET_ACCESS
291296781Sdes	    priv_delset(npset, PRIV_NET_ACCESS) != 0 ||
292296781Sdes#endif
293296781Sdes	    priv_delset(npset, PRIV_PROC_EXEC) != 0 ||
294296781Sdes	    priv_delset(npset, PRIV_PROC_FORK) != 0 ||
295296781Sdes	    priv_delset(npset, PRIV_PROC_INFO) != 0 ||
296296781Sdes	    priv_delset(npset, PRIV_PROC_SESSION) != 0)
297296781Sdes		fatal("priv_delset: %s", strerror(errno));
298296781Sdes
299296781Sdes	if (getppriv(PRIV_PERMITTED, pset) != 0)
300296781Sdes		fatal("getppriv: %s", strerror(errno));
301296781Sdes
302296781Sdes	priv_intersect(pset, npset);
303296781Sdes
304296781Sdes	if (setppriv(PRIV_SET, PRIV_PERMITTED, npset) != 0 ||
305296781Sdes	    setppriv(PRIV_SET, PRIV_LIMIT, npset) != 0 ||
306296781Sdes	    setppriv(PRIV_SET, PRIV_INHERITABLE, npset) != 0)
307296781Sdes		fatal("setppriv: %s", strerror(errno));
308296781Sdes
309296781Sdes	priv_freeset(pset);
310296781Sdes	priv_freeset(npset);
311296781Sdes}
312296781Sdes
313296781Sdesvoid
314296781Sdessolaris_drop_privs_root_pinfo_net(void)
315296781Sdes{
316296781Sdes	priv_set_t *pset = NULL;
317296781Sdes
318296781Sdes	/* Start with "basic" and drop everything we don't need. */
319296781Sdes	if ((pset = solaris_basic_privset()) == NULL)
320296781Sdes		fatal("solaris_basic_privset: %s", strerror(errno));
321296781Sdes
322296781Sdes	if (priv_delset(pset, PRIV_FILE_LINK_ANY) != 0 ||
323296781Sdes#ifdef PRIV_NET_ACCESS
324296781Sdes	    priv_delset(pset, PRIV_NET_ACCESS) != 0 ||
325296781Sdes#endif
326296781Sdes	    priv_delset(pset, PRIV_PROC_INFO) != 0 ||
327296781Sdes	    priv_delset(pset, PRIV_PROC_SESSION) != 0)
328296781Sdes		fatal("priv_delset: %s", strerror(errno));
329296781Sdes
330296781Sdes	if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) != 0 ||
331296781Sdes	    setppriv(PRIV_SET, PRIV_LIMIT, pset) != 0 ||
332296781Sdes	    setppriv(PRIV_SET, PRIV_INHERITABLE, pset) != 0)
333296781Sdes		fatal("setppriv: %s", strerror(errno));
334296781Sdes
335296781Sdes	priv_freeset(pset);
336296781Sdes}
337296781Sdes
338296781Sdesvoid
339296781Sdessolaris_drop_privs_root_pinfo_net_exec(void)
340296781Sdes{
341296781Sdes	priv_set_t *pset = NULL;
342296781Sdes
343296781Sdes
344296781Sdes	/* Start with "basic" and drop everything we don't need. */
345296781Sdes	if ((pset = solaris_basic_privset()) == NULL)
346296781Sdes		fatal("solaris_basic_privset: %s", strerror(errno));
347296781Sdes
348296781Sdes	if (priv_delset(pset, PRIV_FILE_LINK_ANY) != 0 ||
349296781Sdes#ifdef PRIV_NET_ACCESS
350296781Sdes	    priv_delset(pset, PRIV_NET_ACCESS) != 0 ||
351296781Sdes#endif
352296781Sdes	    priv_delset(pset, PRIV_PROC_EXEC) != 0 ||
353296781Sdes	    priv_delset(pset, PRIV_PROC_INFO) != 0 ||
354296781Sdes	    priv_delset(pset, PRIV_PROC_SESSION) != 0)
355296781Sdes		fatal("priv_delset: %s", strerror(errno));
356296781Sdes
357296781Sdes	if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) != 0 ||
358296781Sdes	    setppriv(PRIV_SET, PRIV_LIMIT, pset) != 0 ||
359296781Sdes	    setppriv(PRIV_SET, PRIV_INHERITABLE, pset) != 0)
360296781Sdes		fatal("setppriv: %s", strerror(errno));
361296781Sdes
362296781Sdes	priv_freeset(pset);
363296781Sdes}
364296781Sdes
365296781Sdes#endif
366