Beispiel #1
0
int
write_chalresp_state(FILE *f, CR_STATE *state)
{
  char challenge_hex[CR_CHALLENGE_SIZE * 2 + 1], response_hex[CR_RESPONSE_SIZE * 2 + 1];
  int fd;

  memset(challenge_hex, 0, sizeof(challenge_hex));
  memset(response_hex, 0, sizeof(response_hex));

  yubikey_hex_encode(challenge_hex, (char *)state->challenge, state->challenge_len);
  yubikey_hex_encode(response_hex, (char *)state->response, state->response_len);

  rewind(f);

  fd = fileno(f);
  if (fd == -1)
    goto out;

  if (ftruncate(fd, 0))
    goto out;

  fprintf(f, "v1:%s:%s:%d\n", challenge_hex, response_hex, state->slot);

  if (fflush(f) < 0)
    goto out;

  if (fsync(fd) < 0)
    goto out;

  return 1;
 out:
  return 0;
}
Beispiel #2
0
int
write_chalresp_state(FILE *f, CR_STATE *state)
{
  char challenge_hex[CR_CHALLENGE_SIZE * 2 + 1], response_hex[CR_RESPONSE_SIZE * 2 + 1];
  char salt_hex[CR_SALT_SIZE * 2 + 1], hashed_hex[CR_RESPONSE_SIZE * 2 + 1];
  unsigned char salt[CR_SALT_SIZE], hash[CR_RESPONSE_SIZE];
  YK_PRF_METHOD prf_method = {20, yk_hmac_sha1};
  unsigned int iterations = CR_DEFAULT_ITERATIONS;
  int fd;

  memset(challenge_hex, 0, sizeof(challenge_hex));
  memset(response_hex, 0, sizeof(response_hex));
  memset(salt_hex, 0, sizeof(salt_hex));
  memset(hashed_hex, 0, sizeof(hashed_hex));

  yubikey_hex_encode(challenge_hex, (char *)state->challenge, state->challenge_len);
  yubikey_hex_encode(response_hex, (char *)state->response, state->response_len);

  if(state->iterations > 0) {
    iterations = state->iterations;
  }

  generate_random(salt, CR_SALT_SIZE);
  yk_pbkdf2(response_hex, salt, CR_SALT_SIZE, iterations,
      hash, CR_RESPONSE_SIZE, &prf_method);

  yubikey_hex_encode(hashed_hex, (char *)hash, CR_RESPONSE_SIZE);
  yubikey_hex_encode(salt_hex, (char *)salt, CR_SALT_SIZE);

  rewind(f);

  fd = fileno(f);
  if (fd == -1)
    goto out;

  if (ftruncate(fd, 0))
    goto out;

  fprintf(f, "v2:%s:%s:%s:%u:%d\n", challenge_hex, hashed_hex, salt_hex, iterations, state->slot);

  if (fflush(f) < 0)
    goto out;

  if (fsync(fd) < 0)
    goto out;

  return 1;
 out:
  return 0;
}
const std::string YubikoOtpKeyConfig::getSecretKey() const {
	BOOST_LOG_NAMED_SCOPE("YubikoOtpKeyConfig::getSecretKey()");
	string myRetVal(K_SEC_KEY_SZ, '.');
	yubikey_hex_encode(&myRetVal[0], reinterpret_cast<const char*>(&itsKey),
	YUBIKEY_KEY_SIZE);
	return string(myRetVal);
}
/**
 * Getter.
 *
 * @return Hex-encoded string representing the private id Yubikey token part.
 */
const string YubikoOtpKeyConfig::getPrivateId() const {
	BOOST_LOG_NAMED_SCOPE("YubikoOtpKeyConfig::getPrivateId");
	string myRetVal(K_YBK_PRIVATE_ID_LEN, '.');
	yubikey_hex_encode(&myRetVal[0],
			reinterpret_cast<const char*>(&itsToken.uid), YUBIKEY_UID_SIZE);
	return string(myRetVal);
}
/**
 * @see const string hex2Modhex(const string& p2Modhex) const;
 * @param p2Hex what to decode.
 * @return input from Modhex decoded.
 *
 */
