return 0;
}
-static int parse_seed(const string& seed, bufferlist *seed_bin)
+static int parse_seed(const string& seed, SeedType seed_type, bufferlist *seed_bin)
{
- size_t slen = seed.size() / 2 + 1;
- char secret[slen];
- int result = oath_hex2bin(seed.c_str(), secret, &slen);
+ size_t slen = seed.length();
+ char secret[seed.length()];
+ char *psecret = secret;
+ int result;
+ bool need_free = false;
+
+ seed_bin->clear();
+
+ switch (seed_type) {
+ case OTP_SEED_BASE32:
+ need_free = true; /* oath_base32_decode allocates dest buffer */
+ result = oath_base32_decode(seed.c_str(), seed.length(),
+ &psecret, &slen);
+ break;
+ default: /* just assume hex is the default */
+ result = oath_hex2bin(seed.c_str(), psecret, &slen);
+ }
if (result != OATH_OK) {
CLS_LOG(20, "failed to parse seed");
return -EINVAL;
}
- seed_bin->clear();
- seed_bin->append(secret, slen);
+ seed_bin->append(psecret, slen);
+
+ if (need_free) {
+ free(psecret);
+ }
return 0;
}
}
instance.otp = entry;
- r = parse_seed(instance.otp.seed, &instance.otp.seed_bin);
+ r = parse_seed(instance.otp.seed, instance.otp.seed_type, &instance.otp.seed_bin);
if (r < 0) {
return r;
}
encode_json("type", (int)type, f);
encode_json("id", id, f);
encode_json("seed", seed, f);
+ string st;
+ switch (seed_type) {
+ case rados::cls::otp::OTP_SEED_HEX:
+ st = "hex";
+ break;
+ case rados::cls::otp::OTP_SEED_BASE32:
+ st = "base32";
+ break;
+ default:
+ st = "unknown";
+ }
+ encode_json("seed_type", st, f);
encode_json("time_ofs", time_ofs, f);
encode_json("step_size", step_size, f);
encode_json("window", window, f);
type = (OTPType)t;
JSONDecoder::decode_json("id", id, obj);
JSONDecoder::decode_json("seed", seed, obj);
+ string st;
+ JSONDecoder::decode_json("seed_type", st, obj);
+ if (st == "hex") {
+ seed_type = OTP_SEED_HEX;
+ } else if (st == "base32") {
+ seed_type = OTP_SEED_BASE32;
+ } else {
+ seed_type = OTP_SEED_UNKNOWN;
+ }
JSONDecoder::decode_json("time_ofs", time_ofs, obj);
JSONDecoder::decode_json("step_size", step_size, obj);
JSONDecoder::decode_json("window", window, obj);
OTP_TOTP = 2,
};
+ enum SeedType {
+ OTP_SEED_UNKNOWN = 0,
+ OTP_SEED_HEX = 1,
+ OTP_SEED_BASE32 = 2,
+ };
+
struct otp_info_t {
OTPType type{OTP_TOTP};
string id;
string seed;
+ SeedType seed_type{OTP_SEED_UNKNOWN};
bufferlist seed_bin; /* parsed seed, built automatically by otp_set_op,
* not being json encoded/decoded on purpose
*/
* then we'll need to branch here */
::encode(id, bl);
::encode(seed, bl);
+ ::encode((uint8_t)seed_type, bl);
::encode(seed_bin, bl);
::encode(time_ofs, bl);
::encode(step_size, bl);
type = (OTPType)t;
::decode(id, bl);
::decode(seed, bl);
+ uint8_t st;
+ ::decode(st, bl);
+ seed_type = (SeedType)st;
::decode(seed_bin, bl);
::decode(time_ofs, bl);
::decode(step_size, bl);
string totp_serial;
string totp_seed;
+ string totp_seed_type = "hex";
vector<string> totp_pin;
int totp_seconds = 0;
int totp_window = 0;
totp_pin.push_back(val);
} else if (ceph_argparse_witharg(args, i, &val, "--totp-seed", (char*)NULL)) {
totp_seed = val;
+ } else if (ceph_argparse_witharg(args, i, &val, "--totp-seed-type", (char*)NULL)) {
+ totp_seed_type = val;
} else if (ceph_argparse_witharg(args, i, &val, "--totp-seconds", (char*)NULL)) {
totp_seconds = atoi(val.c_str());
} else if (ceph_argparse_witharg(args, i, &val, "--totp-window", (char*)NULL)) {
return EINVAL;
}
+
+ rados::cls::otp::SeedType seed_type;
+ if (totp_seed_type == "hex") {
+ seed_type = rados::cls::otp::OTP_SEED_HEX;
+ } else if (totp_seed_type == "base32") {
+ seed_type = rados::cls::otp::OTP_SEED_BASE32;
+ } else {
+ cerr << "ERROR: invalid seed type: " << totp_seed_type << std::endl;
+ return EINVAL;
+ }
+
config.id = totp_serial;
config.seed = totp_seed;
+ config.seed_type = seed_type;
if (totp_seconds > 0) {
config.step_size = totp_seconds;