From 4a343149e89047296de529fac3bcc1cc920bbc84 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Jan 23 2006 21:09:17 +0000 Subject: * pam_krb5afs.c(pam_sm_setcred): rename to setcred_int() so that we can do proper debug logging and control whether or not we change the ownership of newly-created files. Remove existing v5 or v4 files which were already created by the module before creating new ones. * pam_krb5afs.c(pam_sm_authenticate): use setcred_int() to avoid tripping over permissions problems. * pam_krb5afs.c(pam_prompt_for): rework to provide varargs functionality for the prompt text. * pam_krb5afs.c(pam_sm_authenticate,pam_prompter): use it. * pam_krb5afs.c(pam_sm_chauthtok): heed use_first_pass when reading the current password, use_authtok when reading the new password. Don't leak the unparsed name. Notify the user of why a change was rejected if that's what happened. * pam_krb5afs.c(parse_user_name): add, to override the local realm name with the configured realm before passing the user name to krb5_parse_name(). Replaces krb5_parse_name() wherever user names are passed in. --- diff --git a/ChangeLog b/ChangeLog index f6778d8..0159ede 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2006-01-23 nalin: + * pam_krb5afs.c(pam_sm_setcred): rename to setcred_int() so that we + can do proper debug logging and control whether or not we change the + ownership of newly-created files. Remove existing v5 or v4 files + which were already created by the module before creating new ones. + * pam_krb5afs.c(pam_sm_authenticate): use setcred_int() to avoid + tripping over permissions problems. + * pam_krb5afs.c(pam_prompt_for): rework to provide varargs + functionality for the prompt text. + * pam_krb5afs.c(pam_sm_authenticate,pam_prompter): use it. + * pam_krb5afs.c(pam_sm_chauthtok): heed use_first_pass when reading + the current password, use_authtok when reading the new password. Don't + leak the unparsed name. Notify the user of why a change was rejected + if that's what happened. + 2006-01-16 nalin: * pam_krb5afs.c(parse_user_name): add, to override the local realm name with the configured realm before passing the user name to diff --git a/pam_krb5.spec b/pam_krb5.spec index 0996a9f..dd9620a 100644 --- a/pam_krb5.spec +++ b/pam_krb5.spec @@ -1,6 +1,6 @@ Summary: A Pluggable Authentication Module for Kerberos 5. Name: pam_krb5 -Version: 1.77 +Version: 1.78 Release: 1 Source0: pam_krb5-%{version}-%{release}.tar.gz License: LGPL @@ -45,6 +45,10 @@ make install DESTDIR=$RPM_BUILD_ROOT mandir=%{_mandir} # $Id$ %changelog +* Wed Jan 18 2006 Nalin Dahyabhai 1.78-1 +- fix processing of the "realm=" option (#117214) +- report the reason for a password change failure to the user + * Wed Jul 27 2005 Nalin Dahyabhai 1.77-1 - only check .k5login during account management if we were called to authenticate the user the first time around (#140325) diff --git a/pam_krb5afs.c b/pam_krb5afs.c index 8f75abc..6c0756c 100644 --- a/pam_krb5afs.c +++ b/pam_krb5afs.c @@ -267,6 +267,8 @@ CRIT(const char *x,...) { int num_words(const char *s); const char *nth_word(const char *s, int w); +static int setcred_int(pam_handle_t *pamh, int flags, + int argc, const char **argv, int set_perms); /* Call krb5_init_ets, if possible. */ static void @@ -316,10 +318,9 @@ num_words(const char *s) * structure. */ static int parse_user_name(krb5_context ctx, const char *name, struct config *config, - krb5_principal *princ) + krb5_principal *princ) { char *tmp; - const char *realm; int length, used, ret; if (name == NULL) { @@ -331,10 +332,11 @@ parse_user_name(krb5_context ctx, const char *name, struct config *config, return ENOMEM; } snprintf(tmp, length, "%s", name); - used = strcspn(tmp, "@"); + used = strcspn(tmp, "\\@"); snprintf(tmp + used, length - used, "@%s", config->realm); ret = krb5_parse_name(ctx, tmp, princ); free(tmp); + return ret; } /* Return the address of the start of the nth whitespace-separated word in the @@ -759,7 +761,7 @@ appdefault_boolean(krb5_context context, const char *option, /* Try the profile [appdefault] section. */ if (!found) { krb5_appdefault_boolean(context, APPDEFAULT_APP, NULL, - option, default_value, ret_value); + option, default_value, ret_value); found = TRUE; } #endif @@ -1034,7 +1036,7 @@ get_config(krb5_context context, int argc, const char **argv) /* Ticket lifetime. */ appdefault_integer(context, "ticket_lifetime", argc, argv, - DEFAULT_LIFE, &ret->ticket_lifetime); + DEFAULT_LIFE, &ret->ticket_lifetime); DEBUG("setting ticket lifetime to %d", ret->ticket_lifetime); krb5_get_init_creds_opt_set_tkt_life(&ret->creds_opt, ret->ticket_lifetime); @@ -1165,9 +1167,10 @@ free_config(struct config *cfg) /* Prompt the user for a single piece of information. */ static int -pam_prompt_for(pam_handle_t *pamh, int msg_style, const char *msg, char **out) +pam_prompt_for(pam_handle_t *pamh, int msg_style, char **out, + const char *msg, ...) { - const struct pam_message prompt_message = {msg_style, msg}; + struct pam_message prompt_message = {msg_style, NULL}; struct pam_response *responses = NULL; const struct pam_conv *converse = NULL; int ret = PAM_SUCCESS; @@ -1175,6 +1178,14 @@ pam_prompt_for(pam_handle_t *pamh, int msg_style, const char *msg, char **out) &prompt_message, NULL }; + char *text; + va_list args; + + /* Allocate space for the formatted text. */ + text = malloc(LINE_MAX); + if (text == NULL) { + return PAM_BUF_ERR; + } /* Get the conversation structure passed in by the app. */ ret = pam_get_item(pamh, PAM_CONV, (const void**) &converse); @@ -1182,6 +1193,12 @@ pam_prompt_for(pam_handle_t *pamh, int msg_style, const char *msg, char **out) CRIT("no conversation function supplied"); } + /* Format the text. */ + va_start(args, msg); + vsnprintf(text, LINE_MAX, msg, args); + prompt_message.msg = text; + va_end(args); + /* Now actually prompt the user for that piece of information. */ if (ret == PAM_SUCCESS) { /* Call the conversation function. */ @@ -1203,6 +1220,7 @@ pam_prompt_for(pam_handle_t *pamh, int msg_style, const char *msg, char **out) } } + free(text); return ret; } @@ -1212,25 +1230,19 @@ static int pam_prompter(krb5_context context, void *data, const char *name, const char *banner, int num_prompts, krb5_prompt prompts[]) { - int i = 0, len = 0, ret = PAM_SUCCESS; - char *result = NULL, *prompt = NULL, *tmp = NULL; + int i = 0, ret = PAM_SUCCESS; + char *result = NULL, *tmp = NULL; for (i = 0; i < num_prompts; i++) { - len = strlen(prompts[i].prompt) + strlen(": ") + 1; - prompt = malloc(len); - if (prompt == NULL) { - return PAM_SYSTEM_ERR; - } - snprintf(prompt, len, "%s: ", prompts[i].prompt); ret = pam_prompt_for(data, prompts[i].hidden ? PAM_PROMPT_ECHO_OFF : PAM_PROMPT_ECHO_ON, - prompt, - &result); + &result, + "%s: ", prompts[i].prompt); if ((ret == PAM_SUCCESS) && (result != NULL)) { tmp = strdup(result); if (tmp == NULL) { - ret = PAM_BUF_ERR; + ret = ENOMEM; } else { prompts[i].reply->length = strlen(tmp); prompts[i].reply->data = tmp; @@ -1442,7 +1454,7 @@ get_v4_tgt_conv(krb5_context context, krb5_principal principal, use_ccache = NULL; strcpy(ccfile, "/tmp/pam_krb5_XXXXXX"); fd = mkstemp(ccfile); - if (fd == -1) { + if (fd == -1) { INFO("error creating temporary file: %s", strerror(errno)); return -1; @@ -1562,7 +1574,7 @@ get_v4_tgt_init(char *principal, char *instance, char *realm, krb_get_err_text(k4rc)); } if (k4rc == KSUCCESS) { - unsigned char *p = ciphertext->dat; + char *p = ciphertext->dat; int len; /* Convert the password to a v4 key. */ @@ -1571,11 +1583,11 @@ get_v4_tgt_init(char *principal, char *instance, char *realm, /* Decrypt the TGT. */ k4rc = des_pcbc_encrypt((C_Block*)ciphertext->dat, - (C_Block*)ciphertext->dat, - ciphertext->length, - key_schedule, - (C_Block*)key, - 0); + (C_Block*)ciphertext->dat, + ciphertext->length, + key_schedule, + (C_Block*)key, + 0); if (k4rc == KSUCCESS) { memset(key, 0, sizeof(key)); memset(key_schedule, 0, sizeof(key_schedule)); @@ -1770,8 +1782,8 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) "prompting for user name"); prc = pam_prompt_for(pamh, PAM_PROMPT_ECHO_ON, - "login: ", - &user); + &user, + "login: "); } if ((prc != PAM_SUCCESS) || (strlen(user) == 0)) { CRIT("cannot determine user's login"); @@ -1890,8 +1902,8 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) config->warn_expiry) { pam_prompt_for(pamh, PAM_ERROR_MSG, - "Account expired. Please contact your system administrator.", - NULL); + NULL, + "Account expired. Please contact your system administrator."); } } else /* If we're just being told that the key expired, try @@ -1914,8 +1926,8 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) config->warn_expiry) { pam_prompt_for(pamh, PAM_ERROR_MSG, - "Password expired. You must change it now.", - NULL); + NULL, + "Password expired. You must change it now."); } } } @@ -1931,8 +1943,8 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) password = NULL; pam_prompt_for(pamh, PAM_PROMPT_ECHO_OFF, - "Password: ", - &password); + &password, + "Password: "); if (password) { tmp = strdup(password); if (tmp) { @@ -1957,8 +1969,8 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) if (!(flags & PAM_SILENT)) { pam_prompt_for(pamh, PAM_ERROR_MSG, - "Your account has expired; please contact your system administrator", - NULL); + NULL, + "Your account has expired; please contact your system administrator."); } } else /* If the key expired, attempt to get a password- @@ -1979,8 +1991,8 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) config->warn_expiry) { pam_prompt_for(pamh, PAM_ERROR_MSG, - "Password expired. You must change it now.", - NULL); + NULL, + "Password expired. You must change it now."); } } } @@ -2091,9 +2103,10 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) #ifdef AFS /* Get tokens if configured to force it. */ if (RC_OK && config->setcred && stash->have_v4_creds) { - prc = pam_sm_setcred(pamh, PAM_ESTABLISH_CRED, argc, argv); + prc = setcred_int(pamh, PAM_ESTABLISH_CRED, argc, argv, FALSE); if (prc == PAM_SUCCESS) { - prc = pam_sm_setcred(pamh, PAM_DELETE_CRED, argc, argv); + prc = setcred_int(pamh, PAM_DELETE_CRED, + argc, argv, FALSE); } } #endif @@ -2126,7 +2139,6 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) if (!stash->have_v5_creds) { free_stash(pamh, stash, PAM_SUCCESS); } - free_config(config); /* Attempt to save the local name associated with the principal * as the PAM_USER item. */ @@ -2138,6 +2150,9 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) prc ? pam_strerror(pamh, prc) : "Success"); /* Done with Kerberos. */ + if (config != NULL) { + free_config(config); + } if (context != NULL) { krb5_free_context(context); } @@ -2148,8 +2163,9 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) /******************************************************************************/ /* Create and delete visible credentials as needed. */ -int -pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) +static int +setcred_int(pam_handle_t *pamh, int flags, int argc, const char **argv, + int set_perms) { krb5_context context; krb5_principal principal; @@ -2178,7 +2194,6 @@ pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) prc = PAM_SYSTEM_ERR; } } - DEBUG("pam_sm_setcred() called"); /* Retrieve information about the user. */ if (RC_OK) { @@ -2225,6 +2240,15 @@ pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) if (prc == PAM_SUCCESS) { DEBUG("credentials retrieved"); + if (strlen(stash->v5_path) != 0) { + DEBUG("removing %s", stash->v5_path); + if (remove(stash->v5_path) == -1) { + CRIT("error removing file %s: %s", + stash->v5_path, strerror(errno)); + } else { + strcpy(stash->v5_path, ""); + } + } if (strlen(stash->v5_path) == 0) { snprintf(v5_path, sizeof(v5_path), "%s/krb5cc_%d_XXXXXX", @@ -2244,9 +2268,11 @@ pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) CRIT("%s opening ccache", strerror(errno)); prc = PAM_SYSTEM_ERR; } else { - /* Mess with the file's ownership to make - * libkrb happy. */ - fchown(tmpfd, getuid(), getgid()); + if (set_perms) { + /* Mess with the file's ownership to + * make libkrb happy. */ + fchown(tmpfd, getuid(), getgid()); + } } } if (RC_OK) { @@ -2309,6 +2335,15 @@ pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) #endif #ifdef HAVE_LIBKRB4 if (RC_OK && stash->have_v4_creds) { + if (strlen(stash->v4_path) != 0) { + DEBUG("removing %s", stash->v4_path); + if (remove(stash->v4_path) == -1) { + CRIT("error removing file %s: %s", + stash->v4_path, strerror(errno)); + } else { + strcpy(stash->v4_path, ""); + } + } if (strlen(stash->v4_path) == 0) { /* Create a new ticket file. */ snprintf(v4_path, sizeof(v4_path), @@ -2329,9 +2364,11 @@ pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) CRIT("%s opening ccache", strerror(errno)); prc = PAM_SYSTEM_ERR; } else { - /* Mess with the file's ownership to make - * libkrb4 happy. */ - fchown(tmpfd, getuid(), getgid()); + if (set_perms) { + /* Mess with the file's ownership to + * make libkrb4 happy. */ + fchown(tmpfd, getuid(), getgid()); + } } } if (RC_OK && strlen(stash->v4_path)) { @@ -2415,21 +2452,27 @@ pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) } #endif - /* Fix permissions on this file so that the user logging in will - * be able to use it. */ - if (RC_OK && - (flags & (PAM_ESTABLISH_CRED | PAM_REINITIALIZE_CRED | PAM_REFRESH_CRED)) && - (strlen(stash->v5_path) > 0)) { - prc = safe_fixup(config, stash->v5_path, stash); - } + if (set_perms) { + /* Fix permissions on this file so that the user logging in will + * be able to use it. */ + if (RC_OK && + (flags & (PAM_ESTABLISH_CRED | + PAM_REINITIALIZE_CRED | + PAM_REFRESH_CRED)) && + (strlen(stash->v5_path) > 0)) { + prc = safe_fixup(config, stash->v5_path, stash); + } #ifdef HAVE_LIBKRB4 - if (RC_OK && - (flags & (PAM_ESTABLISH_CRED | PAM_REINITIALIZE_CRED | PAM_REFRESH_CRED)) && - (strlen(stash->v4_path) > 0)) { - prc = safe_fixup(config, stash->v4_path, stash); - } + if (RC_OK && + (flags & (PAM_ESTABLISH_CRED | + PAM_REINITIALIZE_CRED | + PAM_REFRESH_CRED)) && + (strlen(stash->v4_path) > 0)) { + prc = safe_fixup(config, stash->v4_path, stash); + } #endif + } if (RC_OK && (flags & PAM_DELETE_CRED) && config->retain_token) { DEBUG("retaining token beyond session-closure"); @@ -2482,9 +2525,6 @@ pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) } } - DEBUG("pam_sm_setcred returning %d (%s)", prc, - (prc != PAM_SUCCESS) ? pam_strerror(pamh, prc) : "Success"); - if (user != NULL) { free(user); } @@ -2503,6 +2543,38 @@ pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) return prc; } +int +pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + struct config *config = NULL; + krb5_context context = NULL; + int ret = PAM_SUCCESS; + + if (init_context(&context, argc, argv) != KRB5_SUCCESS) { + ret = PAM_SYSTEM_ERR; + } + if (ret == PAM_SUCCESS) { + if (!(config = get_config(context, argc, argv))) { + ret = PAM_SYSTEM_ERR; + } + } + DEBUG("pam_sm_setcred() called"); + + ret = setcred_int(pamh, flags, argc, argv, TRUE); + + DEBUG("pam_sm_setcred returning %d (%s)", ret, + (ret != PAM_SUCCESS) ? pam_strerror(pamh, ret) : "Success"); + + if (config != NULL) { + free_config(config); + } + if (context != NULL) { + krb5_free_context(context); + } + + return ret; +} + /******************************************************************************/ int @@ -2521,11 +2593,20 @@ pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) } } DEBUG("pam_sm_open_session() called"); + + ret = setcred_int(pamh, flags | PAM_ESTABLISH_CRED, argc, argv, TRUE); + + DEBUG("pam_sm_open_session returning %d (%s)", ret, + (ret != PAM_SUCCESS) ? pam_strerror(pamh, ret) : "Success"); + + if (config != NULL) { + free_config(config); + } if (context != NULL) { krb5_free_context(context); } - return pam_sm_setcred(pamh, flags | PAM_ESTABLISH_CRED, argc, argv); + return ret; } int @@ -2544,11 +2625,18 @@ pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) } } DEBUG("pam_sm_close_session() called"); + + ret = setcred_int(pamh, flags | PAM_DELETE_CRED, argc, argv, TRUE); + + DEBUG("pam_sm_close_session returning %d (%s)", ret, + (ret != PAM_SUCCESS) ? pam_strerror(pamh, ret) : "Success"); + + if (config != NULL) { + free_config(config); + } if (context != NULL) { krb5_free_context(context); } - - return pam_sm_setcred(pamh, flags | PAM_DELETE_CRED, argc, argv); } /* A callback function that simply fails. */ @@ -2666,7 +2754,7 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) } if (RC_OK) { /* Get the name of the TGT for the default realm. */ - snprintf(buf, sizeof(buf), "krbtgt/%*s@%*s", + snprintf(buf, sizeof(buf), "krbtgt/%.*s@%.*s", krb5_princ_realm(context, increds.client)->length, krb5_princ_realm(context, increds.client)->data, krb5_princ_realm(context, increds.client)->length, @@ -2690,7 +2778,8 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) NULL, &rep); DEBUG("krb5_get_in_tkt(%s,%s) with bogus decryption " - "functions = %d", user, buf, krc); + "functions = %d (%s)", user, buf, krc, + error_message(krc)); krb5_free_cred_contents(context, &increds); if (rep != NULL) { krb5_free_kdc_rep(context, rep); @@ -2716,6 +2805,9 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) } } /* Clean up. */ + if (config != NULL) { + free_config(config); + } if (unparsedname != NULL) { krb5_free_unparsed_name(context, unparsedname); } @@ -2737,7 +2829,7 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) { krb5_context context = NULL; krb5_principal principal, server; - char *user = NULL, *authtok = NULL, *old_authtok = NULL; + char *user = NULL, *authtok, *old_authtok, *unparsed_name; char current_pass[LINE_MAX], new_pass[LINE_MAX], retype_pass[LINE_MAX]; struct config *config = NULL; int prc = PAM_SUCCESS, krc = KRB5_SUCCESS; @@ -2799,15 +2891,22 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) } /* Build a principal structure out of the user's login. */ + principal = NULL; + unparsed_name = NULL; if (RC_OK) { krc = parse_user_name(context, user, config, &principal); if (krc != KRB5_SUCCESS) { CRIT("%s", error_message(krc)); } + krc = krb5_unparse_name(context, principal, &unparsed_name); + if (krc != KRB5_SUCCESS) { + CRIT("%s", error_message(krc)); + } } /* Build a principal structure out of the password-changing * service's name. */ + server = NULL; if (RC_OK) { krc = krb5_parse_name(context, PASSWORD_CHANGING_SERVICE, &server); @@ -2817,6 +2916,8 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) } /* Read the old password. It's okay if this fails. */ + old_authtok = NULL; + authtok = NULL; if (RC_OK) { pam_get_item(pamh, PAM_OLDAUTHTOK, (const void**) &old_authtok); pam_get_item(pamh, PAM_AUTHTOK, (const void**) &authtok); @@ -2856,20 +2957,26 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) /* We have two cases we have to deal with. The first: check auth. */ if (RC_OK && (flags & PAM_PRELIM_CHECK)) { if ((old_authtok == NULL) || (strlen(old_authtok) == 0)) { - DEBUG("prompting for current password"); - prc = pam_prompt_for(pamh, - PAM_PROMPT_ECHO_OFF, - current_pass, - &old_authtok); - if (RC_OK) { - pam_set_item(pamh, - PAM_OLDAUTHTOK, - (const void*)old_authtok); + if (!config->try_second_pass) { + INFO("can't read current password for " + "%s, not allowed to ask", unparsed_name); + prc = PAM_AUTHTOK_ERR; } else { - prc = PAM_AUTHTOK_RECOVERY_ERR; - INFO("can't read current password for %s: %d " - "(%s)", user, prc, - pam_strerror(pamh, prc)); + DEBUG("prompting for current password"); + prc = pam_prompt_for(pamh, + PAM_PROMPT_ECHO_OFF, + &old_authtok, + current_pass); + if (RC_OK) { + pam_set_item(pamh, + PAM_OLDAUTHTOK, + (const void*)old_authtok); + } else { + prc = PAM_AUTHTOK_RECOVERY_ERR; + INFO("can't read current password for " + "%s: %d (%s)", unparsed_name, prc, + pam_strerror(pamh, prc)); + } } } if (RC_OK) { @@ -2883,31 +2990,43 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) PASSWORD_CHANGING_SERVICE, &pwchange_creds_opts); if (krc == KRB5_SUCCESS) { - DEBUG("%s cleared for password change", user); + DEBUG("%s (%s) cleared for password change", + user, unparsed_name); + krb5_free_cred_contents(context, &creds); } else { INFO("can't change password for %s: %d (%s)", - user, krc, error_message(krc)); + unparsed_name, krc, error_message(krc)); } } } /* The second one is a bit messier. */ if (RC_OK && (flags & PAM_UPDATE_AUTHTOK)) { - DEBUG("attempting to change password for %s", user); + DEBUG("attempting to change password for %s (%s)", + user, unparsed_name); if ((old_authtok == NULL) || (strlen(old_authtok) == 0)) { - DEBUG("prompting for current password"); - prc = pam_prompt_for(pamh, - PAM_PROMPT_ECHO_OFF, - current_pass, - &old_authtok); - if (prc != PAM_SUCCESS) { - INFO("error in conversation: %s", - pam_strerror(pamh, prc)); - prc = PAM_AUTHTOK_RECOVERY_ERR; + if (!config->try_second_pass) { + INFO("can't read current password for " + "%s, not allowed to ask", unparsed_name); + prc = PAM_AUTHTOK_ERR; + } else { + DEBUG("prompting for current password"); + prc = pam_prompt_for(pamh, + PAM_PROMPT_ECHO_OFF, + &old_authtok, + "%s", current_pass); + if (RC_OK) { + pam_set_item(pamh, + PAM_OLDAUTHTOK, + (const void*)old_authtok); + } else { + INFO("error in conversation: %s", + pam_strerror(pamh, prc)); + prc = PAM_AUTHTOK_RECOVERY_ERR; + } } } - /* DEBUG("old_authtok = `%s'", old_authtok); FIXME */ if (RC_OK && ((authtok == NULL) || (strlen(authtok) == 0)) && !config->use_authtok) { @@ -2916,14 +3035,14 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) DEBUG("prompting for new password (1)"); prc = pam_prompt_for(pamh, PAM_PROMPT_ECHO_OFF, - new_pass, - &authtok); + &authtok, + "%s", new_pass); if (RC_OK) { DEBUG("prompting for new password (2)"); prc = pam_prompt_for(pamh, PAM_PROMPT_ECHO_OFF, - retype_pass, - &authtok2); + &authtok2, + "%s", retype_pass); if (prc != PAM_SUCCESS) { INFO("error in conversation: %s", pam_strerror(pamh, prc)); @@ -2934,8 +3053,8 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) if (strcmp(authtok, authtok2) != 0) { pam_prompt_for(pamh, PAM_ERROR_MSG, - "passwords do not match", - NULL); + NULL, + "passwords do not match"); prc = PAM_TRY_AGAIN; } else { pam_set_item(pamh, @@ -2944,9 +3063,20 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) } } } - /* DEBUG("authtok = `%s'", authtok); FIXME */ - if (RC_OK && ((authtok == NULL) || (strlen(authtok) == 0))) { - prc = PAM_AUTHTOK_ERR; + + if (RC_OK) { + if ((old_authtok == NULL) || + (strlen(old_authtok) == 0)) { + INFO("can't read current password for " + "%s (%s)", user, unparsed_name); + prc = PAM_AUTHTOK_ERR; + } else + if ((authtok == NULL) || + (strlen(authtok) == 0)) { + INFO("can't read new password for " + "%s (%s)", user, unparsed_name); + prc = PAM_AUTHTOK_ERR; + } } if (RC_OK) { @@ -2963,39 +3093,37 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) PASSWORD_CHANGING_SERVICE, &pwchange_creds_opts); if (krc == KRB5_SUCCESS) { - DEBUG("%s prepared for password change", user); + DEBUG("%s (%s) prepared for password change", + user, unparsed_name); } else { - INFO("can't change password for %s: %d (%s)", - user, krc, error_message(krc)); + INFO("can't change password for %s (%s): %d " + "(%s)", user, + unparsed_name, krc, error_message(krc)); } } if (RC_OK) { + memset(&result_code_string, 0, + sizeof(result_code_string)); + memset(&result_string, 0, + sizeof(result_string)); krc = krb5_change_password(context, &creds, authtok, &result_code, &result_code_string, &result_string); - if ((krc == KRB5_SUCCESS) && - (result_code == KRB5_KPASSWD_SUCCESS)) { - char *stash_name, *unparsed_name = NULL; + if ((krc == 0) && (result_code == 0)) { + char *stash_name; struct stash *stash = NULL; INFO("%s's %s password has been changed", - user, config->banner); + unparsed_name, config->banner); /* Get a fresh TGT, in case this is a change- * password-at-login situation. */ - if (krb5_unparse_name(context, principal, - &unparsed_name) != KSUCCESS) { - unparsed_name = NULL; - } - if (unparsed_name != NULL) { - stash_name = module_stash_name(unparsed_name); - pam_get_data(pamh, stash_name, - (void*)&stash); - free(stash_name); - stash_name = NULL; - } + stash_name = module_stash_name(unparsed_name); + pam_get_data(pamh, stash_name, (void*)&stash); + free(stash_name); + stash_name = NULL; if (stash != NULL) { DEBUG("stash retrieved"); krc = krb5_get_init_creds_password(context, @@ -3015,19 +3143,37 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) } } } else { - INFO("can't obtain TGT for %s using new password: %d (%s)", - user, krc, error_message(krc)); + INFO("can't obtain TGT for %s using new password: %d (%s)", + unparsed_name, krc, error_message(krc)); } } } else { - INFO("changing %s's %s password failed: %*s " - "(%d: %*s)", - user, config->banner, - result_string.length, - result_string.data, + INFO("changing %s's %s password failed: %d: " + "%.*s (%.*s)", + unparsed_name, config->banner, result_code, result_code_string.length, - result_code_string.data); + result_code_string.data ? + result_code_string.data : "", + result_string.length, + result_string.data ? + result_string.data : ""); + /* If the request was correctly processed, but + * denied, then we need to report an error. */ + if (result_code != 0) { + prc = PAM_AUTHTOK_ERR; + } + /* Let the user know what's going on. */ + pam_prompt_for(pamh, PAM_ERROR_MSG, NULL, + "%.*s%s%.*s%s", + result_code_string.length, + result_code_string.data ? + result_code_string.data : "", + result_string.length ? " (" : "", + result_string.length, + result_string.data ? + result_string.data : "", + result_string.length ? ")" : ""); } } } @@ -3037,6 +3183,10 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) } /* Clean up and return. */ + if (unparsed_name != NULL) { + krb5_free_unparsed_name(context, unparsed_name); + unparsed_name = NULL; + } if (context != NULL) { krb5_free_context(context); }