ADDED build/ask-pass/cackey-askpass Index: build/ask-pass/cackey-askpass ================================================================== --- build/ask-pass/cackey-askpass +++ build/ask-pass/cackey-askpass @@ -0,0 +1,34 @@ +#! /usr/bin/env bash + +ZENITY="$(which 'zenity' 2>/dev/null)" +GDIALOG="$(which 'gdialog' 2>/dev/null)" +ASKPASS="$(which 'ssh-askpass' 'x11-ssh-askpass' '/usr/libexec/x11-ssh-askpass' 2>/dev/null | head -n 1)" +DIALOG="$(which 'dialog' 2>/dev/null)" +XTERM="$(which 'xterm' 2>/dev/null)" + +if [ -x "${ZENITY}" ]; then + # XXX: TODO: Zenity + true +fi + +if [ -x "${GDIALOG}" ]; then + # XXX: TODO: Gdialog + true +fi + +if [ -x "${ASKPASS}" ]; then + exec "${ASKPASS}" "$@" + exit 1 +fi + +if [ -x "${DIALOG}" -a -x "${XTERM}" ]; then + # XXX: TODO: dialog + true +fi + +if [ -x "${XTERM}" ]; then + # XXX: TODO: xterm + true +fi + +exit 1 Index: cackey.c ================================================================== --- cackey.c +++ cackey.c @@ -874,10 +874,16 @@ /** Extra certificates to include in token **/ struct cackey_pcsc_identity extra_certs[] = { #include "cackey_builtin_certs.h" }; +/* Protected Authentication Path command */ +#define CACKEY_PIN_COMMAND_DEFAULT_XSTR(str) CACKEY_PIN_COMMAND_DEFAULT_STR(str) +#define CACKEY_PIN_COMMAND_DEFAULT_STR(str) #str +static char *cackey_pin_command = NULL; +static char *cackey_pin_command_xonly = NULL; + /* PCSC Global Handles */ static LPSCARDCONTEXT cackey_pcsc_handle = NULL; static unsigned long cackey_getversion(void) { static unsigned long retval = 255; @@ -3054,10 +3060,17 @@ if (pin_len >= 8) { memcpy(cac_pin, pin, 8); } else { memcpy(cac_pin, pin, pin_len); } + + /* Reject PINs which are too short */ + if (pin_len < 5) { + CACKEY_DEBUG_PRINTF("Rejecting PIN which is too short (length = %lu, must be atleast 5)", pin_len); + + return(CACKEY_PCSC_E_BADPIN); + } /* PIV authentication uses a "key_reference" of 0x80 */ pcsc_identities = cackey_read_certs(slot, NULL, &num_certs); if (num_certs > 0 && pcsc_identities != NULL) { switch (pcsc_identities[0].id_type) { @@ -4135,10 +4148,30 @@ return(CKR_CANT_LOCK); } cackey_biglock_init = 1; } + + /* Define a command to prompt user for a PIN */ +#ifdef CACKEY_PIN_COMMAND_DEFAULT + cackey_pin_command = CACKEY_PIN_COMMAND_DEFAULT_XSTR(CACKEY_PIN_COMMAND_DEFAULT); +#endif +#ifdef CACKEY_PIN_COMMAND_XONLY_DEFAULT + cackey_pin_command_xonly = CACKEY_PIN_COMMAND_DEFAULT_XSTR(CACKEY_PIN_COMMAND_XONLY_DEFAULT); +#endif + + if (getenv("DISPLAY") != NULL) { + cackey_pin_command = cackey_pin_command_xonly; + } + + if (getenv("CACKEY_PIN_COMMAND_XONLY") != NULL && getenv("DISPLAY") != NULL) { + cackey_pin_command = getenv("CACKEY_PIN_COMMAND_XONLY"); + } + + if (getenv("CACKEY_PIN_COMMAND") != NULL) { + cackey_pin_command = getenv("CACKEY_PIN_COMMAND"); + } CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK); return(CKR_OK); } @@ -4667,10 +4700,14 @@ pInfo->firmwareVersion.major = 0x00; pInfo->firmwareVersion.minor = 0x00; pInfo->flags = CKF_WRITE_PROTECTED | CKF_USER_PIN_INITIALIZED | CKF_TOKEN_INITIALIZED | cackey_slots[slotID].token_flags; + + if (cackey_pin_command != NULL) { + pInfo->flags |= CKF_PROTECTED_AUTHENTICATION_PATH; + } pInfo->ulMaxSessionCount = (sizeof(cackey_sessions) / sizeof(cackey_sessions[0])) - 1; pInfo->ulSessionCount = CK_UNAVAILABLE_INFORMATION; pInfo->ulMaxRwSessionCount = 0; pInfo->ulRwSessionCount = CK_UNAVAILABLE_INFORMATION; @@ -5130,13 +5167,16 @@ return(CKR_FUNCTION_NOT_SUPPORTED); } CK_DEFINE_FUNCTION(CK_RV, C_Login)(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) { CK_SLOT_ID slotID; + FILE *pinfd; + char *pincmd, pinbuf[64], *fgets_ret; int mutex_retval; int tries_remaining; int login_ret; + int pclose_ret; CACKEY_DEBUG_PRINTF("Called."); if (!cackey_initialized) { CACKEY_DEBUG_PRINTF("Error. Not initialized."); @@ -5184,19 +5224,74 @@ cackey_mutex_unlock(cackey_biglock); return(CKR_GENERAL_ERROR); } + + pincmd = cackey_pin_command; + if (pincmd != NULL) { + CACKEY_DEBUG_PRINTF("CACKEY_PIN_COMMAND = %s", pincmd); + + if (pPin != NULL) { + CACKEY_DEBUG_PRINTF("Protected authentication path in effect and PIN provided !?"); + } + + pinfd = popen(pincmd, "r"); + if (pinfd == NULL) { + CACKEY_DEBUG_PRINTF("Error. %s: Unable to run", pincmd); + + cackey_mutex_unlock(cackey_biglock); + + CACKEY_DEBUG_PRINTF("Returning CKR_PIN_INCORRECT (%i)", (int) CKR_PIN_INCORRECT); + + return(CKR_PIN_INCORRECT); + } + + fgets_ret = fgets(pinbuf, sizeof(pinbuf), pinfd); + if (fgets_ret == NULL) { + pinbuf[0] = '\0'; + } + + pclose_ret = pclose(pinfd); + if (pclose_ret != 0) { + CACKEY_DEBUG_PRINTF("Error. %s: exited with non-zero status of %i", pincmd, pclose_ret); + + cackey_mutex_unlock(cackey_biglock); + + CACKEY_DEBUG_PRINTF("Returning CKR_PIN_INCORRECT (%i)", (int) CKR_PIN_INCORRECT); + + return(CKR_PIN_INCORRECT); + } + + if (strlen(pinbuf) < 1) { + CACKEY_DEBUG_PRINTF("Error. %s: returned no data", pincmd); + + cackey_mutex_unlock(cackey_biglock); + + CACKEY_DEBUG_PRINTF("Returning CKR_PIN_INCORRECT (%i)", (int) CKR_PIN_INCORRECT); + + return(CKR_PIN_INCORRECT); + } + + if (pinbuf[strlen(pinbuf) - 1] == '\n') { + pinbuf[strlen(pinbuf) - 1] = '\0'; + } + + pPin = (CK_UTF8CHAR_PTR) pinbuf; + ulPinLen = strlen(pinbuf); + } login_ret = cackey_login(&cackey_slots[slotID], pPin, ulPinLen, &tries_remaining); if (login_ret != CACKEY_PCSC_S_OK) { cackey_mutex_unlock(cackey_biglock); if (login_ret == CACKEY_PCSC_E_LOCKED) { CACKEY_DEBUG_PRINTF("Error. Token is locked."); cackey_slots[slotID].token_flags |= CKF_USER_PIN_LOCKED; + + CACKEY_DEBUG_PRINTF("Returning CKR_PIN_LOCKED (%i)", (int) CKR_PIN_LOCKED); return(CKR_PIN_LOCKED); } else if (login_ret == CACKEY_PCSC_E_BADPIN) { CACKEY_DEBUG_PRINTF("Error. Invalid PIN."); @@ -5203,10 +5298,12 @@ cackey_slots[slotID].token_flags |= CKF_USER_PIN_COUNT_LOW; if (tries_remaining == 1) { cackey_slots[slotID].token_flags |= CKF_USER_PIN_FINAL_TRY; } + + CACKEY_DEBUG_PRINTF("Returning CKR_PIN_INCORRECT (%i)", (int) CKR_PIN_INCORRECT); return(CKR_PIN_INCORRECT); } CACKEY_DEBUG_PRINTF("Error. Unknown error returned from cackey_login() (%i)", login_ret); Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -162,10 +162,31 @@ ]) if test "${dodcerts}" = 'no'; then AC_DEFINE(CACKEY_NO_EXTRA_CERTS, [1], [Specify that DoD certificates should not be made available]) fi + +dnl Option to hard-code a command to run to request a PIN (enabling protected authentication path) +AC_ARG_WITH(pin-command, AC_HELP_STRING([--with-pin-command=], [Specify a command to run to request a PIN from the user. The user may override this with the CACKEY_PIN_COMMAND environment variable.]), [ + pincommand="${withval}" +], [ + pincommand="no" +]) + +AC_ARG_WITH(pin-command-x, AC_HELP_STRING([--with-pin-command-x=], [Same as --with-pin-command, but only sets Protected Authentication Path if the DISPLAY environment variable is set]), [ + pincommandxonly="${withval}" +], [ + pincommandxonly="no" +]) + +if ! test "${pincommand}" = 'no'; then + AC_DEFINE_UNQUOTED(CACKEY_PIN_COMMAND_DEFAULT, [$pincommand], [Command to run to prompt user for PIN]) +fi + +if ! test "${pincommandxonly}" = 'no'; then + AC_DEFINE_UNQUOTED(CACKEY_PIN_COMMAND_XONLY_DEFAULT, [$pincommandxonly], [Command to run to prompt user for PIN only if DISPLAY environment variable is set]) +fi dnl Set version script, to limit the scope of symbols DC_SETVERSIONSCRIPT(libcackey.vers, libcackey.syms) dnl Upate LDFLAGS to include setting the run-time linker path to the same as our compile-time linker Index: test.c ================================================================== --- test.c +++ test.c @@ -333,11 +333,11 @@ chk_rv = C_GetTokenInfo(slots[0], &tokenInfo); if (chk_rv != CKR_OK) { return(1); } - if ((tokenInfo.flags & CKF_LOGIN_REQUIRED) == CKF_LOGIN_REQUIRED) { + if ((tokenInfo.flags & CKF_LOGIN_REQUIRED) == CKF_LOGIN_REQUIRED && (tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) == 0) { fgets_ret = NULL; while (fgets_ret == NULL) { printf("** ENTER PIN: "); fflush(stdout);