Add syslog/windows event log output
Read multistage configuration Update ROCA mitigation check and warnings
This commit is contained in:
@@ -75,12 +75,15 @@ tool/cmdline.h
|
|||||||
tool/cmdline.o
|
tool/cmdline.o
|
||||||
tool/yubico-piv-tool
|
tool/yubico-piv-tool
|
||||||
tool/yubico-piv-tool.1
|
tool/yubico-piv-tool.1
|
||||||
|
tool/yubico-piv-tool.exe
|
||||||
tool/yubico-piv-tool.o
|
tool/yubico-piv-tool.o
|
||||||
tool/.libs/
|
tool/.libs/
|
||||||
tool/libpiv_cmd.la
|
tool/libpiv_cmd.la
|
||||||
tool/libpiv_cmd_la-cmdline.lo
|
tool/libpiv_cmd_la-cmdline.lo
|
||||||
tool/libpiv_cmd_la-cmdline.o
|
tool/libpiv_cmd_la-cmdline.o
|
||||||
tool/libpiv_util.la
|
tool/libpiv_util.la
|
||||||
|
tool/openssl-compat.lo
|
||||||
|
tool/openssl-compat.o
|
||||||
tool/util.lo
|
tool/util.lo
|
||||||
tool/util.o
|
tool/util.o
|
||||||
tool/tests/cert_9a.pem
|
tool/tests/cert_9a.pem
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@
|
|||||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
AC_INIT([yubico-piv-tool], [1.5.1])
|
AC_INIT([yubico-piv-tool], [1.5.2])
|
||||||
AC_CONFIG_AUX_DIR([build-aux])
|
AC_CONFIG_AUX_DIR([build-aux])
|
||||||
AC_CONFIG_MACRO_DIR([m4])
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
|
|
||||||
|
|||||||
+263
-11
@@ -1,17 +1,28 @@
|
|||||||
|
#ifdef _WIN32
|
||||||
#ifdef _WINDOWS
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <strsafe.h>
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define strcasecmp _stricmp
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* the _WINDOWS define really means Windows native crypto-api/CNG */
|
||||||
|
#ifdef _WINDOWS
|
||||||
#include <wincrypt.h>
|
#include <wincrypt.h>
|
||||||
#include <bcrypt.h>
|
#include <bcrypt.h>
|
||||||
#else
|
#else
|
||||||
#include <openssl/des.h>
|
#include <openssl/des.h>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
#include <openssl/rand.h>
|
#include <openssl/rand.h>
|
||||||
#include <string.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
@@ -19,6 +30,8 @@
|
|||||||
** Definitions
|
** Definitions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* crypt defines */
|
||||||
|
|
||||||
#ifdef _WINDOWS
|
#ifdef _WINDOWS
|
||||||
|
|
||||||
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
||||||
@@ -90,6 +103,24 @@ struct des_key {
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* config defines */
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define _CONFIG_REGKEY "Software\\Yubico\\yubikeypiv"
|
||||||
|
#else
|
||||||
|
#define _CONFIG_FILE "/etc/yubico/yubikeypiv.conf"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define _ENV_PREFIX "YUBIKEY_PIV_"
|
||||||
|
|
||||||
|
char *_strip_ws(char *sz);
|
||||||
|
setting_bool_t _get_bool_config(const char *sz_setting);
|
||||||
|
setting_bool_t _get_bool_env(const char *sz_setting);
|
||||||
|
|
||||||
|
/* log */
|
||||||
|
|
||||||
|
const char szLOG_SOURCE[] = "YubiKey PIV Library";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Methods
|
** Methods
|
||||||
*/
|
*/
|
||||||
@@ -271,14 +302,6 @@ des_rc des_encrypt(des_key* key, const unsigned char* in, const size_t inlen, un
|
|||||||
// reset key usage by encrypting a fake padded block
|
// reset key usage by encrypting a fake padded block
|
||||||
CryptEncrypt(key->hKey, 0, TRUE, 0, buf, (DWORD*)&buflen, (DWORD)buflen);
|
CryptEncrypt(key->hKey, 0, TRUE, 0, buf, (DWORD*)&buflen, (DWORD)buflen);
|
||||||
|
|
||||||
//if (CALG_3DES == key->alg) {
|
|
||||||
// // truncate the final pad block
|
|
||||||
// *outlen = inlen - 8;
|
|
||||||
//}
|
|
||||||
//else {
|
|
||||||
// *outlen = inlen;
|
|
||||||
//}
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
/* openssl returns void */
|
/* openssl returns void */
|
||||||
@@ -443,3 +466,232 @@ pkcs5_rc pkcs5_pbkdf2_sha1(const unsigned char* password, const size_t cb_passwo
|
|||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* settings */
|
||||||
|
|
||||||
|
char *_strip_ws(char *sz) {
|
||||||
|
char *psz_head = sz;
|
||||||
|
char *psz_tail = sz + strlen(sz) - 1;
|
||||||
|
|
||||||
|
/* strip leading whitespace */
|
||||||
|
while (isspace(*psz_head)) {
|
||||||
|
psz_head++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* strip trailing whitespace */
|
||||||
|
while ((psz_tail >= psz_head) && isspace(*psz_tail)) {
|
||||||
|
*psz_tail-- = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return psz_head;
|
||||||
|
}
|
||||||
|
|
||||||
|
setting_bool_t _get_bool_config(const char *sz_setting) {
|
||||||
|
setting_bool_t setting = { false, SETTING_SOURCE_DEFAULT };
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
HKEY hKey = 0;
|
||||||
|
DWORD dwErr = 0;
|
||||||
|
DWORD dwValue = 0;
|
||||||
|
DWORD dwType = 0;
|
||||||
|
DWORD cbValue = sizeof(dwValue);
|
||||||
|
|
||||||
|
/* MINGW doesn't define RRF_SUBKEY_WOW6464KEY for RegGetValue, so read the traditional way */
|
||||||
|
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, _CONFIG_REGKEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey) == 0) {
|
||||||
|
if (RegQueryValueExA(hKey, sz_setting, NULL, &dwType, (LPBYTE)&dwValue, &cbValue) == 0) {
|
||||||
|
setting.value = ((dwType == REG_DWORD) && (dwValue == 1));
|
||||||
|
setting.source = SETTING_SOURCE_ADMIN;
|
||||||
|
}
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
hKey = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
/* read from config file*/
|
||||||
|
char sz_line[256];
|
||||||
|
char *psz_name = 0;
|
||||||
|
char *psz_value = 0;
|
||||||
|
char sz_name[256] = { 0 };
|
||||||
|
char sz_value[256] = { 0 };
|
||||||
|
size_t i = 0;
|
||||||
|
FILE *pf = 0;
|
||||||
|
|
||||||
|
if ((pf = fopen(_CONFIG_FILE, "r"))) {
|
||||||
|
while (!feof(pf)) {
|
||||||
|
if (fgets(sz_line, sizeof(sz_line), pf)) {
|
||||||
|
if (*sz_line == '#') continue;
|
||||||
|
if (*sz_line == '\r') continue;
|
||||||
|
if (*sz_line == '\n') continue;
|
||||||
|
|
||||||
|
if (sscanf(sz_line, "%255[^=]=%255s", sz_name, sz_value) == 2);
|
||||||
|
|
||||||
|
/* strip leading/trailing whitespace */
|
||||||
|
psz_name = _strip_ws(sz_name);
|
||||||
|
|
||||||
|
if (!strcasecmp(psz_name, sz_setting)) {
|
||||||
|
psz_value = _strip_ws(sz_value);
|
||||||
|
|
||||||
|
setting.source = SETTING_SOURCE_ADMIN;
|
||||||
|
setting.value = (!strcmp(psz_value, "1") || !strcasecmp(psz_value, "true"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(pf);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
setting_bool_t _get_bool_env(const char *sz_setting) {
|
||||||
|
setting_bool_t setting = { false, SETTING_SOURCE_DEFAULT };
|
||||||
|
char *psz_value = NULL;
|
||||||
|
char sz_name[256] = { 0 };
|
||||||
|
|
||||||
|
snprintf(sz_name, sizeof(sz_name) - 1, "%s%s", _ENV_PREFIX, sz_setting);
|
||||||
|
|
||||||
|
/* MINGW does not implement getenv_s, only _wgetenv_s */
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
size_t cb_value = 0;
|
||||||
|
char sz_value[100] = { 0 };
|
||||||
|
|
||||||
|
if ((getenv_s(&cb_value, sz_value, sizeof(sz_value) - 1, sz_name) == 0) && (cb_value > 0)) {
|
||||||
|
psz_value = sz_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
psz_value = getenv(sz_name);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (psz_value) {
|
||||||
|
setting.source = SETTING_SOURCE_USER;
|
||||||
|
setting.value = (!strcmp(psz_value, "1") || !strcasecmp(psz_value, "true"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
setting_bool_t setting_get_bool(const char *sz_setting, bool def) {
|
||||||
|
setting_bool_t setting = { def, SETTING_SOURCE_DEFAULT };
|
||||||
|
|
||||||
|
setting = _get_bool_config(sz_setting);
|
||||||
|
|
||||||
|
if (setting.source == SETTING_SOURCE_DEFAULT) {
|
||||||
|
setting = _get_bool_env(sz_setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setting.source == SETTING_SOURCE_DEFAULT) {
|
||||||
|
setting.value = def;
|
||||||
|
}
|
||||||
|
|
||||||
|
return setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* logging */
|
||||||
|
|
||||||
|
void yc_log_event(uint32_t id, yc_log_level_t level, const char * sz_format, ...) {
|
||||||
|
char rgsz_message[4096];
|
||||||
|
va_list vl;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE hLog = NULL;
|
||||||
|
LPCSTR sz_message = rgsz_message;
|
||||||
|
DWORD dw_type = EVENTLOG_SUCCESS;
|
||||||
|
#else
|
||||||
|
int priority = LOG_INFO;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
va_start(vl, sz_format);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
switch (level) {
|
||||||
|
case YC_LOG_LEVEL_ERROR:
|
||||||
|
dw_type = EVENTLOG_ERROR_TYPE;
|
||||||
|
break;
|
||||||
|
case YC_LOG_LEVEL_WARN:
|
||||||
|
dw_type = EVENTLOG_WARNING_TYPE;
|
||||||
|
break;
|
||||||
|
case YC_LOG_LEVEL_INFO:
|
||||||
|
dw_type = EVENTLOG_INFORMATION_TYPE;
|
||||||
|
break;
|
||||||
|
case YC_LOG_LEVEL_VERBOSE:
|
||||||
|
dw_type = EVENTLOG_INFORMATION_TYPE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case YC_LOG_LEVEL_DEBUG:
|
||||||
|
dw_type = EVENTLOG_SUCCESS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(hLog = RegisterEventSourceA(NULL, szLOG_SOURCE))) {
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* format message */
|
||||||
|
|
||||||
|
if (FAILED(StringCbVPrintfA(
|
||||||
|
rgsz_message,
|
||||||
|
sizeof(rgsz_message),
|
||||||
|
sz_format,
|
||||||
|
vl))) {
|
||||||
|
goto Cleanup;
|
||||||
|
};
|
||||||
|
|
||||||
|
// write to the local event log
|
||||||
|
|
||||||
|
ReportEventA(
|
||||||
|
hLog,
|
||||||
|
dw_type,
|
||||||
|
0,
|
||||||
|
(DWORD)id,
|
||||||
|
NULL,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
(LPCSTR *)&sz_message,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
switch (level) {
|
||||||
|
case YC_LOG_LEVEL_ERROR:
|
||||||
|
priority = LOG_ERR;
|
||||||
|
break;
|
||||||
|
case YC_LOG_LEVEL_WARN:
|
||||||
|
priority = LOG_WARNING;
|
||||||
|
break;
|
||||||
|
case YC_LOG_LEVEL_INFO:
|
||||||
|
priority = LOG_NOTICE;
|
||||||
|
break;
|
||||||
|
case YC_LOG_LEVEL_VERBOSE:
|
||||||
|
priority = LOG_INFO;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case YC_LOG_LEVEL_DEBUG:
|
||||||
|
priority = LOG_DEBUG;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vsnprintf(rgsz_message, sizeof(rgsz_message), sz_format, vl) < 0) {
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
openlog(szLOG_SOURCE, LOG_PID | LOG_NDELAY, LOG_USER);
|
||||||
|
syslog(priority, rgsz_message);
|
||||||
|
closelog();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Cleanup:
|
||||||
|
|
||||||
|
va_end(vl);
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (hLog) {
|
||||||
|
DeregisterEventSource(hLog);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -146,6 +146,12 @@ typedef enum {
|
|||||||
PRNG_GENERAL_ERROR = -1
|
PRNG_GENERAL_ERROR = -1
|
||||||
} prng_rc;
|
} prng_rc;
|
||||||
|
|
||||||
|
typedef struct _ykpiv_version_t {
|
||||||
|
uint8_t major;
|
||||||
|
uint8_t minor;
|
||||||
|
uint8_t patch;
|
||||||
|
} ykpiv_version_t;
|
||||||
|
|
||||||
struct ykpiv_state {
|
struct ykpiv_state {
|
||||||
SCARDCONTEXT context;
|
SCARDCONTEXT context;
|
||||||
SCARDHANDLE card;
|
SCARDHANDLE card;
|
||||||
@@ -153,6 +159,8 @@ struct ykpiv_state {
|
|||||||
char *pin;
|
char *pin;
|
||||||
ykpiv_allocator allocator;
|
ykpiv_allocator allocator;
|
||||||
bool isNEO;
|
bool isNEO;
|
||||||
|
ykpiv_version_t ver;
|
||||||
|
uint32_t serial;
|
||||||
};
|
};
|
||||||
|
|
||||||
union u_APDU {
|
union u_APDU {
|
||||||
@@ -191,6 +199,37 @@ void* _ykpiv_realloc(ykpiv_state *state, void *address, size_t size);
|
|||||||
void _ykpiv_free(ykpiv_state *state, void *data);
|
void _ykpiv_free(ykpiv_state *state, void *data);
|
||||||
ykpiv_rc _ykpiv_save_object(ykpiv_state *state, int object_id, unsigned char *indata, size_t len);
|
ykpiv_rc _ykpiv_save_object(ykpiv_state *state, int object_id, unsigned char *indata, size_t len);
|
||||||
ykpiv_rc _ykpiv_fetch_object(ykpiv_state *state, int object_id, unsigned char *data, unsigned long *len);
|
ykpiv_rc _ykpiv_fetch_object(ykpiv_state *state, int object_id, unsigned char *data, unsigned long *len);
|
||||||
|
ykpiv_rc _send_data(ykpiv_state *state, APDU *apdu, unsigned char *data, uint32_t *recv_len, int *sw);
|
||||||
|
ykpiv_rc _ykpiv_get_version(ykpiv_state *state, ykpiv_version_t *p_version);
|
||||||
|
ykpiv_rc _ykpiv_util_get_serial(ykpiv_state *state, uint32_t *p_serial, bool f_force);
|
||||||
|
|
||||||
|
/* authentication functions not ready for public api */
|
||||||
|
ykpiv_rc ykpiv_auth_getchallenge(ykpiv_state *state, uint8_t *challenge, const size_t challenge_len);
|
||||||
|
ykpiv_rc ykpiv_auth_verifyresponse(ykpiv_state *state, uint8_t *response, const size_t response_len);
|
||||||
|
ykpiv_rc ykpiv_auth_deauthenticate(ykpiv_state *state);
|
||||||
|
|
||||||
|
typedef enum _setting_source_t {
|
||||||
|
SETTING_SOURCE_USER,
|
||||||
|
SETTING_SOURCE_ADMIN,
|
||||||
|
SETTING_SOURCE_DEFAULT
|
||||||
|
} setting_source_t;
|
||||||
|
|
||||||
|
typedef struct _setting_bool_t {
|
||||||
|
bool value;
|
||||||
|
setting_source_t source;
|
||||||
|
} setting_bool_t;
|
||||||
|
|
||||||
|
setting_bool_t setting_get_bool(const char *sz_setting, bool f_default);
|
||||||
|
|
||||||
|
typedef enum _yc_log_level_t {
|
||||||
|
YC_LOG_LEVEL_ERROR,
|
||||||
|
YC_LOG_LEVEL_WARN,
|
||||||
|
YC_LOG_LEVEL_INFO,
|
||||||
|
YC_LOG_LEVEL_VERBOSE,
|
||||||
|
YC_LOG_LEVEL_DEBUG
|
||||||
|
} yc_log_level_t;
|
||||||
|
|
||||||
|
void yc_log_event(uint32_t id, yc_log_level_t level, const char *sz_format, ...);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
+114
-13
@@ -699,7 +699,6 @@ ykpiv_rc ykpiv_util_generate_key(ykpiv_state *state, uint8_t slot, uint8_t algor
|
|||||||
ykpiv_rc res = YKPIV_OK;
|
ykpiv_rc res = YKPIV_OK;
|
||||||
unsigned char in_data[11];
|
unsigned char in_data[11];
|
||||||
unsigned char *in_ptr = in_data;
|
unsigned char *in_ptr = in_data;
|
||||||
char version[7];
|
|
||||||
unsigned char data[1024];
|
unsigned char data[1024];
|
||||||
unsigned char templ[] = { 0, YKPIV_INS_GENERATE_ASYMMETRIC, 0, 0 };
|
unsigned char templ[] = { 0, YKPIV_INS_GENERATE_ASYMMETRIC, 0, 0 };
|
||||||
unsigned long recv_len = sizeof(data);
|
unsigned long recv_len = sizeof(data);
|
||||||
@@ -711,21 +710,47 @@ ykpiv_rc ykpiv_util_generate_key(ykpiv_state *state, uint8_t slot, uint8_t algor
|
|||||||
uint8_t *ptr_point = NULL;
|
uint8_t *ptr_point = NULL;
|
||||||
size_t cb_point = 0;
|
size_t cb_point = 0;
|
||||||
|
|
||||||
|
setting_bool_t setting_roca = { 0 };
|
||||||
|
const char sz_setting_roca[] = "Enable_Unsafe_Keygen_ROCA";
|
||||||
|
const char sz_roca_format[] = "YubiKey serial number %u is affected by vulnerability "
|
||||||
|
"CVE-2017-15361 (ROCA) and should be replaced. On-chip key generation %s "
|
||||||
|
"See YSA-2017-01 <https://www.yubico.com/support/security-advisories/ysa-2017-01/> "
|
||||||
|
"for additional information on device replacement and mitigation assistance.\n";
|
||||||
|
const char sz_roca_allow_user[] = "was permitted by an end-user configuration setting, but is not recommended.";
|
||||||
|
const char sz_roca_allow_admin[] = "was permitted by an administrator configuration setting, but is not recommended.";
|
||||||
|
const char sz_roca_block_user[] = "was blocked due to an end-user configuration setting.";
|
||||||
|
const char sz_roca_block_admin[] = "was blocked due to an administrator configuration setting.";
|
||||||
|
const char sz_roca_default[] = "was permitted by default, but is not recommended. "
|
||||||
|
"The default behavior will change in a future Yubico release.";
|
||||||
|
|
||||||
|
if (!state) return YKPIV_ARGUMENT_ERROR;
|
||||||
|
|
||||||
if (ykpiv_util_devicemodel(state) == DEVTYPE_YK4 && (algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048)) {
|
if (ykpiv_util_devicemodel(state) == DEVTYPE_YK4 && (algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048)) {
|
||||||
if ((res = ykpiv_get_version(state, version, sizeof(version))) == YKPIV_OK) {
|
if ((state->ver.major == 4) && (state->ver.minor < 3 || ((state->ver.minor == 3) && (state->ver.patch < 5)))) {
|
||||||
int major, minor, build;
|
const char *psz_msg = NULL;
|
||||||
#pragma warning(push)
|
setting_roca = setting_get_bool(sz_setting_roca, true);
|
||||||
#pragma warning(suppress: 4996) // Suppress _CRT_SECURE_NO_WARNINGS complaint (about sscanf_s)
|
|
||||||
int match = sscanf(version, "%d.%d.%d", &major, &minor, &build);
|
switch (setting_roca.source) {
|
||||||
#pragma warning(pop)
|
case SETTING_SOURCE_ADMIN:
|
||||||
if (match == 3 && major == 4 && (minor < 3 || (minor == 3 && build < 5))) {
|
psz_msg = setting_roca.value ? sz_roca_allow_admin : sz_roca_block_admin;
|
||||||
fprintf(stderr, "On-chip RSA key generation on this YubiKey has been blocked.\n");
|
break;
|
||||||
fprintf(stderr, "Please see https://yubi.co/ysa201701/ for details.\n");
|
|
||||||
|
case SETTING_SOURCE_USER:
|
||||||
|
psz_msg = setting_roca.value ? sz_roca_allow_user : sz_roca_block_user;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
case SETTING_SOURCE_DEFAULT:
|
||||||
|
psz_msg = sz_roca_default;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, sz_roca_format, state->serial, psz_msg);
|
||||||
|
yc_log_event(1, setting_roca.value ? YC_LOG_LEVEL_WARN : YC_LOG_LEVEL_ERROR, sz_roca_format, state->serial, psz_msg);
|
||||||
|
|
||||||
|
if (!setting_roca.value) {
|
||||||
return YKPIV_NOT_SUPPORTED;
|
return YKPIV_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Failed to get device version.\n");
|
|
||||||
return YKPIV_GENERIC_ERROR;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1302,6 +1327,82 @@ uint32_t ykpiv_util_slot_object(uint8_t slot) {
|
|||||||
return object_id;
|
return object_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* caller must make sure that this is wrapped in a transaction for synchronized operation */
|
||||||
|
ykpiv_rc _ykpiv_util_get_serial(ykpiv_state *state, uint32_t *p_serial, bool f_force) {
|
||||||
|
ykpiv_rc res = YKPIV_OK;
|
||||||
|
APDU apdu;
|
||||||
|
const uint8_t templ[] = { 0, YKPIV_INS_SELECT_APPLICATION, 0x04, 0 };
|
||||||
|
const uint8_t yk_applet[] = { 0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01, 0x01 };
|
||||||
|
unsigned char data[0xff];
|
||||||
|
uint32_t recv_len = sizeof(data);
|
||||||
|
int sw;
|
||||||
|
uint8_t *p_temp = NULL;
|
||||||
|
|
||||||
|
if (!state) {
|
||||||
|
return YKPIV_ARGUMENT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!f_force && (state->serial != 0)) {
|
||||||
|
if (p_serial) *p_serial = state->serial;
|
||||||
|
return YKPIV_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this function does not use ykpiv_transfer_data because it requires two apdus and selects a different app */
|
||||||
|
|
||||||
|
memset(apdu.raw, 0, sizeof(apdu));
|
||||||
|
apdu.st.ins = YKPIV_INS_SELECT_APPLICATION;
|
||||||
|
apdu.st.p1 = 0x04;
|
||||||
|
apdu.st.lc = sizeof(yk_applet);
|
||||||
|
memcpy(apdu.st.data, yk_applet, sizeof(yk_applet));
|
||||||
|
|
||||||
|
if ((res = _send_data(state, &apdu, data, &recv_len, &sw)) < YKPIV_OK) {
|
||||||
|
if (state->verbose) {
|
||||||
|
fprintf(stderr, "Failed communicating with card: '%s'\n", ykpiv_strerror(res));
|
||||||
|
}
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
else if (sw != SW_SUCCESS) {
|
||||||
|
if (state->verbose) {
|
||||||
|
fprintf(stderr, "Failed selecting yk application: %04x\n", sw);
|
||||||
|
}
|
||||||
|
res = YKPIV_GENERIC_ERROR;
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
recv_len = sizeof(data);
|
||||||
|
memset(apdu.raw, 0, sizeof(apdu));
|
||||||
|
apdu.st.ins = 0x01;
|
||||||
|
apdu.st.p1 = 0x10;
|
||||||
|
apdu.st.lc = 0x00;
|
||||||
|
|
||||||
|
if ((res = _send_data(state, &apdu, data, &recv_len, &sw)) < YKPIV_OK) {
|
||||||
|
if (state->verbose) {
|
||||||
|
fprintf(stderr, "Failed communicating with card: '%s'\n", ykpiv_strerror(res));
|
||||||
|
}
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
else if (sw != SW_SUCCESS) {
|
||||||
|
if (state->verbose) {
|
||||||
|
fprintf(stderr, "Failed retrieving serial number: %04x\n", sw);
|
||||||
|
}
|
||||||
|
res = YKPIV_GENERIC_ERROR;
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_temp = (uint8_t*)(&state->serial);
|
||||||
|
|
||||||
|
*p_temp++ = data[3];
|
||||||
|
*p_temp++ = data[2];
|
||||||
|
*p_temp++ = data[1];
|
||||||
|
*p_temp++ = data[0];
|
||||||
|
|
||||||
|
if (p_serial) *p_serial = state->serial;
|
||||||
|
|
||||||
|
Cleanup:
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static ykpiv_rc _read_certificate(ykpiv_state *state, uint8_t slot, uint8_t *buf, size_t *buf_len) {
|
static ykpiv_rc _read_certificate(ykpiv_state *state, uint8_t slot, uint8_t *buf, size_t *buf_len) {
|
||||||
ykpiv_rc res = YKPIV_OK;
|
ykpiv_rc res = YKPIV_OK;
|
||||||
uint8_t *ptr = NULL;
|
uint8_t *ptr = NULL;
|
||||||
|
|||||||
+64
-20
@@ -80,14 +80,10 @@
|
|||||||
|
|
||||||
static ykpiv_rc _cache_pin(ykpiv_state *state, const char *pin, size_t len);
|
static ykpiv_rc _cache_pin(ykpiv_state *state, const char *pin, size_t len);
|
||||||
|
|
||||||
static ykpiv_rc _send_data(ykpiv_state *state, APDU *apdu,
|
|
||||||
unsigned char *data, uint32_t *recv_len, int *sw);
|
|
||||||
|
|
||||||
unsigned const char aid[] = {
|
unsigned const char aid[] = {
|
||||||
0xa0, 0x00, 0x00, 0x03, 0x08
|
0xa0, 0x00, 0x00, 0x03, 0x08
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static void* _default_alloc(void *data, size_t cb) {
|
static void* _default_alloc(void *data, size_t cb) {
|
||||||
(void)data;
|
(void)data;
|
||||||
return calloc(cb, 1);
|
return calloc(cb, 1);
|
||||||
@@ -241,7 +237,9 @@ ykpiv_rc _ykpiv_select_application(ykpiv_state *state) {
|
|||||||
unsigned char data[0xff];
|
unsigned char data[0xff];
|
||||||
uint32_t recv_len = sizeof(data);
|
uint32_t recv_len = sizeof(data);
|
||||||
int sw;
|
int sw;
|
||||||
ykpiv_rc res;
|
ykpiv_rc res = YKPIV_OK;
|
||||||
|
|
||||||
|
_ykpiv_util_get_serial(state, NULL, false);
|
||||||
|
|
||||||
memset(apdu.raw, 0, sizeof(apdu));
|
memset(apdu.raw, 0, sizeof(apdu));
|
||||||
apdu.st.ins = YKPIV_INS_SELECT_APPLICATION;
|
apdu.st.ins = YKPIV_INS_SELECT_APPLICATION;
|
||||||
@@ -254,14 +252,15 @@ ykpiv_rc _ykpiv_select_application(ykpiv_state *state) {
|
|||||||
fprintf(stderr, "Failed communicating with card: '%s'\n", ykpiv_strerror(res));
|
fprintf(stderr, "Failed communicating with card: '%s'\n", ykpiv_strerror(res));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
} else if(sw == SW_SUCCESS) {
|
}
|
||||||
return YKPIV_OK;
|
else if(sw != SW_SUCCESS) {
|
||||||
} else {
|
|
||||||
if(state->verbose) {
|
if(state->verbose) {
|
||||||
fprintf(stderr, "Failed selecting application: %04x\n", sw);
|
fprintf(stderr, "Failed selecting application: %04x\n", sw);
|
||||||
}
|
}
|
||||||
return YKPIV_GENERIC_ERROR;
|
return YKPIV_GENERIC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ykpiv_get_version(state, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
ykpiv_rc _ykpiv_ensure_application_selected(ykpiv_state *state) {
|
ykpiv_rc _ykpiv_ensure_application_selected(ykpiv_state *state) {
|
||||||
@@ -574,7 +573,7 @@ Cleanup:
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ykpiv_rc _send_data(ykpiv_state *state, APDU *apdu,
|
ykpiv_rc _send_data(ykpiv_state *state, APDU *apdu,
|
||||||
unsigned char *data, uint32_t *recv_len, int *sw) {
|
unsigned char *data, uint32_t *recv_len, int *sw) {
|
||||||
long rc;
|
long rc;
|
||||||
unsigned int send_len = (unsigned int)apdu->st.lc + 5;
|
unsigned int send_len = (unsigned int)apdu->st.lc + 5;
|
||||||
@@ -944,32 +943,63 @@ Cleanup:
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len) {
|
ykpiv_rc _ykpiv_get_version(ykpiv_state *state, ykpiv_version_t *p_version) {
|
||||||
APDU apdu;
|
APDU apdu;
|
||||||
unsigned char data[261];
|
unsigned char data[261];
|
||||||
uint32_t recv_len = sizeof(data);
|
uint32_t recv_len = sizeof(data);
|
||||||
int sw;
|
int sw;
|
||||||
ykpiv_rc res;
|
ykpiv_rc res;
|
||||||
|
|
||||||
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
|
if (!state) {
|
||||||
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
|
return YKPIV_ARGUMENT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get version from state if already from device */
|
||||||
|
|
||||||
|
if (state->ver.major || state->ver.minor || state->ver.patch) {
|
||||||
|
if (p_version) {
|
||||||
|
memcpy(p_version, &(state->ver), sizeof(ykpiv_version_t));
|
||||||
|
}
|
||||||
|
return YKPIV_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get version from device */
|
||||||
|
|
||||||
memset(apdu.raw, 0, sizeof(apdu));
|
memset(apdu.raw, 0, sizeof(apdu));
|
||||||
apdu.st.ins = YKPIV_INS_GET_VERSION;
|
apdu.st.ins = YKPIV_INS_GET_VERSION;
|
||||||
if((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) {
|
if((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) {
|
||||||
goto Cleanup;
|
return res;
|
||||||
} else if(sw == SW_SUCCESS) {
|
} else if(sw == SW_SUCCESS) {
|
||||||
int result = snprintf(version, len, "%d.%d.%d", data[0], data[1], data[2]);
|
state->ver.major = data[0];
|
||||||
|
state->ver.minor = data[1];
|
||||||
|
state->ver.patch = data[2];
|
||||||
|
if (p_version) {
|
||||||
|
memcpy(p_version, &(state->ver), sizeof(ykpiv_version_t));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res = YKPIV_GENERIC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len) {
|
||||||
|
ykpiv_rc res;
|
||||||
|
int result = 0;
|
||||||
|
ykpiv_version_t ver = {0};
|
||||||
|
|
||||||
|
if ((res = _ykpiv_begin_transaction(state)) < YKPIV_OK) return YKPIV_PCSC_ERROR;
|
||||||
|
if ((res = _ykpiv_ensure_application_selected(state)) < YKPIV_OK) goto Cleanup;
|
||||||
|
|
||||||
|
if ((res = _ykpiv_get_version(state, &ver)) >= YKPIV_OK) {
|
||||||
|
result = snprintf(version, len, "%d.%d.%d", ver.major, ver.minor, ver.patch);
|
||||||
if(result < 0) {
|
if(result < 0) {
|
||||||
res = YKPIV_SIZE_ERROR;
|
res = YKPIV_SIZE_ERROR;
|
||||||
}
|
}
|
||||||
goto Cleanup;
|
|
||||||
} else {
|
|
||||||
res = YKPIV_GENERIC_ERROR;
|
|
||||||
goto Cleanup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Cleanup:
|
Cleanup:
|
||||||
|
|
||||||
_ykpiv_end_transaction(state);
|
_ykpiv_end_transaction(state);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -1050,7 +1080,7 @@ ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries) {
|
|||||||
|
|
||||||
ykpiv_rc ykpiv_verify_select(ykpiv_state *state, const char *pin, const size_t pin_len, int *tries, bool force_select) {
|
ykpiv_rc ykpiv_verify_select(ykpiv_state *state, const char *pin, const size_t pin_len, int *tries, bool force_select) {
|
||||||
ykpiv_rc res = YKPIV_OK;
|
ykpiv_rc res = YKPIV_OK;
|
||||||
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) goto Cleanup;
|
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
|
||||||
if (force_select) {
|
if (force_select) {
|
||||||
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
|
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
|
||||||
}
|
}
|
||||||
@@ -1454,15 +1484,18 @@ ykpiv_rc ykpiv_attest(ykpiv_state *state, const unsigned char key, unsigned char
|
|||||||
bool ret = false;
|
bool ret = false;
|
||||||
unsigned char templ[] = {0, YKPIV_INS_ATTEST, key, 0};
|
unsigned char templ[] = {0, YKPIV_INS_ATTEST, key, 0};
|
||||||
int sw;
|
int sw;
|
||||||
|
unsigned long ul_data_len;
|
||||||
|
|
||||||
if (state == NULL || data == NULL || data_len == NULL) {
|
if (state == NULL || data == NULL || data_len == NULL) {
|
||||||
return YKPIV_ARGUMENT_ERROR;
|
return YKPIV_ARGUMENT_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul_data_len = *data_len;
|
||||||
|
|
||||||
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
|
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
|
||||||
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
|
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
|
||||||
|
|
||||||
if ((res = ykpiv_transfer_data(state, templ, NULL, 0, data, data_len, &sw)) != YKPIV_OK) {
|
if ((res = ykpiv_transfer_data(state, templ, NULL, 0, data, &ul_data_len, &sw)) != YKPIV_OK) {
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
if (SW_SUCCESS != sw) {
|
if (SW_SUCCESS != sw) {
|
||||||
@@ -1477,6 +1510,8 @@ ykpiv_rc ykpiv_attest(ykpiv_state *state, const unsigned char key, unsigned char
|
|||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*data_len = (size_t)ul_data_len;
|
||||||
|
|
||||||
Cleanup:
|
Cleanup:
|
||||||
_ykpiv_end_transaction(state);
|
_ykpiv_end_transaction(state);
|
||||||
return res;
|
return res;
|
||||||
@@ -1562,3 +1597,12 @@ Cleanup:
|
|||||||
_ykpiv_end_transaction(state);
|
_ykpiv_end_transaction(state);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ykpiv_rc ykpiv_auth_deauthenticate(ykpiv_state *state) {
|
||||||
|
ykpiv_rc res = YKPIV_OK;
|
||||||
|
|
||||||
|
if ((res = _ykpiv_begin_transaction(state)) < YKPIV_OK) return res;
|
||||||
|
_ykpiv_util_get_serial(state, NULL, true);
|
||||||
|
_ykpiv_end_transaction(state);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user