/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
*/
#include <stdlib.h>
#include <string.h>
#include <secdb.h>
#include <user_attr.h>
#include <syslog.h>
#include <note.h>
#include <pwd.h>
#include <nss_dbdefs.h>
#include <libintl.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include <security/pam_impl.h>
#define PAM_USER_POLICY_DATA "SUNW-PAM-USER-POLICY-DATA"
/*
* Callback function for _enum_attrs() which looks for the 'pam_policy'
* keyword, passed in via the 'ctxt' argument, in any entries in the
* user_attr(4) database for this user or in any profiles granted to this
* user in the prof_attr(4) database or in policy.conf(4) which are
* passed in as key-value pair attributes in 'kva'.
*/
static int
find_pam_policy_cb(const char *profile, kva_t *kva, void *ctxt, void *result)
{
NOTE(ARGUNUSED(profile))
char *match;
if ((match = kva_match(kva, (char *)ctxt)) != NULL) {
if (*match != '/') {
char *pam_policy;
if ((strstr(match, "/") != NULL) ||
(strlen(match) >= MAXNAMELEN) ||
(*match == '\0')) {
__pam_log(LOG_AUTH | LOG_ERR,
"pam_user_policy: find_pam_policy_cb() "
"invalid path supplied: '%s'", match);
return (0);
}
(void) asprintf(&pam_policy, "%s%s", PAM_POLICY_DIR,
match);
*(char **)result = pam_policy;
} else {
*(char **)result = strdup(match);
}
return (1);
}
return (0);
}
/*
* Cleanup function for pam_set_data(3PAM) which is called from
* pam_end(3PAM) to free the memory allocated for 'pam_policy' which is
* stored here.
*/
static void
pam_policy_data_cleanup(pam_handle_t *pamh, void *data, int pam_status)
{
NOTE(ARGUNUSED(pamh))
NOTE(ARGUNUSED(pam_status))
free(data);
}
/*
* This is the common routine called by all of the PAM service module
* API routines (pam_sm(3PAM)) which implements the core of the
* pam_user_policy(5) functionality. We lookup a user's 'pam_policy' key in
* their user attributes or an assigned profile and then evaluate the
* pam.conf(4) formatted file pointed to by 'pam_policy' using pam_eval(3PAM).
* If no 'pam_policy' has been set for the user the default is 'unix'
* which uses UNIX for authentication, account management, session
* management, and password management. The value of 'pam_policy' is cached
* using pam_set_data(3PAM) so other modules in the same authentication
* transaction don't have to repeat the lookup.
*/
static int
pam_user_policy_common(pam_handle_t *pamh, int flags, int argc,
const char **argv, const char *function_name)
{
int rval;
boolean_t debug = B_FALSE;
char *user;
struct passwd pwd;
char pwd_buf[NSS_BUFLEN_PASSWD];
char *pam_policy = NULL;
if (argc > 0 && strcmp(argv[0], "debug") == 0) {
debug = B_TRUE;
}
if (debug) {
__pam_log(LOG_AUTH | LOG_DEBUG,
"pam_user_policy: %s(flags = 0x%x, argc = %d)",
function_name, flags, argc);
}
/*
* Get the value of PAM_USER; prompting if necessary when called
* by the authentication service module. This username is then
* used to look up that user's respective 'pam_policy' key in
* their user attributes.
*/
if (strcmp(function_name, "pam_sm_authenticate") == 0) {
if ((rval = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS) {
if (debug) {
__pam_log(LOG_AUTH | LOG_DEBUG,
"pam_user_policy: pam_get_user() failed: "
"%s", pam_strerror(pamh, rval));
}
return (rval);
}
} else {
(void) pam_get_item(pamh, PAM_USER, (void **)&user);
}
if (user == NULL || *user == '\0') {
if (debug) {
__pam_log(LOG_AUTH | LOG_DEBUG,
"pam_user_policy: PAM_USER is NULL or empty");
}
return (PAM_IGNORE);
}
if (getpwnam_r(user, &pwd, pwd_buf, sizeof (pwd_buf)) == NULL) {
if (debug) {
__pam_log(LOG_AUTH | LOG_DEBUG,
"pam_user_policy: unknown username '%s'", user);
}
return (PAM_IGNORE);
}
/*
* Check if 'pam_policy' was looked up previously and stashed away
* successfully. Since the 'pam_policy' data is per-user we
* don't check for a previously saved copy unless the user has
* successfully authenticated.
*/
if ((strcmp(function_name, "pam_sm_authenticate") != 0) &&
(pam_get_data(pamh, PAM_USER_POLICY_DATA,
(const void **)&pam_policy) == PAM_SUCCESS)) {
if (debug) {
__pam_log(LOG_AUTH | LOG_DEBUG, "pam_user_policy: "
"pam_get_data: pam_policy = %s for user '%s'",
pam_policy ? pam_policy : "NULL", user);
}
return (pam_eval(pamh, pam_policy));
}
/*
* Walk the user_attr(4) and prof_attr(4) databases for this user's
* attributes and any profiles granted as well as profiles specified
* in policy.conf(4) for the 'pam_policy' keyword and store the value
* associated with that key, if any, in pam_policy_attr.pam_policy.
*/
(void) _enum_attrs(user, find_pam_policy_cb, USERATTR_PAM_POLICY,
&pam_policy);
if (debug) {
__pam_log(LOG_AUTH | LOG_DEBUG, "pam_user_policy: "
"find_pam_policy: pam_policy = %s for user '%s'",
pam_policy ? pam_policy : "NULL", user);
}
/*
* Default to UNIX policy if no pam_policy key set for user.
* This is done by falling through to the rest of /etc/pam.conf
* which by default implements UNIX authentication.
*/
if (pam_policy == NULL) {
return (PAM_IGNORE);
}
/*
* Store the pam_policy in the pam handle in case there are
* additional pam_user_policy(5) modules configured in the stack
* for this PAM authentication transaction.
*/
if ((rval = pam_set_data(pamh, PAM_USER_POLICY_DATA, pam_policy,
pam_policy_data_cleanup)) != PAM_SUCCESS) {
__pam_log(LOG_AUTH | LOG_ERR,
"pam_user_policy: pam_set_data() failed: %s",
pam_strerror(pamh, rval));
free(pam_policy);
return (PAM_SERVICE_ERR);
}
return (pam_eval(pamh, pam_policy));
}
int
pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
return (pam_user_policy_common(pamh, flags, argc, argv,
"pam_sm_acct_mgmt"));
}
int
pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
return (pam_user_policy_common(pamh, flags, argc, argv,
"pam_sm_authenticate"));
}
int
pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
return (pam_user_policy_common(pamh, flags, argc, argv,
"pam_sm_chauthtok"));
}
int
pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
return (pam_user_policy_common(pamh, flags, argc, argv,
"pam_sm_open_session"));
}
int
pam_sm_close_session(pam_handle_t *pamh, int flags, int argc,
const char **argv)
{
return (pam_user_policy_common(pamh, flags, argc, argv,
"pam_sm_close_session"));
}
int
pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
return (pam_user_policy_common(pamh, flags, argc, argv,
"pam_sm_setcred"));
}