Beispiel #1
0
/*
 * Extended API to validate an OTP (hexkey) using either the YubiCloud
 * validation service, or any other validation service.
 *
 * Special CURL settings can be achieved by passing a non-null ykc_in.
 *
 * Prepared to support HMAC validation of server responses using api_key.
 */
int
ykclient_verify_otp_v2 (ykclient_t *ykc_in,
			const char *yubikey_otp,
			unsigned int client_id,
			const char *hexkey,
			size_t urlcount,
			const char **urls,
			const char *api_key)
{
  ykclient_t *ykc;
  int ret;

  /* api_key is currently not used (placeholder argument) */
  if (api_key)
    return YKCLIENT_NOT_IMPLEMENTED;

  /* We currently only support 0 (for default YubiCloud URL) or 1 URL argument,
   * but this function is prepared to support all of Validation protocol 2.0,
   * which supports multiple parallell querys to multiple validation URLs.
   */
  if (urlcount > 1)
    return YKCLIENT_NOT_IMPLEMENTED;

  if (ykc_in == NULL)
    {
      ret = ykclient_init (&ykc);
      if (ret != YKCLIENT_OK)
	return ret;
    }
  else
    {
      ykc = ykc_in;
    }

  ykclient_set_client_hex (ykc, client_id, hexkey);

  if (urlcount == 1)
    ykclient_set_url_template (ykc, urls[0]);

  ret = ykclient_request (ykc, yubikey_otp);

  if (ykc_in == NULL)
    ykclient_done (&ykc);

  return ret;
}
Beispiel #2
0
void
test_v1_validation(int client_id, char *client_b64key)
{
  ykclient_t *ykc;
  int ret;

  TEST(("init self"));
  ret = ykclient_init (&ykc);
  printf ("ykclient_init (%d): %s\n", ret, ykclient_strerror (ret));
  assert(ret == YKCLIENT_OK);

  ykclient_set_url_template
    (ykc, "http://api.yubico.com/wsapi/verify?id=%d&otp=%s");

  TEST(("null client_id, expect REPLAYED_OTP"));
  ykclient_set_verify_signature(ykc, 0);
  ykclient_set_client (ykc, client_id, 0, NULL);

#ifndef TEST_WITHOUT_INTERNET
  ret = ykclient_request (ykc, "dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh");
  printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret));
  printf ("used url: %s\n", ykclient_get_last_url (ykc));
  assert(ret == YKCLIENT_REPLAYED_OTP);
#else
  printf ("Test SKIPPED\n");
#endif

  /* Test signed request. When signing requests to a v1 service, we must clear the nonce first. */

  TEST(("signed request, expect REPLAYED_OTP"));
  ykclient_set_verify_signature(ykc, 1);
  ykclient_set_client_b64 (ykc, client_id, client_b64key);
  ykclient_set_nonce(ykc, NULL);

#ifndef TEST_WITHOUT_INTERNET
  ret = ykclient_request (ykc, "dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh");
  printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret));
  printf ("used url: %s\n", ykclient_get_last_url (ykc));
  assert(ret == YKCLIENT_REPLAYED_OTP);
#else
  printf ("Test SKIPPED\n");
