]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
rgw-admin: add mfa resync command
authorYehuda Sadeh <yehuda@redhat.com>
Sun, 3 Dec 2017 13:53:22 +0000 (05:53 -0800)
committerYehuda Sadeh <yehuda@redhat.com>
Mon, 9 Apr 2018 14:01:48 +0000 (07:01 -0700)
$ radosgw-admin mfa resync --uid=<uid> --totp=serial=<serial> \
                --totp-pin=<pin1> --totp-pin=<pin2>

Signed-off-by: Yehuda Sadeh <yehuda@redhat.com>
src/cls/otp/cls_otp.cc
src/cls/otp/cls_otp_types.cc
src/cls/otp/cls_otp_types.h
src/rgw/CMakeLists.txt
src/rgw/rgw_admin.cc

index 1519ea47025e091753fdb1c1970102e81e2e9e59..a8ca2b5afaefbf8dba8939052c8facc68c2aea7e 100644 (file)
@@ -132,7 +132,7 @@ bool otp_instance::verify(const ceph::real_time& timestamp, const string& val)
   }
 
   result = oath_totp_validate3(secret, slen,
-                                   secs, otp.step_size, 0, otp.window,
+                                   secs, otp.step_size, otp.time_ofs, otp.window,
                                    nullptr /* otp pos */, &index,
                                    val.c_str());
   if (result == OATH_INVALID_OTP ||
index a73b74e997c5a86eda3219c30289aedf41233084..159b3604966cbd8fcb96345c23253ebf41aef124 100644 (file)
@@ -28,7 +28,7 @@ void otp_info_t::dump(Formatter *f) const
   encode_json("type", (int)type, f);
   encode_json("id", id, f);
   encode_json("seed", seed, f);
-  encode_json("time_ofs", utime_t(time_ofs), f);
+  encode_json("time_ofs", time_ofs, f);
   encode_json("step_size", step_size, f);
   encode_json("window", window, f);
 }
@@ -40,9 +40,7 @@ void otp_info_t::decode_json(JSONObj *obj)
   type = (OTPType)t;
   JSONDecoder::decode_json("id", id, obj);
   JSONDecoder::decode_json("seed", seed, obj);
-  utime_t to;
-  JSONDecoder::decode_json("time_ofs", to, obj);
-  time_ofs = to.to_real_time();
+  JSONDecoder::decode_json("time_ofs", time_ofs, obj);
   JSONDecoder::decode_json("step_size", step_size, obj);
   JSONDecoder::decode_json("window", window, obj);
 }
index 7810461e60b8f244fd758d78a50062f719f413e7..ff4e4059e3d063be809a3f905c085670ac5a7fe0 100644 (file)
@@ -23,7 +23,7 @@ namespace rados {
         OTPType type{OTP_TOTP};
         string id;
         string seed;
-        ceph::real_time time_ofs;
+        int32_t time_ofs{0};
         uint32_t step_size{30}; /* num of seconds foreach otp to test */
         uint32_t window{2}; /* num of otp after/before start otp to test */
 
index 8ec7f19ddcc848e85d182f37987dd632e65d24a3..606bdcd72e2bc08ae87334124bf5ef9968251b99 100644 (file)
@@ -155,7 +155,8 @@ target_link_libraries(rgw_a librados cls_otp_client cls_lock_client cls_rgw_clie
   cls_replica_log_client cls_user_client ceph-common common_utf8 global
   ${CURL_LIBRARIES}
   ${EXPAT_LIBRARIES}
-  ${OPENLDAP_LIBRARIES} ${CRYPTO_LIBS})
+  ${OPENLDAP_LIBRARIES} ${CRYPTO_LIBS}
+  oath)
 
 if (WITH_RADOSGW_BEAST_FRONTEND)
   target_compile_definitions(rgw_a PUBLIC BOOST_COROUTINES_NO_DEPRECATION_WARNING)
index bd7df8dc86aba1cd0ca943ad70db02475631a534..c6e601a3f47ce7a839a3f59e3ddf4f45e78018f9 100644 (file)
@@ -8,6 +8,8 @@
 
 #include <boost/optional.hpp>
 
+#include <liboath/oath.h>
+
 #include "auth/Crypto.h"
 #include "compressor/Compressor.h"
 
@@ -508,6 +510,7 @@ enum {
   OPT_MFA_GET,
   OPT_MFA_LIST,
   OPT_MFA_CHECK,
+  OPT_MFA_RESYNC,
 };
 
 static int get_cmd(const char *cmd, const char *prev_cmd, const char *prev_prev_cmd, bool *need_more)
@@ -974,6 +977,8 @@ static int get_cmd(const char *cmd, const char *prev_cmd, const char *prev_prev_
       return OPT_MFA_LIST;
     if (strcmp(cmd, "check") == 0)
       return OPT_MFA_CHECK;
+    if (strcmp(cmd, "resync") == 0)
+      return OPT_MFA_RESYNC;
   }
 
   return -EINVAL;
@@ -2395,6 +2400,60 @@ int create_new_bucket_instance(RGWRados *store,
   return 0;
 }
 
