예제 #1
0
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);
}
예제 #2
0
/**
 * 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;
}
예제 #3
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);
}
예제 #4
0
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);
}
예제 #5
0
/**
 * 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;
}
예제 #6
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;
}
예제 #7
0
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;
}
예제 #8
0
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;
}
예제 #9
0
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;
}
예제 #10
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;
}
예제 #11
0
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;
}
예제 #12
0
파일: ykpamcfg.c 프로젝트: axtl/yubico-pam
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;
}
예제 #13
0
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;
}
예제 #14
0
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;
}