1
0
mirror of https://github.com/Yubico/yubico-pam.git synced 2025-02-08 03:54:18 +01:00

Merge branch 'debug_refactor'

This commit is contained in:
Klas Lindfors 2016-06-16 13:17:10 +02:00
commit 8850659b5f
10 changed files with 232 additions and 212 deletions

View File

@ -49,8 +49,6 @@ libpam_util_la_LIBADD = @LTLIBYUBIKEY@ @YKPERS_LIBS@
libpam_real_la_SOURCES = pam_yubico.c libpam_real_la_SOURCES = pam_yubico.c
DEFS = -DDEBUG_PAM -DPAM_DEBUG @DEFS@
# The command line tools. # The command line tools.
if YKPERS if YKPERS

4
README
View File

@ -151,7 +151,9 @@ and the validation server.
If you want to get one for use with the default YubiCloud If you want to get one for use with the default YubiCloud
service, go https://upgrade.yubico.com/getapikey[here]. service, go https://upgrade.yubico.com/getapikey[here].
debug:: to enable debug output to stdout. debug:: to enable debug output.
debug_file:: filename to write debug to, stdout is default.
alwaysok:: alwaysok::
to enable all authentication attempts to succeed to enable all authentication attempts to succeed

View File

@ -55,40 +55,40 @@ int pam_modutil_drop_priv(pam_handle_t *pamh, struct _ykpam_privs *privs, struct
privs->saved_egid = getegid(); privs->saved_egid = getegid();
if ((privs->saved_euid == pw->pw_uid) && (privs->saved_egid == pw->pw_gid)) { if ((privs->saved_euid == pw->pw_uid) && (privs->saved_egid == pw->pw_gid)) {
D (("Privilges already dropped, pretend it is all right")); D (privs->debug_file, "Privilges already dropped, pretend it is all right");
return 0; return 0;
} }
privs->saved_groups_length = getgroups(0, NULL); privs->saved_groups_length = getgroups(0, NULL);
if (privs->saved_groups_length < 0) { if (privs->saved_groups_length < 0) {
D (("getgroups: %s", strerror(errno))); D (privs->debug_file, "getgroups: %s", strerror(errno));
return -1; return -1;
} }
if (privs->saved_groups_length > SAVED_GROUPS_MAX_LEN) { if (privs->saved_groups_length > SAVED_GROUPS_MAX_LEN) {
D (("to many groups, limiting.")); D (privs->debug_file, "to many groups, limiting.");
privs->saved_groups_length = SAVED_GROUPS_MAX_LEN; privs->saved_groups_length = SAVED_GROUPS_MAX_LEN;
} }
if (privs->saved_groups_length > 0) { if (privs->saved_groups_length > 0) {
if (getgroups(privs->saved_groups_length, privs->saved_groups) < 0) { if (getgroups(privs->saved_groups_length, privs->saved_groups) < 0) {
D (("getgroups: %s", strerror(errno))); D (privs->debug_file, "getgroups: %s", strerror(errno));
goto free_out; goto free_out;
} }
} }
if (initgroups(pw->pw_name, pw->pw_gid) < 0) { if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
D (("initgroups: %s", strerror(errno))); D (privs->debug_file, "initgroups: %s", strerror(errno));
goto free_out; goto free_out;
} }
if (setegid(pw->pw_gid) < 0) { if (setegid(pw->pw_gid) < 0) {
D (("setegid: %s", strerror(errno))); D (privs->debug_file, "setegid: %s", strerror(errno));
goto free_out; goto free_out;
} }
if (seteuid(pw->pw_uid) < 0) { if (seteuid(pw->pw_uid) < 0) {
D (("seteuid: %s", strerror(errno))); D (privs->debug_file, "seteuid: %s", strerror(errno));
goto free_out; goto free_out;
} }
@ -99,22 +99,22 @@ free_out:
int pam_modutil_regain_priv(pam_handle_t *pamh, struct _ykpam_privs *privs) { int pam_modutil_regain_priv(pam_handle_t *pamh, struct _ykpam_privs *privs) {
if ((privs->saved_euid == geteuid()) && (privs->saved_egid == getegid())) { if ((privs->saved_euid == geteuid()) && (privs->saved_egid == getegid())) {
D (("Privilges already as requested, pretend it is all right")); D (privs->debug_file, "Privilges already as requested, pretend it is all right");
return 0; return 0;
} }
if (seteuid(privs->saved_euid) < 0) { if (seteuid(privs->saved_euid) < 0) {
D (("seteuid: %s", strerror(errno))); D (privs->debug_file, "seteuid: %s", strerror(errno));
return -1; return -1;
} }
if (setegid(privs->saved_egid) < 0) { if (setegid(privs->saved_egid) < 0) {
D (("setegid: %s", strerror(errno))); D (privs->debug_file, "setegid: %s", strerror(errno));
return -1; return -1;
} }
if (setgroups(privs->saved_groups_length, privs->saved_groups) < 0) { if (setgroups(privs->saved_groups_length, privs->saved_groups) < 0) {
D (("setgroups: %s", strerror(errno))); D (privs->debug_file, "setgroups: %s", strerror(errno));
return -1; return -1;
} }

View File

@ -34,6 +34,7 @@
#else #else
#include <pwd.h> #include <pwd.h>
#include <stdio.h>
#ifdef HAVE_SECURITY_PAM_APPL_H #ifdef HAVE_SECURITY_PAM_APPL_H
#include <security/pam_appl.h> #include <security/pam_appl.h>
@ -49,11 +50,12 @@ struct _ykpam_privs {
gid_t saved_egid; gid_t saved_egid;
gid_t *saved_groups; gid_t *saved_groups;
int saved_groups_length; int saved_groups_length;
FILE *debug_file;
}; };
#define PAM_MODUTIL_DEF_PRIVS(n) \ #define PAM_MODUTIL_DEF_PRIVS(n) \
gid_t n##_saved_groups[SAVED_GROUPS_MAX_LEN]; \ gid_t n##_saved_groups[SAVED_GROUPS_MAX_LEN]; \
struct _ykpam_privs n = {-1, -1, n##_saved_groups, SAVED_GROUPS_MAX_LEN} struct _ykpam_privs n = {-1, -1, n##_saved_groups, SAVED_GROUPS_MAX_LEN, cfg->debug_file}
int pam_modutil_drop_priv(pam_handle_t *, struct _ykpam_privs *, struct passwd *); int pam_modutil_drop_priv(pam_handle_t *, struct _ykpam_privs *, struct passwd *);
int pam_modutil_regain_priv(pam_handle_t *, struct _ykpam_privs *); int pam_modutil_regain_priv(pam_handle_t *, struct _ykpam_privs *);

View File

@ -15,7 +15,10 @@ The module is for authentication of YubiKeys, either with online validation of O
== OPTIONS == OPTIONS
*debug*:: *debug*::
Turns on debugging to STDOUT Turns on debugging.
*debug_file*=_file_::
File name to write debug to. Defaults to stdout.
*mode=*[_client_|_challenge-response_]:: *mode=*[_client_|_challenge-response_]::
Set the mode of operation, client for OTP validation and challenge-response for challenge-response validation, client is the default. Set the mode of operation, client for OTP validation and challenge-response for challenge-response validation, client is the default.

View File

@ -127,12 +127,13 @@ struct cfg
unsigned int token_id_length; unsigned int token_id_length;
enum key_mode mode; enum key_mode mode;
const char *chalresp_path; const char *chalresp_path;
FILE *debug_file;
}; };
#ifdef DBG #ifdef DBG
#undef DBG #undef DBG
#endif #endif
#define DBG(x) if (cfg->debug) { D(x); } #define DBG(x...) if (cfg->debug) { D(cfg->debug_file, x); }
/* /*
* Authorize authenticated OTP_ID for login as USERNAME using * Authorize authenticated OTP_ID for login as USERNAME using
@ -151,8 +152,8 @@ authorize_user_token (struct cfg *cfg,
/* Administrator had configured the file and specified is name /* Administrator had configured the file and specified is name
as an argument for this module. as an argument for this module.
*/ */
DBG (("Using system-wide auth_file %s", cfg->auth_file)); DBG ("Using system-wide auth_file %s", cfg->auth_file);
retval = check_user_token (cfg->auth_file, username, otp_id, cfg->debug); retval = check_user_token (cfg->auth_file, username, otp_id, cfg->debug, cfg->debug_file);
} }
else else
{ {
@ -165,7 +166,7 @@ authorize_user_token (struct cfg *cfg,
pwres = getpwnam_r (username, &pass, buf, buflen, &p); pwres = getpwnam_r (username, &pass, buf, buflen, &p);
if (p == NULL) { if (p == NULL) {
DBG (("getpwnam_r: %s", strerror(pwres))); DBG ("getpwnam_r: %s", strerror(pwres));
return 0; return 0;
} }
@ -173,18 +174,18 @@ authorize_user_token (struct cfg *cfg,
..... i.e. ~/.yubico/authorized_yubikeys ..... i.e. ~/.yubico/authorized_yubikeys
*/ */
if (! get_user_cfgfile_path (NULL, "authorized_yubikeys", p, &userfile)) { if (! get_user_cfgfile_path (NULL, "authorized_yubikeys", p, &userfile)) {
D (("Failed figuring out per-user cfgfile")); DBG ("Failed figuring out per-user cfgfile");
return 0; return 0;
} }
DBG (("Dropping privileges")); DBG ("Dropping privileges");
if(pam_modutil_drop_priv(pamh, &privs, p)) { if(pam_modutil_drop_priv(pamh, &privs, p)) {
DBG (("could not drop privileges")); DBG ("could not drop privileges");
retval = 0; retval = 0;
goto free_out; goto free_out;
} }
retval = check_user_token (userfile, username, otp_id, cfg->debug); retval = check_user_token (userfile, username, otp_id, cfg->debug, cfg->debug_file);
if(pam_modutil_regain_priv(pamh, &privs)) { if(pam_modutil_regain_priv(pamh, &privs)) {
DBG (("could not restore privileges")); DBG (("could not restore privileges"));
@ -259,7 +260,7 @@ authorize_user_token_ldap (struct cfg *cfg,
rc = ldap_initialize (&ld, cfg->ldap_uri); rc = ldap_initialize (&ld, cfg->ldap_uri);
if (rc != LDAP_SUCCESS) if (rc != LDAP_SUCCESS)
{ {
DBG (("ldap_initialize: %s", ldap_err2string (rc))); DBG ("ldap_initialize: %s", ldap_err2string (rc));
retval = 0; retval = 0;
goto done; goto done;
} }
@ -268,7 +269,7 @@ authorize_user_token_ldap (struct cfg *cfg,
{ {
if ((ld = ldap_init (cfg->ldapserver, PORT_NUMBER)) == NULL) if ((ld = ldap_init (cfg->ldapserver, PORT_NUMBER)) == NULL)
{ {
DBG (("ldap_init")); DBG ("ldap_init");
retval = 0; retval = 0;
goto done; goto done;
} }
@ -283,16 +284,15 @@ authorize_user_token_ldap (struct cfg *cfg,
} }
/* Bind anonymously to the LDAP server. */ /* Bind anonymously to the LDAP server. */
if (cfg->ldap_bind_user && cfg->ldap_bind_password) { if (cfg->ldap_bind_user && cfg->ldap_bind_password) {
DBG (("try bind with: %s:[%s]", cfg->ldap_bind_user, cfg->ldap_bind_password)); DBG ("try bind with: %s:[%s]", cfg->ldap_bind_user, cfg->ldap_bind_password);
rc = ldap_simple_bind_s (ld, cfg->ldap_bind_user, cfg->ldap_bind_password); rc = ldap_simple_bind_s (ld, cfg->ldap_bind_user, cfg->ldap_bind_password);
} else { } else {
DBG (("try bind anonymous")); DBG ("try bind anonymous");
rc = ldap_simple_bind_s (ld, NULL, NULL); rc = ldap_simple_bind_s (ld, NULL, NULL);
} }
if (rc != LDAP_SUCCESS) if (rc != LDAP_SUCCESS)
{ {
DBG (("ldap_simple_bind_s: %s", ldap_err2string (rc))); DBG ("ldap_simple_bind_s: %s", ldap_err2string (rc));
retval = 0;
goto done; goto done;
} }
@ -300,7 +300,7 @@ authorize_user_token_ldap (struct cfg *cfg,
if (cfg->user_attr && cfg->yubi_attr && cfg->ldapdn) { if (cfg->user_attr && cfg->yubi_attr && cfg->ldapdn) {
i = (strlen(cfg->user_attr) + strlen(cfg->ldapdn) + strlen(user) + 3) * sizeof(char); i = (strlen(cfg->user_attr) + strlen(cfg->ldapdn) + strlen(user) + 3) * sizeof(char);
if ((find = malloc(i)) == NULL) { if ((find = malloc(i)) == NULL) {
DBG (("Failed allocating %zu bytes", i)); DBG ("Failed allocating %zu bytes", i);
retval = 0; retval = 0;
goto done; goto done;
} }
@ -315,15 +315,15 @@ authorize_user_token_ldap (struct cfg *cfg,
} }
attrs[0] = (char *) cfg->yubi_attr; attrs[0] = (char *) cfg->yubi_attr;
DBG(("LDAP : look up object base='%s' filter='%s', ask for attribute '%s'", find, DBG("LDAP : look up object base='%s' filter='%s', ask for attribute '%s'", find,
filter ? filter:"(null)", cfg->yubi_attr)); filter ? filter:"(null)", cfg->yubi_attr);
/* Search for the entry. */ /* Search for the entry. */
if ((rc = ldap_search_ext_s (ld, find, scope, if ((rc = ldap_search_ext_s (ld, find, scope,
filter, attrs, 0, NULL, NULL, LDAP_NO_LIMIT, filter, attrs, 0, NULL, NULL, LDAP_NO_LIMIT,
LDAP_NO_LIMIT, &result)) != LDAP_SUCCESS) LDAP_NO_LIMIT, &result)) != LDAP_SUCCESS)
{ {
DBG (("ldap_search_ext_s: %s", ldap_err2string (rc))); DBG ("ldap_search_ext_s: %s", ldap_err2string (rc));
retval = 0; retval = 0;
goto done; goto done;
@ -349,17 +349,17 @@ authorize_user_token_ldap (struct cfg *cfg,
/* Compare each value for the attribute against the token id. */ /* Compare each value for the attribute against the token id. */
for (i = 0; vals[i] != NULL; i++) for (i = 0; vals[i] != NULL; i++)
{ {
DBG(("LDAP : Found %i values - checking if any of them match '%s:%s:%s'", DBG("LDAP : Found %i values - checking if any of them match '%s:%s:%s'",
ldap_count_values_len(vals), ldap_count_values_len(vals),
vals[i]->bv_val, vals[i]->bv_val,
cfg->yubi_attr_prefix ? cfg->yubi_attr_prefix : "", token_id)); cfg->yubi_attr_prefix ? cfg->yubi_attr_prefix : "", token_id);
/* Only values containing this prefix are considered. */ /* Only values containing this prefix are considered. */
if ((!cfg->yubi_attr_prefix || !strncmp (cfg->yubi_attr_prefix, vals[i]->bv_val, yubi_attr_prefix_len))) if ((!cfg->yubi_attr_prefix || !strncmp (cfg->yubi_attr_prefix, vals[i]->bv_val, yubi_attr_prefix_len)))
{ {
if(!strncmp (token_id, vals[i]->bv_val + yubi_attr_prefix_len, strlen (vals[i]->bv_val + yubi_attr_prefix_len))) if(!strncmp (token_id, vals[i]->bv_val + yubi_attr_prefix_len, strlen (vals[i]->bv_val + yubi_attr_prefix_len)))
{ {
DBG (("Token Found :: %s", vals[i]->bv_val)); DBG ("Token Found :: %s", vals[i]->bv_val);
retval = 1; retval = 1;
} }
} }
@ -393,7 +393,7 @@ authorize_user_token_ldap (struct cfg *cfg,
#if HAVE_CR #if HAVE_CR
static int static int
display_error(pam_handle_t *pamh, const char *message) { display_error(pam_handle_t *pamh, const char *message, struct cfg *cfg) {
struct pam_conv *conv; struct pam_conv *conv;
const struct pam_message *pmsg[1]; const struct pam_message *pmsg[1];
struct pam_message msg[1]; struct pam_message msg[1];
@ -402,12 +402,12 @@ display_error(pam_handle_t *pamh, const char *message) {
retval = pam_get_item (pamh, PAM_CONV, (const void **) &conv); retval = pam_get_item (pamh, PAM_CONV, (const void **) &conv);
if (retval != PAM_SUCCESS) { if (retval != PAM_SUCCESS) {
D(("get conv returned error: %s", pam_strerror (pamh, retval))); DBG("get conv returned error: %s", pam_strerror (pamh, retval));
return retval; return retval;
} }
if(!conv || !conv->conv){ if(!conv || !conv->conv){
D(("conv() function invalid")); DBG("conv() function invalid");
return PAM_CONV_ERR; return PAM_CONV_ERR;
} }
pmsg[0] = &msg[0]; pmsg[0] = &msg[0];
@ -416,13 +416,13 @@ display_error(pam_handle_t *pamh, const char *message) {
retval = conv->conv(1, pmsg, &resp, conv->appdata_ptr); retval = conv->conv(1, pmsg, &resp, conv->appdata_ptr);
if (retval != PAM_SUCCESS) { if (retval != PAM_SUCCESS) {
D(("conv returned error: %s", pam_strerror (pamh, retval))); DBG("conv returned error: %s", pam_strerror (pamh, retval));
return retval; return retval;
} }
if (resp) if (resp)
{ {
D(("conv returned: '%s'", resp->resp)); DBG("conv returned: '%s'", resp->resp);
if (resp->resp) if (resp->resp)
free (resp->resp); free (resp->resp);
free (resp); free (resp);
@ -460,62 +460,62 @@ do_challenge_response(pam_handle_t *pamh, struct cfg *cfg, const char *username)
ret = PAM_AUTH_ERR; ret = PAM_AUTH_ERR;
if (! init_yubikey(&yk)) { if (! init_yubikey(&yk)) {
DBG(("Failed initializing YubiKey")); DBG("Failed initializing YubiKey");
goto out; goto out;
} }
if (! check_firmware_version(yk, cfg->debug, true)) { if (! check_firmware_version(yk, cfg->debug, true, cfg->debug_file)) {
DBG(("YubiKey does not support Challenge-Response (version 2.2 required)")); DBG("YubiKey does not support Challenge-Response (version 2.2 required)");
goto out; goto out;
} }
pwres = getpwnam_r (username, &pass, pwbuf, pwbuflen, &p); pwres = getpwnam_r (username, &pass, pwbuf, pwbuflen, &p);
if (p == NULL) { if (p == NULL) {
DBG (("getpwnam_r: %s", strerror(pwres))); DBG ("getpwnam_r: %s", strerror(pwres));
goto out; goto out;
} }
if (! get_user_challenge_file (yk, cfg->chalresp_path, p, &userfile)) { if (! get_user_challenge_file (yk, cfg->chalresp_path, p, &userfile, cfg->debug_file)) {
DBG(("Failed getting user challenge file for user %s", username)); DBG("Failed getting user challenge file for user %s", username);
goto out; goto out;
} }
DBG(("Loading challenge from file %s", userfile)); DBG("Loading challenge from file %s", userfile);
/* Drop privileges before opening user file (if we're not using system-wide dir). */ /* Drop privileges before opening user file (if we're not using system-wide dir). */
if (!cfg->chalresp_path) { if (!cfg->chalresp_path) {
if (pam_modutil_drop_priv(pamh, &privs, p)) { if (pam_modutil_drop_priv(pamh, &privs, p)) {
DBG (("could not drop privileges")); DBG ("could not drop privileges");
goto out; goto out;
} }
} }
fd = open(userfile, O_RDONLY, 0); fd = open(userfile, O_RDONLY, 0);
if (fd < 0) { if (fd < 0) {
DBG (("Cannot open file: %s (%s)", userfile, strerror(errno))); DBG ("Cannot open file: %s (%s)", userfile, strerror(errno));
goto restpriv_out; goto restpriv_out;
} }
if (fstat(fd, &st) < 0) { if (fstat(fd, &st) < 0) {
DBG (("Cannot stat file: %s (%s)", userfile, strerror(errno))); DBG ("Cannot stat file: %s (%s)", userfile, strerror(errno));
close(fd); close(fd);
goto restpriv_out; goto restpriv_out;
} }
if (!S_ISREG(st.st_mode)) { if (!S_ISREG(st.st_mode)) {
DBG (("%s is not a regular file", userfile)); DBG ("%s is not a regular file", userfile);
close(fd); close(fd);
goto restpriv_out; goto restpriv_out;
} }
f = fdopen(fd, "r"); f = fdopen(fd, "r");
if (f == NULL) { if (f == NULL) {
DBG (("fdopen: %s", strerror(errno))); DBG ("fdopen: %s", strerror(errno));
close(fd); close(fd);
goto restpriv_out; goto restpriv_out;
} }
if (! load_chalresp_state(f, &state, cfg->debug)) if (! load_chalresp_state(f, &state, cfg->debug, cfg->debug_file))
goto restpriv_out; goto restpriv_out;
if (fclose(f) < 0) { if (fclose(f) < 0) {
@ -526,7 +526,7 @@ do_challenge_response(pam_handle_t *pamh, struct cfg *cfg, const char *username)
if (!cfg->chalresp_path) { if (!cfg->chalresp_path) {
if (pam_modutil_regain_priv(pamh, &privs)) { if (pam_modutil_regain_priv(pamh, &privs)) {
DBG (("could not restore privileges")); DBG ("could not restore privileges");
goto out; goto out;
} }
} }
@ -534,7 +534,7 @@ do_challenge_response(pam_handle_t *pamh, struct cfg *cfg, const char *username)
if (! challenge_response(yk, state.slot, state.challenge, state.challenge_len, if (! challenge_response(yk, state.slot, state.challenge, state.challenge_len,
true, true, false, true, true, false,
buf, sizeof(buf), &response_len)) { buf, sizeof(buf), &response_len)) {
DBG(("Challenge-response FAILED")); DBG("Challenge-response FAILED");
goto out; goto out;
} }
@ -552,15 +552,15 @@ do_challenge_response(pam_handle_t *pamh, struct cfg *cfg, const char *username)
if (memcmp(buf, state.response, state.response_len) == 0) { if (memcmp(buf, state.response, state.response_len) == 0) {
ret = PAM_SUCCESS; ret = PAM_SUCCESS;
} else { } else {
DBG(("Unexpected C/R response : %s", response_hex)); DBG("Unexpected C/R response : %s", response_hex);
goto out; goto out;
} }
DBG(("Got the expected response, generating new challenge (%u bytes).", CR_CHALLENGE_SIZE)); DBG("Got the expected response, generating new challenge (%u bytes).", CR_CHALLENGE_SIZE);
errstr = "Error generating new challenge, please check syslog or contact your system administrator"; errstr = "Error generating new challenge, please check syslog or contact your system administrator";
if (generate_random(state.challenge, sizeof(state.challenge))) { if (generate_random(state.challenge, sizeof(state.challenge))) {
DBG(("Failed generating new challenge!")); DBG("Failed generating new challenge!");
goto out; goto out;
} }
@ -568,7 +568,7 @@ do_challenge_response(pam_handle_t *pamh, struct cfg *cfg, const char *username)
if (! challenge_response(yk, state.slot, state.challenge, CR_CHALLENGE_SIZE, if (! challenge_response(yk, state.slot, state.challenge, CR_CHALLENGE_SIZE,
true, true, false, true, true, false,
buf, sizeof(buf), &response_len)) { buf, sizeof(buf), &response_len)) {
DBG(("Second challenge-response FAILED")); DBG("Second challenge-response FAILED");
goto out; goto out;
} }
@ -586,7 +586,7 @@ do_challenge_response(pam_handle_t *pamh, 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)) { if (response_len > sizeof(state.response)) {
DBG(("Got too long response ??? (%u/%zu)", response_len, sizeof(state.response))); DBG("Got too long response ??? (%u/%zu)", response_len, sizeof(state.response));
goto out; goto out;
} }
memcpy (state.response, buf, response_len); memcpy (state.response, buf, response_len);
@ -597,7 +597,7 @@ do_challenge_response(pam_handle_t *pamh, struct cfg *cfg, const char *username)
/* Drop privileges before creating new challenge file. */ /* Drop privileges before creating new challenge file. */
if (!cfg->chalresp_path) { if (!cfg->chalresp_path) {
if (pam_modutil_drop_priv(pamh, &privs, p)) { if (pam_modutil_drop_priv(pamh, &privs, p)) {
DBG (("could not drop privileges")); DBG ("could not drop privileges");
goto out; goto out;
} }
} }
@ -611,16 +611,16 @@ do_challenge_response(pam_handle_t *pamh, struct cfg *cfg, const char *username)
fd = mkstemp(tmpfile); fd = mkstemp(tmpfile);
if (fd < 0) { if (fd < 0) {
DBG (("Cannot open file: %s (%s)", tmpfile, strerror(errno))); DBG ("Cannot open file: %s (%s)", tmpfile, strerror(errno));
goto restpriv_out; goto restpriv_out;
} }
if (fchmod (fd, st.st_mode) != 0) { if (fchmod (fd, st.st_mode) != 0) {
DBG (("could not set correct file permissions")); DBG ("could not set correct file permissions");
goto restpriv_out; goto restpriv_out;
} }
if (fchown (fd, st.st_uid, st.st_gid) != 0) { if (fchown (fd, st.st_uid, st.st_gid) != 0) {
DBG (("could not set correct file ownership")); DBG ("could not set correct file ownership");
goto restpriv_out; goto restpriv_out;
} }
@ -658,19 +658,19 @@ restpriv_out:
if (yk_errno) { if (yk_errno) {
if (yk_errno == YK_EUSBERR) { if (yk_errno == YK_EUSBERR) {
syslog(LOG_ERR, "USB error: %s", yk_usb_strerror()); syslog(LOG_ERR, "USB error: %s", yk_usb_strerror());
DBG(("USB error: %s", yk_usb_strerror())); DBG("USB error: %s", yk_usb_strerror());
} else { } else {
syslog(LOG_ERR, "Yubikey core error: %s", yk_strerror(yk_errno)); syslog(LOG_ERR, "Yubikey core error: %s", yk_strerror(yk_errno));
DBG(("Yubikey core error: %s", yk_strerror(yk_errno))); DBG("Yubikey core error: %s", yk_strerror(yk_errno));
} }
} }
if (errstr) if (errstr)
display_error(pamh, errstr); display_error(pamh, errstr, cfg);
if (errno) { if (errno) {
syslog(LOG_ERR, "Challenge response failed: %s", strerror(errno)); syslog(LOG_ERR, "Challenge response failed: %s", strerror(errno));
DBG(("Challenge response failed: %s", strerror(errno))); DBG("Challenge response failed: %s", strerror(errno));
} }
if (yk) if (yk)
@ -695,6 +695,7 @@ parse_cfg (int flags, int argc, const char **argv, struct cfg *cfg)
cfg->client_id = 0; cfg->client_id = 0;
cfg->token_id_length = DEFAULT_TOKEN_ID_LEN; cfg->token_id_length = DEFAULT_TOKEN_ID_LEN;
cfg->mode = CLIENT; cfg->mode = CLIENT;
cfg->debug_file = stdout;
for (i = 0; i < argc; i++) for (i = 0; i < argc; i++)
{ {
@ -752,41 +753,54 @@ parse_cfg (int flags, int argc, const char **argv, struct cfg *cfg)
cfg->mode = CLIENT; cfg->mode = CLIENT;
if (strncmp (argv[i], "chalresp_path=", 14) == 0) if (strncmp (argv[i], "chalresp_path=", 14) == 0)
cfg->chalresp_path = argv[i] + 14; cfg->chalresp_path = argv[i] + 14;
if (strncmp (argv[i], "debug_file=", 11) == 0)
{
if(strncmp (argv[i] + 11, "stderr", 6) == 0)
{
cfg->debug_file = stderr;
}
else
{
FILE *file = fopen(argv[i] + 11, "a");
if(file)
{
cfg->debug_file = file;
}
}
}
} }
if (cfg->debug) DBG ("called.");
{ DBG ("flags %d argc %d", flags, argc);
D (("called.")); for (i = 0; i < argc; i++)
D (("flags %d argc %d", flags, argc)); DBG ("argv[%d]=%s", i, argv[i]);
for (i = 0; i < argc; i++) DBG ("id=%u", cfg->client_id);
D (("argv[%d]=%s", i, argv[i])); DBG ("key=%s", cfg->client_key ? cfg->client_key : "(null)");
D (("id=%u", cfg->client_id)); DBG ("debug=%d", cfg->debug);
D (("key=%s", cfg->client_key ? cfg->client_key : "(null)")); DBG ("debug_file=%d", fileno(cfg->debug_file));
D (("debug=%d", cfg->debug)); DBG ("alwaysok=%d", cfg->alwaysok);
D (("alwaysok=%d", cfg->alwaysok)); DBG ("verbose_otp=%d", cfg->verbose_otp);
D (("verbose_otp=%d", cfg->verbose_otp)); DBG ("try_first_pass=%d", cfg->try_first_pass);
D (("try_first_pass=%d", cfg->try_first_pass)); DBG ("use_first_pass=%d", cfg->use_first_pass);
D (("use_first_pass=%d", cfg->use_first_pass)); DBG ("authfile=%s", cfg->auth_file ? cfg->auth_file : "(null)");
D (("authfile=%s", cfg->auth_file ? cfg->auth_file : "(null)")); DBG ("ldapserver=%s", cfg->ldapserver ? cfg->ldapserver : "(null)");
D (("ldapserver=%s", cfg->ldapserver ? cfg->ldapserver : "(null)")); DBG ("ldap_uri=%s", cfg->ldap_uri ? cfg->ldap_uri : "(null)");
D (("ldap_uri=%s", cfg->ldap_uri ? cfg->ldap_uri : "(null)")); DBG ("ldap_bind_user=%s", cfg->ldap_bind_user ? cfg->ldap_bind_user : "(null)");
D (("ldap_bind_user=%s", cfg->ldap_bind_user ? cfg->ldap_bind_user : "(null)")); DBG ("ldap_bind_password=%s", cfg->ldap_bind_password ? cfg->ldap_bind_password : "(null)");
D (("ldap_bind_password=%s", cfg->ldap_bind_password ? cfg->ldap_bind_password : "(null)")); DBG ("ldap_filter=%s", cfg->ldap_filter ? cfg->ldap_filter : "(null)");
D (("ldap_filter=%s", cfg->ldap_filter ? cfg->ldap_filter : "(null)")); DBG ("ldap_cacertfile=%s", cfg->ldap_cacertfile ? cfg->ldap_cacertfile : "(null)");
D (("ldap_cacertfile=%s", cfg->ldap_cacertfile ? cfg->ldap_cacertfile : "(null)")); DBG ("ldapdn=%s", cfg->ldapdn ? cfg->ldapdn : "(null)");
D (("ldapdn=%s", cfg->ldapdn ? cfg->ldapdn : "(null)")); DBG ("user_attr=%s", cfg->user_attr ? cfg->user_attr : "(null)");
D (("user_attr=%s", cfg->user_attr ? cfg->user_attr : "(null)")); DBG ("yubi_attr=%s", cfg->yubi_attr ? cfg->yubi_attr : "(null)");
D (("yubi_attr=%s", cfg->yubi_attr ? cfg->yubi_attr : "(null)")); DBG ("yubi_attr_prefix=%s", cfg->yubi_attr_prefix ? cfg->yubi_attr_prefix : "(null)");
D (("yubi_attr_prefix=%s", cfg->yubi_attr_prefix ? cfg->yubi_attr_prefix : "(null)")); DBG ("url=%s", cfg->url ? cfg->url : "(null)");
D (("url=%s", cfg->url ? cfg->url : "(null)")); DBG ("urllist=%s", cfg->urllist ? cfg->urllist : "(null)");
D (("urllist=%s", cfg->urllist ? cfg->urllist : "(null)")); DBG ("capath=%s", cfg->capath ? cfg->capath : "(null)");
D (("capath=%s", cfg->capath ? cfg->capath : "(null)")); DBG ("cainfo=%s", cfg->cainfo ? cfg->cainfo : "(null)");
D (("cainfo=%s", cfg->cainfo ? cfg->cainfo : "(null)")); DBG ("proxy=%s", cfg->proxy ? cfg->proxy : "(null)");
D (("proxy=%s", cfg->proxy ? cfg->proxy : "(null)")); DBG ("token_id_length=%d", cfg->token_id_length);
D (("token_id_length=%d", cfg->token_id_length)); DBG ("mode=%s", cfg->mode == CLIENT ? "client" : "chresp" );
D (("mode=%s", cfg->mode == CLIENT ? "client" : "chresp" )); DBG ("chalresp_path=%s", cfg->chalresp_path ? cfg->chalresp_path : "(null)");
D (("chalresp_path=%s", cfg->chalresp_path ? cfg->chalresp_path : "(null)"));
}
} }
PAM_EXTERN int PAM_EXTERN int
@ -816,11 +830,11 @@ pam_sm_authenticate (pam_handle_t * pamh,
parse_cfg (flags, argc, argv, cfg); parse_cfg (flags, argc, argv, cfg);
DBG (("pam_yubico version: %s", VERSION)); DBG ("pam_yubico version: %s", VERSION);
if (cfg->token_id_length > MAX_TOKEN_ID_LEN) if (cfg->token_id_length > MAX_TOKEN_ID_LEN)
{ {
DBG (("configuration error: token_id_length too long. Maximum acceptable value : %u", MAX_TOKEN_ID_LEN)); DBG ("configuration error: token_id_length too long. Maximum acceptable value : %u", MAX_TOKEN_ID_LEN);
retval = PAM_AUTHINFO_UNAVAIL; retval = PAM_AUTHINFO_UNAVAIL;
goto done; goto done;
} }
@ -828,19 +842,19 @@ pam_sm_authenticate (pam_handle_t * pamh,
retval = pam_get_user (pamh, &user, NULL); retval = pam_get_user (pamh, &user, NULL);
if (retval != PAM_SUCCESS) if (retval != PAM_SUCCESS)
{ {
DBG (("get user returned error: %s", pam_strerror (pamh, retval))); DBG ("get user returned error: %s", pam_strerror (pamh, retval));
goto done; goto done;
} }
DBG (("get user returned: %s", user)); DBG ("get user returned: %s", user);
if (cfg->mode == CHRESP) { if (cfg->mode == CHRESP) {
#if HAVE_CR #if HAVE_CR
return do_challenge_response(pamh, cfg, user); retval = do_challenge_response(pamh, cfg, user);
#else #else
DBG (("no support for challenge/response")); DBG ("no support for challenge/response");
retval = PAM_AUTH_ERR; retval = PAM_AUTH_ERR;
goto done;
#endif #endif
goto done;
} }
if (cfg->try_first_pass || cfg->use_first_pass) if (cfg->try_first_pass || cfg->use_first_pass)
@ -848,30 +862,30 @@ pam_sm_authenticate (pam_handle_t * pamh,
retval = pam_get_item (pamh, PAM_AUTHTOK, (const void **) &password); retval = pam_get_item (pamh, PAM_AUTHTOK, (const void **) &password);
if (retval != PAM_SUCCESS) if (retval != PAM_SUCCESS)
{ {
DBG (("get password returned error: %s", DBG ("get password returned error: %s",
pam_strerror (pamh, retval))); pam_strerror (pamh, retval));
goto done; goto done;
} }
DBG (("get password returned: %s", password)); DBG ("get password returned: %s", password);
} }
if (cfg->use_first_pass && password == NULL) if (cfg->use_first_pass && password == NULL)
{ {
DBG (("use_first_pass set and no password, giving up")); DBG ("use_first_pass set and no password, giving up");
retval = PAM_AUTH_ERR; retval = PAM_AUTH_ERR;
goto done; goto done;
} }
if(ykclient_global_init() != YKCLIENT_OK) if(ykclient_global_init() != YKCLIENT_OK)
{ {
DBG (("Failed initializing ykclient library")); DBG ("Failed initializing ykclient library");
retval = PAM_AUTHINFO_UNAVAIL; retval = PAM_AUTHINFO_UNAVAIL;
goto done; goto done;
} }
rc = ykclient_init (&ykc); rc = ykclient_init (&ykc);
if (rc != YKCLIENT_OK) if (rc != YKCLIENT_OK)
{ {
DBG (("ykclient_init() failed (%d): %s", rc, ykclient_strerror (rc))); DBG ("ykclient_init() failed (%d): %s", rc, ykclient_strerror (rc));
retval = PAM_AUTHINFO_UNAVAIL; retval = PAM_AUTHINFO_UNAVAIL;
goto done; goto done;
} }
@ -879,8 +893,8 @@ pam_sm_authenticate (pam_handle_t * pamh,
rc = ykclient_set_client_b64 (ykc, cfg->client_id, cfg->client_key); rc = ykclient_set_client_b64 (ykc, cfg->client_id, cfg->client_key);
if (rc != YKCLIENT_OK) if (rc != YKCLIENT_OK)
{ {
DBG (("ykclient_set_client_b64() failed (%d): %s", DBG ("ykclient_set_client_b64() failed (%d): %s",
rc, ykclient_strerror (rc))); rc, ykclient_strerror (rc));
retval = PAM_AUTHINFO_UNAVAIL; retval = PAM_AUTHINFO_UNAVAIL;
goto done; goto done;
} }
@ -902,8 +916,8 @@ pam_sm_authenticate (pam_handle_t * pamh,
rc = ykclient_set_url_template (ykc, cfg->url); rc = ykclient_set_url_template (ykc, cfg->url);
if (rc != YKCLIENT_OK) if (rc != YKCLIENT_OK)
{ {
DBG (("ykclient_set_url_template() failed (%d): %s", DBG ("ykclient_set_url_template() failed (%d): %s",
rc, ykclient_strerror (rc))); rc, ykclient_strerror (rc));
retval = PAM_AUTHINFO_UNAVAIL; retval = PAM_AUTHINFO_UNAVAIL;
goto done; goto done;
} }
@ -919,7 +933,7 @@ pam_sm_authenticate (pam_handle_t * pamh,
{ {
if(templates == 10) if(templates == 10)
{ {
DBG (("maximum 10 urls supported in list.")); DBG ("maximum 10 urls supported in list.");
retval = PAM_AUTHINFO_UNAVAIL; retval = PAM_AUTHINFO_UNAVAIL;
goto done; goto done;
} }
@ -929,8 +943,8 @@ pam_sm_authenticate (pam_handle_t * pamh,
rc = ykclient_set_url_bases (ykc, templates, (const char **)urls); rc = ykclient_set_url_bases (ykc, templates, (const char **)urls);
if (rc != YKCLIENT_OK) if (rc != YKCLIENT_OK)
{ {
DBG (("ykclient_set_url_bases() failed (%d): %s", DBG ("ykclient_set_url_bases() failed (%d): %s",
rc, ykclient_strerror (rc))); rc, ykclient_strerror (rc));
retval = PAM_AUTHINFO_UNAVAIL; retval = PAM_AUTHINFO_UNAVAIL;
goto done; goto done;
} }
@ -941,7 +955,7 @@ pam_sm_authenticate (pam_handle_t * pamh,
retval = pam_get_item (pamh, PAM_CONV, (const void **) &conv); retval = pam_get_item (pamh, PAM_CONV, (const void **) &conv);
if (retval != PAM_SUCCESS) if (retval != PAM_SUCCESS)
{ {
DBG (("get conv returned error: %s", pam_strerror (pamh, retval))); DBG ("get conv returned error: %s", pam_strerror (pamh, retval));
goto done; goto done;
} }
@ -971,18 +985,18 @@ pam_sm_authenticate (pam_handle_t * pamh,
if (retval != PAM_SUCCESS) if (retval != PAM_SUCCESS)
{ {
DBG (("conv returned error: %s", pam_strerror (pamh, retval))); DBG ("conv returned error: %s", pam_strerror (pamh, retval));
goto done; goto done;
} }
if (resp->resp == NULL) if (resp->resp == NULL)
{ {
DBG (("conv returned NULL passwd?")); DBG ("conv returned NULL passwd?");
retval = PAM_AUTH_ERR; retval = PAM_AUTH_ERR;
goto done; goto done;
} }
DBG (("conv returned %zu bytes", strlen(resp->resp))); DBG ("conv returned %zu bytes", strlen(resp->resp));
password = resp->resp; password = resp->resp;
} }
@ -996,15 +1010,15 @@ pam_sm_authenticate (pam_handle_t * pamh,
skip_bytes = password_len - (cfg->token_id_length + TOKEN_OTP_LEN); skip_bytes = password_len - (cfg->token_id_length + TOKEN_OTP_LEN);
} }
DBG (("Skipping first %i bytes. Length is %zu, token_id set to %u and token OTP always %u.", DBG ("Skipping first %i bytes. Length is %zu, token_id set to %u and token OTP always %u.",
skip_bytes, password_len, cfg->token_id_length, TOKEN_OTP_LEN)); skip_bytes, password_len, cfg->token_id_length, TOKEN_OTP_LEN);
/* Copy full YubiKey output (public ID + OTP) into otp */ /* Copy full YubiKey output (public ID + OTP) into otp */
strncpy (otp, password + skip_bytes, sizeof (otp) - 1); strncpy (otp, password + skip_bytes, sizeof (otp) - 1);
/* Copy only public ID into otp_id. Destination buffer is zeroed. */ /* Copy only public ID into otp_id. Destination buffer is zeroed. */
strncpy (otp_id, password + skip_bytes, cfg->token_id_length); strncpy (otp_id, password + skip_bytes, cfg->token_id_length);
DBG (("OTP: %s ID: %s ", otp, otp_id)); DBG ("OTP: %s ID: %s ", otp, otp_id);
/* user entered their system password followed by generated OTP? */ /* user entered their system password followed by generated OTP? */
if (password_len > TOKEN_OTP_LEN + cfg->token_id_length) if (password_len > TOKEN_OTP_LEN + cfg->token_id_length)
@ -1018,13 +1032,13 @@ pam_sm_authenticate (pam_handle_t * pamh,
onlypasswd[password_len - (TOKEN_OTP_LEN + cfg->token_id_length)] = '\0'; onlypasswd[password_len - (TOKEN_OTP_LEN + cfg->token_id_length)] = '\0';
DBG (("Extracted a probable system password entered before the OTP - " DBG ("Extracted a probable system password entered before the OTP - "
"setting item PAM_AUTHTOK")); "setting item PAM_AUTHTOK");
retval = pam_set_item (pamh, PAM_AUTHTOK, onlypasswd); retval = pam_set_item (pamh, PAM_AUTHTOK, onlypasswd);
if (retval != PAM_SUCCESS) if (retval != PAM_SUCCESS)
{ {
DBG (("set_item returned error: %s", pam_strerror (pamh, retval))); DBG ("set_item returned error: %s", pam_strerror (pamh, retval));
goto done; goto done;
} }
} }
@ -1033,9 +1047,9 @@ pam_sm_authenticate (pam_handle_t * pamh,
rc = ykclient_request (ykc, otp); rc = ykclient_request (ykc, otp);
DBG (("ykclient return value (%d): %s", rc, DBG ("ykclient return value (%d): %s", rc,
ykclient_strerror (rc))); ykclient_strerror (rc));
DBG (("ykclient url used: %s", ykclient_get_last_url(ykc))); DBG ("ykclient url used: %s", ykclient_get_last_url(ykc));
/* authorize the user with supplied token id */ /* authorize the user with supplied token id */
if (cfg->ldapserver != NULL || cfg->ldap_uri != NULL) if (cfg->ldapserver != NULL || cfg->ldap_uri != NULL)
@ -1063,19 +1077,19 @@ pam_sm_authenticate (pam_handle_t * pamh,
} }
break; break;
case 0: case 0:
DBG (("Internal error while validating user")); DBG ("Internal error while validating user");
retval = PAM_AUTHINFO_UNAVAIL; retval = PAM_AUTHINFO_UNAVAIL;
break; break;
case -1: case -1:
DBG (("Unauthorized token for this user")); DBG ("Unauthorized token for this user");
retval = PAM_AUTH_ERR; retval = PAM_AUTH_ERR;
break; break;
case -2: case -2:
DBG (("Unknown user")); DBG ("Unknown user");
retval = PAM_USER_UNKNOWN; retval = PAM_USER_UNKNOWN;
break; break;
default: default:
DBG (("Unhandled value for token-user validation")) DBG ("Unhandled value for token-user validation");
retval = PAM_AUTHINFO_UNAVAIL; retval = PAM_AUTHINFO_UNAVAIL;
} }
@ -1099,10 +1113,10 @@ done:
} }
if (cfg->alwaysok && retval != PAM_SUCCESS) if (cfg->alwaysok && retval != PAM_SUCCESS)
{ {
DBG (("alwaysok needed (otherwise return with %d)", retval)); DBG ("alwaysok needed (otherwise return with %d)", retval);
retval = PAM_SUCCESS; retval = PAM_SUCCESS;
} }
DBG (("done. [%s]", pam_strerror (pamh, retval))); DBG ("done. [%s]", pam_strerror (pamh, retval));
pam_set_data (pamh, "yubico_setcred_return", (void*)(intptr_t)retval, NULL); pam_set_data (pamh, "yubico_setcred_return", (void*)(intptr_t)retval, NULL);
if (resp) if (resp)
@ -1117,6 +1131,11 @@ done:
free((char*)msg[0].msg); free((char*)msg[0].msg);
} }
if(cfg->debug_file != stderr && cfg->debug_file != stdout)
{
fclose(cfg->debug_file);
}
return retval; return retval;
} }
@ -1129,17 +1148,27 @@ pam_sm_setcred (
} }
PAM_EXTERN int PAM_EXTERN int
pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __attribute__((unused)), pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
int argc __attribute__((unused)), const char **argv __attribute__((unused)))
{ {
struct cfg cfg_st;
struct cfg *cfg = &cfg_st; /* for DBG macro */
int retval; int retval;
int rc = pam_get_data(pamh, "yubico_setcred_return", (const void**)&retval); int rc = pam_get_data(pamh, "yubico_setcred_return", (const void**)&retval);
parse_cfg (flags, argc, argv, cfg);
if (rc == PAM_SUCCESS && retval == PAM_SUCCESS) { if (rc == PAM_SUCCESS && retval == PAM_SUCCESS) {
D (("pam_sm_acct_mgmt returing PAM_SUCCESS")); DBG ("pam_sm_acct_mgmt returing PAM_SUCCESS");
return PAM_SUCCESS; retval = PAM_SUCCESS;
} else {
DBG ("pam_sm_acct_mgmt returing PAM_AUTH_ERR:%d", rc);
retval = PAM_AUTH_ERR;
} }
D (("pam_sm_acct_mgmt returing PAM_AUTH_ERR:%d", rc));
return PAM_AUTH_ERR; if(cfg->debug_file != stderr && cfg->debug_file != stdout) {
fclose(cfg->debug_file);
}
return retval;
} }
PAM_EXTERN int PAM_EXTERN int
@ -1148,7 +1177,6 @@ pam_sm_open_session(
int argc __attribute__((unused)), const char *argv[] __attribute__((unused))) int argc __attribute__((unused)), const char *argv[] __attribute__((unused)))
{ {
D(("pam_sm_open_session"));
return (PAM_SUCCESS); return (PAM_SUCCESS);
} }
@ -1157,7 +1185,6 @@ pam_sm_close_session(
pam_handle_t *pamh __attribute__((unused)), int flags __attribute__((unused)), pam_handle_t *pamh __attribute__((unused)), int flags __attribute__((unused)),
int argc __attribute__((unused)), const char *argv[] __attribute__((unused))) int argc __attribute__((unused)), const char *argv[] __attribute__((unused)))
{ {
D(("pam_sm_close_session"));
return (PAM_SUCCESS); return (PAM_SUCCESS);
} }
@ -1166,7 +1193,6 @@ pam_sm_chauthtok(
pam_handle_t *pamh __attribute__((unused)), int flags __attribute__((unused)), pam_handle_t *pamh __attribute__((unused)), int flags __attribute__((unused)),
int argc __attribute__((unused)), const char *argv[] __attribute__((unused))) int argc __attribute__((unused)), const char *argv[] __attribute__((unused)))
{ {
D(("pam_sm_chauthtok"));
return (PAM_SERVICE_ERR); return (PAM_SERVICE_ERR);
} }

View File

@ -74,27 +74,27 @@ static void test_check_user_token(void) {
fprintf(handle, "foo2:cccccccccccc\n"); fprintf(handle, "foo2:cccccccccccc\n");
fclose(handle); fclose(handle);
ret = check_user_token(file, "foobar", "hhhvhvhdhbid", 1); ret = check_user_token(file, "foobar", "hhhvhvhdhbid", 1, stdout);
assert(ret == 1); assert(ret == 1);
ret = check_user_token(file, "foobar", "hnhbhnhbhnhb", 1); ret = check_user_token(file, "foobar", "hnhbhnhbhnhb", 1, stdout);
assert(ret == 1); assert(ret == 1);
ret = check_user_token(file, "foobar", "hnhbhnhbhnhc", 1); ret = check_user_token(file, "foobar", "hnhbhnhbhnhc", 1, stdout);
assert(ret == -1); assert(ret == -1);
ret = check_user_token(file, "kaka", "hihbhdhrhbhj", 1); ret = check_user_token(file, "kaka", "hihbhdhrhbhj", 1, stdout);
assert(ret == 1); assert(ret == 1);
ret = check_user_token(file, "bar", "hnhbhnhbhnhb", 1); ret = check_user_token(file, "bar", "hnhbhnhbhnhb", 1, stdout);
assert(ret == 1); assert(ret == 1);
ret = check_user_token(file, "foo", "hdhrhbhjhvhu", 1); ret = check_user_token(file, "foo", "hdhrhbhjhvhu", 1, stdout);
assert(ret == -2); assert(ret == -2);
ret = check_user_token(file, "foo2", "cccccccccccc", 1); ret = check_user_token(file, "foo2", "cccccccccccc", 1, stdout);
assert(ret == 1); assert(ret == 1);
ret = check_user_token(file, "foo2", "vvvvvvvvvvvv", 1); ret = check_user_token(file, "foo2", "vvvvvvvvvvvv", 1, stdout);
assert(ret == 1); assert(ret == 1);
ret = check_user_token(file, "foo2", "vvvvvvvvvvcc", 1); ret = check_user_token(file, "foo2", "vvvvvvvvvvcc", 1, stdout);
assert(ret == -1); assert(ret == -1);
ret = check_user_token(file, "foo2", "", 1); ret = check_user_token(file, "foo2", "", 1, stdout);
assert(ret == -1); assert(ret == -1);
ret = check_user_token(file, "foo", "", 1); ret = check_user_token(file, "foo", "", 1, stdout);
assert(ret == -2); assert(ret == -2);
remove(file); remove(file);
} }
@ -115,7 +115,7 @@ static void test_load_chalresp_state(void) {
memset(&state, 0, sizeof(state)); memset(&state, 0, sizeof(state));
fprintf(file, "v2:%s:%s:%s:%d:%d\n", CHALLENGE1, RESPONSE1, SALT1, 1000, 2); fprintf(file, "v2:%s:%s:%s:%d:%d\n", CHALLENGE1, RESPONSE1, SALT1, 1000, 2);
rewind(file); rewind(file);
ret = load_chalresp_state(file, &state, true); ret = load_chalresp_state(file, &state, true, stdout);
assert(ret == 1); assert(ret == 1);
assert(state.iterations == 1000); assert(state.iterations == 1000);
assert(state.slot == 2); assert(state.slot == 2);
@ -127,7 +127,7 @@ static void test_load_chalresp_state(void) {
memset(&state, 0, sizeof(state)); memset(&state, 0, sizeof(state));
fprintf(file, "v1:%s:%s:%d\n", CHALLENGE2, RESPONSE2, 1); fprintf(file, "v1:%s:%s:%d\n", CHALLENGE2, RESPONSE2, 1);
rewind(file); rewind(file);
ret = load_chalresp_state(file, &state, true); ret = load_chalresp_state(file, &state, true, stdout);
assert(ret == 1); assert(ret == 1);
assert(state.iterations == CR_DEFAULT_ITERATIONS); assert(state.iterations == CR_DEFAULT_ITERATIONS);
assert(state.slot == 1); assert(state.slot == 1);
@ -139,7 +139,7 @@ static void test_load_chalresp_state(void) {
/* slot 3 should fail.. */ /* slot 3 should fail.. */
fprintf(file, "v2:%s:%s:%s:%d:%d\n", CHALLENGE1, RESPONSE1, SALT1, 1000, 3); fprintf(file, "v2:%s:%s:%s:%d:%d\n", CHALLENGE1, RESPONSE1, SALT1, 1000, 3);
rewind(file); rewind(file);
ret = load_chalresp_state(file, &state, true); ret = load_chalresp_state(file, &state, true, stdout);
assert(ret == 0); assert(ret == 0);
fclose(file); fclose(file);
} }

51
util.c
View File

@ -97,7 +97,8 @@ int
check_user_token (const char *authfile, check_user_token (const char *authfile,
const char *username, const char *username,
const char *otp_id, const char *otp_id,
int verbose) int verbose,
FILE *debug_file)
{ {
char buf[1024]; char buf[1024];
char *s_user, *s_token; char *s_user, *s_token;
@ -109,20 +110,20 @@ check_user_token (const char *authfile,
fd = open(authfile, O_RDONLY, 0); fd = open(authfile, O_RDONLY, 0);
if (fd < 0) { if (fd < 0) {
if(verbose) if(verbose)
D (("Cannot open file: %s (%s)", authfile, strerror(errno))); D (debug_file, "Cannot open file: %s (%s)", authfile, strerror(errno));
return retval; return retval;
} }
if (fstat(fd, &st) < 0) { if (fstat(fd, &st) < 0) {
if(verbose) if(verbose)
D (("Cannot stat file: %s (%s)", authfile, strerror(errno))); D (debug_file, "Cannot stat file: %s (%s)", authfile, strerror(errno));
close(fd); close(fd);
return retval; return retval;
} }
if (!S_ISREG(st.st_mode)) { if (!S_ISREG(st.st_mode)) {
if(verbose) if(verbose)
D (("%s is not a regular file", authfile)); D (debug_file, "%s is not a regular file", authfile);
close(fd); close(fd);
return retval; return retval;
} }
@ -130,7 +131,7 @@ check_user_token (const char *authfile,
opwfile = fdopen(fd, "r"); opwfile = fdopen(fd, "r");
if (opwfile == NULL) { if (opwfile == NULL) {
if(verbose) if(verbose)
D (("fdopen: %s", strerror(errno))); D (debug_file, "fdopen: %s", strerror(errno));
close(fd); close(fd);
return retval; return retval;
} }
@ -144,27 +145,26 @@ check_user_token (const char *authfile,
if (buf[0] == '#') { if (buf[0] == '#') {
/* This is a comment and we may skip it. */ /* This is a comment and we may skip it. */
if(verbose) if(verbose)
D (("Skipping comment line: %s", buf)); D (debug_file, "Skipping comment line: %s", buf);
continue; continue;
} }
if(verbose) if(verbose)
D (("Authorization line: %s", buf)); D (debug_file, "Authorization line: %s", buf);
s_user = strtok_r (buf, ":", &saveptr); s_user = strtok_r (buf, ":", &saveptr);
if (s_user && strcmp (username, s_user) == 0) if (s_user && strcmp (username, s_user) == 0)
{ {
if(verbose) if(verbose)
D (("Matched user: %s", s_user)); D (debug_file, "Matched user: %s", s_user);
retval = -1; //We found at least one line for the user retval = -1; //We found at least one line for the user
do do
{ {
s_token = strtok_r (NULL, ":", &saveptr); s_token = strtok_r (NULL, ":", &saveptr);
if(verbose) if(verbose)
D (("Authorization token: %s", s_token)); D (debug_file, "Authorization token: %s", s_token);
if (s_token && strcmp (otp_id, s_token) == 0) if (s_token && strcmp (otp_id, s_token) == 0)
{ {
if(verbose) if(verbose)
D (("Match user/token as %s/%s", username, otp_id)); D (debug_file, "Match user/token as %s/%s", username, otp_id);
fclose (opwfile);
return 1; return 1;
} }
} }
@ -196,7 +196,7 @@ int generate_random(void *buf, int len)
} }
int int
check_firmware_version(YK_KEY *yk, bool verbose, bool quiet) check_firmware_version(YK_KEY *yk, bool verbose, bool quiet, FILE *debug_file)
{ {
YK_STATUS *st = ykds_alloc(); YK_STATUS *st = ykds_alloc();
@ -206,11 +206,10 @@ check_firmware_version(YK_KEY *yk, bool verbose, bool quiet)
} }
if (verbose) { if (verbose) {
D(("YubiKey Firmware version: %d.%d.%d\n", D(debug_file, "YubiKey Firmware version: %d.%d.%d\n",
ykds_version_major(st), ykds_version_major(st),
ykds_version_minor(st), ykds_version_minor(st),
ykds_version_build(st))); ykds_version_build(st));
fflush(stdout);
} }
if (ykds_version_major(st) < 2 || if (ykds_version_major(st) < 2 ||
@ -282,7 +281,7 @@ int challenge_response(YK_KEY *yk, int slot,
} }
int int
get_user_challenge_file(YK_KEY *yk, const char *chalresp_path, const struct passwd *user, char **fn) get_user_challenge_file(YK_KEY *yk, const char *chalresp_path, const struct passwd *user, char **fn, FILE *debug_file)
{ {
/* Getting file from user home directory, i.e. ~/.yubico/challenge, or /* Getting file from user home directory, i.e. ~/.yubico/challenge, or
* from a system wide directory. * from a system wide directory.
@ -299,7 +298,7 @@ get_user_challenge_file(YK_KEY *yk, const char *chalresp_path, const struct pass
int ret; int ret;
if (! yk_get_serial(yk, 0, 0, &serial)) { if (! yk_get_serial(yk, 0, 0, &serial)) {
D (("Failed to read serial number (serial-api-visible disabled?).")); D (debug_file, "Failed to read serial number (serial-api-visible disabled?).");
if (! chalresp_path) if (! chalresp_path)
filename = "challenge"; filename = "challenge";
else else
@ -330,7 +329,7 @@ get_user_challenge_file(YK_KEY *yk, const char *chalresp_path, const struct pass
} }
int int
load_chalresp_state(FILE *f, CR_STATE *state, bool verbose) load_chalresp_state(FILE *f, CR_STATE *state, bool verbose, FILE *debug_file)
{ {
/* /*
* Load the current challenge and expected response information from a file handle. * Load the current challenge and expected response information from a file handle.
@ -354,13 +353,13 @@ load_chalresp_state(FILE *f, CR_STATE *state, bool verbose)
r = fscanf(f, "v2:%126[0-9a-z]:%40[0-9a-z]:%64[0-9a-z]:%d:%d", challenge_hex, response_hex, salt_hex, &iterations, &slot); r = fscanf(f, "v2:%126[0-9a-z]:%40[0-9a-z]:%64[0-9a-z]:%d:%d", challenge_hex, response_hex, salt_hex, &iterations, &slot);
if(r == 5) { if(r == 5) {
if (! yubikey_hex_p(salt_hex)) { if (! yubikey_hex_p(salt_hex)) {
D(("Invalid salt hex input : %s", salt_hex)); D(debug_file, "Invalid salt hex input : %s", salt_hex);
goto out; goto out;
} }
if(verbose) { if(verbose) {
D(("Challenge: %s, hashed response: %s, salt: %s, iterations: %d, slot: %d", D(debug_file, "Challenge: %s, hashed response: %s, salt: %s, iterations: %d, slot: %d",
challenge_hex, response_hex, salt_hex, iterations, slot)); challenge_hex, response_hex, salt_hex, iterations, slot);
} }
yubikey_hex_decode(state->salt, salt_hex, sizeof(state->salt)); yubikey_hex_decode(state->salt, salt_hex, sizeof(state->salt));
@ -369,12 +368,12 @@ load_chalresp_state(FILE *f, CR_STATE *state, bool verbose)
rewind(f); rewind(f);
r = fscanf(f, "v1:%126[0-9a-z]:%40[0-9a-z]:%d", challenge_hex, response_hex, &slot); r = fscanf(f, "v1:%126[0-9a-z]:%40[0-9a-z]:%d", challenge_hex, response_hex, &slot);
if (r != 3) { if (r != 3) {
D(("Could not parse contents of chalresp_state file (%i)", r)); D(debug_file, "Could not parse contents of chalresp_state file (%i)", r);
goto out; goto out;
} }
if (verbose) { if (verbose) {
D(("Challenge: %s, expected response: %s, slot: %d", challenge_hex, response_hex, slot)); D(debug_file, "Challenge: %s, expected response: %s, slot: %d", challenge_hex, response_hex, slot);
} }
iterations = CR_DEFAULT_ITERATIONS; iterations = CR_DEFAULT_ITERATIONS;
@ -384,17 +383,17 @@ load_chalresp_state(FILE *f, CR_STATE *state, bool verbose)
if (! yubikey_hex_p(challenge_hex)) { if (! yubikey_hex_p(challenge_hex)) {
D(("Invalid challenge hex input : %s", challenge_hex)); D(debug_file, "Invalid challenge hex input : %s", challenge_hex);
goto out; goto out;
} }
if (! yubikey_hex_p(response_hex)) { if (! yubikey_hex_p(response_hex)) {
D(("Invalid expected response hex input : %s", response_hex)); D(debug_file, "Invalid expected response hex input : %s", response_hex);
goto out; goto out;
} }
if (slot != 1 && slot != 2) { if (slot != 1 && slot != 2) {
D(("Invalid slot input : %i", slot)); D(debug_file, "Invalid slot input : %i", slot);
goto out; goto out;
} }

27
util.h
View File

@ -38,23 +38,14 @@
#include <stdint.h> #include <stdint.h>
#include <pwd.h> #include <pwd.h>
#if defined(DEBUG_PAM) #define D(file, x...) do { \
# if defined(HAVE_SECURITY__PAM_MACROS_H) fprintf (file, "debug: %s:%d (%s): ", __FILE__, __LINE__, __FUNCTION__); \
# define DEBUG fprintf (file, x); \
# include <security/_pam_macros.h> fprintf (file, "\n"); \
# else } while (0)
# define D(x) do { \
printf ("debug: %s:%d (%s): ", __FILE__, __LINE__, __FUNCTION__); \
printf x; \
printf ("\n"); \
} while (0)
# endif /* HAVE_SECURITY__PAM_MACROS_H */
#else
# define D(x)
#endif /* DEBUG_PAM */
int get_user_cfgfile_path(const char *common_path, const char *filename, const struct passwd *user, char **fn); int get_user_cfgfile_path(const char *common_path, const char *filename, const struct passwd *user, char **fn);
int check_user_token(const char *authfile, const char *username, const char *otp_id, int verbose); int check_user_token(const char *authfile, const char *username, const char *otp_id, int verbose, FILE *debug_file);
#if HAVE_CR #if HAVE_CR
#include <ykcore.h> #include <ykcore.h>
@ -83,13 +74,13 @@ typedef struct chalresp_state CR_STATE;
int generate_random(void *buf, int len); int generate_random(void *buf, int len);
int get_user_challenge_file(YK_KEY *yk, const char *chalresp_path, const struct passwd *user, char **fn); int get_user_challenge_file(YK_KEY *yk, const char *chalresp_path, const struct passwd *user, char **fn, FILE *debug_file);
int load_chalresp_state(FILE *f, CR_STATE *state, bool verbose); int load_chalresp_state(FILE *f, CR_STATE *state, bool verbose, FILE *debug_file);
int write_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, FILE *debug_file);
int challenge_response(YK_KEY *yk, int slot, int challenge_response(YK_KEY *yk, int slot,
char *challenge, unsigned int len, char *challenge, unsigned int len,
bool hmac, bool may_block, bool verbose, bool hmac, bool may_block, bool verbose,

View File

@ -41,7 +41,6 @@
#include <ykpers.h> #include <ykpers.h>
#undef DEBUG_PAM
#include "util.h" #include "util.h"
#define ACTION_ADD_HMAC_CHALRESP "add_hmac_chalresp" #define ACTION_ADD_HMAC_CHALRESP "add_hmac_chalresp"
@ -185,7 +184,7 @@ do_add_hmac_chalresp(YK_KEY *yk, uint8_t slot, bool verbose, char *output_dir, u
} }
} }
if (! get_user_challenge_file(yk, output_dir, p, &fn)) { if (! get_user_challenge_file(yk, output_dir, p, &fn, stdout)) {
fprintf (stderr, "Failed getting chalresp state filename\n"); fprintf (stderr, "Failed getting chalresp state filename\n");
goto out; goto out;
} }
@ -294,7 +293,7 @@ main(int argc, char **argv)
if (! init_yubikey (&yk)) if (! init_yubikey (&yk))
goto err; goto err;
if (! check_firmware_version(yk, verbose, false)) if (! check_firmware_version(yk, verbose, false, stdout))
goto err; goto err;
if (! do_add_hmac_chalresp (yk, slot, verbose, output_dir, iterations, &exit_code)) if (! do_add_hmac_chalresp (yk, slot, verbose, output_dir, iterations, &exit_code))