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 */
230296633Sdes
231296633Sdes#ifdef USE_SOLARIS_PRIVS
232296633Sdes# ifdef HAVE_PRIV_H
233296633Sdes#  include <priv.h>
234296633Sdes# endif
235296633Sdes
236296633Sdespriv_set_t *
237296633Sdessolaris_basic_privset(void)
238296633Sdes{
239296633Sdes	priv_set_t *pset;
240296633Sdes
241296633Sdes#ifdef HAVE_PRIV_BASICSET
242296633Sdes	if ((pset = priv_allocset()) == NULL) {
243296633Sdes		error("priv_allocset: %s", strerror(errno));
244296633Sdes		return NULL;
245296633Sdes	}
246296633Sdes	priv_basicset(pset);
247296633Sdes#else
248296633Sdes	if ((pset = priv_str_to_set("basic", ",", NULL)) == NULL) {
249296633Sdes		error("priv_str_to_set: %s", strerror(errno));
250296633Sdes		return NULL;
251296633Sdes	}
252296633Sdes#endif
253296633Sdes	return pset;
254296633Sdes}
255296633Sdes
256296633Sdesvoid
257296633Sdessolaris_drop_privs_pinfo_net_fork_exec(void)
258296633Sdes{
259296633Sdes	priv_set_t *pset = NULL, *npset = NULL;
260296633Sdes
261296633Sdes	/*
262296633Sdes	 * Note: this variant avoids dropping DAC filesystem rights, in case
263296633Sdes	 * the process calling it is running as root and should have the
264296633Sdes	 * ability to read/write/chown any file on the system.
265296633Sdes	 *
266296633Sdes	 * We start with the basic set, then *add* the DAC rights to it while
267296633Sdes	 * taking away other parts of BASIC we don't need. Then we intersect
268296633Sdes	 * this with our existing PERMITTED set. In this way we keep any
269296633Sdes	 * DAC rights we had before, while otherwise reducing ourselves to
270296633Sdes	 * the minimum set of privileges we need to proceed.
271296633Sdes	 *
272296633Sdes	 * This also means we drop any other parts of "root" that we don't
273296633Sdes	 * need (e.g. the ability to kill any process, create new device nodes
274296633Sdes	 * etc etc).
275296633Sdes	 */
276296633Sdes
277296633Sdes	if ((pset = priv_allocset()) == NULL)
278296633Sdes		fatal("priv_allocset: %s", strerror(errno));
279296633Sdes	if ((npset = solaris_basic_privset()) == NULL)
280296633Sdes		fatal("solaris_basic_privset: %s", strerror(errno));
281296633Sdes
282296633Sdes	if (priv_addset(npset, PRIV_FILE_CHOWN) != 0 ||
283296633Sdes	    priv_addset(npset, PRIV_FILE_DAC_READ) != 0 ||
284296633Sdes	    priv_addset(npset, PRIV_FILE_DAC_SEARCH) != 0 ||
285296633Sdes	    priv_addset(npset, PRIV_FILE_DAC_WRITE) != 0 ||
286296633Sdes	    priv_addset(npset, PRIV_FILE_OWNER) != 0)
287296633Sdes		fatal("priv_addset: %s", strerror(errno));
288296633Sdes
289296633Sdes	if (priv_delset(npset, PRIV_FILE_LINK_ANY) != 0 ||
290296633Sdes#ifdef PRIV_NET_ACCESS
291296633Sdes	    priv_delset(npset, PRIV_NET_ACCESS) != 0 ||
292296633Sdes#endif
293296633Sdes	    priv_delset(npset, PRIV_PROC_EXEC) != 0 ||
294296633Sdes	    priv_delset(npset, PRIV_PROC_FORK) != 0 ||
295296633Sdes	    priv_delset(npset, PRIV_PROC_INFO) != 0 ||
296296633Sdes	    priv_delset(npset, PRIV_PROC_SESSION) != 0)
297296633Sdes		fatal("priv_delset: %s", strerror(errno));
298296633Sdes
299296633Sdes	if (getppriv(PRIV_PERMITTED, pset) != 0)
300296633Sdes		fatal("getppriv: %s", strerror(errno));
301296633Sdes
302296633Sdes	priv_intersect(pset, npset);
303296633Sdes
304296633Sdes	if (setppriv(PRIV_SET, PRIV_PERMITTED, npset) != 0 ||
305296633Sdes	    setppriv(PRIV_SET, PRIV_LIMIT, npset) != 0 ||
306296633Sdes	    setppriv(PRIV_SET, PRIV_INHERITABLE, npset) != 0)
307296633Sdes		fatal("setppriv: %s", strerror(errno));
308296633Sdes
309296633Sdes	priv_freeset(pset);
310296633Sdes	priv_freeset(npset);
311296633Sdes}
312296633Sdes
313296633Sdesvoid
314296633Sdessolaris_drop_privs_root_pinfo_net(void)
315296633Sdes{
316296633Sdes	priv_set_t *pset = NULL;
317296633Sdes
318296633Sdes	/* Start with "basic" and drop everything we don't need. */
319296633Sdes	if ((pset = solaris_basic_privset()) == NULL)
320296633Sdes		fatal("solaris_basic_privset: %s", strerror(errno));
321296633Sdes
322296633Sdes	if (priv_delset(pset, PRIV_FILE_LINK_ANY) != 0 ||
323296633Sdes#ifdef PRIV_NET_ACCESS
324296633Sdes	    priv_delset(pset, PRIV_NET_ACCESS) != 0 ||
325296633Sdes#endif
326296633Sdes	    priv_delset(pset, PRIV_PROC_INFO) != 0 ||
327296633Sdes	    priv_delset(pset, PRIV_PROC_SESSION) != 0)
328296633Sdes		fatal("priv_delset: %s", strerror(errno));
329296633Sdes
330296633Sdes	if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) != 0 ||
331296633Sdes	    setppriv(PRIV_SET, PRIV_LIMIT, pset) != 0 ||
332296633Sdes	    setppriv(PRIV_SET, PRIV_INHERITABLE, pset) != 0)
333296633Sdes		fatal("setppriv: %s", strerror(errno));
334296633Sdes
335296633Sdes	priv_freeset(pset);
336296633Sdes}
337296633Sdes
338296633Sdesvoid
339296633Sdessolaris_drop_privs_root_pinfo_net_exec(void)
340296633Sdes{
341296633Sdes	priv_set_t *pset = NULL;
342296633Sdes
343296633Sdes
344296633Sdes	/* Start with "basic" and drop everything we don't need. */
345296633Sdes	if ((pset = solaris_basic_privset()) == NULL)
346296633Sdes		fatal("solaris_basic_privset: %s", strerror(errno));
347296633Sdes
348296633Sdes	if (priv_delset(pset, PRIV_FILE_LINK_ANY) != 0 ||
349296633Sdes#ifdef PRIV_NET_ACCESS
350296633Sdes	    priv_delset(pset, PRIV_NET_ACCESS) != 0 ||
351296633Sdes#endif
352296633Sdes	    priv_delset(pset, PRIV_PROC_EXEC) != 0 ||
353296633Sdes	    priv_delset(pset, PRIV_PROC_INFO) != 0 ||
354296633Sdes	    priv_delset(pset, PRIV_PROC_SESSION) != 0)
355296633Sdes		fatal("priv_delset: %s", strerror(errno));
356296633Sdes
357296633Sdes	if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) != 0 ||
358296633Sdes	    setppriv(PRIV_SET, PRIV_LIMIT, pset) != 0 ||
359296633Sdes	    setppriv(PRIV_SET, PRIV_INHERITABLE, pset) != 0)
360296633Sdes		fatal("setppriv: %s", strerror(errno));
361296633Sdes
362296633Sdes	priv_freeset(pset);
363296633Sdes}
364296633Sdes
365296633Sdes#endif
366