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:
parent
319fee4e08
commit
ab47c06c20
91
pam_yubico.c
91
pam_yubico.c
@ -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
80
util.c
@ -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
34
util.h
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user