#endif

  ykclient_done (&ykc);
}
Beispiel #3
0
PAM_EXTERN int
pam_sm_authenticate (pam_handle_t * pamh,
                     int flags, int argc, const char **argv)
{
    int retval, rc;
    const char *user = NULL;
    const char *password = NULL;
    char otp[MAX_TOKEN_ID_LEN + TOKEN_OTP_LEN + 1] = { 0 };
    char otp_id[MAX_TOKEN_ID_LEN + 1] = { 0 };
    int password_len = 0;
    int skip_bytes = 0;
    int valid_token = 0;
    struct pam_conv *conv;
    struct pam_message *pmsg[1], msg[1];
    struct pam_response *resp;
    int nargs = 1;
    ykclient_t *ykc = NULL;
    struct cfg cfg_st;
    struct cfg *cfg = &cfg_st; /* for DBG macro */

    parse_cfg (flags, argc, argv, cfg);

    retval = pam_get_user (pamh, &user, NULL);
    if (retval != PAM_SUCCESS)
    {
        DBG (("get user returned error: %s", pam_strerror (pamh, retval)));
        goto done;
    }
    DBG (("get user returned: %s", user));

    if (cfg->mode == CHRESP) {
#if HAVE_LIBYKPERS_1
        return do_challenge_response(pamh, cfg, user);
#else
        DBG (("no support for challenge/response"));
        retval = PAM_AUTH_ERR;
        goto done;
#endif
    }

    if (cfg->try_first_pass || cfg->use_first_pass)
    {
        retval = pam_get_item (pamh, PAM_AUTHTOK, (const void **) &password);
        if (retval != PAM_SUCCESS)
        {
            DBG (("get password returned error: %s",
                  pam_strerror (pamh, retval)));
            goto done;
        }
        DBG (("get password returned: %s", password));
    }

    if (cfg->use_first_pass && password == NULL)
    {
        DBG (("use_first_pass set and no password, giving up"));
        retval = PAM_AUTH_ERR;
        goto done;
    }

    rc = ykclient_init (&ykc);
    if (rc != YKCLIENT_OK)
    {
        DBG (("ykclient_init() failed (%d): %s", rc, ykclient_strerror (rc)));
        retval = PAM_AUTHINFO_UNAVAIL;
        goto done;
    }

    rc = ykclient_set_client_b64 (ykc, cfg->client_id, cfg->client_key);
    if (rc != YKCLIENT_OK)
    {
        DBG (("ykclient_set_client_b64() failed (%d): %s",
              rc, ykclient_strerror (rc)));
        retval = PAM_AUTHINFO_UNAVAIL;
        goto done;
    }

    if (cfg->capath)
        ykclient_set_ca_path (ykc, cfg->capath);

    if (cfg->url)
        ykclient_set_url_template (ykc, cfg->url);

    if (password == NULL)
    {
        retval = pam_get_item (pamh, PAM_CONV, (const void **) &conv);
        if (retval != PAM_SUCCESS)
        {
            DBG (("get conv returned error: %s", pam_strerror (pamh, retval)));
            goto done;
        }

        pmsg[0] = &msg[0];
        {
            const char *query_template = "Yubikey for `%s': ";
            size_t len = strlen (query_template) + strlen (user);
            size_t wrote;

            msg[0].msg = malloc (len);
            if (!msg[0].msg)
            {
                retval = PAM_BUF_ERR;
                goto done;
            }

            wrote = snprintf ((char *) msg[0].msg, len, query_template, user);
            if (wrote < 0 || wrote >= len)
            {
                retval = PAM_BUF_ERR;
                goto done;
            }
        }
        msg[0].msg_style = cfg->verbose_otp ? PAM_PROMPT_ECHO_ON : PAM_PROMPT_ECHO_OFF;
        resp = NULL;

        retval = conv->conv (nargs, (const struct pam_message **) pmsg,
                             &resp, conv->appdata_ptr);

        free ((char *) msg[0].msg);

        if (retval != PAM_SUCCESS)
        {
            DBG (("conv returned error: %s", pam_strerror (pamh, retval)));
            goto done;
        }

        if (resp->resp == NULL)
        {
            DBG (("conv returned NULL passwd?"));
            goto done;
        }

        DBG (("conv returned %i bytes", strlen(resp->resp)));

        password = resp->resp;
    }

    password_len = strlen (password);
    if (password_len < (cfg->token_id_length + TOKEN_OTP_LEN))
    {
        DBG (("OTP too short to be considered : %i < %i", password_len, (cfg->token_id_length + TOKEN_OTP_LEN)));
        retval = PAM_AUTH_ERR;
        goto done;
    }

    /* In case the input was systempassword+YubiKeyOTP, we want to skip over
       "systempassword" when copying the token_id and OTP to separate buffers */
    skip_bytes = password_len - (cfg->token_id_length + TOKEN_OTP_LEN);

    DBG (("Skipping first %i bytes. Length is %i, token_id set to %i and token OTP always %i.",
          skip_bytes, password_len, cfg->token_id_length, TOKEN_OTP_LEN));

    /* Copy full YubiKey output (public ID + OTP) into otp */
    strncpy (otp, password + skip_bytes, sizeof (otp) - 1);
    /* Copy only public ID into otp_id. Destination buffer is zeroed. */
    strncpy (otp_id, password + skip_bytes, cfg->token_id_length);

    DBG (("OTP: %s ID: %s ", otp, otp_id));

    /* user entered their system password followed by generated OTP? */
    if (password_len > TOKEN_OTP_LEN + cfg->token_id_length)
    {
        char *onlypasswd = strdup (password);

        onlypasswd[password_len - (TOKEN_OTP_LEN + cfg->token_id_length)] = '\0';

        DBG (("Extracted a probable system password entered before the OTP - "
              "setting item PAM_AUTHTOK"));

        retval = pam_set_item (pamh, PAM_AUTHTOK, onlypasswd);
        free (onlypasswd);
        if (retval != PAM_SUCCESS)
        {
            DBG (("set_item returned error: %s", pam_strerror (pamh, retval)));
            goto done;
        }
    }
    else
        password = NULL;

    rc = ykclient_request (ykc, otp);

    DBG (("ykclient return value (%d): %s", rc,
          ykclient_strerror (rc)));

    switch (rc)
    {
    case YKCLIENT_OK:
        break;

    case YKCLIENT_BAD_OTP:
    case YKCLIENT_REPLAYED_OTP:
        retval = PAM_AUTH_ERR;
        goto done;

    default:
        retval = PAM_AUTHINFO_UNAVAIL;
        goto done;
    }

    /* authorize the user with supplied token id */
    if (cfg->ldapserver != NULL || cfg->ldap_uri != NULL)
        valid_token = authorize_user_token_ldap (cfg, user, otp_id);
    else
        valid_token = authorize_user_token (cfg, user, otp_id);

    if (valid_token == 0)
    {
        DBG (("Yubikey not authorized to login as user"));
        retval = PAM_AUTHINFO_UNAVAIL;
        goto done;
    }

    retval = PAM_SUCCESS;

done:
    if (ykc)
        ykclient_done (&ykc);
    if (cfg->alwaysok && retval != PAM_SUCCESS)
    {
        DBG (("alwaysok needed (otherwise return with %d)", retval));
        retval = PAM_SUCCESS;
    }
    DBG (("done. [%s]", pam_strerror (pamh, retval)));
    pam_set_data (pamh, "yubico_setcred_return", (void*) (intptr_t) retval, NULL);

    return retval;
}
Beispiel #4
0
int
main (void)
{
  int client_id = 1851;
  char client_key[] = {
    0xa0, 0x15, 0x5b, 0x36, 0xde, 0xc8, 0x65, 0xe8, 0x59, 0x19,
    0x1f, 0x7d, 0xae, 0xfa, 0xbc, 0x77, 0xa4, 0x59, 0xd4, 0x33
  };
  char *client_hexkey = "a0155b36dec865e859191f7daefabc77a459d433";
  char *client_b64key = "oBVbNt7IZehZGR99rvq8d6RZ1DM=";
  ykclient_t *ykc;
  int ret;

  curl_global_init(CURL_GLOBAL_ALL);

  TEST(("init self"));
  ret = ykclient_init (&ykc);
  printf ("ykclient_init (%d): %s\n", ret, ykclient_strerror (ret));
  assert(ret == YKCLIENT_OK);

  TEST(("null client_id, expect REPLAYED_OTP"));
  ykclient_set_verify_signature(ykc, 0);
  ykclient_set_client (ykc, client_id, 0, NULL);

#ifndef TEST_WITHOUT_INTERNET
  ret = ykclient_request (ykc, "dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh");
  printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret));
  printf ("used url: %s\n", ykclient_get_last_url (ykc));
  assert(ret == YKCLIENT_REPLAYED_OTP);
