/*
SSSD
PAM Responder
Copyright (C) Simo Sorce <ssorce@redhat.com> 2009
Copyright (C) Sumit Bose <sbose@redhat.com> 2009
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <time.h>
#include "util/auth_utils.h"
#include "responder/common/responder_packet.h"
#include "responder/common/responder.h"
#include "responder/common/negcache.h"
#include "providers/data_provider.h"
#include "responder/pam/pam_helpers.h"
#include "responder/common/cache_req/cache_req.h"
enum pam_verbosity {
};
static errno_t
const char *username);
static errno_t
const char *name,
struct tevent_context *ev,
struct pam_auth_req *preq,
const char *user_error_message,
{
size_t p;
return ENOMEM;
}
p = 0;
if (p != *resp_len) {
}
return EOK;
}
{
"pack_user_info_account_expired failed.\n");
} else {
}
}
}
{
int i;
/* If none specific domains got requested via pam, all domains are allowed.
*/
if (!pd->requested_domains) {
return true;
}
for (i = 0; pd->requested_domains[i]; i++) {
continue;
}
return true;
}
return false;
}
size_t *c)
{
auth_token_data = body+(*c);
switch (auth_token_type) {
case SSS_AUTHTOK_TYPE_EMPTY:
break;
if (auth_token_length == 0) {
} else {
}
break;
case SSS_AUTHTOK_TYPE_2FA:
case SSS_AUTHTOK_TYPE_SC_PIN:
break;
default:
return EINVAL;
}
*c += auth_token_length;
return ret;
}
size_t *c) {
/* If the string isn't valid UTF-8, fail */
return EINVAL;
}
*c += size;
return EOK;
}
return EINVAL;
return EOK;
}
{
const char *name;
if (!name) {
return EIO;
}
}
return EOK;
}
{
size_t c;
int ret;
char *requested_domains;
return EINVAL;
}
if (start != SSS_START_OF_PAM_REQUEST
|| terminator != SSS_END_OF_PAM_REQUEST) {
return EINVAL;
}
c = sizeof(uint32_t);
do {
if (type == SSS_END_OF_PAM_REQUEST) {
} else {
/* the uint32_t end maker SSS_END_OF_PAM_REQUEST does not count to
* the remaining buffer */
return EINVAL;
}
switch(type) {
case SSS_PAM_ITEM_USER:
break;
case SSS_PAM_ITEM_SERVICE:
break;
case SSS_PAM_ITEM_TTY:
break;
case SSS_PAM_ITEM_RUSER:
break;
case SSS_PAM_ITEM_RHOST:
break;
&c);
true, &pd->requested_domains,
NULL);
"Failed to parse requested_domains list!\n");
return ret;
}
break;
case SSS_PAM_ITEM_CLI_PID:
break;
case SSS_PAM_ITEM_AUTHTOK:
break;
case SSS_PAM_ITEM_NEWAUTHTOK:
break;
default:
"Ignoring unknown data type [%d].\n", type);
c += size;
}
}
} while(c < blen);
return EOK;
}
{
int ret;
return ret;
}
return EINVAL;
}
return EOK;
}
{
auth_token_data = body+(*c);
switch (auth_token_type) {
case SSS_AUTHTOK_TYPE_EMPTY:
break;
break;
default:
return EINVAL;
}
*c += auth_token_length;
return ret;
}
{
int ret;
end = 0;
/* user name */
if (ret) {
return ret;
}
if (ret) {
return ret;
}
return EOK;
}
/*=Save-Last-Login-State===================================================*/
{
if (!attrs) {
goto fail;
}
goto fail;
}
goto fail;
}
goto fail;
}
goto fail;
} else {
}
return EOK;
fail:
return ret;
}
char * const *pam_filter_opts)
{
size_t c;
const char *var_name;
const char *service;
if (pam_filter_opts == NULL) {
return EOK;
}
for (c = 0; pam_filter_opts[c] != NULL; c++) {
continue;
}
var_name_len = 0;
/* Neither plain ENV nor ENV:, ignored */
continue;
}
/* check if there is a second ':' in the option and use the following
* data, if any, as service name. */
} else {
service++;
/* handle empty service name "ENV:var:" */
if (*service == '\0') {
}
}
}
/* handle empty var name "ENV:" or "ENV::service" */
if (var_name_len == 0) {
}
"Found PAM ENV filter for variable [%.*s] and service [%s].\n",
/* current service does not match the filter */
continue;
}
/* All environment variables should be filtered */
resp->do_not_send_to_client = true;
continue;
}
resp->do_not_send_to_client = true;
}
}
return EOK;
}
struct response_data *resp_list,
{
int ret;
"Failed to read PAM verbosity, not fatal.\n");
}
}
goto done;
}
if (pam_verbosity == PAM_VERBOSITY_NO_MESSAGES) {
resp->do_not_send_to_client = true;
continue;
}
resp->do_not_send_to_client = false;
switch (user_info_type) {
"User info offline auth entry is "
"too short.\n");
goto done;
}
sizeof(int64_t));
if ((expire_date == 0 &&
(expire_date > 0 &&
resp->do_not_send_to_client = true;
}
break;
default:
"User info type [%d] not filtered.\n",
}
resp->do_not_send_to_client = false;
goto done;
}
resp->do_not_send_to_client = true;
}
}
done:
return ret;
}
{
}
const char **password)
{
int ret;
const char *fa2;
switch (sss_authtok_get_type(authtok)) {
break;
case SSS_AUTHTOK_TYPE_2FA:
break;
default:
}
return ret;
}
return EOK;
}
/*
* Add a request to add a variable to the PAM user environment, containing the
* actual (not overridden) user shell, in case session recording is enabled.
*/
const char *var_name)
{
int ret;
bool enabled;
const char *enabled_str;
const char *shell;
char *buf;
/* Create temporary talloc context */
goto done;
}
/* Check if session recording is enabled */
enabled = false;
enabled = true;
} else {
if (enabled_str == NULL) {
"%s attribute not found\n", SYSDB_SESSION_RECORDING);
goto done;
enabled = true;
enabled = false;
} else {
goto done;
}
}
/* Export original shell if recording is enabled and so it's overridden */
if (enabled) {
/* Extract the shell */
goto done;
}
/* Format environment entry */
goto done;
}
/* Add request to add the entry to user environment */
goto done;
}
}
done:
return ret;
}
{
int ret;
int p;
char* pam_account_expired_message;
char* pam_account_locked_message;
int pam_verbosity;
"Failed to read PAM verbosity, not fatal.\n");
}
"pam_reply called with result [%d]: %s.\n",
/* We have Smartcard credentials and the backend indicates that it is
* offline (PAM_AUTHINFO_UNAVAIL) or cannot handle the credentials
* (PAM_BAD_ITEM), so let's try authentication against the Smartcard
* PAM_NO_MODULE_DATA is returned by the krb5 backend if no
* authentication method was found at all, this might happen if the
* user has a Smartcard assigned but the pkint plugin is not available
* on the client. */
"Backend cannot handle Smartcard authentication, "
"trying local Smartcard authentication.\n");
preq->cert_auth_local = true;
return;
}
case SSS_PAM_AUTHENTICATE:
(pd->offline_auth == false)) {
bool use_cached_auth;
/* backup value of preq->use_cached_auth*/
/* set to false to avoid entering this branch when pam_reply()
* is recursively called from pam_handle_cached_login() */
preq->use_cached_auth = false;
/* do auth with offline credentials */
pd->offline_auth = true;
"Fatal: Sysdb CTX not found for domain"
goto done;
}
"get_password_and_type_for_cache_auth failed.\n");
goto done;
}
&exp_date, &delay_until);
return;
}
break;
case SSS_PAM_CHAUTHTOK_PRELIM:
case SSS_PAM_CHAUTHTOK:
"Password change not possible while offline.\n");
(const uint8_t *) &user_info_type);
goto done;
}
break;
/* TODO: we need the pam session cookie here to make sure that cached
* authentication was successful */
case SSS_PAM_SETCRED:
case SSS_PAM_ACCT_MGMT:
case SSS_PAM_OPEN_SESSION:
case SSS_PAM_CLOSE_SESSION:
"Assuming offline authentication setting status for "
break;
default:
}
}
"sysdb_null_last_online_auth_with_curr_token failed: "
goto done;
}
}
if (pd->response_delay > 0) {
goto done;
}
pd->response_delay = 0;
"Failed to add event pam_reply_delay.\n");
goto done;
}
return;
}
/* If this was a successful login, save the lastLogin time */
!pd->offline_auth &&
!pd->last_auth_saved &&
goto done;
}
return;
}
goto done;
}
/* Account expiration warning is printed for sshd. If pam_verbosity
* is equal or above PAM_VERBOSITY_INFO then all services are informed
* about account expiration.
*/
pam_verbosity >= PAM_VERBOSITY_INFO)) {
"Failed to get expiration message: %d:[%s].\n",
goto done;
}
}
if (pd->account_locked) {
"Failed to get expiration message: %d:[%s].\n",
goto done;
}
}
}
goto done;
}
}
/*
* Export non-overridden shell to tlog-rec-session when opening the session
*/
"failed to export the shell to tlog-rec-session.\n");
goto done;
}
}
resp_c = 0;
resp_size = 0;
if (!resp->do_not_send_to_client) {
resp_c++;
}
}
sizeof(int32_t) +
goto done;
}
p = 0;
p += sizeof(int32_t);
p += sizeof(int32_t);
if (!resp->do_not_send_to_client) {
p += sizeof(int32_t);
p += sizeof(int32_t);
}
}
done:
}
bool use_cached_auth)
{
case PAM_SUCCESS:
"talloc_size failed, cannot prepare user info.\n");
} else {
}
}
break;
case PAM_PERM_DENIED:
if (delayed_until >= 0) {
"talloc_size failed, cannot prepare user info.\n");
} else {
"pam_add_response failed.\n");
}
}
}
break;
case PAM_AUTH_ERR:
/* Was this attempt to authenticate from cache? */
if (use_cached_auth) {
/* Don't try cached authentication again, try online check. */
"Cached authentication failed for: %s\n",
preq->cached_auth_failed = true;
return;
}
break;
default:
}
return;
}
/* TODO: we should probably return some sort of cookie that is set in the
* PAM_ENVIRONMENT, so that we can save performing some calls and cache
* data. */
{
const char *key_id;
NULL);
if (terminator != SSS_END_OF_PAM_REQUEST) {
goto done;
}
}
case 1:
break;
case 2:
break;
case 3:
break;
default:
}
goto done;
}
pd->logon_name,
} else {
/* SSS_PAM_PREAUTH request may have a missing name, e.g. if the
* name is determined with the help of a certificate. During
* SSS_PAM_AUTHENTICATE at least a key ID is needed to identify the
* selected certificate. */
== SSS_AUTHTOK_TYPE_SC_KEYPAD)) {
goto done;
}
"Missing logon and Smartcard key ID during "
"authentication.\n");
ret = ERR_NO_CREDS;
goto done;
}
} else {
ret = ERR_NO_CREDS;
goto done;
}
}
done:
return ret;
}
{
/* If there is still a request pending, tell the spy
* the client is going away
*/
}
return 0;
}
{
/* root is always trusted */
if (client_euid(creds) == 0) {
return true;
}
/* All uids are allowed */
if (trusted_uids_count == 0) {
return true;
}
return false;
}
char **public_dom_names,
{
size_t i;
for(i=0; i < public_dom_names_count; i++) {
return true;
}
}
return false;
}
static enum cache_req_dom_type
{
/* By default, only POSIX domains are to be contacted */
for (int i = 0; pctx->app_services[i]; i++) {
break;
}
}
return req_dom_type;
}
struct tevent_context *ev,
struct pam_auth_req *preq,
{
int p11_child_timeout;
char *cert_verification_opts;
"Failed to read p11_child_timeout from confdb: [%d]: %s\n",
return ret;
}
"Failed to read certificate_verification from confdb: [%d]: %s\n",
return ret;
}
pd);
return ENOMEM;
}
return EAGAIN;
}
{
int ret;
if (!preq) {
return ENOMEM;
}
preq->cert_auth_local = false;
return ENOMEM;
}
pctx->trusted_uids);
if (!preq->is_uid_trusted) {
}
} else {
}
goto done;
goto done;
}
/* Determine what domain type to contact */
/* Try backend first for authentication before doing local Smartcard
* authentication if a logon name is available. Otherwise try to derive
* the logon name from the certificate first. */
/* Finish here */
goto done;
}
done:
}
{
struct pam_auth_req);
const char *cert;
goto done;
}
"No certificate found and no logon name given, " \
"authentication not possible.\n");
} else {
"No certificate returned, authentication failed.\n");
} else {
}
}
goto done;
}
goto done;
}
return;
done:
}
{
return EINVAL;
}
return ENOMEM;
}
return EOK;
}
struct cache_req_result **results,
struct ldb_result **ldb_results)
{
int ret;
size_t c;
size_t d;
size_t r = 0;
}
return ENOMEM;
}
if (count == 0) {
*ldb_results = res;
return EOK;
}
return ENOMEM;
}
if (r >= count) {
"More results found then counted before.\n");
goto done;
}
}
}
*ldb_results = res;
done:
}
return ret;
}
{
int ret;
struct pam_auth_req);
goto done;
}
goto done;
}
}
goto done;
}
return;
}
"Found [%zu] certificates and [%zu] related users.\n",
if (cert_user_count == 0) {
"Missing logon name and no certificate user found.\n");
goto done;
}
} else {
"Missing logon name only allowed during (pre-)auth.\n");
goto done;
}
if (cert_count > 1) {
"add_pam_cert_response failed.\n");
}
}
goto done;
}
if (cert_user_count == 1) {
if (cert_user_objs == NULL) {
goto done;
}
cert_user_objs->msgs[0],
SYSDB_NAME, NULL);
"Certificate user object has not name.\n");
goto done;
}
"Found certificate user [%s].\n", cert_user);
"sss_parse_name_for_domains failed.\n");
goto done;
}
}
}
goto done;
}
/* Without user name hints the certificate must map to single user
* if no login name was given */
"More than one user mapped to certificate.\n");
ret = ERR_NO_CREDS;
goto done;
}
/* If logon_name was not given during authentication add a
* SSS_PAM_CERT_INFO message to send the name to the caller. */
goto done;
}
}
/* cert_user will be returned to the PAM client as user name, so
* we can use it here already e.g. to set in initgroups timeout */
goto done;
}
}
}
} else {
}
}
done:
}
{
struct pam_auth_req);
goto done;
}
"p11_refresh_certmap_ctx failed, "
"certificate matching might not work as expected");
}
/* If not, cache_req will error out later */
goto done;
}
goto done;
}
/* try backend first for authentication before doing local Smartcard
* authentication */
/* Finish here */
goto done;
}
done:
}
{
int ret;
return ENOMEM;
}
/* The initgr cache is used to make sure that during a single PAM session
* (auth, acct_mgtm, ....) the backend is contacted only once. logon_name
* is the name provided by the PAM client and will not be modified during
* the request, so it makes sense to use it here instead od the pd->user. */
/* Entry is still valid, force to lookup in the cache first */
cache_req_data_set_bypass_cache(data, false);
/* Call the data provider first */
cache_req_data_set_bypass_cache(data, true);
} else {
return EIO;
}
0,
NULL,
data);
if (!dpreq) {
"Out of memory sending data provider request\n");
return ENOMEM;
}
/* tell caller we are in an async call */
return EAGAIN;
}
{
int ret;
"Fatal error, killing connection!\n");
return;
}
pctx->id_timeout);
"Could not save initgr timestamp."
"Proceeding with PAM actions\n");
}
}
}
}
{
switch (ret) {
case EOK:
break;
case EAGAIN:
/* performing async request, just return */
break;
case ENOENT:
break;
case ERR_NO_CREDS:
break;
default:
break;
}
return EOK;
}
const char* user,
int cached_auth_timeout,
bool *_result)
{
bool result;
"sysdb_get_last_online_auth_with_curr_token failed: %s:[%d]\n",
goto done;
}
done:
}
return ret;
}
{
bool is_cachable;
switch(cmd) {
case SSS_PAM_AUTHENTICATE:
is_cachable = true;
break;
default:
is_cachable = false;
}
return is_cachable;
}
{
bool cachable = false;
if (type == SSS_AUTHTOK_TYPE_PASSWORD) {
cachable = true;
} else {
}
return cachable;
}
int pam_cmd,
struct sss_auth_token *authtok,
const char* user,
bool cached_auth_failed)
{
bool result = false;
if (!cached_auth_failed /* don't try cached auth again */
&& domain->cached_auth_timeout > 0
&& pam_is_cmd_cachable(pam_cmd)) {
&result);
/* non-critical, consider fail as 'non-fresh value' */
"pam_is_last_online_login_fresh failed: %s:[%d]\n",
}
}
return result;
}
{
int ret;
const char *cert_user;
size_t c;
bool found = false;
}
/* Untrusted users can access only public domains. */
if (!preq->is_uid_trusted &&
pctx->public_domains_count)) {
return;
}
/* skip this domain if not requested and the user is trusted
* as untrusted users can't request a domain */
if (preq->is_uid_trusted &&
return;
}
preq->cached_auth_failed)) {
preq->use_cached_auth = true;
return;
}
/* Check if user matches certificate user */
found = false;
if (cert_user_objs == NULL) {
"Unexpected missing certificate user, "
"trying next certificate.\n");
continue;
}
for (c = 0; c < cert_user_objs->count; c++) {
SYSDB_NAME, NULL);
/* Even if there might be other users mapped to the
* certificate a missing SYSDB_NAME indicates some critical
* condition which justifies that the whole request is aborted
* */
"Certificate user object has no name.\n");
return;
}
found = true;
"sss_authtok_set_sc failed, Smartcard "
"authentication detection might fail in "
"the backend.\n");
}
}
}
}
}
}
if (found) {
/* We are done if we do not have to call the backend */
&& preq->cert_auth_local) {
return;
}
} else {
"User and certificate user do not match, "
"continue with other authentication methods.\n");
} else {
"User and certificate user do not match.\n");
return;
}
}
}
} else {
}
}
}
}
}
}
}
}
}
}
{
}
{
{3, "2009-09-14", "make cli_pid mandatory"},
{2, "2009-05-12", "new format <type><size><data>"},
{1, "2008-09-05", "initial version, \\0 terminated strings"},
};
return pam_cli_protocol_version;
}
{
{SSS_CLI_NULL, NULL}
};
return sss_cmds;
}
const char *username,
{
int ret;
goto done;
}
goto done;
}
value);
done:
}
return ret;
}
static errno_t
const char *username)
{
}
static errno_t
const char *name,
{
goto done;
}
goto done;
}
goto done;
}
"sysdb_search_user_by_name failed [%d][%s].\n",
goto done;
}
/* Check offline_auth_cache_timeout */
0);
done:
}
return ret;
}