void nt_challenge_response(const u8 *challenge, const u8 *password, size_t password_len, u8 *response) { u8 password_hash[16]; nt_password_hash(password, password_len, password_hash); challenge_response(challenge, password_hash, response); }
/** * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5 * @challenge: 8-octet Challenge (IN) * @password: 0-to-256-unicode-char Password (IN; ASCII) * @password_len: Length of password * @response: 24-octet Response (OUT) * Returns: 0 on success, -1 on failure */ int nt_challenge_response(const u8 *challenge, const u8 *password, size_t password_len, u8 *response) { u8 password_hash[16]; if (nt_password_hash(password, password_len, password_hash)) return -1; challenge_response(challenge, password_hash, response); return 0; }
void generate_nt_response_pwhash(const u8 *auth_challenge, const u8 *peer_challenge, const u8 *username, size_t username_len, const u8 *password_hash, u8 *response) { u8 challenge[8]; challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge); challenge_response(challenge, password_hash, response); }
void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, const u8 *username, size_t username_len, const u8 *password, size_t password_len, u8 *response) { u8 challenge[8]; u8 password_hash[16]; challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge); nt_password_hash(password, password_len, password_hash); challenge_response(challenge, password_hash, response); }
/** * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1 * @auth_challenge: 16-octet AuthenticatorChallenge (IN) * @peer_challenge: 16-octet PeerChallenge (IN) * @username: 0-to-256-char UserName (IN) * @username_len: Length of username * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @response: 24-octet Response (OUT) * Returns: 0 on success, -1 on failure */ int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, const u8 *username, size_t username_len, const u8 *password, size_t password_len, u8 *response) { u8 challenge[8]; u8 password_hash[16]; if (challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge) || nt_password_hash(password, password_len, password_hash)) return -1; challenge_response(challenge, password_hash, response); return 0; }
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; }
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; }
int nterfacer_line_event(struct esocket *sock, char *newline) { struct sconnect *socket = sock->tag; char *response, *theirnonceh = NULL, *theirivh = NULL; unsigned char theirnonce[16], theiriv[16]; int number, reason; switch(socket->status) { case SS_IDLE: if(strcasecmp(newline, ANTI_FULL_VERSION)) { nterface_log(nrl, NL_INFO, "Protocol mismatch from %s: %s", socket->permit->hostname->content, newline); return 1; } else { unsigned char challenge[32]; char ivhex[16 * 2 + 1], noncehex[16 * 2 + 1]; if(!get_entropy(challenge, 32) || !get_entropy(socket->iv, 16)) { nterface_log(nrl, NL_ERROR, "Unable to open challenge/IV entropy bin!"); return 1; } int_to_hex(challenge, socket->challenge, 32); int_to_hex(socket->iv, ivhex, 16); memcpy(socket->response, challenge_response(socket->challenge, socket->permit->password->content), sizeof(socket->response)); socket->response[sizeof(socket->response) - 1] = '\0'; /* just in case */ socket->status = SS_VERSIONED; if(!generate_nonce(socket->ournonce, 1)) { nterface_log(nrl, NL_ERROR, "Unable to generate nonce!"); return 1; } int_to_hex(socket->ournonce, noncehex, 16); if(esocket_write_line(sock, "%s %s %s", socket->challenge, ivhex, noncehex)) return BUF_ERROR; return 0; } break; case SS_VERSIONED: for(response=newline;*response;response++) { if((*response == ' ') && (*(response + 1))) { *response = '\0'; theirivh = response + 1; break; } } if(theirivh) { for(response=theirivh;*response;response++) { if((*response == ' ') && (*(response + 1))) { *response = '\0'; theirnonceh = response + 1; break; } } } if(!theirivh || (strlen(theirivh) != 32) || !hex_to_int(theirivh, theiriv, sizeof(theiriv)) || !theirnonceh || (strlen(theirnonceh) != 32) || !hex_to_int(theirnonceh, theirnonce, sizeof(theirnonce))) { nterface_log(nrl, NL_INFO, "Protocol error drop: %s", socket->permit->hostname->content); return 1; } if(!memcmp(socket->ournonce, theirnonce, sizeof(theirnonce))) { nterface_log(nrl, NL_INFO, "Bad nonce drop: %s", socket->permit->hostname->content); return 1; } if(!strncasecmp(newline, socket->response, sizeof(socket->response))) { unsigned char theirkey[32], ourkey[32]; derive_key(ourkey, socket->permit->password->content, socket->challenge, socket->ournonce, theirnonce, (unsigned char *)"SERVER", 6); derive_key(theirkey, socket->permit->password->content, socket->response, theirnonce, socket->ournonce, (unsigned char *)"CLIENT", 6); nterface_log(nrl, NL_INFO, "Authed: %s", socket->permit->hostname->content); socket->status = SS_AUTHENTICATED; switch_buffer_mode(sock, ourkey, socket->iv, theirkey, theiriv); if(esocket_write_line(sock, "Oauth")) return BUF_ERROR; } else { nterface_log(nrl, NL_INFO, "Bad CR drop: %s", socket->permit->hostname->content); return 1; } break; case SS_AUTHENTICATED: nterface_log(nrl, NL_INFO|NL_LOG_ONLY, "L(%s): %s", socket->permit->hostname->content, newline); reason = nterfacer_new_rline(newline, sock, &number); if(reason) { if(reason == RE_SOCKET_ERROR) return BUF_ERROR; if(reason != RE_BAD_LINE) { if(esocket_write_line(sock, "%d,E%d,%s", number, reason, request_error(reason))) return BUF_ERROR; return 0; } else { return 1; } } break; } return 0; }
static struct wpabuf * eap_leap_process_request(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_leap_data *data = priv; struct wpabuf *resp; const u8 *pos, *challenge, *identity, *password; u8 challenge_len, *rpos; size_t identity_len, password_len, len; int pwhash; wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request"); identity = eap_get_config_identity(sm, &identity_len); password = eap_get_config_password2(sm, &password_len, &pwhash); if (identity == NULL || password == NULL) return NULL; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len); if (pos == NULL || len < 3) { wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame"); ret->ignore = TRUE; return NULL; } if (*pos != LEAP_VERSION) { wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " "%d", *pos); ret->ignore = TRUE; return NULL; } pos++; pos++; /* skip unused byte */ challenge_len = *pos++; if (challenge_len != LEAP_CHALLENGE_LEN || challenge_len > len - 3) { wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge " "(challenge_len=%d reqDataLen=%lu)", challenge_len, (unsigned long) wpabuf_len(reqData)); ret->ignore = TRUE; return NULL; } challenge = pos; os_memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP", challenge, LEAP_CHALLENGE_LEN); wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response"); resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP, 3 + LEAP_RESPONSE_LEN + identity_len, EAP_CODE_RESPONSE, eap_get_id(reqData)); if (resp == NULL) return NULL; wpabuf_put_u8(resp, LEAP_VERSION); wpabuf_put_u8(resp, 0); /* unused */ wpabuf_put_u8(resp, LEAP_RESPONSE_LEN); rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN); if (pwhash) challenge_response(challenge, password, rpos); else nt_challenge_response(challenge, password, password_len, rpos); os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response", rpos, LEAP_RESPONSE_LEN); wpabuf_put_data(resp, identity, identity_len); data->state = LEAP_WAIT_SUCCESS; return resp; }
static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_leap_data *data = priv; const u8 *pos, *password; u8 response_len, pw_hash[16], pw_hash_hash[16], expected[LEAP_RESPONSE_LEN]; size_t password_len, len; int pwhash; wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response"); password = eap_get_config_password2(sm, &password_len, &pwhash); if (password == NULL) return NULL; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len); if (pos == NULL || len < 3) { wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame"); ret->ignore = TRUE; return NULL; } if (*pos != LEAP_VERSION) { wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " "%d", *pos); ret->ignore = TRUE; return NULL; } pos++; pos++; /* skip unused byte */ response_len = *pos++; if (response_len != LEAP_RESPONSE_LEN || response_len > len - 3) { wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response " "(response_len=%d reqDataLen=%lu)", response_len, (unsigned long) wpabuf_len(reqData)); ret->ignore = TRUE; return NULL; } wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP", pos, LEAP_RESPONSE_LEN); os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN); if (pwhash) { if (hash_nt_password_hash(password, pw_hash_hash)) { ret->ignore = TRUE; return NULL; } } else { if (nt_password_hash(password, password_len, pw_hash) || hash_nt_password_hash(pw_hash, pw_hash_hash)) { ret->ignore = TRUE; return NULL; } } challenge_response(data->ap_challenge, pw_hash_hash, expected); ret->methodState = METHOD_DONE; ret->allowNotifications = FALSE; if (os_memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) { wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid " "response - authentication failed"); wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP", expected, LEAP_RESPONSE_LEN); ret->decision = DECISION_FAIL; return NULL; } ret->decision = DECISION_UNCOND_SUCC; /* LEAP is somewhat odd method since it sends EAP-Success in the middle * of the authentication. Use special variable to transit EAP state * machine to SUCCESS state. */ sm->leap_done = TRUE; data->state = LEAP_DONE; /* No more authentication messages expected; AP will send EAPOL-Key * frames if encryption is enabled. */ return NULL; }
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; 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; }
static u8 * eap_leap_process_response(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { struct eap_leap_data *data = priv; struct wpa_ssid *config = eap_get_config(sm); const struct eap_hdr *resp; const u8 *pos; u8 response_len, pw_hash[16], pw_hash_hash[16], expected[LEAP_RESPONSE_LEN]; wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response"); resp = (const struct eap_hdr *) reqData; pos = (const u8 *) (resp + 1); if (reqDataLen < sizeof(*resp) + 4 || *pos != EAP_TYPE_LEAP) { wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame"); ret->ignore = TRUE; return NULL; } pos++; if (*pos != LEAP_VERSION) { wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " "%d", *pos); ret->ignore = TRUE; return NULL; } pos++; pos++; /* skip unused byte */ response_len = *pos++; if (response_len != LEAP_RESPONSE_LEN || response_len > reqDataLen - sizeof(*resp) - 4) { wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response " "(response_len=%d reqDataLen=%lu", response_len, (unsigned long) reqDataLen); ret->ignore = TRUE; return NULL; } wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP", pos, LEAP_RESPONSE_LEN); memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN); nt_password_hash(config->password, config->password_len, pw_hash); hash_nt_password_hash(pw_hash, pw_hash_hash); challenge_response(data->ap_challenge, pw_hash_hash, expected); ret->methodState = METHOD_DONE; ret->allowNotifications = FALSE; if (memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) { wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid " "response - authentication failed"); wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP", expected, LEAP_RESPONSE_LEN); ret->decision = DECISION_FAIL; return NULL; } ret->decision = DECISION_UNCOND_SUCC; /* LEAP is somewhat odd method since it sends EAP-Success in the middle * of the authentication. Use special variable to transit EAP state * machine to SUCCESS state. */ sm->leap_done = TRUE; data->state = LEAP_DONE; /* No more authentication messages expected; AP will send EAPOL-Key * frames if encryption is enabled. */ return NULL; }