#else
  printf ("Test SKIPPED\n");
#endif

  TEST(("client_id set(20), correct client_key, expect REPLAYED_OTP"));
  ykclient_set_client (ykc, client_id, 20, client_key);

#ifndef TEST_WITHOUT_INTERNET
  ret = ykclient_request (ykc, "dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh");
  printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret));
  printf ("used url: %s\n", ykclient_get_last_url (ykc));
  assert (ret == YKCLIENT_REPLAYED_OTP);
#else
  printf ("Test SKIPPED\n");
#endif

  TEST(("wrong client_id set(10), correct client_key, expect BAD_SIGNATURE"));
  ykclient_set_client (ykc, client_id, 10, client_key);

#ifndef TEST_WITHOUT_INTERNET
  ret = ykclient_request (ykc, "dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh");
  printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret));
  printf ("used url: %s\n", ykclient_get_last_url (ykc));
  assert (ret == YKCLIENT_BAD_SIGNATURE);
#else
  printf ("Test SKIPPED\n");
#endif

  TEST(("invalid client_id set(a), correct client_key, expect HEX_DECODE_ERROR"));
  ret = ykclient_set_client_hex (ykc, client_id, "a");
  printf ("ykclient_set_client_hex (%d): %s\n", ret, ykclient_strerror (ret));
  assert (ret == YKCLIENT_HEX_DECODE_ERROR);

  TEST(("invalid client_id set(xx), correct client_key, expect HEX_DECODE_ERROR"));
  ret = ykclient_set_client_hex (ykc, client_id, "xx");
  printf ("ykclient_set_client_hex (%d): %s\n", ret, ykclient_strerror (ret));
  assert (ret == YKCLIENT_HEX_DECODE_ERROR);

  TEST(("hex client_id set, correct client_key, expect OK"));
  ret = ykclient_set_client_hex (ykc, client_id, client_hexkey);
  printf ("ykclient_set_client_hex (%d): %s\n", ret, ykclient_strerror (ret));
  assert (ret == YKCLIENT_OK);

