static int do_add_hmac_chalresp(YK_KEY *yk, uint8_t slot, bool verbose, char *output_dir, unsigned int iterations, int *exit_code) { char buf[CR_RESPONSE_SIZE + 16]; CR_STATE state; int ret = 0; unsigned int response_len; char *fn; struct passwd *p; FILE *f = NULL; struct stat st; state.iterations = iterations; state.slot = slot; *exit_code = 1; p = getpwuid (getuid ()); if (! p) { fprintf (stderr, "Who am I???"); goto out; } /* * Create default output directory for the user */ if (!output_dir){ char fullpath[256]; snprintf(fullpath, 256,"%s/.yubico",p->pw_dir); //check if directory exists if (stat(fullpath,&st)!=0 ){ if(mkdir(fullpath, S_IRWXU)==-1){ fprintf(stderr, "Failed creating directory '%s' :%s\n", fullpath, strerror(errno)); } if(verbose){ printf("Directory %s created successfully.\n", fullpath); } } else{ if(!S_ISDIR(st.st_mode)){ fprintf(stderr, "Destination %s already exist and is not a directory.\n", fullpath); goto out; } } } if (! get_user_challenge_file(yk, output_dir, p, &fn)) { fprintf (stderr, "Failed getting chalresp state filename\n"); goto out; } if (stat(fn, &st) == 0) { fprintf(stderr, "File %s already exists, refusing to overwrite.\n", fn); goto out; } if (generate_random(state.challenge, CR_CHALLENGE_SIZE)) { fprintf (stderr, "FAILED getting %i bytes of random data\n", CR_CHALLENGE_SIZE); goto out; } state.challenge_len = CR_CHALLENGE_SIZE; if (! challenge_response(yk, state.slot, state.challenge, CR_CHALLENGE_SIZE, true, true, verbose, buf, sizeof(buf), &response_len)) goto out; /* Make sure we get different responses for different challenges There is a firmware bug in YubiKey 2.2 that makes it issue same response for all challenges unless HMAC_LT64 is set. */ { char buf2[CR_RESPONSE_SIZE + 16]; char challenge[CR_CHALLENGE_SIZE]; if (generate_random(challenge, CR_CHALLENGE_SIZE)) { fprintf (stderr, "FAILED getting %i bytes of random data\n", CR_CHALLENGE_SIZE); goto out; } if (! challenge_response(yk, state.slot, challenge, CR_CHALLENGE_SIZE, true, true, verbose, buf2, sizeof(buf2), &response_len)) goto out; if (memcmp(buf, buf2, response_len) == 0) { fprintf (stderr, "FAILED YubiKey is outputting the same response for different challenges." "Make sure you configure the key with the option HMAC_LT64.\n"); goto out; } } if (response_len > sizeof (state.response)) { fprintf (stderr, "Got too long response ??? (%u/%lu)", response_len, (unsigned long) sizeof(state.response)); goto out; } memcpy (state.response, buf, response_len); state.response_len = response_len; umask(077); f = fopen (fn, "w"); if (! f) { fprintf (stderr, "Failed opening '%s' for writing : %s\n", fn, strerror (errno)); goto out; } if (! write_chalresp_state (f, &state)) goto out; printf ("Stored initial challenge and expected response in '%s'.\n", fn); *exit_code = 0; ret = 1; out: if (f) fclose (f); return ret; }
int do_add_hmac_chalresp(YK_KEY *yk, uint8_t slot, bool verbose, char *output_dir, int *exit_code) { char buf[CR_RESPONSE_SIZE + 16]; CR_STATE state; unsigned int flags = 0; int ret = 0; unsigned int response_len; char *fn; struct passwd *p; FILE *f = NULL; state.slot = slot; flags |= YK_FLAG_MAYBLOCK; *exit_code = 1; p = getpwuid (getuid ()); if (! p) { fprintf (stderr, "Who am I???"); goto out; } if (! get_user_challenge_file(yk, output_dir, p->pw_name, &fn)) { fprintf (stderr, "Failed getting chalresp state filename\n"); goto out; } if (generate_random(state.challenge, CR_CHALLENGE_SIZE)) { fprintf (stderr, "FAILED getting %i bytes of random data\n", CR_CHALLENGE_SIZE); goto out; } state.challenge_len = CR_CHALLENGE_SIZE; if (! challenge_response(yk, state.slot, state.challenge, CR_CHALLENGE_SIZE, true, flags, verbose, buf, sizeof(buf), &response_len)) goto out; if (response_len > sizeof (state.response)) { fprintf (stderr, "Got too long response ??? (%i/%i)", response_len, sizeof(state.response)); goto out; } memcpy (state.response, buf, response_len); state.response_len = response_len; f = fopen (fn, "w"); if (! f) { fprintf (stderr, "Failed opening '%s' for writing : %s\n", fn, strerror (errno)); goto out; } if (! write_chalresp_state (f, &state)) goto out; printf ("Stored initial challenge and expected response in '%s'.\n", fn); *exit_code = 0; ret = 1; out: if (f) fclose (f); return ret; }
int do_add_hmac_chalresp(YK_KEY *yk, uint8_t slot, bool verbose, char *output_dir, int *exit_code) { char buf[CR_RESPONSE_SIZE + 16]; CR_STATE state; unsigned int flags = 0; int ret = 0; unsigned int response_len; char *fn; struct passwd *p; FILE *f = NULL; state.slot = slot; flags |= YK_FLAG_MAYBLOCK; *exit_code = 1; p = getpwuid (getuid ()); if (! p) { fprintf (stderr, "Who am I???"); goto out; } if (! get_user_challenge_file(yk, output_dir, p->pw_name, &fn)) { fprintf (stderr, "Failed getting chalresp state filename\n"); goto out; } if (generate_random(state.challenge, CR_CHALLENGE_SIZE)) { fprintf (stderr, "FAILED getting %i bytes of random data\n", CR_CHALLENGE_SIZE); goto out; } state.challenge_len = CR_CHALLENGE_SIZE; if (! challenge_response(yk, state.slot, state.challenge, CR_CHALLENGE_SIZE, true, flags, verbose, buf, sizeof(buf), &response_len)) goto out; /* Make sure we get different responses for different challenges There is a firmware bug in YubiKey 2.2 that makes it issue same response for all challenges unless HMAC_LT64 is set. */ { char buf2[CR_RESPONSE_SIZE + 16]; char challenge[CR_CHALLENGE_SIZE]; CR_STATE state2; if (generate_random(challenge, CR_CHALLENGE_SIZE)) { fprintf (stderr, "FAILED getting %i bytes of random data\n", CR_CHALLENGE_SIZE); goto out; } if (! challenge_response(yk, state.slot, challenge, CR_CHALLENGE_SIZE, true, flags, verbose, buf2, sizeof(buf2), &response_len)) goto out; if (memcmp(buf, buf2, response_len) == 0) { fprintf (stderr, "FAILED YubiKey is outputting the same response for different challenges." "Make sure you configure the key with the option HMAC_LT64.\n"); goto out; } } if (response_len > sizeof (state.response)) { fprintf (stderr, "Got too long response ??? (%u/%lu)", response_len, (unsigned long) sizeof(state.response)); goto out; } memcpy (state.response, buf, response_len); state.response_len = response_len; f = fopen (fn, "w"); if (! f) { fprintf (stderr, "Failed opening '%s' for writing : %s\n", fn, strerror (errno)); goto out; } if (! write_chalresp_state (f, &state)) goto out; printf ("Stored initial challenge and expected response in '%s'.\n", fn); *exit_code = 0; ret = 1; out: if (f) fclose (f); return ret; }
static int do_challenge_response(pam_handle_t *pamh, struct cfg *cfg, const char *username) { char *userfile = NULL, *tmpfile = NULL; FILE *f = NULL; unsigned char buf[CR_RESPONSE_SIZE + 16], response_hex[CR_RESPONSE_SIZE * 2 + 1]; int ret; unsigned int flags = 0; unsigned int response_len = 0; unsigned int expect_bytes = 0; YK_KEY *yk = NULL; CR_STATE state; int len; char *errstr = NULL; ret = PAM_AUTH_ERR; flags |= YK_FLAG_MAYBLOCK; if (! init_yubikey(&yk)) { D(("Failed initializing YubiKey")); goto out; } if (! check_firmware_version(yk, false, true)) { D(("YubiKey does not support Challenge-Response (version 2.2 required)")); goto out; } if (! get_user_challenge_file (yk, cfg->chalresp_path, username, &userfile)) { D(("Failed getting user challenge file for user %s", username)); goto out; } DBG(("Loading challenge from file %s", userfile)); /* XXX should drop root privileges before opening file in user's home directory */ f = fopen(userfile, "r"); if (! load_chalresp_state(f, &state)) goto out; if (fclose(f) < 0) { f = NULL; goto out; } if (! challenge_response(yk, state.slot, state.challenge, state.challenge_len, true, flags, false, buf, sizeof(buf), &response_len)) { D(("Challenge-response FAILED")); goto out; } /* * Check YubiKey response against the expected response */ yubikey_hex_encode(response_hex, (char *)buf, response_len); if (memcmp(buf, state.response, response_len) == 0) { ret = PAM_SUCCESS; } else { D(("Unexpected C/R response : %s", response_hex)); goto out; } DBG(("Got the expected response, generating new challenge (%i bytes).", CR_CHALLENGE_SIZE)); errstr = "Error generating new challenge, please check syslog or contact your system administrator"; if (generate_random(state.challenge, sizeof(state.challenge))) { D(("Failed generating new challenge!")); goto out; } errstr = "Error communicating with Yubikey, please check syslog or contact your system administrator"; if (! challenge_response(yk, state.slot, state.challenge, CR_CHALLENGE_SIZE, true, flags, false, buf, sizeof(buf), &response_len)) { D(("Second challenge-response FAILED")); goto out; } /* the yk_* functions leave 'junk' in errno */ errno = 0; /* * 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; /* Write out the new file */ tmpfile = malloc(strlen(userfile) + 1 + 4); if (! tmpfile) goto out; strcpy(tmpfile, userfile); strcat(tmpfile, ".tmp"); f = fopen(tmpfile, "w"); if (! f) goto out; errstr = "Error updating Yubikey challenge, please check syslog or contact your system administrator"; if (! write_chalresp_state (f, &state)) goto out; if (fclose(f) < 0) { f = NULL; goto out; } f = NULL; if (rename(tmpfile, userfile) < 0) { goto out; } DBG(("Challenge-response success!")); errstr = NULL; out: if (yk_errno) { if (yk_errno == YK_EUSBERR) { syslog(LOG_ERR, "USB error: %s", yk_usb_strerror()); D(("USB error: %s", yk_usb_strerror())); } else { syslog(LOG_ERR, "Yubikey core error: %s", yk_strerror(yk_errno)); D(("Yubikey core error: %s", yk_strerror(yk_errno))); } } if (errstr) display_error(pamh, errstr); if (errno) { syslog(LOG_ERR, "Challenge response failed: %s", strerror(errno)); D(("Challenge response failed: %s", strerror(errno))); } if (yk) yk_close_key(yk); yk_release(); if (f) fclose(f); free(userfile); free(tmpfile); return ret; }
static int do_challenge_response(pam_handle_t *pamh, struct cfg *cfg, const char *username) { char *userfile = NULL, *tmpfile = NULL; FILE *f = NULL; char buf[CR_RESPONSE_SIZE + 16], response_hex[CR_RESPONSE_SIZE * 2 + 1]; int ret, fd; unsigned int flags = 0; unsigned int response_len = 0; YK_KEY *yk = NULL; CR_STATE state; char *errstr = NULL; struct passwd *p; struct stat st; ret = PAM_AUTH_ERR; flags |= YK_FLAG_MAYBLOCK; if (! init_yubikey(&yk)) { D(("Failed initializing YubiKey")); goto out; } if (! check_firmware_version(yk, false, true)) { D(("YubiKey does not support Challenge-Response (version 2.2 required)")); goto out; } if (! get_user_challenge_file (yk, cfg->chalresp_path, username, &userfile)) { D(("Failed getting user challenge file for user %s", username)); goto out; } DBG(("Loading challenge from file %s", userfile)); p = getpwnam (username); if (p == NULL) { DBG (("getpwnam: %s", strerror(errno))); goto out; } /* Drop privileges before opening user file. */ if (drop_privileges(p, pamh) < 0) { D (("could not drop privileges")); goto out; } fd = open(userfile, O_RDONLY, 0); if (fd < 0) { DBG (("Cannot open file: %s (%s)", userfile, strerror(errno))); goto out; } if (fstat(fd, &st) < 0) { DBG (("Cannot stat file: %s (%s)", userfile, strerror(errno))); close(fd); goto out; } if (!S_ISREG(st.st_mode)) { DBG (("%s is not a regular file", userfile)); close(fd); goto out; } f = fdopen(fd, "r"); if (f == NULL) { DBG (("fdopen: %s", strerror(errno))); close(fd); goto out; } if (! load_chalresp_state(f, &state, cfg->debug)) goto out; if (fclose(f) < 0) { f = NULL; goto out; } f = NULL; if (restore_privileges(pamh) < 0) { DBG (("could not restore privileges")); goto out; } if (! challenge_response(yk, state.slot, state.challenge, state.challenge_len, true, flags, false, buf, sizeof(buf), &response_len)) { D(("Challenge-response FAILED")); goto out; } /* * Check YubiKey response against the expected response */ yubikey_hex_encode(response_hex, buf, response_len); if (memcmp(buf, state.response, response_len) == 0) { ret = PAM_SUCCESS; } else { D(("Unexpected C/R response : %s", response_hex)); goto out; } DBG(("Got the expected response, generating new challenge (%i bytes).", CR_CHALLENGE_SIZE)); errstr = "Error generating new challenge, please check syslog or contact your system administrator"; if (generate_random(state.challenge, sizeof(state.challenge))) { D(("Failed generating new challenge!")); goto out; } errstr = "Error communicating with Yubikey, please check syslog or contact your system administrator"; if (! challenge_response(yk, state.slot, state.challenge, CR_CHALLENGE_SIZE, true, flags, false, buf, sizeof(buf), &response_len)) { D(("Second challenge-response FAILED")); goto out; } /* There is a bug that makes the YubiKey 2.2 send the same response for all challenges unless HMAC_LT64 is set, check for that here */ if (memcmp(buf, state.response, response_len) == 0) { errstr = "Same response for second challenge, YubiKey should be reconfigured with the option HMAC_LT64"; goto out; } /* the yk_* functions leave 'junk' in errno */ errno = 0; /* * 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 ??? (%u/%lu)", response_len, (unsigned long) sizeof(state.response))); goto out; } memcpy (state.response, buf, response_len); state.response_len = response_len; /* Drop privileges before creating new challenge file. */ if (drop_privileges(p, pamh) < 0) { D (("could not drop privileges")); goto out; } /* Write out the new file */ tmpfile = malloc(strlen(userfile) + 1 + 4); if (! tmpfile) goto out; strcpy(tmpfile, userfile); strcat(tmpfile, ".tmp"); fd = open(tmpfile, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (fd < 0) { DBG (("Cannot open file: %s (%s)", tmpfile, strerror(errno))); goto out; } f = fdopen(fd, "w"); if (! f) { close(fd); goto out; } errstr = "Error updating Yubikey challenge, please check syslog or contact your system administrator"; if (! write_chalresp_state (f, &state)) goto out; if (fclose(f) < 0) { f = NULL; goto out; } f = NULL; if (rename(tmpfile, userfile) < 0) { goto out; } if (restore_privileges(pamh) < 0) { DBG (("could not restore privileges")); goto out; } DBG(("Challenge-response success!")); errstr = NULL; errno = 0; out: if (yk_errno) { if (yk_errno == YK_EUSBERR) { syslog(LOG_ERR, "USB error: %s", yk_usb_strerror()); D(("USB error: %s", yk_usb_strerror())); } else { syslog(LOG_ERR, "Yubikey core error: %s", yk_strerror(yk_errno)); D(("Yubikey core error: %s", yk_strerror(yk_errno))); } } if (errstr) display_error(pamh, errstr); if (errno) { syslog(LOG_ERR, "Challenge response failed: %s", strerror(errno)); D(("Challenge response failed: %s", strerror(errno))); } if (yk) yk_close_key(yk); yk_release(); if (f) fclose(f); free(userfile); free(tmpfile); return ret; }