diff options
Diffstat (limited to 'crypto/openssh/openbsd-compat/port-solaris.c')
| -rw-r--r-- | crypto/openssh/openbsd-compat/port-solaris.c | 382 | 
1 files changed, 382 insertions, 0 deletions
| diff --git a/crypto/openssh/openbsd-compat/port-solaris.c b/crypto/openssh/openbsd-compat/port-solaris.c new file mode 100644 index 000000000000..05aa1f76b829 --- /dev/null +++ b/crypto/openssh/openbsd-compat/port-solaris.c @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2006 Chad Mynhier. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" +#include "includes.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#ifdef HAVE_FCNTL_H +# include <fcntl.h> +#endif +#include <stdarg.h> +#include <string.h> +#include <unistd.h> + +#include "log.h" + +#ifdef USE_SOLARIS_PROCESS_CONTRACTS + +#include <libcontract.h> +#include <sys/contract/process.h> +#include <sys/ctfs.h> + +#define CT_TEMPLATE	CTFS_ROOT "/process/template" +#define CT_LATEST	CTFS_ROOT "/process/latest" + +static int tmpl_fd = -1; + +/* Lookup the latest process contract */ +static ctid_t +get_active_process_contract_id(void) +{ +	int stat_fd; +	ctid_t ctid = -1; +	ct_stathdl_t stathdl; + +	if ((stat_fd = open64(CT_LATEST, O_RDONLY)) == -1) { +		error("%s: Error opening 'latest' process " +		    "contract: %s", __func__, strerror(errno)); +		return -1; +	} +	if (ct_status_read(stat_fd, CTD_COMMON, &stathdl) != 0) { +		error("%s: Error reading process contract " +		    "status: %s", __func__, strerror(errno)); +		goto out; +	} +	if ((ctid = ct_status_get_id(stathdl)) < 0) { +		error("%s: Error getting process contract id: %s", +		    __func__, strerror(errno)); +		goto out; +	} + +	ct_status_free(stathdl); + out: +	close(stat_fd); +	return ctid; +} + +void +solaris_contract_pre_fork(void) +{ +	if ((tmpl_fd = open64(CT_TEMPLATE, O_RDWR)) == -1) { +		error("%s: open %s: %s", __func__, +		    CT_TEMPLATE, strerror(errno)); +		return; +	} + +	debug2("%s: setting up process contract template on fd %d", +	    __func__, tmpl_fd); + +	/* First we set the template parameters and event sets. */ +	if (ct_pr_tmpl_set_param(tmpl_fd, CT_PR_PGRPONLY) != 0) { +		error("%s: Error setting process contract parameter set " +		    "(pgrponly): %s", __func__, strerror(errno)); +		goto fail; +	} +	if (ct_pr_tmpl_set_fatal(tmpl_fd, CT_PR_EV_HWERR) != 0) { +		error("%s: Error setting process contract template " +		    "fatal events: %s", __func__, strerror(errno)); +		goto fail; +	} +	if (ct_tmpl_set_critical(tmpl_fd, 0) != 0) { +		error("%s: Error setting process contract template " +		    "critical events: %s", __func__, strerror(errno)); +		goto fail; +	} +	if (ct_tmpl_set_informative(tmpl_fd, CT_PR_EV_HWERR) != 0) { +		error("%s: Error setting process contract template " +		    "informative events: %s", __func__, strerror(errno)); +		goto fail; +	} + +	/* Now make this the active template for this process. */ +	if (ct_tmpl_activate(tmpl_fd) != 0) { +		error("%s: Error activating process contract " +		    "template: %s", __func__, strerror(errno)); +		goto fail; +	} +	return; + + fail: +	if (tmpl_fd != -1) { +		close(tmpl_fd); +		tmpl_fd = -1; +	} +} + +void +solaris_contract_post_fork_child() +{ +	debug2("%s: clearing process contract template on fd %d", +	    __func__, tmpl_fd); + +	/* Clear the active template. */ +	if (ct_tmpl_clear(tmpl_fd) != 0) +		error("%s: Error clearing active process contract " +		    "template: %s", __func__, strerror(errno)); + +	close(tmpl_fd); +	tmpl_fd = -1; +} + +void +solaris_contract_post_fork_parent(pid_t pid) +{ +	ctid_t ctid; +	char ctl_path[256]; +	int r, ctl_fd = -1, stat_fd = -1; + +	debug2("%s: clearing template (fd %d)", __func__, tmpl_fd); + +	if (tmpl_fd == -1) +		return; + +	/* First clear the active template. */ +	if ((r = ct_tmpl_clear(tmpl_fd)) != 0) +		error("%s: Error clearing active process contract " +		    "template: %s", __func__, strerror(errno)); + +	close(tmpl_fd); +	tmpl_fd = -1; + +	/* +	 * If either the fork didn't succeed (pid < 0), or clearing +	 * th active contract failed (r != 0), then we have nothing +	 * more do. +	 */ +	if (r != 0 || pid <= 0) +		return; + +	/* Now lookup and abandon the contract we've created. */ +	ctid = get_active_process_contract_id(); + +	debug2("%s: abandoning contract id %ld", __func__, ctid); + +	snprintf(ctl_path, sizeof(ctl_path), +	    CTFS_ROOT "/process/%ld/ctl", ctid); +	if ((ctl_fd = open64(ctl_path, O_WRONLY)) < 0) { +		error("%s: Error opening process contract " +		    "ctl file: %s", __func__, strerror(errno)); +		goto fail; +	} +	if (ct_ctl_abandon(ctl_fd) < 0) { +		error("%s: Error abandoning process contract: %s", +		    __func__, strerror(errno)); +		goto fail; +	} +	close(ctl_fd); +	return; + + fail: +	if (tmpl_fd != -1) { +		close(tmpl_fd); +		tmpl_fd = -1; +	} +	if (stat_fd != -1) +		close(stat_fd); +	if (ctl_fd != -1) +		close(ctl_fd); +} +#endif + +#ifdef USE_SOLARIS_PROJECTS +#include <sys/task.h> +#include <project.h> + +/* + * Get/set solaris default project. + * If we fail, just run along gracefully. + */ +void +solaris_set_default_project(struct passwd *pw) +{ +	struct project  *defaultproject; +	struct project   tempproject; +	char buf[1024]; + +	/* get default project, if we fail just return gracefully  */ +	if ((defaultproject = getdefaultproj(pw->pw_name, &tempproject, &buf, +	    sizeof(buf))) != NULL) { +		/* set default project */ +		if (setproject(defaultproject->pj_name, pw->pw_name, +		    TASK_NORMAL) != 0) +			debug("setproject(%s): %s", defaultproject->pj_name, +			    strerror(errno)); +	} else { +		/* debug on getdefaultproj() error */ +		debug("getdefaultproj(%s): %s", pw->pw_name, strerror(errno)); +	} +} +#endif /* USE_SOLARIS_PROJECTS */ + +#ifdef USE_SOLARIS_PRIVS +# ifdef HAVE_PRIV_H +#  include <priv.h> +# endif + +priv_set_t * +solaris_basic_privset(void) +{ +	priv_set_t *pset; + +#ifdef HAVE_PRIV_BASICSET +	if ((pset = priv_allocset()) == NULL) { +		error("priv_allocset: %s", strerror(errno)); +		return NULL; +	} +	priv_basicset(pset); +#else +	if ((pset = priv_str_to_set("basic", ",", NULL)) == NULL) { +		error("priv_str_to_set: %s", strerror(errno)); +		return NULL; +	} +#endif +	return pset; +} + +void +solaris_drop_privs_pinfo_net_fork_exec(void) +{ +	priv_set_t *pset = NULL, *npset = NULL; + +	/* +	 * Note: this variant avoids dropping DAC filesystem rights, in case +	 * the process calling it is running as root and should have the +	 * ability to read/write/chown any file on the system. +	 * +	 * We start with the basic set, then *add* the DAC rights to it while +	 * taking away other parts of BASIC we don't need. Then we intersect +	 * this with our existing PERMITTED set. In this way we keep any +	 * DAC rights we had before, while otherwise reducing ourselves to +	 * the minimum set of privileges we need to proceed. +	 * +	 * This also means we drop any other parts of "root" that we don't +	 * need (e.g. the ability to kill any process, create new device nodes +	 * etc etc). +	 */ + +	if ((pset = priv_allocset()) == NULL) +		fatal("priv_allocset: %s", strerror(errno)); +	if ((npset = solaris_basic_privset()) == NULL) +		fatal("solaris_basic_privset: %s", strerror(errno)); + +	if (priv_addset(npset, PRIV_FILE_CHOWN) != 0 || +	    priv_addset(npset, PRIV_FILE_DAC_READ) != 0 || +	    priv_addset(npset, PRIV_FILE_DAC_SEARCH) != 0 || +	    priv_addset(npset, PRIV_FILE_DAC_WRITE) != 0 || +	    priv_addset(npset, PRIV_FILE_OWNER) != 0) +		fatal("priv_addset: %s", strerror(errno)); + +	if (priv_delset(npset, PRIV_PROC_EXEC) != 0 || +#ifdef PRIV_NET_ACCESS +	    priv_delset(npset, PRIV_NET_ACCESS) != 0 || +#endif +	    priv_delset(npset, PRIV_PROC_FORK) != 0 || +	    priv_delset(npset, PRIV_PROC_INFO) != 0 || +	    priv_delset(npset, PRIV_PROC_SESSION) != 0) +		fatal("priv_delset: %s", strerror(errno)); + +#ifdef PRIV_XPOLICY +	/* +	 * It is possible that the user has an extended policy +	 * in place; the LIMIT set restricts the extended policy +	 * and so should not be restricted. +	 * PRIV_XPOLICY is newly defined in Solaris 11 though the extended +	 * policy was not implemented until Solaris 11.1. +	 */ +	if (getpflags(PRIV_XPOLICY) == 1) { +		if (getppriv(PRIV_LIMIT, pset) != 0) +			fatal("getppriv: %s", strerror(errno)); +		priv_intersect(pset, npset); +		if (setppriv(PRIV_SET, PRIV_LIMIT, npset) != 0) +			fatal("setppriv: %s", strerror(errno)); +	} else +#endif +	{ +		/* Cannot exec, so we can kill the limit set. */ +		priv_emptyset(pset); +		if (setppriv(PRIV_SET, PRIV_LIMIT, pset) != 0) +			fatal("setppriv: %s", strerror(errno)); +	} + +	if (getppriv(PRIV_PERMITTED, pset) != 0) +		fatal("getppriv: %s", strerror(errno)); + +	priv_intersect(pset, npset); + +	if (setppriv(PRIV_SET, PRIV_PERMITTED, npset) != 0 || +	    setppriv(PRIV_SET, PRIV_INHERITABLE, npset) != 0) +		fatal("setppriv: %s", strerror(errno)); + +	priv_freeset(pset); +	priv_freeset(npset); +} + +void +solaris_drop_privs_root_pinfo_net(void) +{ +	priv_set_t *pset = NULL; + +	/* Start with "basic" and drop everything we don't need. */ +	if ((pset = solaris_basic_privset()) == NULL) +		fatal("solaris_basic_privset: %s", strerror(errno)); + +	if (priv_delset(pset, PRIV_FILE_LINK_ANY) != 0 || +#ifdef PRIV_NET_ACCESS +	    priv_delset(pset, PRIV_NET_ACCESS) != 0 || +#endif +	    priv_delset(pset, PRIV_PROC_INFO) != 0 || +	    priv_delset(pset, PRIV_PROC_SESSION) != 0) +		fatal("priv_delset: %s", strerror(errno)); + +	if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) != 0 || +	    setppriv(PRIV_SET, PRIV_LIMIT, pset) != 0 || +	    setppriv(PRIV_SET, PRIV_INHERITABLE, pset) != 0) +		fatal("setppriv: %s", strerror(errno)); + +	priv_freeset(pset); +} + +void +solaris_drop_privs_root_pinfo_net_exec(void) +{ +	priv_set_t *pset = NULL; + + +	/* Start with "basic" and drop everything we don't need. */ +	if ((pset = solaris_basic_privset()) == NULL) +		fatal("solaris_basic_privset: %s", strerror(errno)); + +	if (priv_delset(pset, PRIV_FILE_LINK_ANY) != 0 || +#ifdef PRIV_NET_ACCESS +	    priv_delset(pset, PRIV_NET_ACCESS) != 0 || +#endif +	    priv_delset(pset, PRIV_PROC_EXEC) != 0 || +	    priv_delset(pset, PRIV_PROC_INFO) != 0) +		fatal("priv_delset: %s", strerror(errno)); + +	if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) != 0 || +	    setppriv(PRIV_SET, PRIV_LIMIT, pset) != 0 || +	    setppriv(PRIV_SET, PRIV_INHERITABLE, pset) != 0) +		fatal("setppriv: %s", strerror(errno)); + +	priv_freeset(pset); +} + +#endif | 
