#include "pam_winbind.h"
+#define _PAM_LOG_FUNCTION_ENTER(function, pamh, ctrl, flags) \
+ do { \
+ _pam_log_debug(pamh, ctrl, LOG_DEBUG, "[pamh: 0x%08x] ENTER: " function " (flags: 0x%04x)", (uint32) pamh, flags); \
+ _pam_log_state(pamh, ctrl); \
+ } while (0)
+
+#define _PAM_LOG_FUNCTION_LEAVE(function, pamh, ctrl, retval) \
+ do { \
+ _pam_log_debug(pamh, ctrl, LOG_DEBUG, "[pamh: 0x%08x] LEAVE: " function " returning %d", (uint32) pamh, retval); \
+ _pam_log_state(pamh, ctrl); \
+ } while (0)
+
/* data tokens */
#define MAX_PASSWD_TRIES 3
}
#endif /* HAVE_PAM_VSYSLOG */
+static BOOL _pam_log_is_silent(int ctrl)
+{
+ return on(ctrl, WINBIND_SILENT);
+}
+
+static void _pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
static void _pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
{
va_list args;
- if (ctrl & WINBIND_SILENT) {
+ if (_pam_log_is_silent(ctrl)) {
return;
}
va_end(args);
}
-static void _pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
+static BOOL _pam_log_is_debug_enabled(int ctrl)
{
- va_list args;
+ if (ctrl == -1) {
+ return False;
+ }
- if (ctrl & WINBIND_SILENT) {
- return;
+ if (_pam_log_is_silent(ctrl)) {
+ return False;
}
if (!(ctrl & WINBIND_DEBUG_ARG)) {
+ return False;
+ }
+
+ return True;
+}
+
+static BOOL _pam_log_is_debug_state_enabled(int ctrl)
+{
+ if (!(ctrl & WINBIND_DEBUG_STATE)) {
+ return False;
+ }
+
+ return _pam_log_is_debug_enabled(ctrl);
+}
+
+static void _pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
+static void _pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
+{
+ va_list args;
+
+ if (!_pam_log_is_debug_enabled(ctrl)) {
return;
}
va_end(args);
}
+static void _pam_log_state_datum(const pam_handle_t *pamh, int ctrl, int item_type, const char *key, int is_string)
+{
+ const void *data = NULL;
+ if (item_type != 0) {
+ pam_get_item(pamh, item_type, &data);
+ } else {
+ pam_get_data(pamh, key, &data);
+ }
+ if (data != NULL) {
+ const char *type = (item_type != 0) ? "ITEM" : "DATA";
+ if (is_string != 0) {
+ _pam_log_debug(pamh, ctrl, LOG_DEBUG, "[pamh: 0x%08x] STATE: %s(%s) = \"%s\" (0x%08x)", (uint32) pamh, type, key, (const char *) data, (uint32) data);
+ } else {
+ _pam_log_debug(pamh, ctrl, LOG_DEBUG, "[pamh: 0x%08x] STATE: %s(%s) = 0x%08x", (uint32) pamh, type, key, (uint32) data);
+ }
+ }
+}
+
+#define _PAM_LOG_STATE_DATA_POINTER(pamh, ctrl, module_data_name) \
+ _pam_log_state_datum(pamh, ctrl, 0, module_data_name, 0)
+
+#define _PAM_LOG_STATE_DATA_STRING(pamh, ctrl, module_data_name) \
+ _pam_log_state_datum(pamh, ctrl, 0, module_data_name, 1)
+
+#define _PAM_LOG_STATE_ITEM_POINTER(pamh, ctrl, item_type) \
+ _pam_log_state_datum(pamh, ctrl, item_type, #item_type, 0)
+
+#define _PAM_LOG_STATE_ITEM_STRING(pamh, ctrl, item_type) \
+ _pam_log_state_datum(pamh, ctrl, item_type, #item_type, 1)
+
+#ifdef DEBUG_PASSWORD
+#define _LOG_PASSWORD_AS_STRING 1
+#else
+#define _LOG_PASSWORD_AS_STRING 0
+#endif
+
+#define _PAM_LOG_STATE_ITEM_PASSWORD(pamh, ctrl, item_type) \
+ _pam_log_state_datum(pamh, ctrl, item_type, #item_type, _LOG_PASSWORD_AS_STRING)
+
+static void _pam_log_state(const pam_handle_t *pamh, int ctrl)
+{
+ if (!_pam_log_is_debug_state_enabled(ctrl)) {
+ return;
+ }
+
+ _PAM_LOG_STATE_ITEM_STRING(pamh, ctrl, PAM_SERVICE);
+ _PAM_LOG_STATE_ITEM_STRING(pamh, ctrl, PAM_USER);
+ _PAM_LOG_STATE_ITEM_STRING(pamh, ctrl, PAM_TTY);
+ _PAM_LOG_STATE_ITEM_STRING(pamh, ctrl, PAM_RHOST);
+ _PAM_LOG_STATE_ITEM_STRING(pamh, ctrl, PAM_RUSER);
+ _PAM_LOG_STATE_ITEM_PASSWORD(pamh, ctrl, PAM_OLDAUTHTOK);
+ _PAM_LOG_STATE_ITEM_PASSWORD(pamh, ctrl, PAM_AUTHTOK);
+ _PAM_LOG_STATE_ITEM_STRING(pamh, ctrl, PAM_USER_PROMPT);
+ _PAM_LOG_STATE_ITEM_POINTER(pamh, ctrl, PAM_CONV);
+#ifdef PAM_FAIL_DELAY
+ _PAM_LOG_STATE_ITEM_POINTER(pamh, ctrl, PAM_FAIL_DELAY);
+#endif
+#ifdef PAM_REPOSITORY
+ _PAM_LOG_STATE_ITEM_POINTER(pamh, ctrl, PAM_REPOSITORY);
+#endif
+
+ _PAM_LOG_STATE_DATA_STRING(pamh, ctrl, PAM_WINBIND_HOMEDIR);
+ _PAM_LOG_STATE_DATA_STRING(pamh, ctrl, PAM_WINBIND_LOGONSCRIPT);
+ _PAM_LOG_STATE_DATA_STRING(pamh, ctrl, PAM_WINBIND_LOGONSERVER);
+ _PAM_LOG_STATE_DATA_STRING(pamh, ctrl, PAM_WINBIND_PROFILEPATH);
+ _PAM_LOG_STATE_DATA_STRING(pamh, ctrl, PAM_WINBIND_NEW_AUTHTOK_REQD); /* Use atoi to get PAM result code */
+ _PAM_LOG_STATE_DATA_STRING(pamh, ctrl, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH);
+ _PAM_LOG_STATE_DATA_POINTER(pamh, ctrl, PAM_WINBIND_PWD_LAST_SET);
+}
+
static int _pam_parse(const pam_handle_t *pamh, int flags, int argc, const char **argv, dictionary **result_d)
{
int ctrl = 0;
ctrl |= WINBIND_DEBUG_ARG;
}
+ if (iniparser_getboolean(d, "global:debug_state", False)) {
+ ctrl |= WINBIND_DEBUG_STATE;
+ }
+
if (iniparser_getboolean(d, "global:cached_login", False)) {
ctrl |= WINBIND_CACHED_LOGIN;
}
if (iniparser_getstr(d, "global:krb5_ccache_type") != NULL) {
ctrl |= WINBIND_KRB5_CCACHE_TYPE;
}
-
+
if ((iniparser_getstr(d, "global:require-membership-of") != NULL) ||
(iniparser_getstr(d, "global:require_membership_of") != NULL)) {
ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
/* generic options */
if (!strcmp(*v,"debug"))
ctrl |= WINBIND_DEBUG_ARG;
+ else if (!strcasecmp(*v, "debug_state"))
+ ctrl |= WINBIND_DEBUG_STATE;
+ else if (!strcasecmp(*v, "silent"))
+ ctrl |= WINBIND_SILENT;
else if (!strcasecmp(*v, "use_authtok"))
ctrl |= WINBIND_USE_AUTHTOK_ARG;
else if (!strcasecmp(*v, "use_first_pass"))
static void _pam_winbind_cleanup_func(pam_handle_t *pamh, void *data, int error_status)
{
+ int ctrl = _pam_parse(pamh, 0, 0, NULL, NULL);
+ if (_pam_log_is_debug_state_enabled(ctrl)) {
+ _pam_log_debug(pamh, ctrl, LOG_DEBUG, "[pamh: 0x%08x] CLEAN: cleaning up PAM data 0x%08x (error_status = %d)", (uint32) pamh, (uint32) data, error_status);
+ }
SAFE_FREE(data);
}
}
-static int _make_remark(pam_handle_t * pamh, int type, const char *text)
+static int _make_remark(pam_handle_t * pamh, int flags, int type, const char *text)
{
int retval = PAM_SUCCESS;
struct pam_message *pmsg[1], msg[1];
struct pam_response *resp;
+ if (flags & WINBIND_SILENT) {
+ return PAM_SUCCESS;
+ }
+
pmsg[0] = &msg[0];
msg[0].msg = CONST_DISCARD(char *, text);
msg[0].msg_style = type;
return retval;
}
-static int _make_remark_format(pam_handle_t * pamh, int type, const char *format, ...)
+static int _make_remark_v(pam_handle_t * pamh, int flags, int type, const char *format, va_list args)
{
- va_list args;
char *var;
int ret;
- va_start(args, format);
- vasprintf(&var, format, args);
- va_end(args);
+ ret = vasprintf(&var, format, args);
+ if (ret < 0) {
+ _pam_log(pamh, 0, LOG_ERR, "memory allocation failure");
+ return ret;
+ }
- ret = _make_remark(pamh, type, var);
+ ret = _make_remark(pamh, flags, type, var);
SAFE_FREE(var);
return ret;
}
+static int _make_remark_format(pam_handle_t * pamh, int flags, int type, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
+static int _make_remark_format(pam_handle_t * pamh, int flags, int type, const char *format, ...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, format);
+ ret = _make_remark_v(pamh, flags, type, format, args);
+ va_end(args);
+ return ret;
+}
+
static int pam_winbind_request(pam_handle_t * pamh, int ctrl,
enum winbindd_cmd req_type,
struct winbindd_request *request,
/* Fill in request and send down pipe */
init_request(request, req_type);
- if (write_sock(request, sizeof(*request), 0) == -1) {
+ if (write_sock(request, sizeof(*request), 0, 0) == -1) {
_pam_log(pamh, ctrl, LOG_ERR, "pam_winbind_request: write to socket failed!");
close_sock();
return PAM_SERVICE_ERR;
close_sock();
/* Copy reply data from socket */
- if (response->result != WINBINDD_OK) {
- if (response->data.auth.pam_error != PAM_SUCCESS) {
- _pam_log(pamh, ctrl, LOG_ERR, "request failed: %s, PAM error was %s (%d), NT error was %s",
- response->data.auth.error_string,
- pam_strerror(pamh, response->data.auth.pam_error),
- response->data.auth.pam_error,
- response->data.auth.nt_status_string);
- return response->data.auth.pam_error;
- } else {
- _pam_log(pamh, ctrl, LOG_ERR, "request failed, but PAM error 0!");
- return PAM_SERVICE_ERR;
- }
+ if (response->result == WINBINDD_OK) {
+ return PAM_SUCCESS;
}
- return PAM_SUCCESS;
+ /* no need to check for pam_error codes for getpwnam() */
+ switch (req_type) {
+
+ case WINBINDD_GETPWNAM:
+ case WINBINDD_LOOKUPNAME:
+ if (strlen(response->data.auth.nt_status_string) > 0) {
+ _pam_log(pamh, ctrl, LOG_ERR, "request failed, NT error was %s",
+ response->data.auth.nt_status_string);
+ } else {
+ _pam_log(pamh, ctrl, LOG_ERR, "request failed");
+ }
+ return PAM_USER_UNKNOWN;
+ default:
+ break;
+ }
+
+ if (response->data.auth.pam_error != PAM_SUCCESS) {
+ _pam_log(pamh, ctrl, LOG_ERR, "request failed: %s, PAM error was %s (%d), NT error was %s",
+ response->data.auth.error_string,
+ pam_strerror(pamh, response->data.auth.pam_error),
+ response->data.auth.pam_error,
+ response->data.auth.nt_status_string);
+ return response->data.auth.pam_error;
+ }
+
+ _pam_log(pamh, ctrl, LOG_ERR, "request failed, but PAM error 0!");
+
+ return PAM_SERVICE_ERR;
}
static int pam_winbind_request_log(pam_handle_t * pamh,
}
return retval;
case PAM_SUCCESS:
- if (req_type == WINBINDD_PAM_AUTH) {
- /* Otherwise, the authentication looked good */
- _pam_log(pamh, ctrl, LOG_NOTICE, "user '%s' granted access", user);
- } else if (req_type == WINBINDD_PAM_CHAUTHTOK) {
- /* Otherwise, the authentication looked good */
- _pam_log(pamh, ctrl, LOG_NOTICE, "user '%s' password changed", user);
- } else {
- /* Otherwise, the authentication looked good */
- _pam_log(pamh, ctrl, LOG_NOTICE, "user '%s' OK", user);
+ /* Otherwise, the authentication looked good */
+ switch (req_type) {
+ case WINBINDD_INFO:
+ break;
+ case WINBINDD_PAM_AUTH:
+ _pam_log(pamh, ctrl, LOG_NOTICE, "user '%s' granted access", user);
+ break;
+ case WINBINDD_PAM_CHAUTHTOK:
+ _pam_log(pamh, ctrl, LOG_NOTICE, "user '%s' password changed", user);
+ break;
+ default:
+ _pam_log(pamh, ctrl, LOG_NOTICE, "user '%s' OK", user);
+ break;
}
return retval;
}
}
-static BOOL _pam_send_password_expiry_message(pam_handle_t *pamh, time_t next_change, time_t now)
+/**
+ * send a password expiry message if required
+ *
+ * @param pamh PAM handle
+ * @param ctrl PAM winbind options.
+ * @param next_change expected (calculated) next expiry date.
+ * @param already_expired pointer to a boolean to indicate if the password is
+ * already expired.
+ *
+ * @return boolean Returns True if message has been sent, False if not.
+ */
+
+static BOOL _pam_send_password_expiry_message(pam_handle_t *pamh,
+ int ctrl,
+ time_t next_change,
+ time_t now,
+ int warn_pwd_expire,
+ BOOL *already_expired)
{
int days = 0;
struct tm tm_now, tm_next_change;
+ if (already_expired) {
+ *already_expired = False;
+ }
+
+ if (next_change <= now) {
+ PAM_WB_REMARK_DIRECT(pamh, ctrl, "NT_STATUS_PASSWORD_EXPIRED");
+ if (already_expired) {
+ *already_expired = True;
+ }
+ return True;
+ }
+
if ((next_change < 0) ||
- (next_change < now) ||
- (next_change > now + DAYS_TO_WARN_BEFORE_PWD_EXPIRES * SECONDS_PER_DAY)) {
+ (next_change > now + warn_pwd_expire * SECONDS_PER_DAY)) {
return False;
}
days = (tm_next_change.tm_yday+tm_next_change.tm_year*365) - (tm_now.tm_yday+tm_now.tm_year*365);
if (days == 0) {
- _make_remark(pamh, PAM_TEXT_INFO, "Your password expires today");
+ _make_remark(pamh, ctrl, PAM_TEXT_INFO, "Your password expires today");
return True;
}
- if (days > 0 && days < DAYS_TO_WARN_BEFORE_PWD_EXPIRES) {
- _make_remark_format(pamh, PAM_TEXT_INFO, "Your password will expire in %d %s",
+ if (days > 0 && days < warn_pwd_expire) {
+ _make_remark_format(pamh, ctrl, PAM_TEXT_INFO, "Your password will expire in %d %s",
days, (days > 1) ? "days":"day");
return True;
}
return False;
}
-static void _pam_warn_password_expires_in_future(pam_handle_t *pamh, struct winbindd_response *response)
+/**
+ * Send a warning if the password expires in the near future
+ *
+ * @param pamh PAM handle
+ * @param ctrl PAM winbind options.
+ * @param response The full authentication response structure.
+ * @param already_expired boolean, is the pwd already expired?
+ *
+ * @return void.
+ */
+
+static void _pam_warn_password_expiry(pam_handle_t *pamh,
+ int flags,
+ const struct winbindd_response *response,
+ int warn_pwd_expire,
+ BOOL *already_expired)
{
time_t now = time(NULL);
time_t next_change = 0;
+ if (already_expired) {
+ *already_expired = False;
+ }
+
/* accounts with ACB_PWNOEXP set never receive a warning */
if (response->data.auth.info3.acct_flags & ACB_PWNOEXP) {
return;
/* check if the info3 must change timestamp has been set */
next_change = response->data.auth.info3.pass_must_change_time;
- if (_pam_send_password_expiry_message(pamh, next_change, now)) {
+ if (_pam_send_password_expiry_message(pamh, flags, next_change, now,
+ warn_pwd_expire,
+ already_expired)) {
return;
}
/* now check for the global password policy */
+ /* good catch from Ralf Haferkamp: an expiry of "never" is translated
+ * to -1 */
if (response->data.auth.policy.expire <= 0) {
return;
}
next_change = response->data.auth.info3.pass_last_set_time +
response->data.auth.policy.expire;
- if (_pam_send_password_expiry_message(pamh, next_change, now)) {
+ if (_pam_send_password_expiry_message(pamh, flags, next_change, now,
+ warn_pwd_expire,
+ already_expired)) {
return;
}
#define IS_SID_STRING(name) (strncmp("S-", name, 2) == 0)
-int safe_append_string(char *dest,
+static BOOL safe_append_string(char *dest,
const char *src,
int dest_buffer_size)
/**
* @param src Source string buffer.
* @param dest_buffer_size Size of dest buffer in bytes.
*
- * @return 0 if dest buffer is not big enough (no bytes copied), non-zero on success.
+ * @return False if dest buffer is not big enough (no bytes copied), True on success.
*/
{
int dest_length = strlen(dest);
int src_length = strlen(src);
if ( dest_length + src_length + 1 > dest_buffer_size ) {
- return 0;
+ return False;
}
memcpy(dest + dest_length, src, src_length + 1);
- return 1;
+ return True;
}
-static int winbind_name_to_sid_string(pam_handle_t *pamh,
+static BOOL winbind_name_to_sid_string(pam_handle_t *pamh,
int ctrl,
const char *user,
const char *name,
* @param sid_list_buffer Where to append the string sid.
* @param sid_list_buffer Size of sid_list_buffer (in bytes).
*
- * @return 0 on failure, non-zero on success.
+ * @return False on failure, True on success.
*/
{
const char* sid_string;
if (pam_winbind_request_log(pamh, ctrl, WINBINDD_LOOKUPNAME, &sid_request, &sid_response, user)) {
_pam_log(pamh, ctrl, LOG_INFO, "could not lookup name: %s\n", name);
- return 0;
+ return False;
}
sid_string = sid_response.data.sid.sid;
}
if (!safe_append_string(sid_list_buffer, sid_string, sid_list_buffer_size)) {
- return 0;
+ return False;
}
- return 1;
+ return True;
}
-static int winbind_name_list_to_sid_string_list(pam_handle_t *pamh,
+static BOOL winbind_name_list_to_sid_string_list(pam_handle_t *pamh,
int ctrl,
const char *user,
const char *name_list,
* @param sid_list_buffer Where to put the list of string sids.
* @param sid_list_buffer Size of sid_list_buffer (in bytes).
*
- * @return 0 on failure, non-zero on success.
+ * @return False on failure, True on success.
*/
{
- int result = 0;
+ BOOL result = False;
char *current_name = NULL;
const char *search_location;
const char *comma;
goto out;
}
- free(current_name);
- current_name = NULL;
+ SAFE_FREE(current_name);
if (!safe_append_string(sid_list_buffer, ",", sid_list_buffer_size)) {
goto out;
goto out;
}
- result = 1;
+ result = True;
out:
- if (current_name != NULL) {
- free(current_name);
- }
+ SAFE_FREE(current_name);
return result;
}
+/**
+ * put krb5ccname variable into environment
+ *
+ * @param pamh PAM handle
+ * @param ctrl PAM winbind options.
+ * @param krb5ccname env variable retrieved from winbindd.
+ *
+ * @return void.
+ */
+
+static void _pam_setup_krb5_env(pam_handle_t *pamh, int ctrl, const char *krb5ccname)
+{
+ char var[PATH_MAX];
+ int ret;
+
+ if (off(ctrl, WINBIND_KRB5_AUTH)) {
+ return;
+ }
+
+ if (!krb5ccname || (strlen(krb5ccname) == 0)) {
+ return;
+ }
+
+ _pam_log_debug(pamh, ctrl, LOG_DEBUG, "request returned KRB5CCNAME: %s", krb5ccname);
+
+ if (snprintf(var, sizeof(var), "KRB5CCNAME=%s", krb5ccname) == -1) {
+ return;
+ }
+
+ ret = pam_putenv(pamh, var);
+ if (ret) {
+ _pam_log(pamh, ctrl, LOG_ERR, "failed to set KRB5CCNAME to %s: %s",
+ var, pam_strerror(pamh, ret));
+ }
+}
+
+/**
+ * Set string into the PAM stack.
+ *
+ * @param pamh PAM handle
+ * @param ctrl PAM winbind options.
+ * @param data_name Key name for pam_set_data.
+ * @param value String value.
+ *
+ * @return void.
+ */
+
+static void _pam_set_data_string(pam_handle_t *pamh, int ctrl, const char *data_name, const char *value)
+{
+ int ret;
+
+ if ( !data_name || !value || (strlen(data_name) == 0) || (strlen(value) == 0) ) {
+ return;
+ }
+
+ ret = pam_set_data(pamh, data_name, (void *)strdup(value), _pam_winbind_cleanup_func);
+ if (ret) {
+ _pam_log_debug(pamh, ctrl, LOG_DEBUG, "Could not set data %s: %s\n",
+ data_name, pam_strerror(pamh, ret));
+ }
+
+}
+
+/**
+ * Set info3 strings into the PAM stack.
+ *
+ * @param pamh PAM handle
+ * @param ctrl PAM winbind options.
+ * @param data_name Key name for pam_set_data.
+ * @param value String value.
+ *
+ * @return void.
+ */
+
+static void _pam_set_data_info3(pam_handle_t *pamh, int ctrl, struct winbindd_response *response)
+{
+ _pam_set_data_string(pamh, ctrl, PAM_WINBIND_HOMEDIR, response->data.auth.info3.home_dir);
+ _pam_set_data_string(pamh, ctrl, PAM_WINBIND_LOGONSCRIPT, response->data.auth.info3.logon_script);
+ _pam_set_data_string(pamh, ctrl, PAM_WINBIND_LOGONSERVER, response->data.auth.info3.logon_srv);
+ _pam_set_data_string(pamh, ctrl, PAM_WINBIND_PROFILEPATH, response->data.auth.info3.profile_path);
+}
+
+/**
+ * Free info3 strings in the PAM stack.
+ *
+ * @param pamh PAM handle
+ *
+ * @return void.
+ */
+
+static void _pam_free_data_info3(pam_handle_t *pamh)
+{
+ pam_set_data(pamh, PAM_WINBIND_HOMEDIR, NULL, NULL);
+ pam_set_data(pamh, PAM_WINBIND_LOGONSCRIPT, NULL, NULL);
+ pam_set_data(pamh, PAM_WINBIND_LOGONSERVER, NULL, NULL);
+ pam_set_data(pamh, PAM_WINBIND_PROFILEPATH, NULL, NULL);
+}
+
+/**
+ * Send PAM_ERROR_MSG for cached or grace logons.
+ *
+ * @param pamh PAM handle
+ * @param ctrl PAM winbind options.
+ * @param username User in PAM request.
+ * @param info3_user_flgs Info3 flags containing logon type bits.
+ *
+ * @return void.
+ */
+
+static void _pam_warn_logon_type(pam_handle_t *pamh, int ctrl, const char *username, uint32 info3_user_flgs)
+{
+ /* inform about logon type */
+ if (PAM_WB_GRACE_LOGON(info3_user_flgs)) {
+
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG,
+ "Grace login. Please change your password as soon you're online again");
+ _pam_log_debug(pamh, ctrl, LOG_DEBUG,
+ "User %s logged on using grace logon\n", username);
+
+ } else if (PAM_WB_CACHED_LOGON(info3_user_flgs)) {
+
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG,
+ "Domain Controller unreachable, using cached credentials instead. Network resources may be unavailable");
+ _pam_log_debug(pamh, ctrl, LOG_DEBUG,
+ "User %s logged on using cached credentials\n", username);
+ }
+}
+
+/**
+ * Send PAM_ERROR_MSG for krb5 errors.
+ *
+ * @param pamh PAM handle
+ * @param ctrl PAM winbind options.
+ * @param username User in PAM request.
+ * @param info3_user_flgs Info3 flags containing logon type bits.
+ *
+ * @return void.
+ */
+
+static void _pam_warn_krb5_failure(pam_handle_t *pamh, int ctrl, const char *username, uint32 info3_user_flgs)
+{
+ if (PAM_WB_KRB5_CLOCK_SKEW(info3_user_flgs)) {
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG,
+ "Failed to establish your Kerberos Ticket cache "
+ "due time differences\n"
+ "with the domain controller. "
+ "Please verify the system time.\n");
+ _pam_log_debug(pamh, ctrl, LOG_DEBUG,
+ "User %s: Clock skew when getting Krb5 TGT\n", username);
+ }
+}
+
+/**
+ * Compose Password Restriction String for a PAM_ERROR_MSG conversation.
+ *
+ * @param response The struct winbindd_response.
+ *
+ * @return string (caller needs to free).
+ */
+
+static char *_pam_compose_pwd_restriction_string(struct winbindd_response *response)
+{
+ char *str = NULL;
+ size_t offset = 0, ret = 0, str_size = 1024;
+
+ str = (char *)malloc(str_size);
+ if (!str) {
+ return NULL;
+ }
+
+ memset(str, '\0', str_size);
+
+ offset = snprintf(str, str_size, "Your password ");
+ if (offset == -1) {
+ goto failed;
+ }
+
+ if (response->data.auth.policy.min_length_password > 0) {
+ ret = snprintf(str+offset, str_size-offset,
+ "must be at least %d characters; ",
+ response->data.auth.policy.min_length_password);
+ if (ret == -1) {
+ goto failed;
+ }
+ offset += ret;
+ }
+
+ if (response->data.auth.policy.password_history > 0) {
+ ret = snprintf(str+offset, str_size-offset,
+ "cannot repeat any of your previous %d passwords; ",
+ response->data.auth.policy.password_history);
+ if (ret == -1) {
+ goto failed;
+ }
+ offset += ret;
+ }
+
+ if (response->data.auth.policy.password_properties & DOMAIN_PASSWORD_COMPLEX) {
+ ret = snprintf(str+offset, str_size-offset,
+ "must contain capitals, numerals or punctuation; "
+ "and cannot contain your account or full name; ");
+ if (ret == -1) {
+ goto failed;
+ }
+ offset += ret;
+ }
+
+ ret = snprintf(str+offset, str_size-offset,
+ "Please type a different password. "
+ "Type a password which meets these requirements in both text boxes.");
+ if (ret == -1) {
+ goto failed;
+ }
+
+ return str;
+
+ failed:
+ SAFE_FREE(str);
+ return NULL;
+}
+
/* talk to winbindd */
static int winbind_auth_request(pam_handle_t * pamh,
int ctrl,
const char *pass,
const char *member,
const char *cctype,
+ const int warn_pwd_expire,
struct winbindd_response *p_response,
time_t *pwd_last_set,
char **user_ret)
struct winbindd_request request;
struct winbindd_response response;
int ret;
+ BOOL already_expired = False;
ZERO_STRUCT(request);
ZERO_STRUCT(response);
request.flags = WBFLAG_PAM_INFO3_TEXT | WBFLAG_PAM_CONTACT_TRUSTDOM;
- if (ctrl & WINBIND_KRB5_AUTH) {
-
+ if (ctrl & (WINBIND_KRB5_AUTH|WINBIND_CACHED_LOGIN)) {
struct passwd *pwd = NULL;
- _pam_log_debug(pamh, ctrl, LOG_DEBUG, "enabling krb5 login flag\n");
-
- request.flags |= WBFLAG_PAM_KRB5 | WBFLAG_PAM_FALLBACK_AFTER_KRB5;
-
pwd = getpwnam(user);
if (pwd == NULL) {
return PAM_USER_UNKNOWN;
request.data.auth.uid = pwd->pw_uid;
}
+ if (ctrl & WINBIND_KRB5_AUTH) {
+
+ _pam_log_debug(pamh, ctrl, LOG_DEBUG, "enabling krb5 login flag\n");
+
+ request.flags |= WBFLAG_PAM_KRB5 | WBFLAG_PAM_FALLBACK_AFTER_KRB5;
+ }
+
if (ctrl & WINBIND_CACHED_LOGIN) {
_pam_log_debug(pamh, ctrl, LOG_DEBUG, "enabling cached login flag\n");
request.flags |= WBFLAG_PAM_CACHED_LOGIN;
*pwd_last_set = response.data.auth.info3.pass_last_set_time;
}
- if ((ctrl & WINBIND_KRB5_AUTH) &&
- response.data.auth.krb5ccname[0] != '\0') {
-
- char var[PATH_MAX];
-
- _pam_log_debug(pamh, ctrl, LOG_DEBUG, "request returned KRB5CCNAME: %s",
- response.data.auth.krb5ccname);
-
- snprintf(var, sizeof(var), "KRB5CCNAME=%s", response.data.auth.krb5ccname);
-
- ret = pam_putenv(pamh, var);
- if (ret != PAM_SUCCESS) {
- _pam_log(pamh, ctrl, LOG_ERR, "failed to set KRB5CCNAME to %s", var);
- return ret;
- }
- }
-
if (p_response) {
/* We want to process the response in the caller. */
*p_response = response;
}
if (ret) {
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PASSWORD_EXPIRED");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PASSWORD_MUST_CHANGE");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_INVALID_WORKSTATION");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_INVALID_LOGON_HOURS");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_ACCOUNT_EXPIRED");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_ACCOUNT_DISABLED");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_ACCOUNT_LOCKED_OUT");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_NO_LOGON_SERVERS");
- }
-
- /* handle the case where the auth was ok, but the password must expire right now */
- /* good catch from Ralf Haferkamp: an expiry of "never" is translated to -1 */
- if ( ! (response.data.auth.info3.acct_flags & ACB_PWNOEXP) &&
- ! (PAM_WB_GRACE_LOGON(response.data.auth.info3.user_flgs)) &&
- (response.data.auth.policy.expire > 0) &&
- (response.data.auth.info3.pass_last_set_time + response.data.auth.policy.expire < time(NULL))) {
-
- ret = PAM_AUTHTOK_EXPIRED;
-
- _pam_log_debug(pamh, ctrl, LOG_DEBUG,"Password has expired (Password was last set: %d, "
- "the policy says it should expire here %d (now it's: %d)\n",
- response.data.auth.info3.pass_last_set_time,
- response.data.auth.info3.pass_last_set_time + response.data.auth.policy.expire,
- time(NULL));
-
- PAM_WB_REMARK_DIRECT_RET(pamh, "NT_STATUS_PASSWORD_EXPIRED");
-
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_PASSWORD_EXPIRED");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_PASSWORD_MUST_CHANGE");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_INVALID_WORKSTATION");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_INVALID_LOGON_HOURS");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_ACCOUNT_EXPIRED");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_ACCOUNT_DISABLED");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_ACCOUNT_LOCKED_OUT");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_NO_LOGON_SERVERS");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_WRONG_PASSWORD");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_ACCESS_DENIED");
}
- /* warn a user if the password is about to expire soon */
- _pam_warn_password_expires_in_future(pamh, &response);
-
- /* inform about logon type */
- if (PAM_WB_GRACE_LOGON(response.data.auth.info3.user_flgs)) {
-
- _make_remark(pamh, PAM_ERROR_MSG,
- "Grace login. Please change your password as soon you're online again");
- _pam_log_debug(pamh, ctrl, LOG_DEBUG,
- "User %s logged on using grace logon\n", user);
-
- } else if (PAM_WB_CACHED_LOGON(response.data.auth.info3.user_flgs)) {
-
- _make_remark(pamh, PAM_ERROR_MSG,
- "Logging on using cached account. Network ressources can be unavailable");
- _pam_log_debug(pamh, ctrl, LOG_DEBUG,
- "User %s logged on using cached account\n", user);
- }
-
- /* save the CIFS homedir for pam_cifs / pam_mount */
- if (response.data.auth.info3.home_dir[0] != '\0') {
+ if (ret == PAM_SUCCESS) {
- int ret2 = pam_set_data(pamh, PAM_WINBIND_HOMEDIR,
- (void *) strdup(response.data.auth.info3.home_dir),
- _pam_winbind_cleanup_func);
- if (ret2) {
- _pam_log_debug(pamh, ctrl, LOG_DEBUG, "Could not set data: %s",
- pam_strerror(pamh, ret2));
+ /* warn a user if the password is about to expire soon */
+ _pam_warn_password_expiry(pamh, ctrl, &response,
+ warn_pwd_expire,
+ &already_expired);
+
+ if (already_expired == True) {
+ _pam_log_debug(pamh, ctrl, LOG_DEBUG, "Password has expired "
+ "(Password was last set: %lld, the policy says "
+ "it should expire here %lld (now it's: %lu))\n",
+ response.data.auth.info3.pass_last_set_time,
+ response.data.auth.info3.pass_last_set_time +
+ response.data.auth.policy.expire,
+ time(NULL));
+
+ return PAM_AUTHTOK_EXPIRED;
}
- }
+ /* inform about logon type */
+ _pam_warn_logon_type(pamh, ctrl, user, response.data.auth.info3.user_flgs);
- /* save the logon script path for other PAM modules */
- if (response.data.auth.info3.logon_script[0] != '\0') {
+ /* inform about krb5 failures */
+ _pam_warn_krb5_failure(pamh, ctrl, user, response.data.auth.info3.user_flgs);
- int ret2 = pam_set_data(pamh, PAM_WINBIND_LOGONSCRIPT,
- (void *) strdup(response.data.auth.info3.logon_script),
- _pam_winbind_cleanup_func);
- if (ret2) {
- _pam_log_debug(pamh, ctrl, LOG_DEBUG, "Could not set data: %s",
- pam_strerror(pamh, ret2));
- }
- }
+ /* set some info3 info for other modules in the stack */
+ _pam_set_data_info3(pamh, ctrl, &response);
- /* save the profile path for other PAM modules */
- if (response.data.auth.info3.profile_path[0] != '\0') {
+ /* put krb5ccname into env */
+ _pam_setup_krb5_env(pamh, ctrl, response.data.auth.krb5ccname);
- int ret2 = pam_set_data(pamh, PAM_WINBIND_PROFILEPATH,
- (void *) strdup(response.data.auth.info3.profile_path),
- _pam_winbind_cleanup_func);
- if (ret2) {
- _pam_log_debug(pamh, ctrl, LOG_DEBUG, "Could not set data: %s",
- pam_strerror(pamh, ret2));
+ /* If winbindd returned a username, return the pointer to it here. */
+ if (user_ret && response.extra_data.data) {
+ /* We have to trust it's a null terminated string. */
+ *user_ret = (char *)response.extra_data.data;
}
}
- /* If winbindd returned a username, return the pointer to it here. */
- if (user_ret && response.extra_data.data) {
- /* We have to trust it's a null terminated string. */
- *user_ret = (char *)response.extra_data.data;
- }
-
return ret;
}
return ret;
}
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_BACKUP_CONTROLLER");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_NO_LOGON_SERVERS");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_ACCESS_DENIED");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_BACKUP_CONTROLLER");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_NO_LOGON_SERVERS");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_ACCESS_DENIED");
/* TODO: tell the min pwd length ? */
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PWD_TOO_SHORT");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_PWD_TOO_SHORT");
/* TODO: tell the minage ? */
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PWD_TOO_RECENT");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_PWD_TOO_RECENT");
/* TODO: tell the history length ? */
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PWD_HISTORY_CONFLICT");
+ PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, ctrl, response, "NT_STATUS_PWD_HISTORY_CONFLICT");
if (!strcasecmp(response.data.auth.nt_status_string, "NT_STATUS_PASSWORD_RESTRICTION")) {
+ char *pwd_restriction_string = NULL;
+
/* FIXME: avoid to send multiple PAM messages after another */
switch (response.data.auth.reject_reason) {
case -1:
case REJECT_REASON_OTHER:
if ((response.data.auth.policy.min_passwordage > 0) &&
(pwd_last_set + response.data.auth.policy.min_passwordage > time(NULL))) {
- PAM_WB_REMARK_DIRECT(pamh, "NT_STATUS_PWD_TOO_RECENT");
+ PAM_WB_REMARK_DIRECT(pamh, ctrl, "NT_STATUS_PWD_TOO_RECENT");
}
break;
case REJECT_REASON_TOO_SHORT:
- PAM_WB_REMARK_DIRECT(pamh, "NT_STATUS_PWD_TOO_SHORT");
+ PAM_WB_REMARK_DIRECT(pamh, ctrl, "NT_STATUS_PWD_TOO_SHORT");
break;
case REJECT_REASON_IN_HISTORY:
- PAM_WB_REMARK_DIRECT(pamh, "NT_STATUS_PWD_HISTORY_CONFLICT");
+ PAM_WB_REMARK_DIRECT(pamh, ctrl, "NT_STATUS_PWD_HISTORY_CONFLICT");
break;
case REJECT_REASON_NOT_COMPLEX:
- _make_remark(pamh, PAM_ERROR_MSG, "Password does not meet complexity requirements");
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG, "Password does not meet complexity requirements");
break;
default:
_pam_log_debug(pamh, ctrl, LOG_DEBUG,
break;
}
- _make_remark_format(pamh, PAM_ERROR_MSG,
- "Your password must be at least %d characters; "
- "cannot repeat any of the your previous %d passwords"
- "%s. "
- "Please type a different password. "
- "Type a password which meets these requirements in both text boxes.",
- response.data.auth.policy.min_length_password,
- response.data.auth.policy.password_history,
- (response.data.auth.policy.password_properties & DOMAIN_PASSWORD_COMPLEX) ?
- "; must contain capitals, numerals or punctuation; and cannot contain your account or full name" :
- "");
-
+ pwd_restriction_string = _pam_compose_pwd_restriction_string(&response);
+ if (pwd_restriction_string) {
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG, pwd_restriction_string);
+ SAFE_FREE(pwd_restriction_string);
+ }
}
return ret;
const char *item;
char *token;
+ _pam_log(pamh, ctrl, LOG_DEBUG, "getting password (0x%08x)", ctrl);
+
/*
* make sure nothing inappropriate gets returned
*/
} else if (item != NULL) { /* we have a password! */
*pass = item;
item = NULL;
+ _pam_log(pamh, ctrl, LOG_DEBUG,
+ "pam_get_item returned a password");
return PAM_SUCCESS;
} else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
/* prepare to converse */
- if (comment != NULL) {
+ if (comment != NULL && off(ctrl, WINBIND_SILENT)) {
pmsg[0] = &msg[0];
msg[0].msg_style = PAM_TEXT_INFO;
msg[0].msg = CONST_DISCARD(char *, comment);
|| strcmp(token, resp[i - 1].resp)) {
_pam_delete(token); /* mistyped */
retval = PAM_AUTHTOK_RECOVER_ERR;
- _make_remark(pamh, PAM_ERROR_MSG, MISTYPED_PASS);
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG, MISTYPED_PASS);
}
}
} else {
return parm_opt;
}
+int get_config_item_int(const pam_handle_t *pamh,
+ int argc,
+ const char **argv,
+ int ctrl,
+ dictionary *d,
+ const char *item)
+{
+ int parm_opt = -1, i = 0;
+ char *key = NULL;
+
+ /* let the pam opt take precedence over the pam_winbind.conf option */
+ for (i = 0; i < argc; i++) {
+
+ if ((strncmp(argv[i], item, strlen(item)) == 0)) {
+ char *p;
+
+ if ( (p = strchr( argv[i], '=' )) == NULL) {
+ _pam_log(pamh, ctrl, LOG_INFO,
+ "no \"=\" delimiter for \"%s\" found\n",
+ item);
+ goto out;
+ }
+ parm_opt = atoi(p + 1);
+ _pam_log_debug(pamh, ctrl, LOG_INFO,
+ "PAM config: %s '%d'\n",
+ item, parm_opt);
+ return parm_opt;
+ }
+ }
+
+ if (d != NULL) {
+ if (!asprintf(&key, "global:%s", item)) {
+ goto out;
+ }
+
+ parm_opt = iniparser_getint(d, key, -1);
+ SAFE_FREE(key);
+
+ _pam_log_debug(pamh, ctrl, LOG_INFO,
+ "CONFIG file: %s '%d'\n",
+ item, parm_opt);
+ }
+out:
+ return parm_opt;
+}
+
const char *get_krb5_cc_type_from_config(const pam_handle_t *pamh, int argc, const char **argv, int ctrl, dictionary *d)
{
return get_conf_item_string(pamh, argc, argv, ctrl, d, "krb5_ccache_type", WINBIND_KRB5_CCACHE_TYPE);
return get_conf_item_string(pamh, argc, argv, ctrl, d, "require-membership-of", WINBIND_REQUIRED_MEMBERSHIP);
}
+int get_warn_pwd_expire_from_config(const pam_handle_t *pamh,
+ int argc,
+ const char **argv,
+ int ctrl,
+ dictionary *d)
+{
+ int ret;
+ ret = get_config_item_int(pamh, argc, argv, ctrl, d,
+ "warn_pwd_expire");
+ /* no or broken setting */
+ if (ret <= 0) {
+ return DEFAULT_DAYS_TO_WARN_BEFORE_PWD_EXPIRES;
+ }
+ return ret;
+}
+
PAM_EXTERN
int pam_sm_authenticate(pam_handle_t *pamh, int flags,
int argc, const char **argv)
const char *password;
const char *member = NULL;
const char *cctype = NULL;
+ int warn_pwd_expire;
int retval = PAM_AUTH_ERR;
dictionary *d = NULL;
char *username_ret = NULL;
+ char *new_authtok_required = NULL;
+ char *real_username = NULL;
/* parse arguments */
int ctrl = _pam_parse(pamh, flags, argc, argv, &d);
goto out;
}
- _pam_log_debug(pamh, ctrl, LOG_DEBUG, "pam_winbind: pam_sm_authenticate (flags: 0x%04x)", flags);
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_authenticate", pamh, ctrl, flags);
/* Get the username */
retval = pam_get_user(pamh, &username, NULL);
goto out;
}
+#if defined(AIX)
+ /* Decode the user name since AIX does not support logn user
+ names by default. The name is encoded as _#uid. */
+
+ if ( username[0] == '_' ) {
+ uid_t id = atoi( &username[1] );
+ struct passwd *pw = NULL;
+
+ if ( (id!=0) && ((pw = getpwuid( id )) != NULL) ) {
+ real_username = strdup( pw->pw_name );
+ }
+ }
+#endif
+
+ if ( !real_username ) {
+ /* Just making a copy of the username we got from PAM */
+ if ( (real_username = strdup( username )) == NULL ) {
+ _pam_log_debug(pamh, ctrl, LOG_DEBUG,
+ "memory allocation failure when copying username");
+ retval = PAM_SERVICE_ERR;
+ goto out;
+ }
+ }
+
retval = _winbind_read_password(pamh, ctrl, NULL,
"Password: ", NULL,
&password);
#ifdef DEBUG_PASSWORD
_pam_log_debug(pamh, ctrl, LOG_INFO, "Verify user '%s' with password '%s'",
- username, password);
+ real_username, password);
#else
- _pam_log_debug(pamh, ctrl, LOG_INFO, "Verify user '%s'", username);
+ _pam_log_debug(pamh, ctrl, LOG_INFO, "Verify user '%s'", real_username);
#endif
member = get_member_from_config(pamh, argc, argv, ctrl, d);
cctype = get_krb5_cc_type_from_config(pamh, argc, argv, ctrl, d);
+ warn_pwd_expire = get_warn_pwd_expire_from_config(pamh, argc, argv,
+ ctrl, d);
+
/* Now use the username to look up password */
retval = winbind_auth_request(pamh, ctrl, username, password, member,
- cctype, NULL, NULL, &username_ret);
+ cctype, warn_pwd_expire, NULL, NULL,
+ &username_ret);
if (retval == PAM_NEW_AUTHTOK_REQD ||
retval == PAM_AUTHTOK_EXPIRED) {
- char *buf;
+ char *new_authtok_required_during_auth = NULL;
- if (!asprintf(&buf, "%d", retval)) {
+ if (!asprintf(&new_authtok_required, "%d", retval)) {
retval = PAM_BUF_ERR;
goto out;
}
- pam_set_data( pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, (void *)buf, _pam_winbind_cleanup_func);
+ pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, new_authtok_required, _pam_winbind_cleanup_func);
retval = PAM_SUCCESS;
+
+ if (!asprintf(&new_authtok_required_during_auth, "%d", True)) {
+ retval = PAM_BUF_ERR;
+ goto out;
+ }
+
+ pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
+ new_authtok_required_during_auth, _pam_winbind_cleanup_func);
+
goto out;
}
free(username_ret);
}
+ if ( real_username ) {
+ free( real_username );
+ }
+
if (d) {
iniparser_freedict(d);
}
+
+ if (!new_authtok_required) {
+ pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, NULL, NULL);
+ }
+
+ if (retval != PAM_SUCCESS) {
+ _pam_free_data_info3(pamh);
+ }
+
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_authenticate", pamh, ctrl, retval);
+
return retval;
}
goto out;
}
- _pam_log_debug(pamh, ctrl, LOG_DEBUG, "pam_winbind: pam_sm_setcred (flags: 0x%04x)", flags);
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_setcred", pamh, ctrl, flags);
switch (flags & ~PAM_SILENT) {
iniparser_freedict(d);
}
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_setcred", pamh, ctrl, ret);
+
return ret;
}
return PAM_SYSTEM_ERR;
}
- _pam_log_debug(pamh, ctrl, LOG_DEBUG, "pam_winbind: pam_sm_acct_mgmt (flags: 0x%04x)", flags);
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_acct_mgmt", pamh, ctrl, flags);
/* Get the username */
iniparser_freedict(d);
}
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_acct_mgmt", pamh, ctrl, ret);
+
return ret;
}
goto out;
}
- _pam_log_debug(pamh, ctrl, LOG_DEBUG, "pam_winbind: pam_sm_open_session handler (flags: 0x%04x)", flags);
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_open_session", pamh, ctrl, flags);
ret = PAM_SUCCESS;
iniparser_freedict(d);
}
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_open_session", pamh, ctrl, ret);
+
return ret;
}
goto out;
}
- _pam_log_debug(pamh, ctrl, LOG_DEBUG, "pam_winbind: pam_sm_close_session handler (flags: 0x%04x)", flags);
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_close_session", pamh, ctrl, flags);
if (!(flags & PAM_DELETE_CRED)) {
retval = PAM_SUCCESS;
ZERO_STRUCT(response);
retval = pam_get_user(pamh, &user, "Username: ");
- if (retval == PAM_SUCCESS) {
- if (user == NULL) {
- _pam_log(pamh, ctrl, LOG_ERR, "username was NULL!");
- retval = PAM_USER_UNKNOWN;
- goto out;
- }
- if (retval == PAM_SUCCESS) {
- _pam_log_debug(pamh, ctrl, LOG_DEBUG, "username [%s] obtained", user);
- }
- } else {
- _pam_log_debug(pamh, ctrl, LOG_DEBUG, "could not identify user");
+ if (retval) {
+ _pam_log(pamh, ctrl, LOG_ERR, "could not identify user");
goto out;
}
+ if (user == NULL) {
+ _pam_log(pamh, ctrl, LOG_ERR, "username was NULL!");
+ retval = PAM_USER_UNKNOWN;
+ goto out;
+ }
+
+ _pam_log_debug(pamh, ctrl, LOG_DEBUG, "username [%s] obtained", user);
+
ccname = pam_getenv(pamh, "KRB5CCNAME");
if (ccname == NULL) {
_pam_log_debug(pamh, ctrl, LOG_DEBUG, "user has no KRB5CCNAME environment");
if (d) {
iniparser_freedict(d);
}
+
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_close_session", pamh, ctrl, retval);
+
return retval;
}
+/**
+ * evaluate whether we need to re-authenticate with kerberos after a password change
+ *
+ * @param pamh PAM handle
+ * @param ctrl PAM winbind options.
+ * @param user The username
+ *
+ * @return boolean Returns True if required, False if not.
+ */
+
+static BOOL _pam_require_krb5_auth_after_chauthtok(pam_handle_t *pamh, int ctrl, const char *user)
+{
+
+ /* Make sure that we only do this if
+ * a) the chauthtok got initiated during a logon attempt (authenticate->acct_mgmt->chauthtok)
+ * b) any later password change via the "passwd" command if done by the user itself
+ */
+
+ char *new_authtok_reqd_during_auth = NULL;
+ struct passwd *pwd = NULL;
+
+ if (!(ctrl & WINBIND_KRB5_AUTH)) {
+ return False;
+ }
+
+ _pam_get_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH, &new_authtok_reqd_during_auth);
+ pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH, NULL, NULL);
+
+ if (new_authtok_reqd_during_auth) {
+ return True;
+ }
+
+ pwd = getpwnam(user);
+ if (!pwd) {
+ return False;
+ }
+
+ if (getuid() == pwd->pw_uid) {
+ return True;
+ }
+
+ return False;
+}
PAM_EXTERN
int retry = 0;
dictionary *d = NULL;
+ char *username_ret = NULL;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(response);
ctrl = _pam_parse(pamh, flags, argc, argv, &d);
if (ctrl == -1) {
goto out;
}
- _pam_log_debug(pamh, ctrl, LOG_DEBUG, "pam_winbind: pam_sm_chauthtok (flags: 0x%04x)", flags);
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_chauthtok", pamh, ctrl, flags);
/* clearing offline bit for the auth in the password change */
ctrl &= ~WINBIND_CACHED_LOGIN;
* First get the name of a user
*/
ret = pam_get_user(pamh, &user, "Username: ");
- if (ret == PAM_SUCCESS) {
- if (user == NULL) {
- _pam_log(pamh, ctrl, LOG_ERR, "username was NULL!");
- ret = PAM_USER_UNKNOWN;
- goto out;
- }
- if (ret == PAM_SUCCESS) {
- _pam_log_debug(pamh, ctrl, LOG_DEBUG, "username [%s] obtained",
- user);
- }
- } else {
- _pam_log_debug(pamh, ctrl, LOG_DEBUG,
+ if (ret) {
+ _pam_log(pamh, ctrl, LOG_ERR,
"password - could not identify user");
goto out;
}
+ if (user == NULL) {
+ _pam_log(pamh, ctrl, LOG_ERR, "username was NULL!");
+ ret = PAM_USER_UNKNOWN;
+ goto out;
+ }
+
+ _pam_log_debug(pamh, ctrl, LOG_DEBUG, "username [%s] obtained", user);
+
/* check if this is really a user in winbindd, not only in NSS */
ret = valid_user(pamh, ctrl, user);
switch (ret) {
*/
if (flags & PAM_PRELIM_CHECK) {
- struct winbindd_response response;
time_t pwdlastset_prelim = 0;
/* instruct user what is happening */
goto out;
}
- /* We don't need krb5 env set for password change test. */
- ctrl &= ~WINBIND_KRB5_AUTH;
-
/* verify that this is the password for this user */
ret = winbind_auth_request(pamh, ctrl, user, pass_old,
- NULL, NULL, &response, &pwdlastset_prelim, NULL);
+ NULL, NULL, 0, &response,
+ &pwdlastset_prelim, NULL);
if (ret != PAM_ACCT_EXPIRED &&
ret != PAM_AUTHTOK_EXPIRED &&
ret != PAM_NEW_AUTHTOK_REQD &&
ret != PAM_SUCCESS) {
pass_old = NULL;
- if (d) {
- iniparser_freedict(d);
- }
- /* Deal with offline errors. */
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh,
- response,
- "NT_STATUS_NO_LOGON_SERVERS");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh,
- response,
- "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh,
- response,
- "NT_STATUS_ACCESS_DENIED");
- return ret;
+ goto out;
}
pam_set_data(pamh, PAM_WINBIND_PWD_LAST_SET, (void *)pwdlastset_prelim, NULL);
goto out;
}
- lctrl = ctrl;
+ lctrl = ctrl & ~WINBIND_TRY_FIRST_PASS_ARG;
if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
lctrl |= WINBIND_USE_FIRST_PASS_ARG;
goto out;
}
- /* just in case we need krb5 creds after a password change over msrpc */
-
- if (ctrl & WINBIND_KRB5_AUTH) {
- struct winbindd_response response;
+ if (_pam_require_krb5_auth_after_chauthtok(pamh, ctrl, user)) {
const char *member = get_member_from_config(pamh, argc, argv, ctrl, d);
const char *cctype = get_krb5_cc_type_from_config(pamh, argc, argv, ctrl, d);
+ const int warn_pwd_expire =
+ get_warn_pwd_expire_from_config(pamh, argc, argv, ctrl,
+ d);
ret = winbind_auth_request(pamh, ctrl, user, pass_new,
- member, cctype, &response, NULL, NULL);
+ member, cctype, 0, &response,
+ NULL, &username_ret);
_pam_overwrite(pass_new);
_pam_overwrite(pass_old);
pass_old = pass_new = NULL;
- if (d) {
- iniparser_freedict(d);
+
+ if (ret == PAM_SUCCESS) {
+
+ /* warn a user if the password is about to expire soon */
+ _pam_warn_password_expiry(pamh, ctrl, &response,
+ warn_pwd_expire , NULL);
+
+ /* set some info3 info for other modules in the stack */
+ _pam_set_data_info3(pamh, ctrl, &response);
+
+ /* put krb5ccname into env */
+ _pam_setup_krb5_env(pamh, ctrl, response.data.auth.krb5ccname);
+
+ if (username_ret) {
+ pam_set_item (pamh, PAM_USER, username_ret);
+ _pam_log_debug(pamh, ctrl, LOG_INFO, "Returned user was '%s'", username_ret);
+ free(username_ret);
+ }
}
- /* Deal with offline errors. */
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh,
- response,
- "NT_STATUS_NO_LOGON_SERVERS");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh,
- response,
- "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
- PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh,
- response,
- "NT_STATUS_ACCESS_DENIED");
- return ret;
+
+ goto out;
}
} else {
ret = PAM_SERVICE_ERR;
if (d) {
iniparser_freedict(d);
}
+
+ /* Deal with offline errors. */
+ PAM_WB_REMARK_CHECK_RESPONSE(pamh, ctrl, response, "NT_STATUS_NO_LOGON_SERVERS");
+ PAM_WB_REMARK_CHECK_RESPONSE(pamh, ctrl, response, "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
+ PAM_WB_REMARK_CHECK_RESPONSE(pamh, ctrl, response, "NT_STATUS_ACCESS_DENIED");
+
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_chauthtok", pamh, ctrl, ret);
+
return ret;
}