+static int scan_totp(CephContext *cct, ceph::real_time& now, rados::cls::otp::otp_info_t& totp, vector<string>& pins,
+                     time_t *pofs)
+{
+#define MAX_TOTP_SKEW_HOURS (24 * 7)
+  assert(pins.size() == 2);
+
+  size_t slen = totp.seed.size() / 2 + 1;
+  char seed[slen];
+  int rc = oath_hex2bin(totp.seed.c_str(), seed, &slen);
+  if (rc != OATH_OK) {
+    cerr << "ERROR: failed to parse seed" << std::endl;
+    return -EINVAL;
+  }
+
+  time_t start_time = ceph::real_clock::to_time_t(now);
+  time_t time_ofs = 0, time_ofs_abs = 0;
+  time_t step_size = totp.step_size;
+  if (step_size == 0) {
+    step_size = OATH_TOTP_DEFAULT_TIME_STEP_SIZE;
+  }
+  uint32_t count = 0;
+  int sign = 1;
+
+  uint32_t max_skew = MAX_TOTP_SKEW_HOURS * 3600;
+
+  while (time_ofs_abs < max_skew) {
+    rc = oath_totp_validate2(seed, slen,
+                             start_time, 
+                             step_size,
+                             time_ofs,
+                             1,
+                             nullptr,
+                             pins[0].c_str());
+    if (rc != OATH_INVALID_OTP) {
+      rc = oath_totp_validate2(seed, slen,
+                               start_time, 
+                               step_size,
+                               time_ofs - step_size, /* smaller time_ofs moves time forward */
+                               1,
+                               nullptr,
+                               pins[1].c_str());
+      if (rc != OATH_INVALID_OTP) {
+        *pofs = time_ofs - step_size + step_size * totp.window / 2;
+        ldout(cct, 20) << "found at time=" << start_time - time_ofs << " time_ofs=" << time_ofs << dendl;
+        return 0;
+      }
+    }
+    sign = -sign;
+    time_ofs_abs = (++count) * step_size;
+    time_ofs = sign * time_ofs_abs;
+  }
+
+  return -ENOENT;
+}
 
 #ifdef BUILDING_FOR_EMBEDDED
 extern "C" int cephd_rgw_admin(int argc, const char **argv)
@@ -2566,7 +2625,7 @@ int main(int argc, const char **argv)
 
   string totp_serial;
   string totp_seed;
-  string totp_pin;
+  vector<string> totp_pin;
   int totp_seconds = 0;
   int totp_window = 0;
 
@@ -2894,7 +2953,7 @@ int main(int argc, const char **argv)
     } else if (ceph_argparse_witharg(args, i, &val, "--totp-serial", (char*)NULL)) {
       totp_serial = val;
     } else if (ceph_argparse_witharg(args, i, &val, "--totp-pin", (char*)NULL)) {
-      totp_pin = val;
+      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-seconds", (char*)NULL)) {
@@ -3054,6 +3113,7 @@ int main(int argc, const char **argv)
   rgw_bucket_init(store->meta_mgr);
   rgw_otp_init(store);
 
+
   struct rgw_curl_setup {
     rgw_curl_setup() {
       rgw::curl::setup_curl(boost::none);
@@ -3063,6 +3123,8 @@ int main(int argc, const char **argv)
     }
   } curl_cleanup;
 
+  oath_init();
+
   StoreDestructor store_destructor(store);
 
   if (raw_storage_op) {
@@ -7446,7 +7508,7 @@ next:
     }
 
     list<rados::cls::otp::otp_info_t> result;
-    int ret = store->check_mfa(user_id, totp_serial, totp_pin);
+    int ret = store->check_mfa(user_id, totp_serial, totp_pin.front());
     if (ret < 0) {
       cerr << "MFA check failed, error: " << cpp_strerror(-ret) << std::endl;
       return -ret;
@@ -7455,5 +7517,63 @@ next:
     cout << "ok" << std::endl;
   }
 
+ if (opt_cmd == OPT_MFA_RESYNC) {
+    if (user_id.empty()) {
+      cerr << "ERROR: user id was not provided (via --uid)" << std::endl;
+      return EINVAL;
+    }
+
+    if (totp_serial.empty()) {
+      cerr << "ERROR: TOTP device serial number was not provided (via --totp-serial)" << std::endl;
+      return EINVAL;
+    }
+
+    if (totp_pin.size() != 2) {
+      cerr << "ERROR: missing two --totp-pin params (--totp-pin=<first> --totp-pin=<second>)" << std::endl;
+    }
+
+    rados::cls::otp::otp_info_t config;
+    int ret = store->get_mfa(user_id, totp_serial, &config);
+    if (ret < 0) {
+      if (ret == -ENOENT || ret == -ENODATA) {
+        cerr << "MFA serial id not found" << std::endl;
+      } else {
+        cerr << "MFA retrieval failed, error: " << cpp_strerror(-ret) << std::endl;
+      }
+      return -ret;
+    }
+
+    ceph::real_time now = real_clock::now(); // need to get it from the osd
+
+    time_t time_ofs;
+
+    ret = scan_totp(store->ctx(), now, config, totp_pin, &time_ofs);
+    if (ret < 0) {
+      if (ret == -ENOENT) {
+        cerr << "failed to resync, TOTP values not found in range" << std::endl;
+      } else {
+        cerr << "ERROR: failed to scan for TOTP values: " << cpp_strerror(-ret) << std::endl;
+      }
+      return -ret;
+    }
+
+    config.time_ofs = time_ofs;
+
+    /* now update the backend */
+    real_time mtime = real_clock::now();
+    string oid = store->get_mfa_oid(user_id);
+
+    ret = store->meta_mgr->mutate(rgw_otp_get_handler(), oid, mtime, &objv_tracker,
+                                  MDLOG_STATUS_WRITE, RGWMetadataHandler::APPLY_ALWAYS,
+                                  [&] {
+      return store->create_mfa(user_id, config, &objv_tracker, mtime);
+    });
+    if (ret < 0) {
+      cerr << "MFA update failed, error: " << cpp_strerror(-ret) << std::endl;
+      return -ret;
+    }
+
+  }
+
   return 0;
 }