pamsrv_cmd.c revision a97f6203967b801d666ac686cdb7c76a7dfe55a9
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek PAM Responder
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek Copyright (C) Simo Sorce <ssorce@redhat.com> 2009
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek Copyright (C) Sumit Bose <sbose@redhat.com> 2009
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek This program is free software; you can redistribute it and/or modify
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek it under the terms of the GNU General Public License as published by
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek the Free Software Foundation; either version 3 of the License, or
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek (at your option) any later version.
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek This program is distributed in the hope that it will be useful,
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek but WITHOUT ANY WARRANTY; without even the implied warranty of
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek GNU General Public License for more details.
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek You should have received a copy of the GNU General Public License
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek along with this program. If not, see <http://www.gnu.org/licenses/>.
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek#define DEFAULT_PAM_VERBOSITY PAM_VERBOSITY_IMPORTANT
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozekstatic void pam_reply(struct pam_auth_req *preq);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozekstatic int extract_authtok(uint32_t *type, uint32_t *size, uint8_t **tok,
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek if (data_size < sizeof(uint32_t) || *c+data_size > blen ||
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek SAFEALIGN_COPY_UINT32_CHECK(type, &body[*c], blen, c);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozekstatic int extract_string(char **var, size_t size, uint8_t *body, size_t blen,
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek if (*c+size > blen || SIZE_T_OVERFLOW(*c, size)) return EINVAL;
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek /* If the string isn't valid UTF-8, fail */
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozekstatic int extract_uint32_t(uint32_t *var, size_t size, uint8_t *body,
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek if (size != sizeof(uint32_t) || *c+size > blen || SIZE_T_OVERFLOW(*c, size))
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek SAFEALIGN_COPY_UINT32_CHECK(var, &body[*c], blen, c);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozekstatic int pd_set_primary_name(const struct ldb_message *msg,struct pam_data *pd)
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek const char *name;
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, ("A user with no name?\n"));
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek DEBUG(SSSDBG_TRACE_FUNC, ("User's primary name is %s\n", name));
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozekstatic int pam_parse_in_data_v2(struct sss_domain_info *domains,
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek ((uint32_t *)body)[0] != SSS_START_OF_PAM_REQUEST ||
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek memcmp(&body[blen - sizeof(uint32_t)], &terminator, sizeof(uint32_t)) != 0) {
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek SAFEALIGN_COPY_UINT32_CHECK(&type, &body[c], blen, &c);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek SAFEALIGN_COPY_UINT32_CHECK(&size, &body[c], blen, &c);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek /* the uint32_t end maker SSS_END_OF_PAM_REQUEST does not count to
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek * the remaining buffer */
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek ret = extract_string(&pam_user, size, body, blen, &c);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek ret = extract_string(&pd->service, size, body, blen, &c);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek ret = extract_string(&pd->tty, size, body, blen, &c);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek ret = extract_string(&pd->ruser, size, body, blen, &c);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek ret = extract_string(&pd->rhost, size, body, blen, &c);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek ret = extract_authtok(&pd->authtok_type, &pd->authtok_size,
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek DEBUG(1,("Ignoring unknown data type [%d].\n", type));
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek } while(c < blen);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek if (pd->user == NULL || *pd->user == '\0') return EINVAL;
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozekstatic int pam_parse_in_data_v3(struct sss_domain_info *domains,
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek ret = pam_parse_in_data_v2(domains, default_domain, pd, body, blen);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozekstatic int pam_parse_in_data(struct sss_domain_info *domains,
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek /* user name */
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek for (start = end; end < last; end++) if (body[end] == '\0') break;
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek ret = sss_parse_name_for_domains(pd, domains, default_domain,
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek for (start = end; end < last; end++) if (body[end] == '\0') break;
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek for (start = end; end < last; end++) if (body[end] == '\0') break;
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek for (start = end; end < last; end++) if (body[end] == '\0') break;
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek for (start = end; end < last; end++) if (body[end] == '\0') break;
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek DEBUG(1, ("Invalid authtok size: %d\n", pd->authtok_size));
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek if (pd->newauthtok_size >= blen) return EINVAL;
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek DEBUG(1, ("Invalid newauthtok size: %d\n", pd->newauthtok_size));
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek/*=Save-Last-Login-State===================================================*/
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozekstatic errno_t set_last_login(struct pam_auth_req *preq)
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_ONLINE_AUTH, time(NULL));
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_LOGIN, time(NULL));
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek DEBUG(0, ("Fatal: Sysdb context not found for this domain!\n"));
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek ret = sysdb_set_user_attr(dbctx, preq->pd->user, attrs, SYSDB_MOD_REP);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek talloc_asprintf(mem_ctx, "%s/logins/%s", selinux_policy_root(), username)
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozekstatic errno_t write_selinux_login_file(const char *username, char *string)
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n"));
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek tmp_path = talloc_asprintf(tmp_ctx, "%sXXXXXX", path);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek DEBUG(SSSDBG_OP_FAILURE, ("creating the temp file for SELinux "
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek full_string = talloc_asprintf(tmp_ctx, "%s:%s", ALL_SERVICES, string);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek written = sss_atomic_write_s(fd, full_string, len);
2745b0156f12df7a7eb93d57716233243658e4d9Jakub Hrozek DEBUG(SSSDBG_OP_FAILURE, ("writing to SELinux data file %s"
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek DEBUG(SSSDBG_OP_FAILURE, ("Expected to write %d bytes, wrote %d",
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek DEBUG(SSSDBG_MINOR_FAILURE, ("Could not remove file [%s]",
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozekstatic errno_t remove_selinux_login_file(const char *username)
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek /* Just return success if the file was not there */
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek ("Could not remove login file %s [%d]: %s\n",
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozekstatic errno_t process_selinux_mappings(struct pam_auth_req *preq)
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek DEBUG(SSSDBG_FATAL_FAILURE, ("Fatal: Sysdb CTX not found for "
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek ret = sysdb_search_selinux_config(tmp_ctx, sysdb, NULL, &config);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek DEBUG(SSSDBG_TRACE_INTERNAL, ("No SELinux support found for the domain\n"));
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek default_user = ldb_msg_find_attr_as_string(config,
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek if (!default_user || default_user[0] == '\0') {
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek /* Skip creating the maps altogether if there is no default
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek * or empty default
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek DEBUG(SSSDBG_OP_FAILURE, ("No map order given!\n"));
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek /* The "order" string contains one or more SELinux user records
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek * separated by $. Now we need to create an array of string from
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek * this one string. First find out how many elements in the array
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek * will be. This way only one alloc will be necessary for the array
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek for (i = 0; i < len; i++) {
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek order_array = talloc_array(tmp_ctx, char *, order_count);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek /* Now fill the array with pointers to the original string. Also
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek * use binary zeros to make multiple string out of the one.
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek for (i = 0; i < len; i++) {
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek /* Fetch all maps applicable to the user who is currently logging in */
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek ret = sysdb_search_selinux_usermap_by_username(tmp_ctx, sysdb, pd->user,
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek /* If no maps match, we'll use the default SELinux user from the
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek file_content = talloc_strdup(tmp_ctx, default_user);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek /* Iterate through the order array and try to find SELinux users
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek * in fetched maps. The order array contains all SELinux users
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek * allowed in the domain in the same order they should appear
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek * in the SELinux config file. If any user from the order array
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek * is not in fetched user maps, it means it should not be allowed
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek * for the user who is just logging in.
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek * Right now we have empty content of the SELinux config file,
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek * we shall add only those SELinux users that are present both in
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek * the order array and user maps applicable to the user who is
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek * logging in.
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek for (i = 0; i < order_count; i++) {
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek tmp_str = sss_selinux_map_get_seuser(usermaps[j]);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek if (tmp_str && !strcasecmp(tmp_str, order_array[i])) {
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek /* If file_content contained something, overwrite it.
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek * This record has higher priority.
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek file_content = talloc_strdup(tmp_ctx, tmp_str);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek ret = write_selinux_login_file(pd->user, file_content);
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek /* Don't overwrite original error condition if there was one */
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozekstatic errno_t filter_responses(struct confdb_ctx *cdb,
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek ret = confdb_get_int(cdb, CONFDB_PAM_CONF_ENTRY,
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek DEBUG(1, ("Failed to read PAM verbosity, not fatal.\n"));
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek if (pam_verbosity == PAM_VERBOSITY_NO_MESSAGES) {
45aeb924ec3ac448bb8d174a5cc061ed98b147c7Jakub Hrozek memcpy(&user_info_type, resp->data, sizeof(uint32_t));
switch (user_info_type) {
return EINVAL;
sizeof(int64_t));
if ((expire_date == 0 &&
(expire_date > 0 &&
return EOK;
int ret;
case SSS_PAM_AUTHENTICATE:
goto done;
case SSS_PAM_CHAUTHTOK_PRELIM:
case SSS_PAM_CHAUTHTOK:
goto done;
case SSS_PAM_SETCRED:
case SSS_PAM_ACCT_MGMT:
case SSS_PAM_OPEN_SESSION:
case SSS_PAM_CLOSE_SESSION:
goto done;
goto done;
goto done;
#ifdef HAVE_SELINUX_LOGIN_DIR
goto done;
goto done;
resp_c = 0;
resp_size = 0;
resp_c++;
sizeof(int32_t) +
goto done;
p += sizeof(int32_t);
p += sizeof(int32_t);
p += sizeof(int32_t);
p += sizeof(int32_t);
done:
switch (ret) {
case EOK:
case ENOENT:
case EINVAL:
case EACCES:
if (delayed_until >= 0) {
goto done;
done:
return ret;
int ret;
if (!preq) {
return ENOMEM;
return ENOMEM;
goto done;
goto done;
goto done;
if (!dom) {
goto done;
goto done;
done:
struct pam_auth_req);
goto done;
goto done;
goto done;
done:
int ret;
while (dom) {
if (!dom) break;
if (!name) {
return ENOMEM;
return EIO;
return EFAULT;
return EIO;
return ENOENT;
return ENOENT;
SYSDB_CACHE_EXPIRE, 0);
return ret;
return EOK;
if (!dom) {
if (!dpreq) {
return ENOMEM;
if(!cb_ctx) {
return ENOMEM;
return EAGAIN;
return ENOENT;
char *err_msg;
&err_msg);
switch (ret) {
case EOK:
case EAGAIN:
case ENOENT:
return EOK;
int ret;
char *name;
if (err_maj) {
if (!name) {
goto done;
done:
if (ret) {
int ret;
return pam_cli_protocol_version;
return sss_cmds;