diff options
-rw-r--r-- | src/man/pam_sss.8.xml | 22 | ||||
-rw-r--r-- | src/providers/krb5/krb5_auth.c | 7 | ||||
-rw-r--r-- | src/providers/ldap/ldap_auth.c | 8 | ||||
-rw-r--r-- | src/sss_client/pam_sss.c | 206 |
4 files changed, 235 insertions, 8 deletions
diff --git a/src/man/pam_sss.8.xml b/src/man/pam_sss.8.xml index f6ac9f477..9a681960a 100644 --- a/src/man/pam_sss.8.xml +++ b/src/man/pam_sss.8.xml @@ -85,6 +85,28 @@ </para> </refsect1> + <refsect1 id='files'> + <title>FILES</title> + <para>If a password reset by root fails, because the corresponding SSSD + provider does not support password resets, an individual message can be + displayed. This message can e.g. contain instructions about how to reset + a password.</para> + + <para>The message is read from the file + <filename>pam_sss_pw_reset_message.LOC</filename> where LOC stands for a + locale string returned by <citerefentry> + <refentrytitle>setlocale</refentrytitle><manvolnum>3</manvolnum> + </citerefentry>. If there is no matching file the content of + <filename>pam_sss_pw_reset_message.txt</filename> is displayed. Root + must be the owner of the files and only root may have read and write + permissions while all other users must have only read + permisssions.</para> + + <para>These files are searched in the directory + <filename>/etc/sssd/customize/DOMAIN_NAME/</filename>. If no matching + file is present a generic message is displayed.</para> + </refsect1> + <refsect1 id='see_also'> <title>SEE ALSO</title> <para> diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c index 650ae53fb..0c08fe16e 100644 --- a/src/providers/krb5/krb5_auth.c +++ b/src/providers/krb5/krb5_auth.c @@ -722,7 +722,14 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx, switch (state->pd->cmd) { case SSS_PAM_AUTHENTICATE: case SSS_PAM_CHAUTHTOK: + break; case SSS_PAM_CHAUTHTOK_PRELIM: + if (state->pd->priv == 1 && state->pd->authtok_size == 0) { + DEBUG(4, ("Password reset by root is not supported.\n")); + state->pam_status = PAM_PERM_DENIED; + state->dp_err = DP_ERR_OK; + goto done; + } break; case SSS_PAM_ACCT_MGMT: case SSS_PAM_SETCRED: diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c index c78f50313..83b175dc8 100644 --- a/src/providers/ldap/ldap_auth.c +++ b/src/providers/ldap/ldap_auth.c @@ -701,6 +701,14 @@ void sdap_pam_chpass_handler(struct be_req *breq) goto done; } + if (pd->priv == 1 && pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM && + pd->authtok_size == 0) { + DEBUG(4, ("Password reset by root is not supported.\n")); + pd->pam_status = PAM_PERM_DENIED; + dp_err = DP_ERR_OK; + goto done; + } + DEBUG(2, ("starting password change request for user [%s].\n", pd->user)); pd->pam_status = PAM_SYSTEM_ERR; diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c index 77dec19cb..3f88d6890 100644 --- a/src/sss_client/pam_sss.c +++ b/src/sss_client/pam_sss.c @@ -35,6 +35,10 @@ #include <stdio.h> #include <syslog.h> #include <time.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <locale.h> #include <security/pam_modules.h> #include <security/pam_ext.h> @@ -53,6 +57,9 @@ #define PWEXP_FLAG "pam_sss:password_expired_flag" +#define PW_RESET_MSG_FILENAME_TEMPLATE SSSD_CONF_DIR"/customize/%s/pam_sss_pw_reset_message.%s" +#define PW_RESET_MSG_MAX_SIZE 4096 + struct pam_items { const char* pam_service; const char* pam_user; @@ -74,6 +81,7 @@ struct pam_items { size_t pam_newauthtok_size; pid_t cli_pid; const char *login_name; + char *domain_name; }; #define DEBUG_MGS_LEN 1024 @@ -183,7 +191,7 @@ static size_t add_string_item(enum pam_item_type type, const char *str, return rp; } -static void overwrite_and_free_authtoks(struct pam_items *pi) +static void overwrite_and_free_pam_items(struct pam_items *pi) { if (pi->pam_authtok != NULL) { _pam_overwrite_n((void *)pi->pam_authtok, pi->pam_authtok_size); @@ -196,6 +204,8 @@ static void overwrite_and_free_authtoks(struct pam_items *pi) free((void *)pi->pam_newauthtok); pi->pam_newauthtok = NULL; } + + free(pi->domain_name); } static int pack_message_v3(struct pam_items *pi, size_t *size, @@ -389,6 +399,162 @@ static int do_pam_conversation(pam_handle_t *pamh, const int msg_style, return PAM_SUCCESS; } +static errno_t display_pw_reset_message(pam_handle_t *pamh, + const char *domain_name, + const char *suffix) +{ + int ret; + struct stat stat_buf; + char *msg_buf = NULL; + int fd = -1; + size_t size; + size_t total_len; + char *filename = NULL; + + if (strchr(suffix, '/') != NULL || strchr(domain_name, '/') != NULL) { + D(("Suffix [%s] or domain name [%s] contain illegal character.", suffix, + domain_name)); + return EINVAL; + } + + size = sizeof(PW_RESET_MSG_FILENAME_TEMPLATE) + strlen(domain_name) + + strlen(suffix); + filename = malloc(size); + if (filename == NULL) { + D(("malloc failed.")); + ret = ENOMEM; + goto done; + } + ret = snprintf(filename, size, PW_RESET_MSG_FILENAME_TEMPLATE, domain_name, + suffix); + if (ret < 0 || ret >= size) { + D(("snprintf failed.")); + ret = EFAULT; + goto done; + } + + fd = open(filename, O_RDONLY); + if (fd == -1) { + ret = errno; + D(("open failed [%d][%s].\n", ret, strerror(ret))); + goto done; + } + + ret = fstat(fd, &stat_buf); + if (ret == -1) { + ret = errno; + D(("fstat failed [%d][%s].", ret, strerror(ret))); + goto done; + } + + if (!S_ISREG(stat_buf.st_mode)) { + logger(pamh, LOG_ERR, + "Password reset message file is not a regular file."); + ret = EINVAL; + goto done; + } + + if (stat_buf.st_uid != 0 || stat_buf.st_gid != 0 || + (stat_buf.st_mode & ~S_IFMT) != 0644) { + logger(pamh, LOG_ERR,"Permission error, " + "file [%s] must be owned by root with permissions 0644.", + filename); + ret = EPERM; + goto done; + } + + if (stat_buf.st_size > PW_RESET_MSG_MAX_SIZE) { + logger(pamh, LOG_ERR, "Password reset message file is too large."); + ret = EFBIG; + goto done; + } + + msg_buf = malloc(stat_buf.st_size + 1); + if (msg_buf == NULL) { + D(("malloc failed.")); + ret = ENOMEM; + goto done; + } + + + total_len = 0; + while (total_len < stat_buf.st_size) { + ret = read(fd, msg_buf + total_len, stat_buf.st_size - total_len); + if (ret == -1) { + if (errno == EINTR) continue; + ret = errno; + D(("read failed [%d][%s].", ret, strerror(ret))); + goto done; + } + total_len += ret; + } + + ret = close(fd); + fd = -1; + if (ret == -1) { + ret = errno; + D(("close failed [%d][%s].", ret, strerror(ret))); + } + + if (total_len != stat_buf.st_size) { + D(("read fewer bytes [%d] than expected [%d].", total_len, + stat_buf.st_size)); + ret = EIO; + goto done; + } + + msg_buf[stat_buf.st_size] = '\0'; + + ret = do_pam_conversation(pamh, PAM_TEXT_INFO, msg_buf, NULL, NULL); + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation failed.")); + } + +done: + if (fd != -1) { + close(fd); + } + free(msg_buf); + free(filename); + + return ret; +} + +static errno_t select_pw_reset_message(pam_handle_t *pamh, struct pam_items *pi) +{ + int ret; + char *locale; + const char *domain_name; + + domain_name = pi->domain_name; + if (domain_name == NULL || *domain_name == '\0') { + D(("Domain name is unknown.")); + return EINVAL; + } + + locale = setlocale(LC_MESSAGES, NULL); + + ret = -1; + if (locale != NULL) { + ret = display_pw_reset_message(pamh, domain_name, locale); + } + + if (ret != 0) { + ret = display_pw_reset_message(pamh, domain_name, "txt"); + } + + if (ret != 0) { + ret = do_pam_conversation(pamh, PAM_TEXT_INFO, + _("Password reset by root is not supported."), + NULL, NULL); + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation failed.")); + } + } + + return ret; +} + static int user_info_offline_auth(pam_handle_t *pamh, size_t buflen, uint8_t *buf) { @@ -679,7 +845,8 @@ static int eval_user_info_response(pam_handle_t *pamh, size_t buflen, return ret; } -static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf) +static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf, + struct pam_items *pi) { int ret; size_t p=0; @@ -727,7 +894,15 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf) logger(pamh, LOG_INFO, "system info: [%s]", &buf[p]); break; case SSS_PAM_DOMAIN_NAME: + if (buf[p + (len -1)] != '\0') { + D(("domain name does not end with \\0.")); + break; + } D(("domain name: [%s]", &buf[p])); + pi->domain_name = strdup((char *) &buf[p]); + if (pi->domain_name == NULL) { + D(("strdup failed")); + } break; case SSS_ENV_ITEM: case SSS_PAM_ENV_ITEM: @@ -835,6 +1010,8 @@ static int get_pam_items(pam_handle_t *pamh, struct pam_items *pi) pi->login_name = pam_modutil_getlogin(pamh); if (pi->login_name == NULL) pi->login_name=""; + pi->domain_name = NULL; + return PAM_SUCCESS; } @@ -896,7 +1073,7 @@ static int send_and_receive(pam_handle_t *pamh, struct pam_items *pi, } pam_status = ((int32_t *)repbuf)[0]; - ret = eval_response(pamh, replen, repbuf); + ret = eval_response(pamh, replen, repbuf, pi); if (ret != PAM_SUCCESS) { D(("eval_response failed.")); pam_status = ret; @@ -1211,16 +1388,18 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, exp_data = malloc(sizeof(int)); if (exp_data == NULL) { D(("malloc failed.")); - return PAM_BUF_ERR; + ret = PAM_BUF_ERR; + break; } *exp_data = 1; ret = pam_set_data(pamh, PWEXP_FLAG, exp_data, free_exp_data); if (ret != PAM_SUCCESS) { D(("pam_set_data failed.")); - return ret; + ret = ret; + break; } - return PAM_SUCCESS; + ret = PAM_SUCCESS; } break; case SSS_PAM_ACCT_MGMT: @@ -1233,7 +1412,7 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, if (ret != PAM_SUCCESS) { D(("do_pam_conversation failed.")); } - return PAM_NEW_AUTHTOK_REQD; + ret = PAM_NEW_AUTHTOK_REQD; } break; case SSS_PAM_CHAUTHTOK: @@ -1250,6 +1429,17 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, } } break; + case SSS_PAM_CHAUTHTOK_PRELIM: + if (ret == PAM_PERM_DENIED && pi.pam_authtok_size == 0 && + getuid() == 0 && + pam_get_data(pamh, PWEXP_FLAG, (const void **) &exp_data) != + PAM_SUCCESS) { + + ret = select_pw_reset_message(pamh, &pi); + if (ret != 0) { + } + ret = PAM_PERM_DENIED; + } default: /* nothing to do */ break; @@ -1257,7 +1447,7 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, - overwrite_and_free_authtoks(&pi); + overwrite_and_free_pam_items(&pi); return ret; } |