#ifndef TEST_WITHOUT_INTERNET
  TEST(("validation request, expect REPLAYED_OTP"));
  ret = ykclient_request (ykc, "dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh");
  printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret));
  printf ("used url: %s\n", ykclient_get_last_url (ykc));
  assert (ret == YKCLIENT_REPLAYED_OTP);
#else
  printf ("Test SKIPPED\n");
#endif

  TEST(("set deadbeef client_id, expect OK"));
  ret = ykclient_set_client_hex (ykc, client_id, "deadbeef");
  printf ("ykclient_set_client_hex (%d): %s\n", ret, ykclient_strerror (ret));
  assert (ret == YKCLIENT_OK);

#ifndef TEST_WITHOUT_INTERNET
  TEST(("validation request, expect BAD_SIGNATURE"));
  ret = ykclient_request (ykc, "dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh");
  printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret));
  printf ("used url: %s\n", ykclient_get_last_url (ykc));
  assert (ret == YKCLIENT_BAD_SIGNATURE);
#else
  printf ("Test SKIPPED\n");
#endif

  TEST(("b64 set deadbeef client_id, expect OK"));
  ret = ykclient_set_client_b64 (ykc, client_id, "deadbeef");
  printf ("ykclient_set_client_b64 (%d): %s\n", ret, ykclient_strerror (ret));
  assert (ret == YKCLIENT_OK);

#ifndef TEST_WITHOUT_INTERNET
  /* When the server dislikes our signature, it will sign the response with a
     NULL key, so the API call will fail with BAD_SERVER_SIGNATURE even though
     the server returned status=BAD_SIGNATURE.
  */
  TEST(("validation request, expect BAD_SERVER_SIGNATURE"));
  ret = ykclient_request (ykc, "dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh");
  printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret));
  printf ("used url: %s\n", ykclient_get_last_url (ykc));
  assert (ret == YKCLIENT_BAD_SERVER_SIGNATURE);
#else
  printf ("Test SKIPPED\n");
#endif

#ifndef TEST_WITHOUT_INTERNET
  /* Now, disable our checking of the servers signature to get the error
     the server returned (server will use 00000 as key when signing this
     error response).
  */
  TEST(("validation request, expect BAD_SIGNATURE"));
  ykclient_set_verify_signature (ykc, 0);
  ret = ykclient_request (ykc, "dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh");
  printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret));
  printf ("used url: %s\n", ykclient_get_last_url (ykc));
  assert (ret == YKCLIENT_BAD_SIGNATURE);
#else
  printf ("Test SKIPPED\n");