const string YubikoOtpKeyConfig::modhex2Hex(const std::string& p2Hex) {
	const unsigned long mySz = p2Hex.size();
	const unsigned long mySz2 = mySz / 2;
	vector<uint8_t> myBytes(mySz2, uint8_t(0));
	yubikey_modhex_decode(reinterpret_cast<char*>(&myBytes[0]), p2Hex.c_str(),
			mySz2);
	string myPubId(mySz + 1, '\0');
	yubikey_hex_encode(&myPubId[0], reinterpret_cast<char*>(&myBytes[0]),
			mySz2);
	myPubId.resize(mySz);
	return myPubId;
}
int YubiKeyUtil::hexModhexEncode(char *result, size_t *resultLen,
                                 const unsigned char *str, size_t strLen,
                                 bool modhex)
{
    *resultLen = strLen * 2;
    if (modhex) {
        yubikey_modhex_encode((char *)result, (char *)str, strLen);
        return 1;
    } else {
        yubikey_hex_encode((char *)result, (char *)str, strLen);
        return 1;
    }

    return 0;
}
const string YubikoOtpKeyConfig::token2json() const {
	string myUid(YUBIKEY_UID_SIZE * 2 + 1, ' ');
	yubikey_hex_encode(&myUid[0],
			reinterpret_cast<const char*>(&getToken().uid),
			YUBIKEY_UID_SIZE);
	ostringstream myOstr;
	myOstr << "yubikey_token_st:{";
	myOstr << "   uid  :\"" << myUid.c_str() << "\"";
	myOstr << "   ctr  :\"" << int(getToken().ctr) << "\"";
	myOstr << "   use  :\"" << int(getToken().use) << "\"";
	myOstr << "   rnd  :\"" << int(getToken().rnd) << "\"";
	myOstr << "   tstpl:\"" << int(getToken().tstpl) << "\"";
	myOstr << "   tstph:\"" << int(getToken().tstph) << "\"";
	myOstr << "   crc  :\"" << int(getToken().crc) << "\"";
	myOstr << "}";
	return myOstr.str();
}
Beispiel #8
0
static void
hex_test2 (void)
{
  char buf[1024];
  char buf2[1024];

  yubikey_hex_encode (buf, "test", 4);
  printf ("hex-encode(\"test\") = %s\n", buf);
  assert (strcmp (buf, "74657374") == 0);
  printf ("Hex-2.1 success\n");

  printf ("hex-decode(\"%s\") = ", buf);
  yubikey_hex_decode (buf2, buf, sizeof (buf2));
  printf ("%.*s\n", 4, buf2);
  assert (memcmp (buf2, "test", 4) == 0);
  printf ("Hex-2.2 success\n");
}
int _ykp_json_export_cfg(const YKP_CONFIG *cfg, char *json, size_t len) {
	json_object *jobj = json_object_new_object();
	json_object *yprod_json = json_object_new_object();
	json_object *options_json = json_object_new_object();
	if(cfg) {
		YK_CONFIG ycfg = cfg->ykcore_config;

		int mode = MODE_OTP_YUBICO;
		struct map_st *p;
		json_object *target_config = NULL;
		json_object *prot_obj = NULL;
		int protection = ykp_get_acccode_type(cfg);

		if((ycfg.tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP){
			if((ycfg.cfgFlags & CFGFLAG_CHAL_HMAC) == CFGFLAG_CHAL_HMAC) {
				mode = MODE_CHAL_HMAC;
			} else if((ycfg.cfgFlags & CFGFLAG_CHAL_YUBICO) == CFGFLAG_CHAL_YUBICO) {
				mode = MODE_CHAL_YUBICO;
			} else {
				mode = MODE_OATH_HOTP;
			}
		}
		else if((ycfg.cfgFlags & CFGFLAG_STATIC_TICKET) == CFGFLAG_STATIC_TICKET) {
			mode = MODE_STATIC_TICKET;
		}

		for(p = _modes_map; p->flag; p++) {
			if(p->flag == mode) {
				json_object *jmode = json_object_new_string(p->json_text);
				json_object_object_add(yprod_json, "mode", jmode);
				break;
			}
		}

		if(cfg->command == SLOT_CONFIG) {
			target_config = json_object_new_int(1);
		} else if(cfg->command == SLOT_CONFIG2) {
			target_config = json_object_new_int(2);
		}
		if(target_config) {
			json_object_object_add(yprod_json, "targetConfig", target_config);
		}

		if(protection == YKP_ACCCODE_NONE) {
			prot_obj = json_object_new_string("none");
		} else if(protection == YKP_ACCCODE_RANDOM) {
			prot_obj = json_object_new_string("random");
		} else if(protection == YKP_ACCCODE_SERIAL) {
			prot_obj = json_object_new_string("id");
		}
		if(prot_obj) {
			json_object_object_add(yprod_json, "protection", prot_obj);
		}

		json_object_object_add(jobj, "yubiProdConfig", yprod_json);
		json_object_object_add(yprod_json, "options", options_json);


		if(ycfg.fixedSize != 0 && mode != MODE_STATIC_TICKET) {
			json_object *jPrefix;
			char prefix[5] = {0};

			json_object *scope;
			if(mode == MODE_OTP_YUBICO &&
					ycfg.fixed[0] == 0x00 && ycfg.fixed[1] == 0x00) {
				scope = json_object_new_string("yubiCloud");
			} else {
				scope = json_object_new_string("privatePrefix");
			}
			json_object_object_add(yprod_json, "scope", scope);

			yubikey_modhex_encode(prefix, (const char*)ycfg.fixed, 2);
			if(mode == MODE_OATH_HOTP) {
				int flag = ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX;
				json_object *fixed_modhex = json_object_new_boolean(
						flag == CFGFLAG_OATH_FIXED_MODHEX ? 1 : 0);
				json_object_object_add(options_json, "fixedModhex", fixed_modhex);

				if(flag == 0) {
					yubikey_hex_encode(prefix, (const char*)ycfg.fixed, 2);
				} else if(flag == CFGFLAG_OATH_FIXED_MODHEX1) {
					yubikey_hex_encode(prefix + 2, (const char*)ycfg.fixed + 1, 1);
				}
			}
			jPrefix = json_object_new_string(prefix);
			json_object_object_add(yprod_json, "prefix", jPrefix);
		} else if(mode != MODE_STATIC_TICKET) {
			json_object *scope = json_object_new_string("noPublicId");
			json_object_object_add(yprod_json, "scope", scope);
		}

		if(mode == MODE_OATH_HOTP) {
			json_object *oathDigits;
			json_object *randomSeed;
			if((ycfg.cfgFlags & CFGFLAG_OATH_HOTP8) == CFGFLAG_OATH_HOTP8) {
				oathDigits = json_object_new_int(8);
			} else {
				oathDigits = json_object_new_int(6);
			}
			json_object_object_add(options_json, "oathDigits", oathDigits);

			if((ycfg.uid[5] == 0x01 || ycfg.uid[5] == 0x00) && ycfg.uid[4] == 0x00) {
				json_object *fixedSeedvalue = json_object_new_int(ycfg.uid[5] << 4);
				json_object_object_add(options_json, "fixedSeedvalue", fixedSeedvalue);
				randomSeed = json_object_new_boolean(0);
			} else {
				randomSeed = json_object_new_boolean(1);
			}
			json_object_object_add(options_json, "randomSeed", randomSeed);
		}

		for(p = _ticket_flags_map; p->flag; p++) {
			if(!p->json_text) {
				continue;
			}
			if(p->mode && (mode & p->mode) == mode) {
				int set = (ycfg.tktFlags & p->flag) == p->flag;
				json_object *jsetting = json_object_new_boolean(set);
				json_object_object_add(options_json, p->json_text, jsetting);
			}
		}

		for(p = _config_flags_map; p->flag; p++) {
			if(!p->json_text) {
				continue;
			}
			if(p->mode && (mode & p->mode) == mode) {
				int set = (ycfg.cfgFlags & p->flag) == p->flag;
				json_object *jsetting = json_object_new_boolean(set);
				json_object_object_add(options_json, p->json_text, jsetting);
			}
		}

		for(p = _extended_flags_map; p->flag; p++) {
			if(!p->json_text) {
				continue;
			}
			if(p->mode && (mode & p->mode) == mode) {
				int set = (ycfg.extFlags & p->flag) == p->flag;
				json_object *jsetting = json_object_new_boolean(set);
				json_object_object_add(options_json, p->json_text, jsetting);
			}
		}
	}

#ifdef HAVE_JSON_OBJECT_TO_JSON_STRING_EXT
	strncpy(json, json_object_to_json_string_ext(jobj, JSON_C_TO_STRING_PRETTY), len);
#else
	strncpy(json, json_object_to_json_string(jobj), len);
#endif

	/* free the root object, will free all children */
	json_object_put(jobj);
	return strlen(json);
}
Beispiel #10
0
static int _ykp_legacy_export_config(const YKP_CONFIG *cfg, char *buf, size_t len) {
	if (cfg) {
		char buffer[256];
		struct map_st *p;
		unsigned char t_flags;
		bool key_bits_in_uid = false;
		YK_CONFIG ycfg = cfg->ykcore_config;
		int mode = MODE_OTP_YUBICO;

		int pos = 0;

		if((ycfg.tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP){
			if((ycfg.cfgFlags & CFGFLAG_CHAL_HMAC) == CFGFLAG_CHAL_HMAC) {
				mode = MODE_CHAL_HMAC;
			} else if((ycfg.cfgFlags & CFGFLAG_CHAL_YUBICO) == CFGFLAG_CHAL_YUBICO) {
				mode = MODE_CHAL_YUBICO;
			} else {
				mode = MODE_OATH_HOTP;
			}
		}
		else if((ycfg.cfgFlags & CFGFLAG_STATIC_TICKET) == CFGFLAG_STATIC_TICKET) {
			mode = MODE_STATIC_TICKET;
		}

		/* for OATH-HOTP and HMAC-SHA1 challenge response, there is four bytes
		 *  additional key data in the uid field
		 */
		key_bits_in_uid = (_get_supported_key_length(cfg) == 20);

		/* fixed: or OATH id: */
		if ((ycfg.tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP &&
		    ycfg.fixedSize) {
			/* First byte (vendor id) */
			if ((ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX1) == CFGFLAG_OATH_FIXED_MODHEX1 ||
			    (ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX2) == CFGFLAG_OATH_FIXED_MODHEX2 ||
			    (ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX) == CFGFLAG_OATH_FIXED_MODHEX) {
				yubikey_modhex_encode(buffer, (const char *)ycfg.fixed, 1);
			} else {
				yubikey_hex_encode(buffer, (const char *)ycfg.fixed, 1);
			}
			/* Second byte (token type) */
			if ((ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX2) == CFGFLAG_OATH_FIXED_MODHEX2 ||
			    (ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX) == CFGFLAG_OATH_FIXED_MODHEX) {
				yubikey_modhex_encode(buffer + 2, (const char *)ycfg.fixed + 1, 1);
			} else {
				yubikey_hex_encode(buffer + 2, (const char *)ycfg.fixed + 1, 1);
			}
			/* bytes 3-12 - MUI */
			if ((ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX) == CFGFLAG_OATH_FIXED_MODHEX) {
				yubikey_modhex_encode(buffer + 4, (const char *)ycfg.fixed + 2, 8);
			} else {
				yubikey_hex_encode(buffer + 4, (const char *)ycfg.fixed + 2, 8);
			}
			buffer[12] = 0;
			pos += snprintf(buf, len - (size_t)pos, "%s%s%s\n", str_oath_id, str_key_value_separator, buffer);
		} else {
			yubikey_modhex_encode(buffer, (const char *)ycfg.fixed, ycfg.fixedSize);
			pos += snprintf(buf, len - (size_t)pos, "%s%s%s%s\n", str_fixed, str_key_value_separator, str_modhex_prefix, buffer);
		}

		/* uid: */
		if (key_bits_in_uid) {
			strncpy(buffer, "n/a", 3);
		} else {
			yubikey_hex_encode(buffer, (const char *)ycfg.uid, UID_SIZE);
		}
		pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s\n", str_uid, str_key_value_separator, buffer);

		/* key: */
		yubikey_hex_encode(buffer, (const char *)ycfg.key, KEY_SIZE);
		if (key_bits_in_uid) {
			yubikey_hex_encode(buffer + KEY_SIZE * 2, (const char *)ycfg.uid, 4);
		}
		pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s%s\n", str_key, str_key_value_separator, str_hex_prefix, buffer);

		/* acc_code: */
		yubikey_hex_encode(buffer, (const char*)ycfg.accCode, ACC_CODE_SIZE);
		pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s%s\n", str_acc_code, str_key_value_separator, str_hex_prefix, buffer);

		/* OATH IMF: */
		if ((ycfg.tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP &&
		    capability_has_oath_imf(cfg)) {
			pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s%lx\n", str_oath_imf, str_key_value_separator, str_hex_prefix, ykp_get_oath_imf(cfg));
		}

		/* ticket_flags: */
		buffer[0] = '\0';
		for (p = _ticket_flags_map; p->flag; p++) {
			if ((ycfg.tktFlags & p->flag) == p->flag
			    && p->capability(cfg)
			    && (mode & p->mode) == mode) {
				if (*buffer) {
					strcat(buffer, str_flags_separator);
					strcat(buffer, p->flag_text);
				} else {
					strcpy(buffer, p->flag_text);
				}
			}
		}
		pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s\n", str_ticket_flags, str_key_value_separator, buffer);

		/* config_flags: */
		buffer[0] = '\0';
		t_flags = ycfg.cfgFlags;
		for (p = _config_flags_map; p->flag; p++) {
			if ((t_flags & p->flag) == p->flag
			    && p->capability(cfg)
			    && (mode & p->mode) == mode) {
				if (*buffer) {
					strcat(buffer, str_flags_separator);
					strcat(buffer, p->flag_text);
				} else {
					strcpy(buffer, p->flag_text);
				}
				/* make sure we don't show more than one cfgFlag per value -
				   some cfgflags share value in different contexts
				*/
				t_flags -= p->flag;
			}
		}
		pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s\n", str_config_flags, str_key_value_separator, buffer);

		/* extended_flags: */
		buffer[0] = '\0';
		for (p = _extended_flags_map; p->flag; p++) {
			if ((ycfg.extFlags & p->flag) == p->flag
			    && p->capability(cfg)
			    && (mode & p->mode) == mode) {
				if (*buffer) {
					strcat(buffer, str_flags_separator);
					strcat(buffer, p->flag_text);
				} else {
					strcpy(buffer, p->flag_text);
				}
			}
		}
		pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s\n", str_extended_flags, str_key_value_separator, buffer);

		return pos;
	}
	return 0;
}
Beispiel #11
0
int
main (void)
{
  char buf[1024];
  size_t i;
  int rc;
  yubikey_token_st tok;


  /* Test Modhex */
  yubikey_modhex_encode (buf, "test", 4);
  printf ("modhex-encode(\"test\") = %s\n", buf);
  if (strcmp (buf, "ifhgieif") != 0)
    {
      printf ("ModHex failure\n");
      return 1;
    }
  printf ("Modhex-1 success\n");

  printf ("modhex-decode(\"%s\") = ", buf);
  yubikey_modhex_decode (buf, buf, strlen ((char *) buf));
  printf ("%.*s\n", 4, buf);
  if (memcmp (buf, "test", 4) != 0)
    {
      printf ("ModHex failure\n");
      return 1;
    }
  printf ("Modhex-2 success\n");

  strcpy (buf, "cbdefghijklnrtuv");
  rc = yubikey_modhex_p (buf);
  printf ("hex-p(\"%s\") = %d\n", buf, rc);
  if (!rc)
    {
      printf ("Hex_p failure\n");
      return 1;
    }
  printf ("Hex-3 success\n");

  strcpy (buf, "0123Xabc");
  rc = yubikey_hex_p (buf);
  printf ("hex-p(\"%s\") = %d\n", buf, rc);
  if (rc)
    {
      printf ("Hex_p failure\n");
      return 1;
    }
  printf ("Hex-3 success\n");

  /* Test Hex */

  yubikey_hex_encode (buf, "test", 4);
  printf ("hex-encode(\"test\") = %s\n", buf);
  if (strcmp (buf, "74657374") != 0)
    {
      printf ("Hex failure\n");
      return 1;
    }
  printf ("Hex-1 success\n");

  printf ("hex-decode(\"%s\") = ", buf);
  yubikey_hex_decode (buf, buf, strlen ((char *) buf));
  printf ("%.*s\n", 4, buf);
  if (memcmp (buf, "test", 4) != 0)
    {
      printf ("Hex failure\n");
      return 1;
    }
  printf ("Hex-2 success\n");

  strcpy (buf, "0123456789abcdef");
  rc = yubikey_hex_p (buf);
  printf ("hex-p(\"%s\") = %d\n", buf, rc);
  if (!rc)
    {
      printf ("Hex_p failure\n");
      return 1;
    }
  printf ("Hex-3 success\n");

  strcpy (buf, "0123Xabc");
  rc = yubikey_hex_p (buf);
  printf ("hex-p(\"%s\") = %d\n", buf, rc);
  if (rc)
    {
      printf ("Hex_p failure\n");
      return 1;
    }
  printf ("Hex-3 success\n");

  /* Test AES */

  {
    uint8_t buf[1024];
    char out[1024];
    uint8_t key[16 + 1];

    memcpy (buf, "0123456789abcdef\0", 17);
    memcpy (key, "abcdef0123456789\0", 17);
    printf ("aes-decrypt (data=%s, key=%s)\n => ", (char *) buf,
	    (char *) key);
    yubikey_aes_decrypt (buf, key);
    for (i = 0; i < 16; i++)
      printf ("%02x", buf[i] & 0xFF);
    printf ("\n");

    if (memcmp (buf,
		"\x83\x8a\x46\x7f\x34\x63\x95\x51"
		"\x75\x5b\xd3\x2a\x4a\x2f\x15\xe1", 16) != 0)
      {
	printf ("AES failure\n");
	return 1;
      }
    printf ("AES-1 success\n");

    yubikey_aes_encrypt (buf, key);
    if (memcmp (buf, "0123456789abcdef", 16) != 0)
      {
	printf ("AES encryption failure\n");
	return 1;
      }
    printf ("AES-2 success\n");

    /* Test OTP */

    memcpy ((void *) &tok,
	    "\x16\xe1\xe5\xd9\xd3\x99\x10\x04\x45\x20\x07\xe3\x02\x00\x00",
	    16);
    memcpy (key, "abcdef0123456789", 16);

    yubikey_generate ((void *) &tok, key, out);
    yubikey_parse ((uint8_t *) out, key, &tok);

    if (memcmp
	(&tok, "\x16\xe1\xe5\xd9\xd3\x99\x10\x04\x45\x20\x07\xe3\x02\x00\x00",
	 16) != 0)
      {
	printf ("OTP generation - parse failure\n");
	return 1;
      }
    printf ("OTP-1 success\n");
  }

  return 0;
}
Beispiel #12
0
int main(int argc, char **argv) {
	unsigned int version = 0, help = 0;
	char challenge_old[CHALLENGELEN + 1],
		challenge_new[CHALLENGELEN + 1],
		response_old[RESPONSELEN],
		response_new[RESPONSELEN],
		passphrase_old[PASSPHRASELEN + 1],
		passphrase_new[PASSPHRASELEN + 1];
		const char * tmp;
	char challengefilename[sizeof(CHALLENGEDIR) + 11 /* "/challenge-" */ + 10 /* unsigned int in char */ + 1],
		challengefiletmpname[sizeof(CHALLENGEDIR) + 11 /* "/challenge-" */ + 10 /* unsigned int in char */ + 7 /* -XXXXXX */ + 1];
	int challengefile = 0, challengefiletmp = 0;
	struct timeval tv;
	int i;
	size_t len;
	int8_t rc = EXIT_FAILURE;
	/* cryptsetup */
	const char * device_name;
	int8_t luks_slot = -1;
	struct crypt_device *cryptdevice;
	crypt_status_info cryptstatus;
	crypt_keyslot_info cryptkeyslot;
	char * passphrase = NULL;
	/* keyutils */
	key_serial_t key;
	void * payload = NULL;
	char * second_factor = NULL, * new_2nd_factor = NULL, * new_2nd_factor_verify = NULL;
	/* yubikey */
	YK_KEY * yk;
	uint8_t yk_slot = SLOT_CHAL_HMAC2;
	unsigned int serial = 0;
	/* iniparser */
	dictionary * ini;
	char section_ykslot[10 /* unsigned int in char */ + 1 + sizeof(CONFYKSLOT) + 1];
	char section_luksslot[10 + 1 + sizeof(CONFLUKSSLOT) + 1];

	/* get command line options */
	while ((i = getopt_long(argc, argv, optstring, options_long, NULL)) != -1)
		switch (i) {
			case 'h':
				help++;
				break;
			case 'n':
			case 'N':
				if (new_2nd_factor != NULL) {
					fprintf(stderr, "We already have a new second factor. Did you specify it twice?\n");
					goto out10;
				}

				if (optarg == NULL) { /* N */
					if ((new_2nd_factor = ask_secret("new second factor")) == NULL)
						goto out10;

					if ((new_2nd_factor_verify = ask_secret("new second factor for verification")) == NULL)
						goto out10;

					if (strcmp(new_2nd_factor, new_2nd_factor_verify) != 0) {
						fprintf(stderr, "Verification failed, given strings do not match.\n");
						goto out10;
					}
				} else { /* n */
					new_2nd_factor = strdup(optarg);
					memset(optarg, '*', strlen(optarg));
				}

				break;
			case 's':
			case 'S':
				if (second_factor != NULL) {
					fprintf(stderr, "We already have a second factor. Did you specify it twice?\n");
					goto out10;
				}

				if (optarg == NULL) { /* S */
					second_factor = ask_secret("current second factor");
				} else { /* s */
					second_factor = strdup(optarg);
					memset(optarg, '*', strlen(optarg));
				}

				break;
			case 'V':
				version++;
				break;
		}

	if (version > 0)
		printf("%s: %s v%s (compiled: " __DATE__ ", " __TIME__ ")\n", argv[0], PROGNAME, VERSION);

	if (help > 0)
		fprintf(stderr, "usage: %s [-h|--help] [-n|--new-2nd-factor <new-2nd-factor>] [-N|--ask-new-2nd-factor]\n"
				"        [-s|--2nd-factor <2nd-factor>] [-S|--ask-2nd-factor] [-V|--version]\n", argv[0]);

	if (version > 0 || help > 0)
		return EXIT_SUCCESS;


	/* initialize random seed */
	gettimeofday(&tv, NULL);
	srand(tv.tv_usec * tv.tv_sec);

	/* initialize static buffers */
	memset(challenge_old, 0, CHALLENGELEN + 1);
	memset(challenge_new, 0, CHALLENGELEN + 1);
	memset(response_old, 0, RESPONSELEN);
	memset(response_new, 0, RESPONSELEN);
	memset(passphrase_old, 0, PASSPHRASELEN + 1);
	memset(passphrase_new, 0, PASSPHRASELEN + 1);

	if ((ini = iniparser_load(CONFIGFILE)) == NULL) {
		fprintf(stderr, "Could not parse configuration file.\n");
		goto out10;
	}

	if ((device_name = iniparser_getstring(ini, "general:" CONFDEVNAME, NULL)) == NULL) {
		/* read from crypttab? */
		/* get device from currently open devices? */
		fprintf(stderr, "Could not read LUKS device from configuration file.\n");
		goto out20;
	}

	/* init and open first Yubikey */
	if (yk_init() == 0) {
		perror("yk_init() failed");
		goto out20;
	}

	if ((yk = yk_open_first_key()) == NULL) {
		fprintf(stderr, "No Yubikey available.\n");
		goto out30;
	}

	/* read the serial number from key */
	if (yk_get_serial(yk, 0, 0, &serial) == 0) {
		perror("yk_get_serial() failed");
		goto out40;
	}

	/* get the yk slot */
	sprintf(section_ykslot, "%d:" CONFYKSLOT, serial);
	yk_slot = iniparser_getint(ini, "general:" CONFYKSLOT, yk_slot);
	yk_slot = iniparser_getint(ini, section_ykslot, yk_slot);
	switch (yk_slot) {
		case 1:
		case SLOT_CHAL_HMAC1:
			yk_slot = SLOT_CHAL_HMAC1;
			break;
		case 2:
		case SLOT_CHAL_HMAC2:
		default:
			yk_slot = SLOT_CHAL_HMAC2;
			break;
	}

	/* get the luks slot */
	sprintf(section_luksslot, "%d:" CONFLUKSSLOT, serial);
	luks_slot = iniparser_getint(ini, section_luksslot, luks_slot);
	if (luks_slot < 0) {
		fprintf(stderr, "Please set LUKS key slot for Yubikey with serial %d!\n"
				"Add something like this to " CONFIGFILE ":\n\n"
				"[%d]\nluks slot = 1\n", serial, serial);
		goto out40;
	}

	if (second_factor == NULL) {
		/* get second factor from key store */
		if ((key = request_key("user", "ykfde-2f", NULL, 0)) < 0)
			fprintf(stderr, "Failed requesting key. That's ok if you do not use\n"
					"second factor. Give it manually if required.\n");

		if (key > -1) {
			/* if we have a key id we have a key - so this should succeed */
			if (keyctl_read_alloc(key, &payload) < 0) {
				perror("Failed reading payload from key");
				goto out40;
			}
			second_factor = payload;
		} else
			second_factor = strdup("");
	}

	/* warn when second factor is not enabled in config */
	if ((*second_factor != 0 || new_2nd_factor != NULL) &&
			iniparser_getboolean(ini, "general:" CONF2NDFACTOR, 0) == 0)
		fprintf(stderr, "Warning: Processing second factor, but not enabled in config!\n");

	/* get random number and limit to printable ASCII character (32 to 126) */
	for(i = 0; i < CHALLENGELEN; i++)
		challenge_new[i] = (rand() % (126 - 32)) + 32;

	/* these are the filenames for challenge
	 * we need this for reading and writing */
	sprintf(challengefilename, CHALLENGEDIR "/challenge-%d", serial);
	sprintf(challengefiletmpname, CHALLENGEDIR "/challenge-%d-XXXXXX", serial);

	/* write new challenge to file */
	if ((challengefiletmp = mkstemp(challengefiletmpname)) < 0) {
		fprintf(stderr, "Could not open file %s for writing.\n", challengefiletmpname);
		goto out40;
	}
	if (write(challengefiletmp, challenge_new, CHALLENGELEN) < 0) {
		fprintf(stderr, "Failed to write challenge to file.\n");
		goto out50;
	}
	challengefiletmp = close(challengefiletmp);

	/* now that the new challenge has been written to file...
	 * add second factor to new challenge */
	tmp = new_2nd_factor ? new_2nd_factor : second_factor;
	len = strlen(tmp);
	memcpy(challenge_new, tmp, len < MAX2FLEN ? len : MAX2FLEN);

	/* do challenge/response and encode to hex */
	if (yk_challenge_response(yk, yk_slot, true,
			CHALLENGELEN, (unsigned char *) challenge_new,
			RESPONSELEN, (unsigned char *) response_new) == 0) {
		perror("yk_challenge_response() failed");
		goto out50;
	}
	yubikey_hex_encode((char *) passphrase_new, (char *) response_new, SHA1_DIGEST_SIZE);

	/* get status of crypt device
	 * We expect this to be active (or busy). It is the actual root device, no? */
	cryptstatus = crypt_status(cryptdevice, device_name);
	if (cryptstatus != CRYPT_ACTIVE && cryptstatus != CRYPT_BUSY) {
                fprintf(stderr, "Device %s is invalid or inactive.\n", device_name);
		goto out50;
	}

	/* initialize crypt device */
	if (crypt_init_by_name(&cryptdevice, device_name) < 0) {
		fprintf(stderr, "Device %s failed to initialize.\n", device_name);
		goto out60;
	}

	cryptkeyslot = crypt_keyslot_status(cryptdevice, luks_slot);

	if (cryptkeyslot == CRYPT_SLOT_INVALID) {
		fprintf(stderr, "Key slot %d is invalid.\n", luks_slot);
		goto out60;
	} else if (cryptkeyslot == CRYPT_SLOT_ACTIVE || cryptkeyslot == CRYPT_SLOT_ACTIVE_LAST) {
		/* read challenge from file */
		if ((challengefile = open(challengefilename, O_RDONLY)) < 0) {
			perror("Failed opening challenge file for reading");
			goto out60;
		}

		if (read(challengefile, challenge_old, CHALLENGELEN) < 0) {
			perror("Failed reading challenge from file");
			goto out60;
		}

		challengefile = close(challengefile);
		/* finished reading challenge */

		/* copy the second factor */
		len = strlen(second_factor);
		memcpy(challenge_old, second_factor, len < MAX2FLEN ? len : MAX2FLEN);

		/* do challenge/response and encode to hex */
		if (yk_challenge_response(yk, yk_slot, true,
				CHALLENGELEN, (unsigned char *) challenge_old,
				RESPONSELEN, (unsigned char *) response_old) == 0) {
			perror("yk_challenge_response() failed");
			goto out60;
		}
		yubikey_hex_encode((char *) passphrase_old, (char *) response_old, SHA1_DIGEST_SIZE);

		if (crypt_keyslot_change_by_passphrase(cryptdevice, luks_slot, luks_slot,
				passphrase_old, PASSPHRASELEN,
				passphrase_new, PASSPHRASELEN) < 0) {
			fprintf(stderr, "Could not update passphrase for key slot %d.\n", luks_slot);
			goto out60;
		}

		if (unlink(challengefilename) < 0) {
			fprintf(stderr, "Failed to delete old challenge file.\n");
			goto out60;
		}
	} else { /* ck == CRYPT_SLOT_INACTIVE */
		if ((passphrase = ask_secret("existing LUKS passphrase")) == NULL)
			goto out60;

		if (crypt_keyslot_add_by_passphrase(cryptdevice, luks_slot,
				passphrase, strlen(passphrase),
				passphrase_new, PASSPHRASELEN) < 0) {
			fprintf(stderr, "Could not add passphrase for key slot %d.\n", luks_slot);
			goto out60;
		}
	}

	if (rename(challengefiletmpname, challengefilename) < 0) {
		fprintf(stderr, "Failed to rename new challenge file.\n");
		goto out60;
	}

	rc = EXIT_SUCCESS;

out60:
	/* free crypt context */
	crypt_free(cryptdevice);

out50:
	/* close the challenge file */
	if (challengefile)
		close(challengefile);
	if (challengefiletmp)
		close(challengefiletmp);
	if (access(challengefiletmpname, F_OK) == 0)
		unlink(challengefiletmpname);

out40:
	/* close Yubikey */
	if (yk_close_key(yk) == 0)
		perror("yk_close_key() failed");

out30:
	/* release Yubikey */
	if (yk_release() == 0)
		perror("yk_release() failed");

out20:
	/* free iniparser dictionary */
	iniparser_freedict(ini);

out10:
	/* wipe response (cleartext password!) from memory */
	/* This is statically allocated and always save to wipe! */
	memset(challenge_old, 0, CHALLENGELEN + 1);
	memset(challenge_new, 0, CHALLENGELEN + 1);
	memset(response_old, 0, RESPONSELEN);
	memset(response_new, 0, RESPONSELEN);
	memset(passphrase_old, 0, PASSPHRASELEN + 1);
	memset(passphrase_new, 0, PASSPHRASELEN + 1);

	free(passphrase);
	free(new_2nd_factor_verify);
	free(new_2nd_factor);
	free(second_factor);

	return rc;
}
static int
yubikey_login(const char *username, const char *password)
{
	char fn[MAXPATHLEN];
	char hexkey[33], key[YUBIKEY_KEY_SIZE];
	char hexuid[13], uid[YUBIKEY_UID_SIZE];
	FILE *f;
	yubikey_token_st tok;
	u_int32_t last_ctr = 0, ctr;
	int r, i = 0, mapok = 0, crcok = 0;

	snprintf(fn, sizeof(fn), "%s/%s.uid", path, username);
	if ((f = fopen(fn, "r")) == NULL) {
		syslog(LOG_ERR, "user %s: fopen: %s: %m", username, fn);
		return (AUTH_FAILED);
	}
	if (fscanf(f, "%12s", hexuid) != 1) {
		syslog(LOG_ERR, "user %s: fscanf: %s: %m", username, fn);
		fclose(f);
		return (AUTH_FAILED);
	}
	fclose(f);

	snprintf(fn, sizeof(fn), "%s/%s.key", path, username);
	if ((f = fopen(fn, "r")) == NULL) {
		syslog(LOG_ERR, "user %s: fopen: %s: %m", username, fn);
		return (AUTH_FAILED);
	}
	if (fscanf(f, "%32s", hexkey) != 1) {
		syslog(LOG_ERR, "user %s: fscanf: %s: %m", username, fn);
		fclose(f);
		return (AUTH_FAILED);
	}
	fclose(f);
	if (strlen(hexkey) != 32) {
		syslog(LOG_ERR, "user %s: key len != 32", username);
		return (AUTH_FAILED);
	}

	snprintf(fn, sizeof(fn), "%s/%s.ctr", path, username);
	if ((f = fopen(fn, "r")) != NULL) {
		if (fscanf(f, "%u", &last_ctr) != 1)
			last_ctr = 0;
		fclose(f);
	}

	yubikey_hex_decode(uid, hexuid, YUBIKEY_UID_SIZE);
	yubikey_hex_decode(key, hexkey, YUBIKEY_KEY_SIZE);

	/* 
	 * Cycle through the key mapping table.
         * XXX brute force, unoptimized; a lookup table for valid mappings may
	 * be appropriate.
	 */
	while (1) {
		r = yubikey_parse((uint8_t *)password, (uint8_t *)key, &tok, i++);
		switch (r) {
		case EMSGSIZE:
			syslog(LOG_INFO, "user %s failed: password too short.",
			    username);
			return (AUTH_FAILED);
		case EINVAL: 	/* keyboard mapping invalid */
			continue;
		case 0:		/* found a valid keyboard mapping */
			mapok++;
			if (!yubikey_crc_ok_p((uint8_t *)&tok))
				continue;	/* try another one */
			crcok++;
			syslog(LOG_DEBUG, "user %s: crc %04x ok",
			    username, tok.crc);

			if (memcmp(tok.uid, uid, YUBIKEY_UID_SIZE)) {
				char h[13];

				yubikey_hex_encode(h, (const char *)tok.uid,
				    YUBIKEY_UID_SIZE);
				syslog(LOG_DEBUG, "user %s: uid %s != %s",
				    username, h, hexuid);
				continue;	/* try another one */
			}
			break; /* uid matches */
		case -1:
			syslog(LOG_INFO, "user %s: could not decode password "
			    "with any keymap (%d crc ok)",
			    username, crcok);
			return (AUTH_FAILED);
		default: 
			syslog(LOG_DEBUG, "user %s failed: %s",
			    username, strerror(r));
			return (AUTH_FAILED);
		}
		break; /* only reached through the bottom of case 0 */
	}

	syslog(LOG_INFO, "user %s uid %s: %d matching keymaps (%d checked), "
	    "%d crc ok", username, hexuid, mapok, i, crcok);

	ctr = ((u_int32_t)yubikey_counter(tok.ctr) << 8) | tok.use;
	if (ctr <= last_ctr) {
		syslog(LOG_INFO, "user %s: counter %u.%u <= %u.%u "
		    "(REPLAY ATTACK!)", username, ctr / 256, ctr % 256,
		    last_ctr / 256, last_ctr % 256);
		return (AUTH_FAILED);
	}
	syslog(LOG_INFO, "user %s: counter %u.%u > %u.%u",
	    username, ctr / 256, ctr % 256, last_ctr / 256, last_ctr % 256);
	umask(S_IRWXO);
	if ((f = fopen(fn, "w")) == NULL) {
		syslog(LOG_ERR, "user %s: fopen: %s: %m", username, fn);
		return (AUTH_FAILED);
	}
	fprintf(f, "%u", ctr);
	fclose(f);

	return (AUTH_OK);
}
Beispiel #14
0
int ykp_write_config(const YKP_CONFIG *cfg,
		     int (*writer)(const char *buf, size_t count,
				   void *userdata),
		     void *userdata)
{
	if (cfg) {
		char buffer[256];
		struct map_st *p;
		unsigned char t_flags;
		bool key_bits_in_uid = false;

		/* for OATH-HOTP and HMAC-SHA1 challenge response, there is four bytes
		 *  additional key data in the uid field
		 */
		key_bits_in_uid = (_get_supported_key_length(cfg) == 20);

		/* fixed: */
		writer(str_fixed, strlen(str_fixed), userdata);
		writer(str_key_value_separator,
		       strlen(str_key_value_separator),
		       userdata);
		/* XXX print OATH-HOTP fixed differently based on oath-fixed-modhex etc. */
		writer(str_modhex_prefix,
		       strlen(str_key_value_separator),
		       userdata);
		yubikey_modhex_encode(buffer, (char *)cfg->ykcore_config.fixed, cfg->ykcore_config.fixedSize);
		writer(buffer, strlen(buffer), userdata);
		writer("\n", 1, userdata);

		/* uid: */
		writer(str_uid, strlen(str_uid), userdata);
		writer(str_key_value_separator,
		       strlen(str_key_value_separator),
		       userdata);
		if (key_bits_in_uid) {
			writer("n/a", 3, userdata);
		} else {
			writer(str_hex_prefix,
			       strlen(str_key_value_separator),
			       userdata);
			yubikey_hex_encode(buffer, (char *)cfg->ykcore_config.uid, UID_SIZE);
			writer(buffer, strlen(buffer), userdata);
		}
		writer("\n", 1, userdata);

		/* key: */
		writer(str_key, strlen(str_key), userdata);
		writer(str_key_value_separator,
		       strlen(str_key_value_separator),
		       userdata);
		writer(str_hex_prefix,
		       strlen(str_key_value_separator),
		       userdata);
		yubikey_hex_encode(buffer, (char *)cfg->ykcore_config.key, KEY_SIZE);
		if (key_bits_in_uid) {
			yubikey_hex_encode(buffer + KEY_SIZE * 2, (char *)cfg->ykcore_config.uid, 4);
		}
		writer(buffer, strlen(buffer), userdata);
		writer("\n", 1, userdata);

		/* acc_code: */
		writer(str_acc_code, strlen(str_acc_code), userdata);
		writer(str_key_value_separator,
		       strlen(str_key_value_separator),
		       userdata);
		writer(str_hex_prefix,
		       strlen(str_key_value_separator),
		       userdata);
		yubikey_hex_encode(buffer, (char *)cfg->ykcore_config.accCode, ACC_CODE_SIZE);
		writer(buffer, strlen(buffer), userdata);
		writer("\n", 1, userdata);

		/* ticket_flags: */
		buffer[0] = '\0';
		for (p = ticket_flags_map; p->flag; p++) {
			if ((cfg->ykcore_config.tktFlags & p->flag) == p->flag
			    && p->vcheck(cfg)) {
				if (*buffer) {
					strcat(buffer, str_flags_separator);
					strcat(buffer, p->flag_text);
				} else {
					strcpy(buffer, p->flag_text);
				}
			}
		}
		writer(str_ticket_flags, strlen(str_ticket_flags), userdata);
		writer(str_key_value_separator,
		       strlen(str_key_value_separator),
		       userdata);
		writer(buffer, strlen(buffer), userdata);
		writer("\n", 1, userdata);

		/* config_flags: */
		buffer[0] = '\0';
		t_flags = cfg->ykcore_config.cfgFlags;
		for (p = config_flags_map; p->flag; p++) {
			if ((t_flags & p->flag) == p->flag
			    && p->vcheck(cfg)
			    && (cfg->ykcore_config.tktFlags & p->tkt_context) == p->tkt_context) {
				if (*buffer) {
					strcat(buffer, str_flags_separator);
					strcat(buffer, p->flag_text);
				} else {
					strcpy(buffer, p->flag_text);
				}
				/* make sure we don't show more than one cfgFlag per value -
				   some cfgflags share value in different contexts
				*/
				t_flags -= p->flag;
			}
		}
		writer(str_config_flags, strlen(str_config_flags), userdata);
		writer(str_key_value_separator,
		       strlen(str_key_value_separator),
		       userdata);
		writer(buffer, strlen(buffer), userdata);
		writer("\n", 1, userdata);

		/* extended_flags: */
		buffer[0] = '\0';
		for (p = extended_flags_map; p->flag; p++) {
			if ((cfg->ykcore_config.extFlags & p->flag) == p->flag
			    && p->vcheck(cfg)) {
				if (*buffer) {
					strcat(buffer, str_flags_separator);
					strcat(buffer, p->flag_text);
				} else {
					strcpy(buffer, p->flag_text);
				}
			}
		}
		writer(str_extended_flags, strlen(str_extended_flags), userdata);
		writer(str_key_value_separator,
		       strlen(str_key_value_separator),
		       userdata);
		writer(buffer, strlen(buffer), userdata);
		writer("\n", 1, userdata);

		return 1;
	}
	return 0;
}
Beispiel #15
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;
}
Beispiel #16
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 #17
0
int main(int argc, char **argv) {
	char challenge_old[CHALLENGELEN + 1],
		challenge_new[CHALLENGELEN + 1],
		repose_old[RESPONSELEN],
		repose_new[RESPONSELEN],
		passphrase_old[PASSPHRASELEN + 1],
		passphrase_new[PASSPHRASELEN + 1];
	char challengefilename[sizeof(CHALLENGEDIR) + 11 /* "/challenge-" */ + 10 /* unsigned int in char */ + 1],
		challengefiletmpname[sizeof(CHALLENGEDIR) + 11 /* "/challenge-" */ + 10 /* unsigned int in char */ + 7 /* -XXXXXX */ + 1];
	int challengefile = 0, challengefiletmp = 0;
	struct timeval tv;
	int i;
	int8_t rc = EXIT_FAILURE;
	/* cryptsetup */
	char * device_name;
	int8_t luks_slot = -1;
	struct crypt_device *cd;
	crypt_status_info cs;
	crypt_keyslot_info ck;
	/* yubikey */
	YK_KEY * yk;
	uint8_t yk_slot = SLOT_CHAL_HMAC2;
	unsigned int serial = 0;
	/* iniparser */
	dictionary * ini;
	char section_ykslot[10 /* unsigned int in char */ + 1 + sizeof(CONFYKSLOT) + 1];
	char section_luksslot[10 /* unsigned int in char */ + 1 + sizeof(CONFLUKSSLOT) + 1];

	/* initialize random seed */
	gettimeofday(&tv, NULL);
	srand(tv.tv_usec * tv.tv_sec);

	memset(challenge_old, 0, CHALLENGELEN + 1);
	memset(challenge_new, 0, CHALLENGELEN + 1);
	memset(repose_old, 0, RESPONSELEN);
	memset(repose_new, 0, RESPONSELEN);
	memset(passphrase_old, 0, PASSPHRASELEN + 1);
	memset(passphrase_new, 0, PASSPHRASELEN + 1);

	if ((ini = iniparser_load(CONFIGFILE)) == NULL) {
		rc = EXIT_FAILURE;
		fprintf(stderr, "Could not parse configuration file.\n");
		goto out10;
	}

	if ((device_name = iniparser_getstring(ini, "general:" CONFDEVNAME, NULL)) == NULL) {
		rc = EXIT_FAILURE;
		/* read from crypttab? */
		/* get device from currently open devices? */
		fprintf(stderr, "Could not read LUKS device from configuration file.\n");
		goto out20;
	}

	/* init and open first Yubikey */
	if ((rc = yk_init()) < 0) {
		perror("yk_init() failed");
		goto out20;
	}

	if ((yk = yk_open_first_key()) == NULL) {
		rc = EXIT_FAILURE;
		perror("yk_open_first_key() failed");
		goto out30;
	}

	/* read the serial number from key */
	if ((rc = yk_get_serial(yk, 0, 0, &serial)) < 0) {
		perror("yk_get_serial() failed");
		goto out40;
	}

	/* get the yk slot */
	sprintf(section_ykslot, "%d:" CONFYKSLOT, serial);
	yk_slot = iniparser_getint(ini, "general:" CONFYKSLOT, yk_slot);
	yk_slot = iniparser_getint(ini, section_ykslot, yk_slot);
	switch (yk_slot) {
		case 1:
		case SLOT_CHAL_HMAC1:
			yk_slot = SLOT_CHAL_HMAC1;
			break;
		case 2:
		case SLOT_CHAL_HMAC2:
		default:
			yk_slot = SLOT_CHAL_HMAC2;
			break;
	}

	/* get the luks slot */
	sprintf(section_luksslot, "%d:" CONFLUKSSLOT, serial);
	luks_slot = iniparser_getint(ini, section_luksslot, luks_slot);
	if (luks_slot < 0) {
		rc = EXIT_FAILURE;
		fprintf(stderr, "Please set LUKS key slot for Yubikey with serial %d!\n", serial);
		printf("Add something like this to " CONFIGFILE ":\n\n[%d]\nluks slot = 1\n", serial);
		goto out40;
	}

	/* get random number and limit to printable ASCII character (32 to 126) */
	for(i = 0; i < CHALLENGELEN; i++)
		challenge_new[i] = (rand() % (126 - 32)) + 32;

	/* do challenge/response and encode to hex */
	if ((rc = yk_challenge_response(yk, yk_slot, true,
				CHALLENGELEN, (unsigned char *)challenge_new,
				RESPONSELEN, (unsigned char *)repose_new)) < 0) {
		perror("yk_challenge_response() failed");
		goto out40;
	}
	yubikey_hex_encode((char *)passphrase_new, (char *)repose_new, 20);

	/* initialize crypt device */
	if ((rc = crypt_init_by_name(&cd, device_name)) < 0) {
		fprintf(stderr, "Device %s failed to initialize.\n", device_name);
		goto out40;
	}

	/* these are the filenames for challenge
	 * we need this for reading and writing */
	sprintf(challengefilename, CHALLENGEDIR "/challenge-%d", serial);
	sprintf(challengefiletmpname, CHALLENGEDIR "/challenge-%d-XXXXXX", serial);

	/* write new challenge to file */
	if ((rc = challengefiletmp = mkstemp(challengefiletmpname)) < 0) {
		fprintf(stderr, "Could not open file %s for writing.\n", challengefiletmpname);
		goto out50;
	}
	if ((rc = write(challengefiletmp, challenge_new, CHALLENGELEN)) < 0) {
		fprintf(stderr, "Failed to write challenge to file.\n");
		goto out60;
	}
	challengefiletmp = close(challengefiletmp);

	/* get status of crypt device
	 * We expect this to be active (or busy). It is the actual root device, no? */
	cs = crypt_status(cd, device_name);
	if (cs != CRYPT_ACTIVE && cs != CRYPT_BUSY) {
		rc = EXIT_FAILURE;
                fprintf(stderr, "Device %s is invalid or inactive.\n", device_name);
		goto out60;
	}

	ck = crypt_keyslot_status(cd, luks_slot);
	if (ck == CRYPT_SLOT_INVALID) {
		rc = EXIT_FAILURE;
		fprintf(stderr, "Key slot %d is invalid.\n", luks_slot);
		goto out60;
	} else if (ck == CRYPT_SLOT_ACTIVE || ck == CRYPT_SLOT_ACTIVE_LAST) {
		/* read challenge from file */
		if ((rc = challengefile = open(challengefilename, O_RDONLY)) < 0) {
			perror("Failed opening challenge file for reading");
			goto out60;
		}

		if ((rc = read(challengefile, challenge_old, CHALLENGELEN)) < 0) {
			perror("Failed reading challenge from file");
			goto out60;
		}

		challengefile = close(challengefile);
		/* finished reading challenge */

		/* do challenge/response and encode to hex */
		if ((rc = yk_challenge_response(yk, yk_slot, true,
					CHALLENGELEN, (unsigned char *)challenge_old,
					RESPONSELEN, (unsigned char *)repose_old)) < 0) {
			perror("yk_challenge_response() failed");
			goto out60;
		}
		yubikey_hex_encode((char *)passphrase_old, (char *)repose_old, 20);

		if ((rc = crypt_keyslot_change_by_passphrase(cd, luks_slot, luks_slot,
				passphrase_old, PASSPHRASELEN,
				passphrase_new, PASSPHRASELEN)) < 0) {
			fprintf(stderr, "Could not update passphrase for key slot %d.\n", luks_slot);
			goto out60;
		}

		if ((rc = unlink(challengefilename)) < 0) {
			fprintf(stderr, "Failed to delete old challenge file.\n");
			goto out60;
		}
	} else { /* ck == CRYPT_SLOT_INACTIVE */
		if ((rc = crypt_keyslot_add_by_passphrase(cd, luks_slot, NULL, 0,
				passphrase_new, PASSPHRASELEN)) < 0) {
			fprintf(stderr, "Could add passphrase for key slot %d.\n", luks_slot);
			goto out60;
		}
	}

	if ((rc = rename(challengefiletmpname, challengefilename)) < 0) {
		fprintf(stderr, "Failed to rename new challenge file.\n");
		goto out60;
	}

	rc = EXIT_SUCCESS;

out60:
	/* close the challenge file */
	if (challengefile)
		close(challengefile);
	if (challengefiletmp)
		close(challengefiletmp);
	if (access(challengefiletmpname, F_OK) == 0 )
		unlink(challengefiletmpname);

out50:
	/* free crypt context */
	crypt_free(cd);

out40:
	/* close Yubikey */
	if (!yk_close_key(yk))
		perror("yk_close_key() failed");

out30:
	/* release Yubikey */
	if (!yk_release())
		perror("yk_release() failed");

out20:
	/* free iniparser dictionary */
	iniparser_freedict(ini);


out10:
	/* wipe response (cleartext password!) from memory */
	/* This is statically allocated and always save to wipe! */
	memset(challenge_old, 0, CHALLENGELEN + 1);
	memset(challenge_new, 0, CHALLENGELEN + 1);
	memset(repose_old, 0, RESPONSELEN);
	memset(repose_new, 0, RESPONSELEN);
	memset(passphrase_old, 0, PASSPHRASELEN + 1);
	memset(passphrase_new, 0, PASSPHRASELEN + 1);

	return rc;
}