src/common/pkcs11_lib.c | 34 +++- src/common/pkcs11_lib.h | 3 +- src/pam_pkcs11/pam_pkcs11.c | 403 +++++++++++++++++++++++++++++++++++-------- src/tools/pkcs11_inspect.c | 2 +- src/tools/pkcs11_listcerts.c | 2 +- src/tools/pklogin_finder.c | 2 +- 6 files changed, 361 insertions(+), 85 deletions(-) diff --git a/src/common/pkcs11_lib.c b/src/common/pkcs11_lib.c index 1ca184c..d548b6e 100644 --- a/src/common/pkcs11_lib.c +++ b/src/common/pkcs11_lib.c @@ -1401,21 +1401,29 @@ int wait_for_token(pkcs11_handle_t *h, return rv; } -int open_pkcs11_session(pkcs11_handle_t *h, unsigned int slot) +int open_pkcs11_session(pkcs11_handle_t *h, unsigned int slot, int rw) { int rv; - - DBG1("opening a new PKCS #11 session for slot %d", slot + 1); + + DBG2("opening a new %s PKCS #11 session for slot %d", + rw ? "R/W" : "RO", slot + 1); if (slot >= h->slot_count) { set_error("invalid slot number %d", slot); return -1; } - /* open a readonly user-session */ - rv = h->fl->C_OpenSession(h->slots[slot].id, CKF_SERIAL_SESSION, NULL, NULL, &h->session); + + unsigned int flags = CKF_SERIAL_SESSION; + if ( rw ) { + flags |= CKF_RW_SESSION; + } + DBG1("C_OpenSession flags: 0x%08lX", flags); + + rv = h->fl->C_OpenSession(h->slots[slot].id, flags, NULL, NULL, &h->session); if (rv != CKR_OK) { set_error("C_OpenSession() failed: 0x%08lX", rv); return -1; } + h->current_slot = slot; return 0; } @@ -1436,6 +1444,22 @@ int pkcs11_login(pkcs11_handle_t *h, char *password) return 0; } +int pkcs11_setpin(pkcs11_handle_t *h, char *old_pass, char *new_pass) +{ + int rv; + + rv = h->fl->C_SetPIN( h->session, old_pass, strlen(old_pass), + new_pass, strlen(new_pass) ); + + if ( rv != CKR_OK ) { + DBG1("C_SetPIN() failed: 0x%08lX", rv); + set_error("C_SetPIN() failed: 0x%08lX", rv); + return -1; + } else { + return 0; + } +} + int get_slot_login_required(pkcs11_handle_t *h) { int rv; diff --git a/src/common/pkcs11_lib.h b/src/common/pkcs11_lib.h index 628caa1..3d8c277 100644 --- a/src/common/pkcs11_lib.h +++ b/src/common/pkcs11_lib.h @@ -54,7 +54,7 @@ PKCS11_EXTERN int wait_for_token_by_slotlabel(pkcs11_handle_t *h, unsigned int *slot); PKCS11_EXTERN const X509 *get_X509_certificate(cert_object_t *cert); PKCS11_EXTERN void release_pkcs11_module(pkcs11_handle_t *h); -PKCS11_EXTERN int open_pkcs11_session(pkcs11_handle_t *h, unsigned int slot); +PKCS11_EXTERN int open_pkcs11_session(pkcs11_handle_t *h, unsigned int slot, int rw); PKCS11_EXTERN int close_pkcs11_session(pkcs11_handle_t *h); PKCS11_EXTERN int pkcs11_login(pkcs11_handle_t *h, char *password); PKCS11_EXTERN int pkcs11_pass_login(pkcs11_handle_t *h, int nullok); @@ -68,6 +68,7 @@ PKCS11_EXTERN int sign_value(pkcs11_handle_t *h, cert_object_t *, unsigned char *data, unsigned long length, unsigned char **signature, unsigned long *signature_length); PKCS11_EXTERN int get_random_value(unsigned char *data, int length); +PKCS11_EXTERN int pkcs11_setpin(pkcs11_handle_t *h, char *old_pass, char *new_pass); #undef PKCS11_EXTERN diff --git a/src/pam_pkcs11/pam_pkcs11.c b/src/pam_pkcs11/pam_pkcs11.c index a8be0af..1e4b15c 100644 --- a/src/pam_pkcs11/pam_pkcs11.c +++ b/src/pam_pkcs11/pam_pkcs11.c @@ -149,7 +149,7 @@ static int pam_pkcs11_prompt(const pam_handle_t *pamh, int style, char **resp, c /* - * Gets the users password. Depending whether it was already asked, either + * Gets the user password. Depending whether it was already asked, either * a prompt is shown or the old value is returned. */ static int pam_get_pwd(pam_handle_t *pamh, char **pwd, char *text, int oitem, int nitem) @@ -204,6 +204,163 @@ static int pam_get_pwd(pam_handle_t *pamh, char **pwd, char *text, int oitem, in return PAM_CRED_INSUFFICIENT; } +static void _get_pwd_error( pam_handle_t *pamh, + struct configuration_st *configuration, + int rv ) +{ + if (!configuration->quiet) { + pam_prompt(pamh, PAM_ERROR_MSG , NULL, + _("Error 2316: password could not be read")); + sleep(configuration->err_display_time); + } + pam_syslog(pamh, LOG_ERR, + "pam_get_pwd() failed: %s", pam_strerror(pamh, rv)); +} + +static int check_pwd( pam_handle_t *pamh, + struct configuration_st *configuration, + char *password ) +{ +#ifdef DEBUG_SHOW_PASSWORD + DBG1("password = [%s]", password); +#endif + + /* check password length */ + if ( !configuration->nullok && strlen(password) == 0 ) { + memset(password, 0, strlen(password)); + pam_syslog(pamh, LOG_ERR, + "password length is zero but the 'nullok' " \ + "argument was not defined."); + if (!configuration->quiet) { + pam_prompt(pamh, PAM_ERROR_MSG , NULL, + _("Error 2318: Empty smartcard PIN not allowed.")); + sleep(configuration->err_display_time); + } + return PAM_AUTH_ERR; + } + + return 0; +} + +static int pkcs11_module_load_init( pam_handle_t *pamh, + struct configuration_st *configuration, + pkcs11_handle_t **ph ) +{ + int rv; + + /* load pkcs #11 module */ + DBG("loading pkcs #11 module..."); + rv = load_pkcs11_module(configuration->pkcs11_modulepath, ph); + + if (rv != 0) { + ERR2("load_pkcs11_module() failed loading %s: %s", + configuration->pkcs11_modulepath, get_error()); + if (!configuration->quiet) { + pam_syslog(pamh, LOG_ERR, + "load_pkcs11_module() failed loading %s: %s", + configuration->pkcs11_modulepath, get_error()); + pam_prompt(pamh, PAM_ERROR_MSG , NULL, + _("Error 2302: PKCS#11 module failed loading")); + sleep(configuration->err_display_time); + } + return PAM_AUTHINFO_UNAVAIL; + } + + /* initialise pkcs #11 module */ + DBG("initializing pkcs #11 module..."); + rv = init_pkcs11_module( *ph, configuration->support_threads ); + + if (rv != 0) { + release_pkcs11_module( *ph ); + ERR1("init_pkcs11_module() failed: %s", get_error()); + if (!configuration->quiet) { + pam_syslog(pamh, LOG_ERR, + "init_pkcs11_module() failed: %s", + get_error()); + pam_prompt(pamh, PAM_ERROR_MSG , NULL, + _("Error 2304: PKCS#11 module could not be initialized")); + sleep(configuration->err_display_time); + } + return PAM_AUTHINFO_UNAVAIL; + } + + return rv; +} + +static int pkcs11_find_slot( pam_handle_t *pamh, + struct configuration_st *configuration, + const char *login_token_name, + pkcs11_handle_t *ph, + unsigned int *slot_num ) +{ + int rv = -1; + + if (configuration->slot_description != NULL) { + rv = find_slot_by_slotlabel_and_tokenlabel( + ph, + configuration->slot_description, + login_token_name, + slot_num + ); + } else if (configuration->slot_num != -1) { + rv = find_slot_by_number_and_label(ph, configuration->slot_num, + login_token_name, slot_num); + } + + if (rv != 0) { + ERR("no suitable token available"); + if (!configuration->quiet) { + pam_syslog(pamh, LOG_ERR, "no suitable token available"); + pam_prompt(pamh, PAM_ERROR_MSG , NULL, _("Error 2306: No suitable token available")); + sleep(configuration->err_display_time); + } + } + + return rv; +} + +static int pkcs11_open_session( pam_handle_t *pamh, + struct configuration_st *configuration, + pkcs11_handle_t *ph, + unsigned int slot_num, + int rw ) +{ + int rv; + + rv = open_pkcs11_session( ph, slot_num, rw ); + + if (rv != 0) { + ERR1("open_pkcs11_session() failed: %s", get_error()); + if (!configuration->quiet) { + pam_syslog(pamh, LOG_ERR, "open_pkcs11_session() failed: %s", get_error()); + pam_prompt(pamh, PAM_ERROR_MSG , NULL, _("Error 2312: open PKCS#11 session failed")); + sleep(configuration->err_display_time); + } + } + + return rv; +} + +static int pkcs11_close_session( pam_handle_t *pamh, + struct configuration_st *configuration, + pkcs11_handle_t *ph ) +{ + int rv; + + rv = close_pkcs11_session(ph); + + if (rv != 0) { + ERR1("close_pkcs11_session() failed: %s", get_error()); + if (!configuration->quiet) { + pam_syslog(pamh, LOG_ERR, "close_pkcs11_module() failed: %s", get_error()); + pam_prompt(pamh, PAM_ERROR_MSG , NULL, ("Error 2344: Closing PKCS#11 session failed")); + sleep(configuration->err_display_time); + } + } + + return rv; +} + PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) { int i, rv; @@ -342,43 +499,12 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, cons return PAM_IGNORE; } - /* load pkcs #11 module */ - DBG("loading pkcs #11 module..."); - rv = load_pkcs11_module(configuration->pkcs11_modulepath, &ph); - if (rv != 0) { - ERR2("load_pkcs11_module() failed loading %s: %s", - configuration->pkcs11_modulepath, get_error()); - if (!configuration->quiet) { - pam_syslog(pamh, LOG_ERR, "load_pkcs11_module() failed loading %s: %s", - configuration->pkcs11_modulepath, get_error()); - pam_prompt(pamh, PAM_ERROR_MSG , NULL, _("Error 2302: PKCS#11 module failed loading")); - sleep(configuration->err_display_time); - } - return PAM_AUTHINFO_UNAVAIL; - } - - /* initialise pkcs #11 module */ - DBG("initialising pkcs #11 module..."); - rv = init_pkcs11_module(ph,configuration->support_threads); - if (rv != 0) { - release_pkcs11_module(ph); - ERR1("init_pkcs11_module() failed: %s", get_error()); - if (!configuration->quiet) { - pam_syslog(pamh, LOG_ERR, "init_pkcs11_module() failed: %s", get_error()); - pam_prompt(pamh, PAM_ERROR_MSG , NULL, _("Error 2304: PKCS#11 module could not be initialized")); - sleep(configuration->err_display_time); - } - return PAM_AUTHINFO_UNAVAIL; + rv = pkcs11_module_load_init( pamh, configuration, &ph ); + if ( rv != 0 ) { + return rv; } - /* open pkcs #11 session */ - if (configuration->slot_description != NULL) { - rv = find_slot_by_slotlabel_and_tokenlabel(ph, - configuration->slot_description, login_token_name, &slot_num); - } else if (configuration->slot_num != -1) { - rv = find_slot_by_number_and_label(ph, configuration->slot_num, - login_token_name, &slot_num); - } + rv = pkcs11_find_slot( pamh, configuration, login_token_name, ph, &slot_num ); if (!configuration->card_only || !login_token_name) { /* Allow to pass to the next module if the auth isn't @@ -462,14 +588,9 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, cons pam_prompt(pamh, PAM_TEXT_INFO, NULL, _("%s found."), _(configuration->token_type)); } - rv = open_pkcs11_session(ph, slot_num); + + rv = pkcs11_open_session( pamh, configuration, ph, slot_num, 0 ); if (rv != 0) { - ERR1("open_pkcs11_session() failed: %s", get_error()); - if (!configuration->quiet) { - pam_syslog(pamh, LOG_ERR, "open_pkcs11_session() failed: %s", get_error()); - pam_prompt(pamh, PAM_ERROR_MSG , NULL, _("Error 2312: open PKCS#11 session failed")); - sleep(configuration->err_display_time); - } release_pkcs11_module(ph); return pkcs11_pam_fail; } @@ -676,31 +797,17 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, cons } else { rv = pam_get_pwd(pamh, &password, password_prompt, 0, PAM_AUTHTOK); } + if (rv != PAM_SUCCESS) { - if (!configuration->quiet) { - pam_prompt(pamh, PAM_ERROR_MSG , NULL, _("Error 2316: password could not be read")); - sleep(configuration->err_display_time); - } + _get_pwd_error( pamh, configuration, rv ); release_pkcs11_module(ph); - pam_syslog(pamh, LOG_ERR, - "pam_get_pwd() failed: %s", pam_strerror(pamh, rv)); return pkcs11_pam_fail; } -#ifdef DEBUG_SHOW_PASSWORD - DBG1("password = [%s]", password); -#endif - /* check password length */ - if (!configuration->nullok && strlen(password) == 0) { + rv = check_pwd( pamh, configuration, password ); + if ( rv != 0) { release_pkcs11_module(ph); - memset(password, 0, strlen(password)); free(password); - pam_syslog(pamh, LOG_ERR, - "password length is zero but the 'nullok' argument was not defined."); - if (!configuration->quiet) { - pam_prompt(pamh, PAM_ERROR_MSG , NULL, _("Error 2318: Empty smartcard PIN not allowed.")); - sleep(configuration->err_display_time); - } return PAM_AUTH_ERR; } } @@ -865,15 +972,9 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, cons unload_mappers(); /* close pkcs #11 session */ - rv = close_pkcs11_session(ph); + rv = pkcs11_close_session( pamh, configuration, ph ); if (rv != 0) { release_pkcs11_module(ph); - ERR1("close_pkcs11_session() failed: %s", get_error()); - if (!configuration->quiet) { - pam_syslog(pamh, LOG_ERR, "close_pkcs11_module() failed: %s", get_error()); - pam_prompt(pamh, PAM_ERROR_MSG , NULL, ("Error 2344: Closing PKCS#11 session failed")); - sleep(configuration->err_display_time); - } return pkcs11_pam_fail; } @@ -936,16 +1037,166 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const c { char *login_token_name; - ERR("Warning: Function pam_sm_chauthtok() is not implemented in this module"); - pam_syslog(pamh, LOG_WARNING, - "Function pam_sm_chauthtok() is not implemented in this module"); - login_token_name = getenv("PKCS11_LOGIN_TOKEN_NAME"); - if (login_token_name && (flags & PAM_PRELIM_CHECK)) { - pam_prompt(pamh, PAM_TEXT_INFO, NULL, - _("Cannot change the password on your smart card.")); + if (login_token_name) { + char *old_pass; + char *new_pass; + int rv; unsigned int slot_num; + struct configuration_st *configuration; + pkcs11_handle_t *ph; + + if (flags & PAM_PRELIM_CHECK) { + return PAM_SUCCESS; + } + + configuration = pk_configure(argc,argv); + if (!configuration ) { + ERR("Error setting configuration parameters"); + return PAM_AUTHINFO_UNAVAIL; + } + + rv = pkcs11_module_load_init( pamh, configuration, &ph ); + if ( rv != 0 ) { + return rv; + } + + rv = pkcs11_find_slot( pamh, configuration, login_token_name, ph, &slot_num ); + if ( rv != 0 ) { + release_pkcs11_module(ph); + if ( configuration->card_only ) { + return PAM_AUTHINFO_UNAVAIL; + } else { + return PAM_IGNORE; + } + } + + rv = pkcs11_open_session( pamh, configuration, ph, slot_num, 1 ); + if (rv != 0) { + release_pkcs11_module(ph); + return PAM_AUTHINFO_UNAVAIL; + } + + rv = get_slot_protected_authentication_path( ph ); + if ((-1 == rv) || (0 == rv)) { + /* no CKF_PROTECTED_AUTHENTICATION_PATH */ + char password_prompt[128]; + char *confirm; + + /* Old PIN */ + snprintf(password_prompt, sizeof(password_prompt), + _("Old %s PIN: "), _(configuration->token_type)); + rv = pam_get_pwd(pamh, &old_pass, password_prompt, + 0, PAM_AUTHTOK); + + if (rv != PAM_SUCCESS) { + _get_pwd_error( pamh, configuration, rv ); + release_pkcs11_module(ph); + return PAM_AUTHTOK_RECOVERY_ERR; + } + + rv = check_pwd( pamh, configuration, old_pass ); + if ( rv != 0 ) { + release_pkcs11_module(ph); + free(old_pass); + return PAM_AUTHTOK_RECOVERY_ERR; + } + + /* New PIN */ + snprintf(password_prompt, sizeof(password_prompt), + _("New %s PIN: "), _(configuration->token_type)); + rv = pam_get_pwd(pamh, &new_pass, password_prompt, + 0, PAM_AUTHTOK); + + if (rv != PAM_SUCCESS) { + _get_pwd_error( pamh, configuration, rv ); + release_pkcs11_module(ph); + return PAM_AUTHTOK_ERR; + } + + rv = check_pwd( pamh, configuration, new_pass ); + if ( rv != 0 ) { + release_pkcs11_module(ph); + memset( old_pass, 0, strlen(old_pass) ); + free( old_pass ); + free( new_pass ); + return PAM_AUTHTOK_ERR; + } + + /* Confirm new PIN */ + snprintf(password_prompt, sizeof(password_prompt), + _("Confirm new PIN: ")); + rv = pam_get_pwd(pamh, &confirm, password_prompt, + 0, PAM_AUTHTOK); + + if (rv != PAM_SUCCESS) { + _get_pwd_error( pamh, configuration, rv ); + release_pkcs11_module(ph); + memset( old_pass, 0, strlen(old_pass) ); + free( old_pass ); + memset( new_pass, 0, strlen(new_pass) ); + free( new_pass ); + return PAM_AUTHTOK_ERR; + } + + if ( strcmp(new_pass, confirm) != 0 ) { + ERR("Confirm PIN mismatch"); + if (!configuration->quiet) { + pam_syslog(pamh, LOG_ERR, "Confirm PIN mismatch"); + pam_prompt(pamh, PAM_ERROR_MSG , NULL, + _("Confirm PIN mismatch")); + sleep(configuration->err_display_time); + } + release_pkcs11_module(ph); + memset( old_pass, 0, strlen(old_pass) ); + free( old_pass ); + memset( new_pass, 0, strlen(new_pass) ); + free( new_pass ); + memset( confirm, 0, strlen(confirm) ); + free( confirm ); + return PAM_AUTHTOK_ERR; + } else { + memset( confirm, 0, strlen(confirm) ); + free( confirm ); + } + } else { + pam_prompt(pamh, PAM_TEXT_INFO, NULL, + _("Now use the pinpad to change your %s PIN"), + _(configuration->token_type)); + old_pass = NULL; + new_pass = NULL; + } + + rv = pkcs11_login( ph, old_pass ); + if ( rv == 0 ) { + rv = pkcs11_setpin( ph, old_pass, new_pass ); + } + pkcs11_close_session( pamh, configuration, ph ); + release_pkcs11_module( ph ); + + if ( old_pass ) { + memset( old_pass, 0, strlen(old_pass) ); + free( old_pass ); + } + if ( new_pass) { + memset( new_pass, 0, strlen(new_pass) ); + free( new_pass ); + } + + if ( rv == 0 ) { + return PAM_SUCCESS; + } else { + ERR("C_SetPIN error"); + if (!configuration->quiet) { + pam_syslog(pamh, LOG_ERR, "C_SetPIN error"); + pam_prompt(pamh, PAM_ERROR_MSG , NULL, + _("Error: Unable to set new PIN")); + sleep(configuration->err_display_time); + } + return PAM_AUTHTOK_ERR; + } + } else { + return PAM_IGNORE; } - return PAM_SERVICE_ERR; } #ifdef PAM_STATIC diff --git a/src/tools/pkcs11_inspect.c b/src/tools/pkcs11_inspect.c index 6aac407..3cc0021 100644 --- a/src/tools/pkcs11_inspect.c +++ b/src/tools/pkcs11_inspect.c @@ -96,7 +96,7 @@ int main(int argc, const char **argv) { return 1; } - rv = open_pkcs11_session(ph, slot_num); + rv = open_pkcs11_session(ph, slot_num, 0); if (rv != 0) { release_pkcs11_module(ph); ERR1("open_pkcs11_session() failed: %s", get_error()); diff --git a/src/tools/pkcs11_listcerts.c b/src/tools/pkcs11_listcerts.c index 726addf..e8b693a 100644 --- a/src/tools/pkcs11_listcerts.c +++ b/src/tools/pkcs11_listcerts.c @@ -95,7 +95,7 @@ int main(int argc, const char **argv) { return 1; } - rv = open_pkcs11_session(ph, slot_num); + rv = open_pkcs11_session(ph, slot_num, 0); if (rv != 0) { release_pkcs11_module(ph); DBG1("open_pkcs11_session() failed: %s", get_error()); diff --git a/src/tools/pklogin_finder.c b/src/tools/pklogin_finder.c index 9ef04b9..e03b131 100644 --- a/src/tools/pklogin_finder.c +++ b/src/tools/pklogin_finder.c @@ -95,7 +95,7 @@ int main(int argc, const char **argv) { DBG("no token available"); return 1; } - rv = open_pkcs11_session(ph, slot_num); + rv = open_pkcs11_session(ph, slot_num, 0); if (rv != 0) { release_pkcs11_module(ph); DBG1("open_pkcs11_session() failed: %s", get_error());