1
0
mirror of https://github.com/Yubico/yubico-pam.git synced 2024-12-01 15:24:12 +01:00

Further cleanups to challenge response code, and move more code

to util.c.
This commit is contained in:
Fredrik Thulin 2011-03-17 16:10:42 +01:00
parent 319fee4e08
commit ab47c06c20
3 changed files with 135 additions and 70 deletions

View File

@ -51,19 +51,6 @@
#include <security/pam_modules.h> #include <security/pam_modules.h>
#endif #endif
#if defined(DEBUG_PAM)
# if defined(HAVE_SECURITY__PAM_MACROS_H)
# define DEBUG
# include <security/_pam_macros.h>
# else
# define D(x) do { \
printf ("debug: %s:%d (%s): ", __FILE__, __LINE__, __FUNCTION__); \
printf x; \
printf ("\n"); \
} while (0)
# endif
#endif
#ifdef HAVE_LIBLDAP #ifdef HAVE_LIBLDAP
/* Some functions like ldap_init, ldap_simple_bind_s, ldap_unbind are /* Some functions like ldap_init, ldap_simple_bind_s, ldap_unbind are
deprecated but still available. We will drop support for 'ldapserver' deprecated but still available. We will drop support for 'ldapserver'
@ -88,12 +75,6 @@
#define MAX_TOKEN_ID_LEN 16 #define MAX_TOKEN_ID_LEN 16
#define DEFAULT_TOKEN_ID_LEN 12 #define DEFAULT_TOKEN_ID_LEN 12
/* Challenges can be 0..63 or 64 bytes long, depending on YubiKey configuration.
* We settle for 63 bytes to have something that works with all configurations.
*/
#define CR_CHALLENGE_SIZE 63
#define CR_RESPONSE_SIZE 20
/* /*
* This function will look for users name with valid user token id. It * This function will look for users name with valid user token id. It
* will returns 0 for failure and 1 for success. * will returns 0 for failure and 1 for success.
@ -396,17 +377,15 @@ do_challenge_response(struct cfg *cfg, const char *username)
{ {
char *userfile = NULL; char *userfile = NULL;
FILE *f = NULL; FILE *f = NULL;
unsigned char challenge[CR_CHALLENGE_SIZE + 1]; unsigned char buf[CR_RESPONSE_SIZE + 16], response_hex[CR_RESPONSE_SIZE * 2 + 1];
unsigned char challenge_hex[sizeof(challenge) * 2 + 1], expected_response[CR_RESPONSE_SIZE * 2 + 1]; int ret;
int r, slot, ret, fd;
unsigned char response[CR_RESPONSE_SIZE + 16]; /* Need some extra bytes in this read buffer */
unsigned char response_hex[CR_RESPONSE_SIZE * 2 + 1];
int yk_cmd;
unsigned int flags = 0; unsigned int flags = 0;
unsigned int response_len = 0; unsigned int response_len = 0;
unsigned int expect_bytes = 0; unsigned int expect_bytes = 0;
YK_KEY *yk = NULL; YK_KEY *yk = NULL;
CR_STATE state;
int len; int len;
ret = PAM_AUTH_ERR; ret = PAM_AUTH_ERR;
@ -421,31 +400,9 @@ do_challenge_response(struct cfg *cfg, const char *username)
/* XXX should drop root privileges before opening file in user's home directory */ /* XXX should drop root privileges before opening file in user's home directory */
f = fopen(userfile, "r+"); f = fopen(userfile, "r+");
if (! f)
goto out;
/* XXX not ideal with hard coded lengths in this scan string.
* 126 corresponds to twice the size of CR_CHALLENGE_SIZE,
* 40 is twice the size of CR_RESPONSE_SIZE
* (twice because we hex encode the challenge and response)
*/
r = fscanf(f, "%126[0-9a-z]:%40[0-9a-z]:%d", &challenge_hex, &expected_response, &slot);
D(("Challenge: %s, response: %s, slot: %d", challenge_hex, expected_response, slot));
if (r != 3)
goto out;
if (! yubikey_hex_p(challenge_hex)) { if (! load_chalresp_state(f, &state))
D(("Invalid challenge hex input : %s", challenge_hex));
goto out; goto out;
}
if (! yubikey_hex_p(expected_response)) {
D(("Invalid expected response hex input : %s", expected_response));
goto out;
}
if (slot != 1 && slot != 2) {
D(("Invalid slot input : %i", slot));
}
if (! init_yubikey(&yk)) { if (! init_yubikey(&yk)) {
D(("Failed initializing YubiKey")); D(("Failed initializing YubiKey"));
@ -457,11 +414,9 @@ do_challenge_response(struct cfg *cfg, const char *username)
goto out; goto out;
} }
yubikey_hex_decode(challenge, challenge_hex, sizeof(challenge)); if (! challenge_response(yk, state.slot, state.challenge, state.challenge_len,
len = strlen(challenge_hex) / 2; true, flags, false,
buf, sizeof(buf), &response_len)) {
if (! challenge_response(yk, slot, challenge, len, true, flags, false,
response, sizeof(response), &response_len)) {
D(("Challenge-response FAILED")); D(("Challenge-response FAILED"));
goto out; goto out;
} }
@ -470,9 +425,9 @@ do_challenge_response(struct cfg *cfg, const char *username)
* Check YubiKey response against the expected response * Check YubiKey response against the expected response
*/ */
yubikey_hex_encode(response_hex, (char *)response, response_len); yubikey_hex_encode(response_hex, (char *)buf, response_len);
if (strcmp(response_hex, expected_response) == 0) { if (memcmp(buf, state.response, response_len) == 0) {
ret = PAM_SUCCESS; ret = PAM_SUCCESS;
} else { } else {
D(("Unexpected C/R response : %s", response_hex)); D(("Unexpected C/R response : %s", response_hex));
@ -481,13 +436,14 @@ do_challenge_response(struct cfg *cfg, const char *username)
D(("Got the expected response, generating new challenge (%i bytes).", CR_CHALLENGE_SIZE)); D(("Got the expected response, generating new challenge (%i bytes).", CR_CHALLENGE_SIZE));
if (generate_random(challenge, CR_CHALLENGE_SIZE)) { if (generate_random(state.challenge, sizeof(state.challenge))) {
D(("Failed generating new challenge!")); D(("Failed generating new challenge!"));
goto out; goto out;
} }
if (! challenge_response(yk, slot, challenge, CR_CHALLENGE_SIZE, true, flags, false, if (! challenge_response(yk, state.slot, state.challenge, CR_CHALLENGE_SIZE,
response, sizeof(response), &response_len)) { true, flags, false,
buf, sizeof(buf), &response_len)) {
D(("Second challenge-response FAILED")); D(("Second challenge-response FAILED"));
goto out; goto out;
} }
@ -498,19 +454,14 @@ do_challenge_response(struct cfg *cfg, const char *username)
/* /*
* Write the challenge and response we will expect the next time to the state file. * Write the challenge and response we will expect the next time to the state file.
*/ */
if (response_len > sizeof(state.response)) {
D(("Got too long response ??? (%i/%i)", response_len, sizeof(state.response)));
goto out;
}
memcpy (state.response, buf, response_len);
state.response_len = response_len;
memset(challenge_hex, 0, sizeof(challenge_hex)); if (! write_chalresp_state (f, &state))
memset(response_hex, 0, sizeof(response_hex));
yubikey_hex_encode(challenge_hex, (char *)challenge, CR_CHALLENGE_SIZE);
yubikey_hex_encode(response_hex, (char *)response, response_len);
rewind(f);
fd = fileno(f);
if (fd == -1)
goto out;
if (ftruncate(fd, 0))
goto out;
fprintf(f, "%s:%s:%d\n", challenge_hex, response_hex, slot);
if (fsync(fd) < 0)
goto out; goto out;
D(("Challenge-response success!")); D(("Challenge-response success!"));

80
util.c
View File

@ -184,3 +184,83 @@ int challenge_response(YK_KEY *yk, int slot,
return 1; return 1;
} }
int
load_chalresp_state(FILE *f, CR_STATE *state)
{
unsigned char challenge_hex[CR_CHALLENGE_SIZE * 2 + 1], response_hex[CR_RESPONSE_SIZE * 2 + 1];
int slot;
int r;
if (! f)
goto out;
/* XXX not ideal with hard coded lengths in this scan string.
* 126 corresponds to twice the size of CR_CHALLENGE_SIZE,
* 40 is twice the size of CR_RESPONSE_SIZE
* (twice because we hex encode the challenge and response)
*/
r = fscanf(f, "%126[0-9a-z]:%40[0-9a-z]:%d", &challenge_hex, &response_hex, &slot);
D(("Challenge: %s, response: %s, slot: %d", challenge_hex, response_hex, slot));
if (r != 3)
goto out;
if (! yubikey_hex_p(challenge_hex)) {
D(("Invalid challenge hex input : %s", challenge_hex));
goto out;
}
if (! yubikey_hex_p(response_hex)) {
D(("Invalid expected response hex input : %s", response_hex));
goto out;
}
if (slot != 1 && slot != 2) {
D(("Invalid slot input : %i", slot));
goto out;
}
yubikey_hex_decode(state->challenge, challenge_hex, sizeof(state->challenge));
state->challenge_len = strlen(challenge_hex) / 2;
yubikey_hex_decode(state->response, response_hex, sizeof(state->response));
state->response_len = strlen(response_hex) / 2;
state->slot = slot;
return 1;
out:
return 0;
}
int
write_chalresp_state(FILE *f, CR_STATE *state)
{
unsigned char challenge_hex[CR_CHALLENGE_SIZE * 2 + 1], response_hex[CR_RESPONSE_SIZE * 2 + 1];
int fd;
memset(challenge_hex, 0, sizeof(challenge_hex));
memset(response_hex, 0, sizeof(response_hex));
yubikey_hex_encode(challenge_hex, (char *)state->challenge, state->challenge_len);
yubikey_hex_encode(response_hex, (char *)state->response, state->response_len);
rewind(f);
fd = fileno(f);
if (fd == -1)
goto out;
if (ftruncate(fd, 0))
goto out;
fprintf(f, "%s:%s:%d\n", challenge_hex, response_hex, state->slot);
if (fsync(fd) < 0)
goto out;
return 1;
out:
return 0;
}

34
util.h
View File

@ -36,9 +36,43 @@
#include <ykstatus.h> #include <ykstatus.h>
#include <ykdef.h> #include <ykdef.h>
#ifndef D
#if defined(DEBUG_PAM)
# if defined(HAVE_SECURITY__PAM_MACROS_H)
# define DEBUG
# include <security/_pam_macros.h>
# else
# define D(x) do { \
printf ("debug: %s:%d (%s): ", __FILE__, __LINE__, __FUNCTION__); \
printf x; \
printf ("\n"); \
} while (0)
# endif /* HAVE_SECURITY__PAM_MACROS_H */
#endif /* DEBUG_PAM */
#endif /* D */
/* Challenges can be 0..63 or 64 bytes long, depending on YubiKey configuration.
* We settle for 63 bytes to have something that works with all configurations.
*/
#define CR_CHALLENGE_SIZE 63
#define CR_RESPONSE_SIZE 20
struct chalresp_state {
unsigned char challenge[CR_CHALLENGE_SIZE];
uint8_t challenge_len;
unsigned char response[CR_RESPONSE_SIZE];
uint8_t response_len;
uint8_t slot;
};
typedef struct chalresp_state CR_STATE;
int generate_random(char *buf, int len); int generate_random(char *buf, int len);
int get_user_cfgfile_path(const char *common_path, const char *filename, const char *username, char **fn); int get_user_cfgfile_path(const char *common_path, const char *filename, const char *username, char **fn);
int load_chalresp_state(FILE *f, CR_STATE *state);
int write_chalresp_state(FILE *f, CR_STATE *state);
int init_yubikey(YK_KEY **yk); int init_yubikey(YK_KEY **yk);
int check_firmware_version(YK_KEY *yk, bool verbose, bool quiet); int check_firmware_version(YK_KEY *yk, bool verbose, bool quiet);
int challenge_response(YK_KEY *yk, int slot, int challenge_response(YK_KEY *yk, int slot,