2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <nss_dbdefs.h>
2N/A#include <pwd.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <syslog.h>
2N/A#include <unistd.h>
2N/A#include <auth_attr.h>
2N/A#include <deflt.h>
2N/A#include <priv.h>
2N/A#include <secdb.h>
2N/A#include <user_attr.h>
2N/A#include <sys/task.h>
2N/A#include <sys/stat.h>
2N/A#include <libintl.h>
2N/A#include <project.h>
2N/A#include <errno.h>
2N/A#include <alloca.h>
2N/A#include <xpol.h>
2N/A
2N/A#include <bsm/adt.h>
2N/A#include <bsm/adt_event.h> /* adt_get_auid() */
2N/A
2N/A#include <security/pam_appl.h>
2N/A#include <security/pam_modules.h>
2N/A#include <security/pam_impl.h>
2N/A
2N/A#define PROJECT "project="
2N/A#define PROJSZ (sizeof (PROJECT) - 1)
2N/A
2N/A/*
2N/A * unix_cred - PAM auth modules must contain both pam_sm_authenticate
2N/A * and pam_sm_setcred. Some other auth module is responsible
2N/A * for authentication (e.g., pam_unix_auth.so), this module
2N/A * only implements pam_sm_setcred so that the authentication
2N/A * can be separated without knowledge of the Solaris Unix style
2N/A * credential setting.
2N/A * Solaris Unix style credential setting includes initializing
2N/A * the audit characteristics if not already initialized and
2N/A * setting the user's default and limit privileges.
2N/A */
2N/A
2N/A/*
2N/A * unix_cred - pam_sm_authenticate
2N/A *
2N/A * Returns PAM_IGNORE.
2N/A */
2N/A
2N/A/*ARGSUSED*/
2N/Aint
2N/Apam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
2N/A{
2N/A return (PAM_IGNORE);
2N/A}
2N/A
2N/A/*
2N/A * Set the privilege set. The attributes are enumerated by _enum_attrs,
2N/A * including the attribues user_attr, prof_attr and policy.conf
2N/A */
2N/Astatic int
2N/Agetset(char *str, priv_set_t **res)
2N/A{
2N/A priv_set_t *tmp;
2N/A char *badp;
2N/A int len;
2N/A
2N/A if (str == NULL)
2N/A return (0);
2N/A
2N/A len = strlen(str) + 1;
2N/A badp = alloca(len);
2N/A (void) memset(badp, '\0', len);
2N/A do {
2N/A const char *q, *endp;
2N/A tmp = priv_str_to_set(str, ",", &endp);
2N/A if (tmp == NULL) {
2N/A if (endp == NULL)
2N/A break;
2N/A
2N/A /* Now remove the bad privilege endp points to */
2N/A q = strchr(endp, ',');
2N/A if (q == NULL)
2N/A q = endp + strlen(endp);
2N/A
2N/A if (*badp != '\0')
2N/A (void) strlcat(badp, ",", len);
2N/A /* Memset above guarantees NUL termination */
2N/A /* LINTED */
2N/A (void) strncat(badp, endp, q - endp);
2N/A /* excise bad privilege; strtok ignores 2x sep */
2N/A (void) memmove((void *)endp, q, strlen(q) + 1);
2N/A }
2N/A } while (tmp == NULL && *str != '\0');
2N/A
2N/A if (tmp == NULL) {
2N/A syslog(LOG_AUTH|LOG_ERR,
2N/A "pam_setcred: can't parse privilege specification: %m\n");
2N/A return (-1);
2N/A } else if (*badp != '\0') {
2N/A syslog(LOG_AUTH|LOG_DEBUG,
2N/A "pam_setcred: unrecognized privilege(s): %s\n", badp);
2N/A }
2N/A *res = tmp;
2N/A return (0);
2N/A}
2N/A
2N/Atypedef struct deflim {
2N/A char *def;
2N/A char *lim;
2N/A} deflim_t;
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Afinddeflim(const char *name, kva_t *kva, void *ctxt, void *pres)
2N/A{
2N/A deflim_t *pdef = pres;
2N/A char *val;
2N/A
2N/A if (pdef->def == NULL) {
2N/A val = kva_match(kva, USERATTR_DFLTPRIV_KW);
2N/A if (val != NULL)
2N/A pdef->def = strdup(val);
2N/A }
2N/A if (pdef->lim == NULL) {
2N/A val = kva_match(kva, USERATTR_LIMPRIV_KW);
2N/A if (val != NULL)
2N/A pdef->lim = strdup(val);
2N/A }
2N/A return (pdef->lim != NULL && pdef->def != NULL);
2N/A}
2N/A
2N/Astatic void
2N/Acreate_varuser(struct passwd *pwd)
2N/A{
2N/A char *userdir;
2N/A
2N/A (void) asprintf(&userdir, "/var/user/%s", pwd->pw_name);
2N/A if (userdir == NULL) { /* memory error */
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: error allocating userdir buffer space.");
2N/A return;
2N/A }
2N/A if (mkdir(userdir, 0700) != 0) {
2N/A /* Don't complain if /var/user is not writable */
2N/A if (errno == EROFS) {
2N/A free(userdir);
2N/A return;
2N/A } else if (errno != EEXIST) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: error creating %s: %m", userdir);
2N/A } else {
2N/A /* dir exists, but chmod 0700 to be safe */
2N/A if (chmod(userdir, 0700) != 0 && errno != EROFS)
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: chmod error on %s: %m",
2N/A userdir);
2N/A }
2N/A }
2N/A if (chown(userdir, pwd->pw_uid, pwd->pw_gid) != 0 && errno != EROFS)
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: chown error on %s: %m",
2N/A userdir);
2N/A
2N/A free(userdir);
2N/A}
2N/A
2N/A/*
2N/A * unix_cred - pam_sm_setcred
2N/A *
2N/A * Entry flags = PAM_ESTABLISH_CRED, set up Solaris Unix cred.
2N/A * PAM_DELETE_CRED, NOP, return PAM_SUCCESS.
2N/A * PAM_REINITIALIZE_CRED, set up Solaris Unix cred,
2N/A * or merge the current context with the new
2N/A * user.
2N/A * PAM_REFRESH_CRED, set up Solaris Unix cred.
2N/A * PAM_SILENT, print no messages to user.
2N/A *
2N/A * Returns PAM_SUCCESS, if all successful.
2N/A * PAM_CRED_ERR, if unable to set credentials.
2N/A * PAM_USER_UNKNOWN, if PAM_USER not set, or unable to find
2N/A * user in databases.
2N/A * PAM_SYSTEM_ERR, if no valid flag, or unable to get/set
2N/A * user's audit state.
2N/A */
2N/A
2N/Aint
2N/Apam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
2N/A{
2N/A int i;
2N/A int debug = 0;
2N/A uint_t nowarn = flags & PAM_SILENT;
2N/A int ret = PAM_SUCCESS;
2N/A char *user;
2N/A char *auser;
2N/A char *rhost;
2N/A char *tty;
2N/A au_id_t auid;
2N/A adt_session_data_t *ah;
2N/A adt_termid_t *termid = NULL;
2N/A priv_set_t *lim, *def, *tset, *rset;
2N/A char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
2N/A char buf[PROJECT_BUFSZ];
2N/A struct project proj, *pproj;
2N/A int error;
2N/A char *projname;
2N/A char *kvs;
2N/A struct passwd pwd;
2N/A char pwbuf[NSS_BUFLEN_PASSWD];
2N/A deflim_t deflim;
2N/A xpol_ctxt_t *ctxt;
2N/A
2N/A for (i = 0; i < argc; i++) {
2N/A if (strcmp(argv[i], "debug") == 0)
2N/A debug = 1;
2N/A else if (strcmp(argv[i], "nowarn") == 0)
2N/A nowarn |= 1;
2N/A }
2N/A
2N/A if (debug)
2N/A syslog(LOG_AUTH | LOG_DEBUG,
2N/A "pam_unix_cred: pam_sm_setcred(flags = %x, argc= %d)",
2N/A flags, argc);
2N/A
2N/A (void) pam_get_item(pamh, PAM_USER, (void **)&user);
2N/A
2N/A if (user == NULL || *user == '\0') {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: USER NULL or empty!\n");
2N/A return (PAM_USER_UNKNOWN);
2N/A }
2N/A (void) pam_get_item(pamh, PAM_AUSER, (void **)&auser);
2N/A (void) pam_get_item(pamh, PAM_RHOST, (void **)&rhost);
2N/A (void) pam_get_item(pamh, PAM_TTY, (void **)&tty);
2N/A if (debug)
2N/A syslog(LOG_AUTH | LOG_DEBUG,
2N/A "pam_unix_cred: user = %s, auser = %s, rhost = %s, "
2N/A "tty = %s", user,
2N/A (auser == NULL) ? "NULL" : (*auser == '\0') ? "ZERO" :
2N/A auser,
2N/A (rhost == NULL) ? "NULL" : (*rhost == '\0') ? "ZERO" :
2N/A rhost,
2N/A (tty == NULL) ? "NULL" : (*tty == '\0') ? "ZERO" :
2N/A tty);
2N/A
2N/A /* validate flags */
2N/A switch (flags & (PAM_ESTABLISH_CRED | PAM_DELETE_CRED |
2N/A PAM_REINITIALIZE_CRED | PAM_REFRESH_CRED)) {
2N/A case 0:
2N/A /* set default flag */
2N/A flags |= PAM_ESTABLISH_CRED;
2N/A break;
2N/A case PAM_ESTABLISH_CRED:
2N/A case PAM_REINITIALIZE_CRED:
2N/A case PAM_REFRESH_CRED:
2N/A break;
2N/A case PAM_DELETE_CRED:
2N/A return (PAM_SUCCESS);
2N/A default:
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: invalid flags %x", flags);
2N/A return (PAM_SYSTEM_ERR);
2N/A }
2N/A
2N/A /*
2N/A * if auditing on and process audit state not set,
2N/A * setup audit context for process.
2N/A */
2N/A if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: cannot create start audit session %m");
2N/A return (PAM_SYSTEM_ERR);
2N/A }
2N/A adt_get_auid(ah, &auid);
2N/A if (debug) {
2N/A int state = AUC_DISABLED;
2N/A
2N/A (void) auditon(A_GETCOND, (caddr_t)&state, sizeof (state));
2N/A syslog(LOG_AUTH | LOG_DEBUG,
2N/A "pam_unix_cred: state = %d, auid = %d", state, auid);
2N/A }
2N/A if (getpwnam_r(user, &pwd, pwbuf, sizeof (pwbuf)) == NULL) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: cannot get passwd entry for user = %s",
2N/A user);
2N/A ret = PAM_USER_UNKNOWN;
2N/A goto adt_done;
2N/A }
2N/A
2N/A if ((auid == AU_NOAUDITID) &&
2N/A (flags & PAM_ESTABLISH_CRED)) {
2N/A struct passwd apwd;
2N/A char apwbuf[NSS_BUFLEN_PASSWD];
2N/A
2N/A errno = 0;
2N/A if ((rhost == NULL || *rhost == '\0')) {
2N/A if (adt_load_ttyname(tty, &termid) != 0) {
2N/A if (errno == ENETDOWN) {
2N/A /*
2N/A * tolerate not being able to
2N/A * translate local hostname
2N/A * to a termid -- it will be
2N/A * "loopback".
2N/A */
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: cannot load "
2N/A "ttyname: %m, continuing.");
2N/A goto adt_setuser;
2N/A } else if (errno != 0) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: cannot load "
2N/A "ttyname: %m.");
2N/A } else {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: cannot load "
2N/A "ttyname.");
2N/A }
2N/A ret = PAM_SYSTEM_ERR;
2N/A goto adt_done;
2N/A }
2N/A } else {
2N/A if (adt_load_hostname(rhost, &termid) != 0) {
2N/A if (errno != 0) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: cannot load "
2N/A "hostname: %m.");
2N/A } else {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: cannot load "
2N/A "hostname.");
2N/A }
2N/A ret = PAM_SYSTEM_ERR;
2N/A goto adt_done;
2N/A }
2N/A }
2N/Aadt_setuser:
2N/A if ((auser != NULL) && (*auser != '\0') &&
2N/A (getpwnam_r(auser, &apwd, apwbuf,
2N/A sizeof (apwbuf)) != NULL)) {
2N/A /*
2N/A * set up the initial audit for user coming
2N/A * from another user
2N/A */
2N/A if (adt_set_user(ah, apwd.pw_uid, apwd.pw_gid,
2N/A apwd.pw_uid, apwd.pw_gid, termid, ADT_NEW) != 0) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: cannot set auser audit "
2N/A "%m");
2N/A ret = PAM_SYSTEM_ERR;
2N/A goto adt_done;
2N/A }
2N/A if (adt_set_user(ah, pwd.pw_uid, pwd.pw_gid,
2N/A pwd.pw_uid, pwd.pw_gid, NULL,
2N/A ADT_UPDATE) != 0) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: cannot merge user audit "
2N/A "%m");
2N/A ret = PAM_SYSTEM_ERR;
2N/A goto adt_done;
2N/A }
2N/A if (debug) {
2N/A syslog(LOG_AUTH | LOG_DEBUG,
2N/A "pam_unix_cred: new audit set for %d:%d",
2N/A apwd.pw_uid, pwd.pw_uid);
2N/A }
2N/A } else {
2N/A /*
2N/A * No authenticated user or authenticated user is
2N/A * not a local user, no remote attribution, set
2N/A * up the initial audit as for direct user login
2N/A */
2N/A if (adt_set_user(ah, pwd.pw_uid, pwd.pw_gid,
2N/A pwd.pw_uid, pwd.pw_gid, termid, ADT_NEW) != 0) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: cannot set user audit %m");
2N/A ret = PAM_SYSTEM_ERR;
2N/A goto adt_done;
2N/A }
2N/A }
2N/A if (adt_set_proc(ah) != 0) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: cannot set process audit %m");
2N/A ret = PAM_CRED_ERR;
2N/A goto adt_done;
2N/A }
2N/A if (debug) {
2N/A syslog(LOG_AUTH | LOG_DEBUG,
2N/A "pam_unix_cred: new audit set for %d",
2N/A pwd.pw_uid);
2N/A }
2N/A } else if ((auid != AU_NOAUDITID) &&
2N/A (flags & PAM_REINITIALIZE_CRED)) {
2N/A if (adt_set_user(ah, pwd.pw_uid, pwd.pw_gid, pwd.pw_uid,
2N/A pwd.pw_gid, NULL, ADT_UPDATE) != 0) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: cannot set user audit %m");
2N/A ret = PAM_SYSTEM_ERR;
2N/A goto adt_done;
2N/A }
2N/A if (adt_set_proc(ah) != 0) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: cannot set process audit %m");
2N/A ret = PAM_CRED_ERR;
2N/A goto adt_done;
2N/A }
2N/A if (debug) {
2N/A syslog(LOG_AUTH | LOG_DEBUG,
2N/A "pam_unix_cred: audit merged for %d:%d",
2N/A auid, pwd.pw_uid);
2N/A }
2N/A } else if (debug) {
2N/A syslog(LOG_AUTH | LOG_DEBUG,
2N/A "pam_unix_cred: audit already set for %d", auid);
2N/A }
2N/Aadt_done:
2N/A if (termid != NULL)
2N/A free(termid);
2N/A if (adt_end_session(ah) != 0) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: unable to end audit session");
2N/A }
2N/A
2N/A if (ret != PAM_SUCCESS)
2N/A return (ret);
2N/A
2N/A /* Initialize the user's project */
2N/A (void) pam_get_item(pamh, PAM_RESOURCE, (void **)&kvs);
2N/A if (kvs != NULL) {
2N/A char *tmp, *lasts, *tok;
2N/A
2N/A kvs = tmp = strdup(kvs);
2N/A if (kvs == NULL)
2N/A return (PAM_BUF_ERR);
2N/A
2N/A while ((tok = strtok_r(tmp, ";", &lasts)) != NULL) {
2N/A if (strncmp(tok, PROJECT, PROJSZ) == 0) {
2N/A projname = tok + PROJSZ;
2N/A break;
2N/A }
2N/A tmp = NULL;
2N/A }
2N/A } else {
2N/A projname = NULL;
2N/A }
2N/A
2N/A if (projname == NULL || *projname == '\0') {
2N/A pproj = getdefaultproj(user, &proj, (void *)&buf,
2N/A PROJECT_BUFSZ);
2N/A } else {
2N/A pproj = getprojbyname(projname, &proj, (void *)&buf,
2N/A PROJECT_BUFSZ);
2N/A }
2N/A /* projname points into kvs, so this is the first opportunity to free */
2N/A if (kvs != NULL)
2N/A free(kvs);
2N/A if (pproj == NULL) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: no default project for user %s", user);
2N/A if (!nowarn) {
2N/A (void) snprintf(messages[0], sizeof (messages[0]),
2N/A dgettext(TEXT_DOMAIN, "No default project!"));
2N/A (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
2N/A 1, messages, NULL);
2N/A }
2N/A return (PAM_SYSTEM_ERR);
2N/A }
2N/A if ((error = setproject(proj.pj_name, user, TASK_NORMAL)) != 0) {
2N/A kva_t *kv_array;
2N/A
2N/A switch (error) {
2N/A case SETPROJ_ERR_TASK:
2N/A if (errno == EAGAIN) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: project \"%s\" resource "
2N/A "control limit has been reached",
2N/A proj.pj_name);
2N/A (void) snprintf(messages[0],
2N/A sizeof (messages[0]), dgettext(
2N/A TEXT_DOMAIN,
2N/A "Resource control limit has been "
2N/A "reached"));
2N/A } else {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: user %s could not join "
2N/A "project \"%s\": %m", user, proj.pj_name);
2N/A (void) snprintf(messages[0],
2N/A sizeof (messages[0]), dgettext(
2N/A TEXT_DOMAIN,
2N/A "Could not join default project"));
2N/A }
2N/A if (!nowarn)
2N/A (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1,
2N/A messages, NULL);
2N/A break;
2N/A case SETPROJ_ERR_POOL:
2N/A (void) snprintf(messages[0], sizeof (messages[0]),
2N/A dgettext(TEXT_DOMAIN,
2N/A "Could not bind to resource pool"));
2N/A switch (errno) {
2N/A case EACCES:
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: project \"%s\" could not "
2N/A "bind to resource pool: No resource pool "
2N/A "accepting default bindings exists",
2N/A proj.pj_name);
2N/A (void) snprintf(messages[1],
2N/A sizeof (messages[1]),
2N/A dgettext(TEXT_DOMAIN,
2N/A "No resource pool accepting "
2N/A "default bindings exists"));
2N/A break;
2N/A case ESRCH:
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: project \"%s\" could not "
2N/A "bind to resource pool: The resource pool "
2N/A "is unknown", proj.pj_name);
2N/A (void) snprintf(messages[1],
2N/A sizeof (messages[1]),
2N/A dgettext(TEXT_DOMAIN,
2N/A "The specified resource pool "
2N/A "is unknown"));
2N/A break;
2N/A default:
2N/A (void) snprintf(messages[1],
2N/A sizeof (messages[1]),
2N/A dgettext(TEXT_DOMAIN,
2N/A "Failure during pool binding"));
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: project \"%s\" could not "
2N/A "bind to resource pool: %m", proj.pj_name);
2N/A }
2N/A if (!nowarn)
2N/A (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
2N/A 2, messages, NULL);
2N/A break;
2N/A default:
2N/A /*
2N/A * Resource control assignment failed. Unlike
2N/A * newtask(1m), we treat this as an error.
2N/A */
2N/A if (error < 0) {
2N/A /*
2N/A * This isn't supposed to happen, but in
2N/A * case it does, this error message
2N/A * doesn't use error as an index, like
2N/A * the others might.
2N/A */
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: unkwown error joining "
2N/A "project \"%s\" (%d)", proj.pj_name, error);
2N/A (void) snprintf(messages[0],
2N/A sizeof (messages[0]),
2N/A dgettext(TEXT_DOMAIN,
2N/A "unkwown error joining project \"%s\""
2N/A " (%d)"), proj.pj_name, error);
2N/A } else if ((kv_array = _str2kva(proj.pj_attr, KV_ASSIGN,
2N/A KV_DELIMITER)) != NULL) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: %s resource control "
2N/A "assignment failed for project \"%s\"",
2N/A kv_array->data[error - 1].key,
2N/A proj.pj_name);
2N/A (void) snprintf(messages[0],
2N/A sizeof (messages[0]),
2N/A dgettext(TEXT_DOMAIN,
2N/A "%s resource control assignment failed for "
2N/A "project \"%s\""),
2N/A kv_array->data[error - 1].key,
2N/A proj.pj_name);
2N/A _kva_free(kv_array);
2N/A } else {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_unix_cred: resource control "
2N/A "assignment failed for project \"%s\""
2N/A "attribute %d", proj.pj_name, error);
2N/A (void) snprintf(messages[0],
2N/A sizeof (messages[0]),
2N/A dgettext(TEXT_DOMAIN,
2N/A "resource control assignment failed for "
2N/A "project \"%s\" attribute %d"),
2N/A proj.pj_name, error);
2N/A }
2N/A if (!nowarn)
2N/A (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
2N/A 1, messages, NULL);
2N/A }
2N/A return (PAM_SYSTEM_ERR);
2N/A }
2N/A
2N/A rset = tset = def = lim = NULL;
2N/A ctxt = NULL;
2N/A deflim.def = deflim.lim = NULL;
2N/A
2N/A (void) _enum_attrs(user, finddeflim, NULL, &deflim);
2N/A
2N/A if (deflim.def != NULL &&
2N/A xpol_parse_ruleset(deflim.def, &ctxt, &def) != 0 &&
2N/A getset(deflim.def, &def) != 0) {
2N/A if (pwd.pw_uid != 0) {
2N/A ret = PAM_SYSTEM_ERR;
2N/A goto out;
2N/A }
2N/A /* Always continue for root */
2N/A ctxt = NULL;
2N/A def = NULL;
2N/A }
2N/A if (ctxt != NULL && (rset = xpol_restricted_privs(ctxt)) == NULL) {
2N/A ret = PAM_SYSTEM_ERR;
2N/A goto out;
2N/A }
2N/A
2N/A if (getset(deflim.lim, &lim) != 0) {
2N/A ret = PAM_SYSTEM_ERR;
2N/A goto out;
2N/A }
2N/A
2N/A if (def == NULL) {
2N/A def = priv_allocset();
2N/A if (def == NULL) {
2N/A ret = PAM_SYSTEM_ERR;
2N/A goto out;
2N/A }
2N/A priv_basicset(def);
2N/A errno = 0;
2N/A if ((pathconf("/", _PC_CHOWN_RESTRICTED) == -1) && (errno == 0))
2N/A (void) priv_addset(def, PRIV_FILE_CHOWN_SELF);
2N/A }
2N/A if (rset != NULL) {
2N/A priv_inverse(rset);
2N/A priv_intersect(rset, def);
2N/A }
2N/A /*
2N/A * Silently limit the privileges to those actually available
2N/A * in the current zone.
2N/A */
2N/A tset = priv_allocset();
2N/A if (tset == NULL) {
2N/A ret = PAM_SYSTEM_ERR;
2N/A goto out;
2N/A }
2N/A if (getppriv(PRIV_PERMITTED, tset) != 0) {
2N/A ret = PAM_SYSTEM_ERR;
2N/A goto out;
2N/A }
2N/A if (!priv_issubset(def, tset))
2N/A priv_intersect(tset, def);
2N/A /*
2N/A * We set privilege awareness here so that I gets copied to
2N/A * P & E when the final setuid(uid) happens.
2N/A */
2N/A (void) setpflags(PRIV_AWARE, 1);
2N/A if (setppriv(PRIV_SET, PRIV_INHERITABLE, def) != 0) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_setcred: setppriv(defaultpriv) failed: %m");
2N/A ret = PAM_CRED_ERR;
2N/A }
2N/A
2N/A if (lim != NULL) {
2N/A /*
2N/A * Silently limit the privileges to the limit set available.
2N/A */
2N/A if (getppriv(PRIV_LIMIT, tset) != 0) {
2N/A ret = PAM_SYSTEM_ERR;
2N/A goto out;
2N/A }
2N/A if (!priv_issubset(lim, tset))
2N/A priv_intersect(tset, lim);
2N/A if (setppriv(PRIV_SET, PRIV_LIMIT, lim) != 0) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_setcred: setppriv(limitpriv) failed: %m");
2N/A ret = PAM_CRED_ERR;
2N/A goto out;
2N/A }
2N/A /*
2N/A * In order not to surprise certain applications, we
2N/A * need to get rid of privilege awareness and thus we must
2N/A * set this flag which will cause a reset on set*uid().
2N/A */
2N/A (void) setpflags(PRIV_AWARE_RESET, 1);
2N/A }
2N/A /*
2N/A * This may fail but we do not care as this will be reset later
2N/A * when the uids are set to their final values.
2N/A */
2N/A (void) setpflags(PRIV_AWARE, 0);
2N/A /*
2N/A * Remove PRIV_PFEXEC; stop running as if we are under a profile
2N/A * shell. A user with a profile shell will set PRIV_PFEXEC.
2N/A */
2N/A (void) setpflags(PRIV_PFEXEC, 0);
2N/A /*
2N/A * Remove PRIV_XPOLICY; this removes the extended policy.
2N/A * Install the new extended policy, if required.
2N/A */
2N/A (void) setpflags(PRIV_XPOLICY, 0);
2N/A if (ctxt != NULL && xpol_context_install(ctxt) != 0) {
2N/A syslog(LOG_AUTH | LOG_ERR,
2N/A "pam_setcred: Extended Policy failed to install: %m");
2N/A ret = PAM_CRED_ERR;
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * Create the /var/user/$USER area if it is not already
2N/A * present. pwd buffer refers to PAM_USER.
2N/A * PAM_REFRESH_CRED is usually a screen unlock like event
2N/A * so that isn't an appropriate time to create this.
2N/A */
2N/A if ((flags & (PAM_ESTABLISH_CRED | PAM_REINITIALIZE_CRED)) &&
2N/A ret == PAM_SUCCESS)
2N/A create_varuser(&pwd);
2N/Aout:
2N/A free(deflim.lim);
2N/A free(deflim.def);
2N/A
2N/A if (lim != NULL)
2N/A priv_freeset(lim);
2N/A if (def != NULL)
2N/A priv_freeset(def);
2N/A if (tset != NULL)
2N/A priv_freeset(tset);
2N/A if (rset != NULL)
2N/A priv_freeset(rset);
2N/A if (ctxt != NULL)
2N/A xpol_context_free(ctxt);
2N/A
2N/A return (ret);
2N/A}