#endif

  TEST(("b64 set client_b64key, expect OK"));
  ret = ykclient_set_client_b64 (ykc, client_id, client_b64key);
  printf ("ykclient_set_client_b64 (%d): %s\n", ret, ykclient_strerror (ret));
  assert (ret == YKCLIENT_OK);

#ifndef TEST_WITHOUT_INTERNET
  TEST(("validation request, expect REPLAYED_OTP"));
  ret = ykclient_request (ykc, "dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh");
  printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret));
  printf ("used url: %s\n", ykclient_get_last_url (ykc));
  assert (ret == YKCLIENT_REPLAYED_OTP);
#else
  printf ("Test SKIPPED\n");
#endif

  TEST(("set WS 2.0 URL template"));
  /* Set one URL and run tests with that. */
  ykclient_set_url_template
    (ykc, "http://api.yubico.com/wsapi/2.0/verify?id=%d&otp=%s");

#ifndef TEST_WITHOUT_INTERNET
  TEST(("validation request, expect REPLAYED_OTP"));
  ret = ykclient_request (ykc, "dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh");
  printf ("yubikey_request (%d): %s\n", ret, ykclient_strerror (ret));
  printf ("used url: %s\n", ykclient_get_last_url (ykc));
  assert (ret == YKCLIENT_REPLAYED_OTP);
#else
  printf ("Test SKIPPED\n");
#endif

  ykclient_set_verify_signature(ykc, 1);

  TEST(("validation request with valid signature, expect REPLAYED_OTP"));
  // Check a genuine signature.
  ykclient_set_client (ykc, client_id, 20, client_key);
#ifndef TEST_WITHOUT_INTERNET
  ret = ykclient_request (ykc, "dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh");
  printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret));
  printf ("used url: %s\n", ykclient_get_last_url (ykc));
  assert (ret == YKCLIENT_REPLAYED_OTP);
#else
  printf ("Test SKIPPED\n");
#endif

  TEST(("validation request with bad key, expect YKCLIENT_BAD_SERVER_SIGNATURE"));
  // Check a genuine signature with a truncated key.
  ykclient_set_client (ykc, client_id, 10, client_key);
#ifndef TEST_WITHOUT_INTERNET
  ret = ykclient_request (ykc, "dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh");
  printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret));
  printf ("used url: %s\n", ykclient_get_last_url (ykc));
  assert (ret == YKCLIENT_BAD_SERVER_SIGNATURE);
#else
  printf ("Test SKIPPED\n");
#endif

  TEST(("Set and use several V2.0 URLs"));
  const char *templates[] = {
    "http://api.yubico.com/wsapi/2.0/verify?id=%d&otp=%s",
    "http://api2.yubico.com/wsapi/2.0/verify?id=%d&otp=%s",
    "http://api3.yubico.com/wsapi/2.0/verify?id=%d&otp=%s",
    "http://api4.yubico.com/wsapi/2.0/verify?id=%d&otp=%s",
    "http://api5.yubico.com/wsapi/2.0/verify?id=%d&otp=%s",
  };
  ykclient_set_url_templates(ykc, 5, templates);
  ykclient_set_client (ykc, client_id, 20, client_key);
#ifndef TEST_WITHOUT_INTERNET
  ret = ykclient_request (ykc, "dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh");
  printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret));
  printf ("used url: %s\n", ykclient_get_last_url (ykc));
  assert (ret == YKCLIENT_REPLAYED_OTP);
#else
  printf ("Test SKIPPED\n");
#endif

  ykclient_done (&ykc);

  TEST(("strerror 0"));
  printf ("strerror(0): %s\n", ykclient_strerror (0));
  ret = strcmp(ykclient_strerror (0), "Success"); assert (ret == 0);

  TEST(("strerror BAD_OTP"));
  printf ("strerror(BAD_OTP): %s\n", ykclient_strerror (YKCLIENT_BAD_OTP));
  ret = strcmp(ykclient_strerror (YKCLIENT_BAD_OTP), "Yubikey OTP was bad (BAD_OTP)"); assert (ret == 0);

  test_v1_validation(client_id, client_b64key);

  test_base64();

  test_hmac();

  printf ("All tests passed\n");

  curl_global_cleanup();

  return 0;
}