Beispiel #1
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;
}
Beispiel #2
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;
}
Beispiel #3
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;

  /* 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;
}
Beispiel #4
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;
}
Beispiel #5
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;
}