summaryrefslogtreecommitdiffstats
path: root/src/auth/cephx
diff options
context:
space:
mode:
Diffstat (limited to 'src/auth/cephx')
-rw-r--r--src/auth/cephx/CephxAuthorizeHandler.cc50
-rw-r--r--src/auth/cephx/CephxAuthorizeHandler.h39
-rw-r--r--src/auth/cephx/CephxClientHandler.cc333
-rw-r--r--src/auth/cephx/CephxClientHandler.h78
-rw-r--r--src/auth/cephx/CephxKeyServer.cc479
-rw-r--r--src/auth/cephx/CephxKeyServer.h323
-rw-r--r--src/auth/cephx/CephxProtocol.cc604
-rw-r--r--src/auth/cephx/CephxProtocol.h527
-rw-r--r--src/auth/cephx/CephxServiceHandler.cc421
-rw-r--r--src/auth/cephx/CephxServiceHandler.h54
-rw-r--r--src/auth/cephx/CephxSessionHandler.cc194
-rw-r--r--src/auth/cephx/CephxSessionHandler.h44
12 files changed, 3146 insertions, 0 deletions
diff --git a/src/auth/cephx/CephxAuthorizeHandler.cc b/src/auth/cephx/CephxAuthorizeHandler.cc
new file mode 100644
index 000000000..615b87500
--- /dev/null
+++ b/src/auth/cephx/CephxAuthorizeHandler.cc
@@ -0,0 +1,50 @@
+#include "CephxProtocol.h"
+#include "CephxAuthorizeHandler.h"
+#include "common/dout.h"
+
+#define dout_subsys ceph_subsys_auth
+
+bool CephxAuthorizeHandler::verify_authorizer(
+ CephContext *cct,
+ const KeyStore& keys,
+ const ceph::bufferlist& authorizer_data,
+ size_t connection_secret_required_len,
+ ceph::bufferlist *authorizer_reply,
+ EntityName *entity_name,
+ uint64_t *global_id,
+ AuthCapsInfo *caps_info,
+ CryptoKey *session_key,
+ std::string *connection_secret,
+ std::unique_ptr<AuthAuthorizerChallenge> *challenge)
+{
+ auto iter = authorizer_data.cbegin();
+
+ if (!authorizer_data.length()) {
+ ldout(cct, 1) << "verify authorizer, authorizer_data.length()=0" << dendl;
+ return false;
+ }
+
+ CephXServiceTicketInfo auth_ticket_info;
+
+ bool isvalid = cephx_verify_authorizer(cct, keys, iter,
+ connection_secret_required_len,
+ auth_ticket_info,
+ challenge, connection_secret,
+ authorizer_reply);
+
+ if (isvalid) {
+ *caps_info = auth_ticket_info.ticket.caps;
+ *entity_name = auth_ticket_info.ticket.name;
+ *global_id = auth_ticket_info.ticket.global_id;
+ *session_key = auth_ticket_info.session_key;
+ }
+
+ return isvalid;
+}
+
+// Return type of crypto used for this session's data; for cephx, symmetric authentication
+
+int CephxAuthorizeHandler::authorizer_session_crypto()
+{
+ return SESSION_SYMMETRIC_AUTHENTICATE;
+}
diff --git a/src/auth/cephx/CephxAuthorizeHandler.h b/src/auth/cephx/CephxAuthorizeHandler.h
new file mode 100644
index 000000000..626119078
--- /dev/null
+++ b/src/auth/cephx/CephxAuthorizeHandler.h
@@ -0,0 +1,39 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef CEPH_CEPHXAUTHORIZEHANDLER_H
+#define CEPH_CEPHXAUTHORIZEHANDLER_H
+
+#include "auth/AuthAuthorizeHandler.h"
+#include "include/common_fwd.h"
+
+struct CephxAuthorizeHandler : public AuthAuthorizeHandler {
+ bool verify_authorizer(
+ CephContext *cct,
+ const KeyStore& keys,
+ const ceph::buffer::list& authorizer_data,
+ size_t connection_secret_required_len,
+ ceph::buffer::list *authorizer_reply,
+ EntityName *entity_name,
+ uint64_t *global_id,
+ AuthCapsInfo *caps_info,
+ CryptoKey *session_key,
+ std::string *connection_secret,
+ std::unique_ptr<AuthAuthorizerChallenge> *challenge) override;
+ int authorizer_session_crypto() override;
+};
+
+
+
+#endif
diff --git a/src/auth/cephx/CephxClientHandler.cc b/src/auth/cephx/CephxClientHandler.cc
new file mode 100644
index 000000000..76ccca735
--- /dev/null
+++ b/src/auth/cephx/CephxClientHandler.cc
@@ -0,0 +1,333 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+
+#include <errno.h>
+
+#include "CephxClientHandler.h"
+#include "CephxProtocol.h"
+
+#include "auth/KeyRing.h"
+#include "include/random.h"
+#include "common/ceph_context.h"
+#include "common/config.h"
+#include "common/dout.h"
+
+#define dout_subsys ceph_subsys_auth
+#undef dout_prefix
+#define dout_prefix *_dout << "cephx client: "
+
+using std::string;
+
+using ceph::bufferlist;
+
+void CephxClientHandler::reset()
+{
+ ldout(cct,10) << __func__ << dendl;
+ starting = true;
+ server_challenge = 0;
+}
+
+int CephxClientHandler::build_request(bufferlist& bl) const
+{
+ ldout(cct, 10) << "build_request" << dendl;
+
+ if (need & CEPH_ENTITY_TYPE_AUTH) {
+ /* authenticate */
+ CephXRequestHeader header;
+ header.request_type = CEPHX_GET_AUTH_SESSION_KEY;
+ encode(header, bl);
+
+ CryptoKey secret;
+ const bool got = keyring->get_secret(cct->_conf->name, secret);
+ if (!got) {
+ ldout(cct, 20) << "no secret found for entity: " << cct->_conf->name << dendl;
+ return -ENOENT;
+ }
+
+ // is the key OK?
+ if (!secret.get_secret().length()) {
+ ldout(cct, 20) << "secret for entity " << cct->_conf->name << " is invalid" << dendl;
+ return -EINVAL;
+ }
+
+ CephXAuthenticate req;
+ req.client_challenge = ceph::util::generate_random_number<uint64_t>();
+ std::string error;
+ cephx_calc_client_server_challenge(cct, secret, server_challenge,
+ req.client_challenge, &req.key, error);
+ if (!error.empty()) {
+ ldout(cct, 20) << "cephx_calc_client_server_challenge error: " << error << dendl;
+ return -EIO;
+ }
+
+ req.old_ticket = ticket_handler->ticket;
+
+ // for nautilus+ servers: request other keys at the same time
+ req.other_keys = need;
+
+ if (req.old_ticket.blob.length()) {
+ ldout(cct, 20) << "old ticket len=" << req.old_ticket.blob.length() << dendl;
+ }
+
+ encode(req, bl);
+
+ ldout(cct, 10) << "get auth session key: client_challenge "
+ << std::hex << req.client_challenge << std::dec << dendl;
+ return 0;
+ }
+
+ if (_need_tickets()) {
+ /* get service tickets */
+ ldout(cct, 10) << "get service keys: want=" << want << " need=" << need << " have=" << have << dendl;
+
+ CephXRequestHeader header;
+ header.request_type = CEPHX_GET_PRINCIPAL_SESSION_KEY;
+ encode(header, bl);
+
+ CephXAuthorizer *authorizer = ticket_handler->build_authorizer(global_id);
+ if (!authorizer)
+ return -EINVAL;
+ bl.claim_append(authorizer->bl);
+ delete authorizer;
+
+ CephXServiceTicketRequest req;
+ req.keys = need;
+ encode(req, bl);
+ }
+
+ return 0;
+}
+
+bool CephxClientHandler::_need_tickets() const
+{
+ // do not bother (re)requesting tickets if we *only* need the MGR
+ // ticket; that can happen during an upgrade and we want to avoid a
+ // loop. we'll end up re-requesting it later when the secrets
+ // rotating.
+ return need && need != CEPH_ENTITY_TYPE_MGR;
+}
+
+int CephxClientHandler::handle_response(
+ int ret,
+ bufferlist::const_iterator& indata,
+ CryptoKey *session_key,
+ std::string *connection_secret)
+{
+ ldout(cct, 10) << this << " handle_response ret = " << ret << dendl;
+
+ if (ret < 0)
+ return ret; // hrm!
+
+ if (starting) {
+ CephXServerChallenge ch;
+ try {
+ decode(ch, indata);
+ } catch (ceph::buffer::error& e) {
+ ldout(cct, 1) << __func__ << " failed to decode CephXServerChallenge: "
+ << e.what() << dendl;
+ return -EPERM;
+ }
+ server_challenge = ch.server_challenge;
+ ldout(cct, 10) << " got initial server challenge "
+ << std::hex << server_challenge << std::dec << dendl;
+ starting = false;
+
+ tickets.invalidate_ticket(CEPH_ENTITY_TYPE_AUTH);
+ return -EAGAIN;
+ }
+
+ struct CephXResponseHeader header;
+ try {
+ decode(header, indata);
+ } catch (ceph::buffer::error& e) {
+ ldout(cct, 1) << __func__ << " failed to decode CephXResponseHeader: "
+ << e.what() << dendl;
+ return -EPERM;
+ }
+
+ switch (header.request_type) {
+ case CEPHX_GET_AUTH_SESSION_KEY:
+ {
+ ldout(cct, 10) << " get_auth_session_key" << dendl;
+ CryptoKey secret;
+ const bool got = keyring->get_secret(cct->_conf->name, secret);
+ if (!got) {
+ ldout(cct, 0) << "key not found for " << cct->_conf->name << dendl;
+ return -ENOENT;
+ }
+
+ if (!tickets.verify_service_ticket_reply(secret, indata)) {
+ ldout(cct, 0) << "could not verify service_ticket reply" << dendl;
+ return -EACCES;
+ }
+ ldout(cct, 10) << " want=" << want << " need=" << need << " have=" << have << dendl;
+ if (!indata.end()) {
+ bufferlist cbl, extra_tickets;
+ using ceph::decode;
+ try {
+ decode(cbl, indata);
+ decode(extra_tickets, indata);
+ } catch (ceph::buffer::error& e) {
+ ldout(cct, 1) << __func__ << " failed to decode tickets: "
+ << e.what() << dendl;
+ return -EPERM;
+ }
+ ldout(cct, 10) << " got connection bl " << cbl.length()
+ << " and extra tickets " << extra_tickets.length()
+ << dendl;
+ // for msgr1, both session_key and connection_secret are NULL
+ // so we skip extra_tickets and incur an additional round-trip
+ // to get service tickets via CEPHX_GET_PRINCIPAL_SESSION_KEY
+ // as if talking to a pre-nautilus mon
+ // this wasn't intended but turns out to be needed because in
+ // msgr1 case MonClient doesn't explicitly wait for the monmap
+ // (which is shared together with CEPHX_GET_AUTH_SESSION_KEY
+ // reply)
+ // instead, it waits for CEPHX_GET_PRINCIPAL_SESSION_KEY reply
+ // which comes after the monmap and hence the monmap is always
+ // handled by the time authentication is considered finished
+ // if we start to always process extra_tickets here, MonClient
+ // would have no reason to send CEPHX_GET_PRINCIPAL_SESSION_KEY
+ // and RadosClient::connect() or similar could return with no
+ // actual monmap but just an initial bootstrap stub, leading
+ // to mon commands going out with zero fsid and other issues
+ if (session_key && connection_secret) {
+ CephXTicketHandler& ticket_handler =
+ tickets.get_handler(CEPH_ENTITY_TYPE_AUTH);
+ if (session_key) {
+ *session_key = ticket_handler.session_key;
+ }
+ if (cbl.length() && connection_secret) {
+ auto p = cbl.cbegin();
+ string err;
+ if (decode_decrypt(cct, *connection_secret, *session_key, p,
+ err)) {
+ lderr(cct) << __func__ << " failed to decrypt connection_secret"
+ << dendl;
+ } else {
+ ldout(cct, 10) << " got connection_secret "
+ << connection_secret->size() << " bytes" << dendl;
+ }
+ }
+ if (extra_tickets.length()) {
+ auto p = extra_tickets.cbegin();
+ if (!tickets.verify_service_ticket_reply(
+ *session_key, p)) {
+ lderr(cct) << "could not verify extra service_tickets" << dendl;
+ } else {
+ ldout(cct, 10) << " got extra service_tickets" << dendl;
+ }
+ }
+ }
+ }
+ validate_tickets();
+ if (_need_tickets())
+ ret = -EAGAIN;
+ else
+ ret = 0;
+ }
+ break;
+
+ case CEPHX_GET_PRINCIPAL_SESSION_KEY:
+ {
+ CephXTicketHandler& ticket_handler = tickets.get_handler(CEPH_ENTITY_TYPE_AUTH);
+ ldout(cct, 10) << " get_principal_session_key session_key " << ticket_handler.session_key << dendl;
+
+ if (!tickets.verify_service_ticket_reply(ticket_handler.session_key, indata)) {
+ ldout(cct, 0) << "could not verify service_ticket reply" << dendl;
+ return -EACCES;
+ }
+ validate_tickets();
+ if (!_need_tickets()) {
+ ret = 0;
+ }
+ }
+ break;
+
+ case CEPHX_GET_ROTATING_KEY:
+ {
+ ldout(cct, 10) << " get_rotating_key" << dendl;
+ if (rotating_secrets) {
+ RotatingSecrets secrets;
+ CryptoKey secret_key;
+ const bool got = keyring->get_secret(cct->_conf->name, secret_key);
+ if (!got) {
+ ldout(cct, 0) << "key not found for " << cct->_conf->name << dendl;
+ return -ENOENT;
+ }
+ std::string error;
+ if (decode_decrypt(cct, secrets, secret_key, indata, error)) {
+ ldout(cct, 0) << "could not set rotating key: decode_decrypt failed. error:"
+ << error << dendl;
+ return -EINVAL;
+ } else {
+ rotating_secrets->set_secrets(std::move(secrets));
+ }
+ }
+ }
+ break;
+
+ default:
+ ldout(cct, 0) << " unknown request_type " << header.request_type << dendl;
+ ceph_abort();
+ }
+ return ret;
+}
+
+
+AuthAuthorizer *CephxClientHandler::build_authorizer(uint32_t service_id) const
+{
+ ldout(cct, 10) << "build_authorizer for service " << ceph_entity_type_name(service_id) << dendl;
+ return tickets.build_authorizer(service_id);
+}
+
+
+bool CephxClientHandler::build_rotating_request(bufferlist& bl) const
+{
+ ldout(cct, 10) << "build_rotating_request" << dendl;
+ CephXRequestHeader header;
+ header.request_type = CEPHX_GET_ROTATING_KEY;
+ encode(header, bl);
+ return true;
+}
+
+void CephxClientHandler::prepare_build_request()
+{
+ ldout(cct, 10) << "validate_tickets: want=" << want << " need=" << need
+ << " have=" << have << dendl;
+ validate_tickets();
+ ldout(cct, 10) << "want=" << want << " need=" << need << " have=" << have
+ << dendl;
+
+ ticket_handler = &(tickets.get_handler(CEPH_ENTITY_TYPE_AUTH));
+}
+
+void CephxClientHandler::validate_tickets()
+{
+ // lock should be held for write
+ tickets.validate_tickets(want, have, need);
+}
+
+bool CephxClientHandler::need_tickets()
+{
+ validate_tickets();
+
+ ldout(cct, 20) << "need_tickets: want=" << want
+ << " have=" << have
+ << " need=" << need
+ << dendl;
+
+ return _need_tickets();
+}
diff --git a/src/auth/cephx/CephxClientHandler.h b/src/auth/cephx/CephxClientHandler.h
new file mode 100644
index 000000000..601a5c69f
--- /dev/null
+++ b/src/auth/cephx/CephxClientHandler.h
@@ -0,0 +1,78 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef CEPH_CEPHXCLIENTHANDLER_H
+#define CEPH_CEPHXCLIENTHANDLER_H
+
+#include "auth/AuthClientHandler.h"
+#include "CephxProtocol.h"
+#include "auth/RotatingKeyRing.h"
+#include "include/common_fwd.h"
+
+class KeyRing;
+
+class CephxClientHandler : public AuthClientHandler {
+ bool starting;
+
+ /* envelope protocol parameters */
+ uint64_t server_challenge;
+
+ CephXTicketManager tickets;
+ CephXTicketHandler* ticket_handler;
+
+ RotatingKeyRing* rotating_secrets;
+ KeyRing *keyring;
+
+public:
+ CephxClientHandler(CephContext *cct_,
+ RotatingKeyRing *rsecrets)
+ : AuthClientHandler(cct_),
+ starting(false),
+ server_challenge(0),
+ tickets(cct_),
+ ticket_handler(NULL),
+ rotating_secrets(rsecrets),
+ keyring(rsecrets->get_keyring())
+ {
+ reset();
+ }
+
+ CephxClientHandler* clone() const override {
+ return new CephxClientHandler(*this);
+ }
+
+ void reset() override;
+ void prepare_build_request() override;
+ int build_request(ceph::buffer::list& bl) const override;
+ int handle_response(int ret, ceph::buffer::list::const_iterator& iter,
+ CryptoKey *session_key,
+ std::string *connection_secret) override;
+ bool build_rotating_request(ceph::buffer::list& bl) const override;
+
+ int get_protocol() const override { return CEPH_AUTH_CEPHX; }
+
+ AuthAuthorizer *build_authorizer(uint32_t service_id) const override;
+
+ bool need_tickets() override;
+
+ void set_global_id(uint64_t id) override {
+ global_id = id;
+ tickets.global_id = id;
+ }
+private:
+ void validate_tickets() override;
+ bool _need_tickets() const;
+};
+
+#endif
diff --git a/src/auth/cephx/CephxKeyServer.cc b/src/auth/cephx/CephxKeyServer.cc
new file mode 100644
index 000000000..236ac451a
--- /dev/null
+++ b/src/auth/cephx/CephxKeyServer.cc
@@ -0,0 +1,479 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/config.h"
+#include "CephxKeyServer.h"
+#include "common/dout.h"
+#include <sstream>
+
+#define dout_subsys ceph_subsys_auth
+#undef dout_prefix
+#define dout_prefix *_dout << "cephx keyserverdata: "
+
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+
+using ceph::bufferptr;
+using ceph::bufferlist;
+using ceph::Formatter;
+
+bool KeyServerData::get_service_secret(CephContext *cct, uint32_t service_id,
+ CryptoKey& secret, uint64_t& secret_id,
+ double& ttl) const
+{
+ auto iter = rotating_secrets.find(service_id);
+ if (iter == rotating_secrets.end()) {
+ ldout(cct, 10) << "get_service_secret service " << ceph_entity_type_name(service_id) << " not found " << dendl;
+ return false;
+ }
+
+ const RotatingSecrets& secrets = iter->second;
+
+ // second to oldest, unless it's expired
+ auto riter = secrets.secrets.begin();
+ if (secrets.secrets.size() > 1)
+ ++riter;
+
+ utime_t now = ceph_clock_now();
+ if (riter->second.expiration < now)
+ ++riter; // "current" key has expired, use "next" key instead
+
+ secret_id = riter->first;
+ secret = riter->second.key;
+
+ // ttl may have just been increased by the user
+ // cap it by expiration of "next" key to prevent handing out a ticket
+ // with a bogus, possibly way into the future, validity
+ ttl = service_id == CEPH_ENTITY_TYPE_AUTH ?
+ cct->_conf->auth_mon_ticket_ttl : cct->_conf->auth_service_ticket_ttl;
+ ttl = std::min(ttl, static_cast<double>(
+ secrets.secrets.rbegin()->second.expiration - now));
+
+ ldout(cct, 30) << __func__ << " service "
+ << ceph_entity_type_name(service_id) << " secret_id "
+ << secret_id << " " << riter->second << " ttl " << ttl
+ << dendl;
+ return true;
+}
+
+bool KeyServerData::get_service_secret(CephContext *cct, uint32_t service_id,
+ uint64_t secret_id, CryptoKey& secret) const
+{
+ auto iter = rotating_secrets.find(service_id);
+ if (iter == rotating_secrets.end()) {
+ ldout(cct, 10) << __func__ << " no rotating_secrets for service " << service_id
+ << " " << ceph_entity_type_name(service_id) << dendl;
+ return false;
+ }
+
+ const RotatingSecrets& secrets = iter->second;
+ auto riter = secrets.secrets.find(secret_id);
+
+ if (riter == secrets.secrets.end()) {
+ ldout(cct, 10) << "get_service_secret service " << ceph_entity_type_name(service_id)
+ << " secret " << secret_id << " not found" << dendl;
+ ldout(cct, 30) << " I have:" << dendl;
+ for (auto iter = secrets.secrets.begin();
+ iter != secrets.secrets.end();
+ ++iter)
+ ldout(cct, 30) << " id " << iter->first << " " << iter->second << dendl;
+ return false;
+ }
+
+ secret = riter->second.key;
+
+ return true;
+}
+bool KeyServerData::get_auth(const EntityName& name, EntityAuth& auth) const {
+ auto iter = secrets.find(name);
+ if (iter != secrets.end()) {
+ auth = iter->second;
+ return true;
+ }
+ return extra_secrets->get_auth(name, auth);
+}
+
+bool KeyServerData::get_secret(const EntityName& name, CryptoKey& secret) const {
+ auto iter = secrets.find(name);
+ if (iter != secrets.end()) {
+ secret = iter->second.key;
+ return true;
+ }
+ return extra_secrets->get_secret(name, secret);
+}
+
+bool KeyServerData::get_caps(CephContext *cct, const EntityName& name,
+ const string& type, AuthCapsInfo& caps_info) const
+{
+ caps_info.allow_all = false;
+
+ ldout(cct, 10) << "get_caps: name=" << name.to_str() << dendl;
+ auto iter = secrets.find(name);
+ if (iter != secrets.end()) {
+ ldout(cct, 10) << "get_caps: num of caps=" << iter->second.caps.size() << dendl;
+ auto capsiter = iter->second.caps.find(type);
+ if (capsiter != iter->second.caps.end()) {
+ caps_info.caps = capsiter->second;
+ }
+ return true;
+ }
+
+ return extra_secrets->get_caps(name, type, caps_info);
+}
+
+
+#undef dout_prefix
+#define dout_prefix *_dout << "cephx keyserver: "
+
+
+KeyServer::KeyServer(CephContext *cct_, KeyRing *extra_secrets)
+ : cct(cct_),
+ data(extra_secrets),
+ lock{ceph::make_mutex("KeyServer::lock")}
+{
+}
+
+int KeyServer::start_server()
+{
+ std::scoped_lock l{lock};
+ _dump_rotating_secrets();
+ return 0;
+}
+
+void KeyServer::dump()
+{
+ _dump_rotating_secrets();
+}
+
+void KeyServer::_dump_rotating_secrets()
+{
+ ldout(cct, 30) << "_dump_rotating_secrets" << dendl;
+ for (auto iter = data.rotating_secrets.begin();
+ iter != data.rotating_secrets.end();
+ ++iter) {
+ RotatingSecrets& key = iter->second;
+ for (auto mapiter = key.secrets.begin();
+ mapiter != key.secrets.end();
+ ++mapiter)
+ ldout(cct, 30) << "service " << ceph_entity_type_name(iter->first)
+ << " id " << mapiter->first
+ << " key " << mapiter->second << dendl;
+ }
+}
+
+int KeyServer::_rotate_secret(uint32_t service_id, KeyServerData &pending_data)
+{
+ RotatingSecrets& r = pending_data.rotating_secrets[service_id];
+ int added = 0;
+ utime_t now = ceph_clock_now();
+ double ttl = service_id == CEPH_ENTITY_TYPE_AUTH ? cct->_conf->auth_mon_ticket_ttl : cct->_conf->auth_service_ticket_ttl;
+
+ while (r.need_new_secrets(now)) {
+ ExpiringCryptoKey ek;
+ generate_secret(ek.key);
+ if (r.empty()) {
+ ek.expiration = now;
+ } else {
+ utime_t next_ttl = now;
+ next_ttl += ttl;
+ ek.expiration = std::max(next_ttl, r.next().expiration);
+ }
+ ek.expiration += ttl;
+ uint64_t secret_id = r.add(ek);
+ ldout(cct, 10) << "_rotate_secret adding " << ceph_entity_type_name(service_id) << dendl;
+ ldout(cct, 30) << "_rotate_secret adding " << ceph_entity_type_name(service_id)
+ << " id " << secret_id << " " << ek
+ << dendl;
+ added++;
+ }
+ return added;
+}
+
+bool KeyServer::get_secret(const EntityName& name, CryptoKey& secret) const
+{
+ std::scoped_lock l{lock};
+ return data.get_secret(name, secret);
+}
+
+bool KeyServer::get_auth(const EntityName& name, EntityAuth& auth) const
+{
+ std::scoped_lock l{lock};
+ return data.get_auth(name, auth);
+}
+
+bool KeyServer::get_caps(const EntityName& name, const string& type,
+ AuthCapsInfo& caps_info) const
+{
+ std::scoped_lock l{lock};
+
+ return data.get_caps(cct, name, type, caps_info);
+}
+
+bool KeyServer::get_service_secret(uint32_t service_id, CryptoKey& secret,
+ uint64_t& secret_id, double& ttl) const
+{
+ std::scoped_lock l{lock};
+
+ return data.get_service_secret(cct, service_id, secret, secret_id, ttl);
+}
+
+bool KeyServer::get_service_secret(uint32_t service_id,
+ uint64_t secret_id, CryptoKey& secret) const
+{
+ std::scoped_lock l{lock};
+
+ return data.get_service_secret(cct, service_id, secret_id, secret);
+}
+
+void KeyServer::note_used_pending_key(const EntityName& name, const CryptoKey& key)
+{
+ std::scoped_lock l(lock);
+ used_pending_keys[name] = key;
+}
+
+void KeyServer::clear_used_pending_keys()
+{
+ std::scoped_lock l(lock);
+ used_pending_keys.clear();
+}
+
+std::map<EntityName,CryptoKey> KeyServer::get_used_pending_keys()
+{
+ std::map<EntityName,CryptoKey> ret;
+ std::scoped_lock l(lock);
+ ret.swap(used_pending_keys);
+ return ret;
+}
+
+bool KeyServer::generate_secret(CryptoKey& secret)
+{
+ bufferptr bp;
+ CryptoHandler *crypto = cct->get_crypto_handler(CEPH_CRYPTO_AES);
+ if (!crypto)
+ return false;
+
+ if (crypto->create(cct->random(), bp) < 0)
+ return false;
+
+ secret.set_secret(CEPH_CRYPTO_AES, bp, ceph_clock_now());
+
+ return true;
+}
+
+bool KeyServer::generate_secret(EntityName& name, CryptoKey& secret)
+{
+ if (!generate_secret(secret))
+ return false;
+
+ std::scoped_lock l{lock};
+
+ EntityAuth auth;
+ auth.key = secret;
+
+ data.add_auth(name, auth);
+
+ return true;
+}
+
+bool KeyServer::contains(const EntityName& name) const
+{
+ std::scoped_lock l{lock};
+
+ return data.contains(name);
+}
+
+int KeyServer::encode_secrets(Formatter *f, stringstream *ds) const
+{
+ std::scoped_lock l{lock};
+ auto mapiter = data.secrets_begin();
+
+ if (mapiter == data.secrets_end())
+ return -ENOENT;
+
+ if (f)
+ f->open_array_section("auth_dump");
+
+ while (mapiter != data.secrets_end()) {
+ const EntityName& name = mapiter->first;
+ if (ds) {
+ *ds << name.to_str() << std::endl;
+ *ds << "\tkey: " << mapiter->second.key << std::endl;
+ }
+ if (f) {
+ f->open_object_section("auth_entities");
+ f->dump_string("entity", name.to_str());
+ f->dump_stream("key") << mapiter->second.key;
+ f->open_object_section("caps");
+ }
+
+ auto capsiter = mapiter->second.caps.begin();
+ for (; capsiter != mapiter->second.caps.end(); ++capsiter) {
+ // FIXME: need a const_iterator for bufferlist, but it doesn't exist yet.
+ bufferlist *bl = const_cast<bufferlist*>(&capsiter->second);
+ auto dataiter = bl->cbegin();
+ string caps;
+ using ceph::decode;
+ decode(caps, dataiter);
+ if (ds)
+ *ds << "\tcaps: [" << capsiter->first << "] " << caps << std::endl;
+ if (f)
+ f->dump_string(capsiter->first.c_str(), caps);
+ }
+ if (f) {
+ f->close_section(); // caps
+ f->close_section(); // auth_entities
+ }
+
+ ++mapiter;
+ }
+
+ if (f)
+ f->close_section(); // auth_dump
+ return 0;
+}
+
+void KeyServer::encode_formatted(string label, Formatter *f, bufferlist &bl)
+{
+ ceph_assert(f != NULL);
+ f->open_object_section(label.c_str());
+ encode_secrets(f, NULL);
+ f->close_section();
+ f->flush(bl);
+}
+
+void KeyServer::encode_plaintext(bufferlist &bl)
+{
+ stringstream os;
+ encode_secrets(NULL, &os);
+ bl.append(os.str());
+}
+
+bool KeyServer::prepare_rotating_update(bufferlist& rotating_bl)
+{
+ std::scoped_lock l{lock};
+ ldout(cct, 20) << __func__ << " before: data.rotating_ver=" << data.rotating_ver
+ << dendl;
+
+ KeyServerData pending_data(nullptr);
+ pending_data.rotating_ver = data.rotating_ver + 1;
+ pending_data.rotating_secrets = data.rotating_secrets;
+
+ int added = 0;
+ added += _rotate_secret(CEPH_ENTITY_TYPE_AUTH, pending_data);
+ added += _rotate_secret(CEPH_ENTITY_TYPE_MON, pending_data);
+ added += _rotate_secret(CEPH_ENTITY_TYPE_OSD, pending_data);
+ added += _rotate_secret(CEPH_ENTITY_TYPE_MDS, pending_data);
+ added += _rotate_secret(CEPH_ENTITY_TYPE_MGR, pending_data);
+ if (!added) {
+ return false;
+ }
+
+ ldout(cct, 20) << __func__ << " after: pending_data.rotating_ver="
+ << pending_data.rotating_ver
+ << dendl;
+ pending_data.encode_rotating(rotating_bl);
+ return true;
+}
+
+bool KeyServer::get_rotating_encrypted(const EntityName& name,
+ bufferlist& enc_bl) const
+{
+ std::scoped_lock l{lock};
+
+ auto mapiter = data.find_name(name);
+ if (mapiter == data.secrets_end())
+ return false;
+
+ const CryptoKey& specific_key = mapiter->second.key;
+
+ auto rotate_iter = data.rotating_secrets.find(name.get_type());
+ if (rotate_iter == data.rotating_secrets.end())
+ return false;
+
+ RotatingSecrets secrets = rotate_iter->second;
+
+ std::string error;
+ if (encode_encrypt(cct, secrets, specific_key, enc_bl, error))
+ return false;
+
+ return true;
+}
+
+bool KeyServer::_get_service_caps(const EntityName& name, uint32_t service_id,
+ AuthCapsInfo& caps_info) const
+{
+ string s = ceph_entity_type_name(service_id);
+
+ return data.get_caps(cct, name, s, caps_info);
+}
+
+bool KeyServer::get_service_caps(const EntityName& name, uint32_t service_id,
+ AuthCapsInfo& caps_info) const
+{
+ std::scoped_lock l{lock};
+ return _get_service_caps(name, service_id, caps_info);
+}
+
+
+int KeyServer::_build_session_auth_info(uint32_t service_id,
+ const AuthTicket& parent_ticket,
+ CephXSessionAuthInfo& info,
+ double ttl)
+{
+ info.service_id = service_id;
+ info.ticket = parent_ticket;
+ info.ticket.init_timestamps(ceph_clock_now(), ttl);
+ info.validity.set_from_double(ttl);
+
+ generate_secret(info.session_key);
+
+ // mon keys are stored externally. and the caps are blank anyway.
+ if (service_id != CEPH_ENTITY_TYPE_MON) {
+ string s = ceph_entity_type_name(service_id);
+ if (!data.get_caps(cct, info.ticket.name, s, info.ticket.caps)) {
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+int KeyServer::build_session_auth_info(uint32_t service_id,
+ const AuthTicket& parent_ticket,
+ CephXSessionAuthInfo& info)
+{
+ double ttl;
+ if (!get_service_secret(service_id, info.service_secret, info.secret_id,
+ ttl)) {
+ return -EACCES;
+ }
+
+ std::scoped_lock l{lock};
+ return _build_session_auth_info(service_id, parent_ticket, info, ttl);
+}
+
+int KeyServer::build_session_auth_info(uint32_t service_id,
+ const AuthTicket& parent_ticket,
+ const CryptoKey& service_secret,
+ uint64_t secret_id,
+ CephXSessionAuthInfo& info)
+{
+ info.service_secret = service_secret;
+ info.secret_id = secret_id;
+
+ std::scoped_lock l{lock};
+ return _build_session_auth_info(service_id, parent_ticket, info,
+ cct->_conf->auth_service_ticket_ttl);
+}
+
diff --git a/src/auth/cephx/CephxKeyServer.h b/src/auth/cephx/CephxKeyServer.h
new file mode 100644
index 000000000..64915c8ce
--- /dev/null
+++ b/src/auth/cephx/CephxKeyServer.h
@@ -0,0 +1,323 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef CEPH_KEYSSERVER_H
+#define CEPH_KEYSSERVER_H
+
+#include "auth/KeyRing.h"
+#include "CephxProtocol.h"
+#include "common/ceph_mutex.h"
+#include "include/common_fwd.h"
+
+struct KeyServerData {
+ version_t version;
+
+ /* for each entity */
+ std::map<EntityName, EntityAuth> secrets;
+ KeyRing *extra_secrets;
+
+ /* for each service type */
+ version_t rotating_ver;
+ std::map<uint32_t, RotatingSecrets> rotating_secrets;
+
+ explicit KeyServerData(KeyRing *extra)
+ : version(0),
+ extra_secrets(extra),
+ rotating_ver(0) {}
+
+ void encode(ceph::buffer::list& bl) const {
+ __u8 struct_v = 1;
+ using ceph::encode;
+ encode(struct_v, bl);
+ encode(version, bl);
+ encode(rotating_ver, bl);
+ encode(secrets, bl);
+ encode(rotating_secrets, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ __u8 struct_v;
+ decode(struct_v, bl);
+ decode(version, bl);
+ decode(rotating_ver, bl);
+ decode(secrets, bl);
+ decode(rotating_secrets, bl);
+ }
+
+ void encode_rotating(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ __u8 struct_v = 1;
+ encode(struct_v, bl);
+ encode(rotating_ver, bl);
+ encode(rotating_secrets, bl);
+ }
+ void decode_rotating(ceph::buffer::list& rotating_bl) {
+ using ceph::decode;
+ auto iter = rotating_bl.cbegin();
+ __u8 struct_v;
+ decode(struct_v, iter);
+ decode(rotating_ver, iter);
+ decode(rotating_secrets, iter);
+ }
+
+ bool contains(const EntityName& name) const {
+ return (secrets.find(name) != secrets.end());
+ }
+
+ void clear_secrets() {
+ version = 0;
+ secrets.clear();
+ rotating_ver = 0;
+ rotating_secrets.clear();
+ }
+
+ void add_auth(const EntityName& name, EntityAuth& auth) {
+ secrets[name] = auth;
+ }
+
+ void remove_secret(const EntityName& name) {
+ auto iter = secrets.find(name);
+ if (iter == secrets.end())
+ return;
+ secrets.erase(iter);
+ }
+
+ bool get_service_secret(CephContext *cct, uint32_t service_id,
+ CryptoKey& secret, uint64_t& secret_id,
+ double& ttl) const;
+ bool get_service_secret(CephContext *cct, uint32_t service_id,
+ uint64_t secret_id, CryptoKey& secret) const;
+ bool get_auth(const EntityName& name, EntityAuth& auth) const;
+ bool get_secret(const EntityName& name, CryptoKey& secret) const;
+ bool get_caps(CephContext *cct, const EntityName& name,
+ const std::string& type, AuthCapsInfo& caps) const;
+
+ std::map<EntityName, EntityAuth>::iterator secrets_begin()
+ { return secrets.begin(); }
+ std::map<EntityName, EntityAuth>::const_iterator secrets_begin() const
+ { return secrets.begin(); }
+ std::map<EntityName, EntityAuth>::iterator secrets_end()
+ { return secrets.end(); }
+ std::map<EntityName, EntityAuth>::const_iterator secrets_end() const
+ { return secrets.end(); }
+ std::map<EntityName, EntityAuth>::iterator find_name(const EntityName& name)
+ { return secrets.find(name); }
+ std::map<EntityName, EntityAuth>::const_iterator find_name(const EntityName& name) const
+ { return secrets.find(name); }
+
+
+ // -- incremental updates --
+ typedef enum {
+ AUTH_INC_NOP,
+ AUTH_INC_ADD,
+ AUTH_INC_DEL,
+ AUTH_INC_SET_ROTATING,
+ } IncrementalOp;
+
+ struct Incremental {
+ IncrementalOp op;
+ ceph::buffer::list rotating_bl; // if SET_ROTATING. otherwise,
+ EntityName name;
+ EntityAuth auth;
+
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ __u8 struct_v = 1;
+ encode(struct_v, bl);
+ __u32 _op = (__u32)op;
+ encode(_op, bl);
+ if (op == AUTH_INC_SET_ROTATING) {
+ encode(rotating_bl, bl);
+ } else {
+ encode(name, bl);
+ encode(auth, bl);
+ }
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ __u8 struct_v;
+ decode(struct_v, bl);
+ __u32 _op;
+ decode(_op, bl);
+ op = (IncrementalOp)_op;
+ ceph_assert(op >= AUTH_INC_NOP && op <= AUTH_INC_SET_ROTATING);
+ if (op == AUTH_INC_SET_ROTATING) {
+ decode(rotating_bl, bl);
+ } else {
+ decode(name, bl);
+ decode(auth, bl);
+ }
+ }
+ };
+
+ void apply_incremental(Incremental& inc) {
+ switch (inc.op) {
+ case AUTH_INC_ADD:
+ add_auth(inc.name, inc.auth);
+ break;
+
+ case AUTH_INC_DEL:
+ remove_secret(inc.name);
+ break;
+
+ case AUTH_INC_SET_ROTATING:
+ decode_rotating(inc.rotating_bl);
+ break;
+
+ case AUTH_INC_NOP:
+ break;
+
+ default:
+ ceph_abort();
+ }
+ }
+
+};
+WRITE_CLASS_ENCODER(KeyServerData)
+WRITE_CLASS_ENCODER(KeyServerData::Incremental)
+
+
+
+
+class KeyServer : public KeyStore {
+ CephContext *cct;
+ KeyServerData data;
+ std::map<EntityName, CryptoKey> used_pending_keys;
+ mutable ceph::mutex lock;
+
+ int _rotate_secret(uint32_t service_id, KeyServerData &pending_data);
+ void _dump_rotating_secrets();
+ int _build_session_auth_info(uint32_t service_id,
+ const AuthTicket& parent_ticket,
+ CephXSessionAuthInfo& info,
+ double ttl);
+ bool _get_service_caps(const EntityName& name, uint32_t service_id,
+ AuthCapsInfo& caps) const;
+public:
+ KeyServer(CephContext *cct_, KeyRing *extra_secrets);
+ bool generate_secret(CryptoKey& secret);
+
+ bool get_secret(const EntityName& name, CryptoKey& secret) const override;
+ bool get_auth(const EntityName& name, EntityAuth& auth) const;
+ bool get_caps(const EntityName& name, const std::string& type, AuthCapsInfo& caps) const;
+ bool get_active_rotating_secret(const EntityName& name, CryptoKey& secret) const;
+
+ void note_used_pending_key(const EntityName& name, const CryptoKey& key);
+ void clear_used_pending_keys();
+ std::map<EntityName,CryptoKey> get_used_pending_keys();
+
+ int start_server();
+ void rotate_timeout(double timeout);
+
+ void dump();
+
+ int build_session_auth_info(uint32_t service_id,
+ const AuthTicket& parent_ticket,
+ CephXSessionAuthInfo& info);
+ int build_session_auth_info(uint32_t service_id,
+ const AuthTicket& parent_ticket,
+ const CryptoKey& service_secret,
+ uint64_t secret_id,
+ CephXSessionAuthInfo& info);
+
+ /* get current secret for specific service type */
+ bool get_service_secret(uint32_t service_id, CryptoKey& secret,
+ uint64_t& secret_id, double& ttl) const;
+ bool get_service_secret(uint32_t service_id, uint64_t secret_id,
+ CryptoKey& secret) const override;
+
+ bool generate_secret(EntityName& name, CryptoKey& secret);
+
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ encode(data, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ std::scoped_lock l{lock};
+ using ceph::decode;
+ decode(data, bl);
+ }
+ bool contains(const EntityName& name) const;
+ int encode_secrets(ceph::Formatter *f, std::stringstream *ds) const;
+ void encode_formatted(std::string label, ceph::Formatter *f, ceph::buffer::list &bl);
+ void encode_plaintext(ceph::buffer::list &bl);
+ int list_secrets(std::stringstream& ds) const {
+ return encode_secrets(NULL, &ds);
+ }
+ version_t get_ver() const {
+ std::scoped_lock l{lock};
+ return data.version;
+ }
+
+ void clear_secrets() {
+ std::scoped_lock l{lock};
+ data.clear_secrets();
+ }
+
+ void apply_data_incremental(KeyServerData::Incremental& inc) {
+ std::scoped_lock l{lock};
+ data.apply_incremental(inc);
+ }
+ void set_ver(version_t ver) {
+ std::scoped_lock l{lock};
+ data.version = ver;
+ }
+
+ void add_auth(const EntityName& name, EntityAuth& auth) {
+ std::scoped_lock l{lock};
+ data.add_auth(name, auth);
+ }
+
+ void remove_secret(const EntityName& name) {
+ std::scoped_lock l{lock};
+ data.remove_secret(name);
+ }
+
+ bool has_secrets() {
+ auto b = data.secrets_begin();
+ return (b != data.secrets_end());
+ }
+ int get_num_secrets() {
+ std::scoped_lock l{lock};
+ return data.secrets.size();
+ }
+
+ void clone_to(KeyServerData& dst) const {
+ std::scoped_lock l{lock};
+ dst = data;
+ }
+ void export_keyring(KeyRing& keyring) {
+ std::scoped_lock l{lock};
+ for (auto p = data.secrets.begin(); p != data.secrets.end(); ++p) {
+ keyring.add(p->first, p->second);
+ }
+ }
+
+ bool prepare_rotating_update(ceph::buffer::list& rotating_bl);
+
+ bool get_rotating_encrypted(const EntityName& name, ceph::buffer::list& enc_bl) const;
+
+ ceph::mutex& get_lock() const { return lock; }
+ bool get_service_caps(const EntityName& name, uint32_t service_id,
+ AuthCapsInfo& caps) const;
+
+ std::map<EntityName, EntityAuth>::iterator secrets_begin()
+ { return data.secrets_begin(); }
+ std::map<EntityName, EntityAuth>::iterator secrets_end()
+ { return data.secrets_end(); }
+};
+WRITE_CLASS_ENCODER(KeyServer)
+
+
+#endif
diff --git a/src/auth/cephx/CephxProtocol.cc b/src/auth/cephx/CephxProtocol.cc
new file mode 100644
index 000000000..87a8b86b9
--- /dev/null
+++ b/src/auth/cephx/CephxProtocol.cc
@@ -0,0 +1,604 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2009-2011 New Dream Network
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "CephxProtocol.h"
+#include "common/Clock.h"
+#include "common/ceph_context.h"
+#include "common/config.h"
+#include "common/debug.h"
+#include "include/buffer.h"
+
+#define dout_subsys ceph_subsys_auth
+#undef dout_prefix
+#define dout_prefix *_dout << "cephx: "
+
+using std::dec;
+using std::hex;
+using std::vector;
+
+using ceph::bufferlist;
+using ceph::decode;
+using ceph::encode;
+
+void cephx_calc_client_server_challenge(CephContext *cct, CryptoKey& secret, uint64_t server_challenge,
+ uint64_t client_challenge, uint64_t *key, std::string &error)
+{
+ CephXChallengeBlob b;
+ b.server_challenge = server_challenge;
+ b.client_challenge = client_challenge;
+
+ bufferlist enc;
+ if (encode_encrypt(cct, b, secret, enc, error))
+ return;
+
+ uint64_t k = 0;
+ const ceph_le64 *p = (const ceph_le64 *)enc.c_str();
+ for (int pos = 0; pos + sizeof(k) <= enc.length(); pos+=sizeof(k), p++)
+ k ^= *p;
+ *key = k;
+}
+
+
+/*
+ * Authentication
+ */
+
+bool cephx_build_service_ticket_blob(CephContext *cct, CephXSessionAuthInfo& info,
+ CephXTicketBlob& blob)
+{
+ CephXServiceTicketInfo ticket_info;
+ ticket_info.session_key = info.session_key;
+ ticket_info.ticket = info.ticket;
+ ticket_info.ticket.caps = info.ticket.caps;
+
+ ldout(cct, 10) << "build_service_ticket service "
+ << ceph_entity_type_name(info.service_id)
+ << " secret_id " << info.secret_id
+ << " ticket_info.ticket.name="
+ << ticket_info.ticket.name.to_str()
+ << " ticket.global_id " << info.ticket.global_id << dendl;
+ blob.secret_id = info.secret_id;
+ std::string error;
+ if (!info.service_secret.get_secret().length())
+ error = "invalid key"; // Bad key?
+ else
+ encode_encrypt_enc_bl(cct, ticket_info, info.service_secret, blob.blob, error);
+ if (!error.empty()) {
+ ldout(cct, -1) << "cephx_build_service_ticket_blob failed with error "
+ << error << dendl;
+ return false;
+ }
+ return true;
+}
+
+/*
+ * AUTH SERVER: authenticate
+ *
+ * Authenticate principal, respond with AuthServiceTicketInfo
+ *
+ * {session key, validity}^principal_secret
+ * {principal_ticket, session key}^service_secret ... "enc_ticket"
+ */
+bool cephx_build_service_ticket_reply(CephContext *cct,
+ CryptoKey& principal_secret,
+ vector<CephXSessionAuthInfo> ticket_info_vec,
+ bool should_encrypt_ticket,
+ CryptoKey& ticket_enc_key,
+ bufferlist& reply)
+{
+ __u8 service_ticket_reply_v = 1;
+ using ceph::encode;
+ encode(service_ticket_reply_v, reply);
+
+ uint32_t num = ticket_info_vec.size();
+ encode(num, reply);
+ ldout(cct, 10) << "build_service_ticket_reply encoding " << num
+ << " tickets with secret " << principal_secret << dendl;
+
+ for (auto ticket_iter = ticket_info_vec.begin();
+ ticket_iter != ticket_info_vec.end();
+ ++ticket_iter) {
+ CephXSessionAuthInfo& info = *ticket_iter;
+ encode(info.service_id, reply);
+
+ __u8 service_ticket_v = 1;
+ encode(service_ticket_v, reply);
+
+ CephXServiceTicket msg_a;
+ msg_a.session_key = info.session_key;
+ msg_a.validity = info.validity;
+ std::string error;
+ if (encode_encrypt(cct, msg_a, principal_secret, reply, error)) {
+ ldout(cct, -1) << "error encoding encrypted: " << error << dendl;
+ return false;
+ }
+
+ bufferlist service_ticket_bl;
+ CephXTicketBlob blob;
+ if (!cephx_build_service_ticket_blob(cct, info, blob)) {
+ return false;
+ }
+ encode(blob, service_ticket_bl);
+
+ ldout(cct, 30) << "service_ticket_blob is ";
+ service_ticket_bl.hexdump(*_dout);
+ *_dout << dendl;
+
+ encode((__u8)should_encrypt_ticket, reply);
+ if (should_encrypt_ticket) {
+ if (encode_encrypt(cct, service_ticket_bl, ticket_enc_key, reply, error)) {
+ ldout(cct, -1) << "error encoding encrypted ticket: " << error << dendl;
+ return false;
+ }
+ } else {
+ encode(service_ticket_bl, reply);
+ }
+ }
+ return true;
+}
+
+/*
+ * PRINCIPAL: verify our attempt to authenticate succeeded. fill out
+ * this ServiceTicket with the result.
+ */
+bool CephXTicketHandler::verify_service_ticket_reply(
+ CryptoKey& secret,
+ bufferlist::const_iterator& indata)
+{
+ using ceph::decode;
+ try {
+ __u8 service_ticket_v;
+ decode(service_ticket_v, indata);
+
+ CephXServiceTicket msg_a;
+ std::string error;
+ if (decode_decrypt(cct, msg_a, secret, indata, error)) {
+ ldout(cct, 0) << __func__ << " failed decode_decrypt, error is: " << error
+ << dendl;
+ return false;
+ }
+
+ __u8 ticket_enc;
+ decode(ticket_enc, indata);
+
+ bufferlist service_ticket_bl;
+ if (ticket_enc) {
+ ldout(cct, 10) << __func__ << " got encrypted ticket" << dendl;
+ std::string error;
+ if (decode_decrypt(cct, service_ticket_bl, session_key, indata, error)) {
+ ldout(cct, 10) << __func__ << " decode_decrypt failed "
+ << "with " << error << dendl;
+ return false;
+ }
+ } else {
+ decode(service_ticket_bl, indata);
+ }
+ auto iter = service_ticket_bl.cbegin();
+ decode(ticket, iter);
+ ldout(cct, 10) << __func__ << " ticket.secret_id=" << ticket.secret_id
+ << dendl;
+
+ ldout(cct, 10) << __func__ << " service "
+ << ceph_entity_type_name(service_id)
+ << " secret_id " << ticket.secret_id
+ << " session_key " << msg_a.session_key
+ << " validity=" << msg_a.validity << dendl;
+ session_key = msg_a.session_key;
+ if (!msg_a.validity.is_zero()) {
+ expires = ceph_clock_now();
+ expires += msg_a.validity;
+ renew_after = expires;
+ renew_after -= ((double)msg_a.validity.sec() / 4);
+ ldout(cct, 10) << __func__ << " ticket expires=" << expires
+ << " renew_after=" << renew_after << dendl;
+ }
+
+ have_key_flag = true;
+ return true;
+ } catch (ceph::buffer::error& e) {
+ ldout(cct, 1) << __func__ << " decode error: " << e.what() << dendl;
+ return false;
+ }
+}
+
+bool CephXTicketHandler::have_key()
+{
+ if (have_key_flag) {
+ have_key_flag = ceph_clock_now() < expires;
+ }
+
+ return have_key_flag;
+}
+
+bool CephXTicketHandler::need_key() const
+{
+ if (have_key_flag) {
+ return (!expires.is_zero()) && (ceph_clock_now() >= renew_after);
+ }
+
+ return true;
+}
+
+bool CephXTicketManager::have_key(uint32_t service_id)
+{
+ auto iter = tickets_map.find(service_id);
+ if (iter == tickets_map.end())
+ return false;
+ return iter->second.have_key();
+}
+
+bool CephXTicketManager::need_key(uint32_t service_id) const
+{
+ auto iter = tickets_map.find(service_id);
+ if (iter == tickets_map.end())
+ return true;
+ return iter->second.need_key();
+}
+
+void CephXTicketManager::set_have_need_key(uint32_t service_id, uint32_t& have, uint32_t& need)
+{
+ auto iter = tickets_map.find(service_id);
+ if (iter == tickets_map.end()) {
+ have &= ~service_id;
+ need |= service_id;
+ ldout(cct, 10) << "set_have_need_key no handler for service "
+ << ceph_entity_type_name(service_id) << dendl;
+ return;
+ }
+
+ //ldout(cct, 10) << "set_have_need_key service " << ceph_entity_type_name(service_id)
+ //<< " (" << service_id << ")"
+ //<< " need=" << iter->second.need_key() << " have=" << iter->second.have_key() << dendl;
+ if (iter->second.need_key())
+ need |= service_id;
+ else
+ need &= ~service_id;
+
+ if (iter->second.have_key())
+ have |= service_id;
+ else
+ have &= ~service_id;
+}
+
+void CephXTicketManager::invalidate_ticket(uint32_t service_id)
+{
+ auto iter = tickets_map.find(service_id);
+ if (iter != tickets_map.end())
+ iter->second.invalidate_ticket();
+}
+
+/*
+ * PRINCIPAL: verify our attempt to authenticate succeeded. fill out
+ * this ServiceTicket with the result.
+ */
+bool CephXTicketManager::verify_service_ticket_reply(CryptoKey& secret,
+ bufferlist::const_iterator& indata)
+{
+ __u8 service_ticket_reply_v;
+ uint32_t num = 0;
+ try {
+ decode(service_ticket_reply_v, indata);
+ decode(num, indata);
+ } catch (ceph::buffer::error& e) {
+ ldout(cct, 10) << __func__ << " failed to decode ticket v or count: "
+ << e.what() << dendl;
+ }
+ ldout(cct, 10) << "verify_service_ticket_reply got " << num << " keys" << dendl;
+
+ for (int i=0; i<(int)num; i++) {
+ uint32_t type = 0;
+ try {
+ decode(type, indata);
+ } catch (ceph::buffer::error& e) {
+ ldout(cct, 10) << __func__ << " failed to decode ticket type: " << e.what()
+ << dendl;
+ }
+ ldout(cct, 10) << "got key for service_id " << ceph_entity_type_name(type) << dendl;
+ CephXTicketHandler& handler = get_handler(type);
+ if (!handler.verify_service_ticket_reply(secret, indata)) {
+ return false;
+ }
+ handler.service_id = type;
+ }
+
+ return true;
+}
+
+/*
+ * PRINCIPAL: build authorizer to access the service.
+ *
+ * ticket, {timestamp}^session_key
+ */
+CephXAuthorizer *CephXTicketHandler::build_authorizer(uint64_t global_id) const
+{
+ CephXAuthorizer *a = new CephXAuthorizer(cct);
+ a->session_key = session_key;
+ cct->random()->get_bytes((char*)&a->nonce, sizeof(a->nonce));
+
+ __u8 authorizer_v = 1; // see AUTH_MODE_* in Auth.h
+ encode(authorizer_v, a->bl);
+ encode(global_id, a->bl);
+ encode(service_id, a->bl);
+
+ encode(ticket, a->bl);
+ a->base_bl = a->bl;
+
+ CephXAuthorize msg;
+ msg.nonce = a->nonce;
+
+ std::string error;
+ if (encode_encrypt(cct, msg, session_key, a->bl, error)) {
+ ldout(cct, 0) << "failed to encrypt authorizer: " << error << dendl;
+ delete a;
+ return 0;
+ }
+ return a;
+}
+
+/*
+ * PRINCIPAL: build authorizer to access the service.
+ *
+ * ticket, {timestamp}^session_key
+ */
+CephXAuthorizer *CephXTicketManager::build_authorizer(uint32_t service_id) const
+{
+ auto iter = tickets_map.find(service_id);
+ if (iter == tickets_map.end()) {
+ ldout(cct, 0) << "no TicketHandler for service "
+ << ceph_entity_type_name(service_id) << dendl;
+ return NULL;
+ }
+
+ const CephXTicketHandler& handler = iter->second;
+ return handler.build_authorizer(global_id);
+}
+
+void CephXTicketManager::validate_tickets(uint32_t mask, uint32_t& have, uint32_t& need)
+{
+ uint32_t i;
+ need = 0;
+ for (i = 1; i<=mask; i<<=1) {
+ if (mask & i) {
+ set_have_need_key(i, have, need);
+ }
+ }
+ ldout(cct, 10) << "validate_tickets want " << mask << " have " << have
+ << " need " << need << dendl;
+}
+
+bool cephx_decode_ticket(CephContext *cct, KeyStore *keys,
+ uint32_t service_id,
+ const CephXTicketBlob& ticket_blob,
+ CephXServiceTicketInfo& ticket_info)
+{
+ uint64_t secret_id = ticket_blob.secret_id;
+ CryptoKey service_secret;
+
+ if (!ticket_blob.blob.length()) {
+ return false;
+ }
+
+ if (secret_id == (uint64_t)-1) {
+ if (!keys->get_secret(cct->_conf->name, service_secret)) {
+ ldout(cct, 0) << "ceph_decode_ticket could not get general service secret for service_id="
+ << ceph_entity_type_name(service_id) << " secret_id=" << secret_id << dendl;
+ return false;
+ }
+ } else {
+ if (!keys->get_service_secret(service_id, secret_id, service_secret)) {
+ ldout(cct, 0) << "ceph_decode_ticket could not get service secret for service_id="
+ << ceph_entity_type_name(service_id) << " secret_id=" << secret_id << dendl;
+ return false;
+ }
+ }
+
+ std::string error;
+ decode_decrypt_enc_bl(cct, ticket_info, service_secret, ticket_blob.blob, error);
+ if (!error.empty()) {
+ ldout(cct, 0) << "ceph_decode_ticket could not decrypt ticket info. error:"
+ << error << dendl;
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * SERVICE: verify authorizer and generate reply authorizer
+ *
+ * {timestamp + 1}^session_key
+ */
+bool cephx_verify_authorizer(CephContext *cct, const KeyStore& keys,
+ bufferlist::const_iterator& indata,
+ size_t connection_secret_required_len,
+ CephXServiceTicketInfo& ticket_info,
+ std::unique_ptr<AuthAuthorizerChallenge> *challenge,
+ std::string *connection_secret,
+ bufferlist *reply_bl)
+{
+ __u8 authorizer_v;
+ uint32_t service_id;
+ uint64_t global_id;
+ CryptoKey service_secret;
+ // ticket blob
+ CephXTicketBlob ticket;
+
+ try {
+ decode(authorizer_v, indata);
+ decode(global_id, indata);
+ decode(service_id, indata);
+ decode(ticket, indata);
+ } catch (ceph::buffer::end_of_buffer &e) {
+ // Unable to decode!
+ return false;
+ }
+ ldout(cct, 10) << "verify_authorizer decrypted service "
+ << ceph_entity_type_name(service_id)
+ << " secret_id=" << ticket.secret_id << dendl;
+
+ if (ticket.secret_id == (uint64_t)-1) {
+ EntityName name;
+ name.set_type(service_id);
+ if (!keys.get_secret(name, service_secret)) {
+ ldout(cct, 0) << "verify_authorizer could not get general service secret for service "
+ << ceph_entity_type_name(service_id) << " secret_id=" << ticket.secret_id << dendl;
+ return false;
+ }
+ } else {
+ if (!keys.get_service_secret(service_id, ticket.secret_id, service_secret)) {
+ ldout(cct, 0) << "verify_authorizer could not get service secret for service "
+ << ceph_entity_type_name(service_id) << " secret_id=" << ticket.secret_id << dendl;
+ if (cct->_conf->auth_debug && ticket.secret_id == 0)
+ ceph_abort_msg("got secret_id=0");
+ return false;
+ }
+ }
+ std::string error;
+ if (!service_secret.get_secret().length())
+ error = "invalid key"; // Bad key?
+ else
+ decode_decrypt_enc_bl(cct, ticket_info, service_secret, ticket.blob, error);
+ if (!error.empty()) {
+ ldout(cct, 0) << "verify_authorizer could not decrypt ticket info: error: "
+ << error << dendl;
+ return false;
+ }
+
+ if (ticket_info.ticket.global_id != global_id) {
+ ldout(cct, 0) << "verify_authorizer global_id mismatch: declared id=" << global_id
+ << " ticket_id=" << ticket_info.ticket.global_id << dendl;
+ return false;
+ }
+
+ ldout(cct, 10) << "verify_authorizer global_id=" << global_id << dendl;
+
+ // CephXAuthorize
+ CephXAuthorize auth_msg;
+ if (decode_decrypt(cct, auth_msg, ticket_info.session_key, indata, error)) {
+ ldout(cct, 0) << "verify_authorizercould not decrypt authorize request with error: "
+ << error << dendl;
+ return false;
+ }
+
+ if (challenge) {
+ auto *c = static_cast<CephXAuthorizeChallenge*>(challenge->get());
+ if (!auth_msg.have_challenge || !c) {
+ c = new CephXAuthorizeChallenge;
+ challenge->reset(c);
+ cct->random()->get_bytes((char*)&c->server_challenge, sizeof(c->server_challenge));
+ ldout(cct,10) << __func__ << " adding server_challenge " << c->server_challenge
+ << dendl;
+
+ encode_encrypt_enc_bl(cct, *c, ticket_info.session_key, *reply_bl, error);
+ if (!error.empty()) {
+ ldout(cct, 10) << "verify_authorizer: encode_encrypt error: " << error << dendl;
+ return false;
+ }
+ return false;
+ }
+ ldout(cct, 10) << __func__ << " got server_challenge+1 "
+ << auth_msg.server_challenge_plus_one
+ << " expecting " << c->server_challenge + 1 << dendl;
+ if (c->server_challenge + 1 != auth_msg.server_challenge_plus_one) {
+ return false;
+ }
+ }
+
+ /*
+ * Reply authorizer:
+ * {timestamp + 1}^session_key
+ */
+ CephXAuthorizeReply reply;
+ // reply.trans_id = auth_msg.trans_id;
+ reply.nonce_plus_one = auth_msg.nonce + 1;
+ if (connection_secret) {
+ // generate a connection secret
+ connection_secret->resize(connection_secret_required_len);
+ if (connection_secret_required_len) {
+#ifdef WITH_SEASTAR
+ std::random_device rd;
+ std::generate_n(connection_secret->data(),
+ connection_secret_required_len,
+ std::default_random_engine{rd()});
+#else
+ cct->random()->get_bytes(connection_secret->data(),
+ connection_secret_required_len);
+#endif
+ }
+ reply.connection_secret = *connection_secret;
+ }
+ if (encode_encrypt(cct, reply, ticket_info.session_key, *reply_bl, error)) {
+ ldout(cct, 10) << "verify_authorizer: encode_encrypt error: " << error << dendl;
+ return false;
+ }
+
+ ldout(cct, 10) << "verify_authorizer ok nonce " << hex << auth_msg.nonce << dec
+ << " reply_bl.length()=" << reply_bl->length() << dendl;
+ return true;
+}
+
+bool CephXAuthorizer::verify_reply(bufferlist::const_iterator& indata,
+ std::string *connection_secret)
+{
+ CephXAuthorizeReply reply;
+
+ std::string error;
+ if (decode_decrypt(cct, reply, session_key, indata, error)) {
+ ldout(cct, 0) << "verify_reply couldn't decrypt with error: " << error << dendl;
+ return false;
+ }
+
+ uint64_t expect = nonce + 1;
+ if (expect != reply.nonce_plus_one) {
+ ldout(cct, 0) << "verify_authorizer_reply bad nonce got " << reply.nonce_plus_one << " expected " << expect
+ << " sent " << nonce << dendl;
+ return false;
+ }
+
+ if (connection_secret &&
+ reply.connection_secret.size()) {
+ *connection_secret = reply.connection_secret;
+ }
+ return true;
+}
+
+bool CephXAuthorizer::add_challenge(CephContext *cct,
+ const bufferlist& challenge)
+{
+ bl = base_bl;
+
+ CephXAuthorize msg;
+ msg.nonce = nonce;
+
+ auto p = challenge.begin();
+ if (!p.end()) {
+ std::string error;
+ CephXAuthorizeChallenge ch;
+ decode_decrypt_enc_bl(cct, ch, session_key, challenge, error);
+ if (!error.empty()) {
+ ldout(cct, 0) << "failed to decrypt challenge (" << challenge.length() << " bytes): "
+ << error << dendl;
+ return false;
+ }
+ msg.have_challenge = true;
+ msg.server_challenge_plus_one = ch.server_challenge + 1;
+ }
+
+ std::string error;
+ if (encode_encrypt(cct, msg, session_key, bl, error)) {
+ ldout(cct, 0) << __func__ << " failed to encrypt authorizer: " << error << dendl;
+ return false;
+ }
+ return true;
+}
diff --git a/src/auth/cephx/CephxProtocol.h b/src/auth/cephx/CephxProtocol.h
new file mode 100644
index 000000000..8a28b7306
--- /dev/null
+++ b/src/auth/cephx/CephxProtocol.h
@@ -0,0 +1,527 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef CEPH_CEPHXPROTOCOL_H
+#define CEPH_CEPHXPROTOCOL_H
+
+/*
+ Ceph X protocol
+
+ See doc/dev/cephx.rst
+
+*/
+
+/* authenticate requests */
+#define CEPHX_GET_AUTH_SESSION_KEY 0x0100
+#define CEPHX_GET_PRINCIPAL_SESSION_KEY 0x0200
+#define CEPHX_GET_ROTATING_KEY 0x0400
+
+#define CEPHX_REQUEST_TYPE_MASK 0x0F00
+#define CEPHX_CRYPT_ERR 1
+
+#include "auth/Auth.h"
+#include <errno.h>
+#include <sstream>
+
+#include "include/common_fwd.h"
+/*
+ * Authentication
+ */
+
+// initial server -> client challenge
+struct CephXServerChallenge {
+ uint64_t server_challenge;
+
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ __u8 struct_v = 1;
+ encode(struct_v, bl);
+ encode(server_challenge, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ __u8 struct_v;
+ decode(struct_v, bl);
+ decode(server_challenge, bl);
+ }
+};
+WRITE_CLASS_ENCODER(CephXServerChallenge)
+
+
+// request/reply headers, for subsequent exchanges.
+
+struct CephXRequestHeader {
+ __u16 request_type;
+
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ encode(request_type, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ decode(request_type, bl);
+ }
+};
+WRITE_CLASS_ENCODER(CephXRequestHeader)
+
+struct CephXResponseHeader {
+ uint16_t request_type;
+ int32_t status;
+
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ encode(request_type, bl);
+ encode(status, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ decode(request_type, bl);
+ decode(status, bl);
+ }
+};
+WRITE_CLASS_ENCODER(CephXResponseHeader)
+
+struct CephXTicketBlob {
+ uint64_t secret_id;
+ ceph::buffer::list blob;
+
+ CephXTicketBlob() : secret_id(0) {}
+
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ __u8 struct_v = 1;
+ encode(struct_v, bl);
+ encode(secret_id, bl);
+ encode(blob, bl);
+ }
+
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ __u8 struct_v;
+ decode(struct_v, bl);
+ decode(secret_id, bl);
+ decode(blob, bl);
+ }
+};
+WRITE_CLASS_ENCODER(CephXTicketBlob)
+
+// client -> server response to challenge
+struct CephXAuthenticate {
+ uint64_t client_challenge;
+ uint64_t key;
+ CephXTicketBlob old_ticket;
+ uint32_t other_keys = 0; // replaces CephXServiceTicketRequest
+
+ bool old_ticket_may_be_omitted;
+
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ __u8 struct_v = 3;
+ encode(struct_v, bl);
+ encode(client_challenge, bl);
+ encode(key, bl);
+ encode(old_ticket, bl);
+ encode(other_keys, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ __u8 struct_v;
+ decode(struct_v, bl);
+ decode(client_challenge, bl);
+ decode(key, bl);
+ decode(old_ticket, bl);
+ if (struct_v >= 2) {
+ decode(other_keys, bl);
+ }
+
+ // v2 and v3 encodings are the same, but:
+ // - some clients that send v1 or v2 don't populate old_ticket
+ // on reconnects (but do on renewals)
+ // - any client that sends v3 or later is expected to populate
+ // old_ticket both on reconnects and renewals
+ old_ticket_may_be_omitted = struct_v < 3;
+ }
+};
+WRITE_CLASS_ENCODER(CephXAuthenticate)
+
+struct CephXChallengeBlob {
+ uint64_t server_challenge, client_challenge;
+
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ encode(server_challenge, bl);
+ encode(client_challenge, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ decode(server_challenge, bl);
+ decode(client_challenge, bl);
+ }
+};
+WRITE_CLASS_ENCODER(CephXChallengeBlob)
+
+void cephx_calc_client_server_challenge(CephContext *cct,
+ CryptoKey& secret, uint64_t server_challenge, uint64_t client_challenge,
+ uint64_t *key, std::string &error);
+
+
+/*
+ * getting service tickets
+ */
+struct CephXSessionAuthInfo {
+ uint32_t service_id;
+ uint64_t secret_id;
+ AuthTicket ticket;
+ CryptoKey session_key;
+ CryptoKey service_secret;
+ utime_t validity;
+};
+
+
+extern bool cephx_build_service_ticket_blob(CephContext *cct,
+ CephXSessionAuthInfo& ticket_info, CephXTicketBlob& blob);
+
+extern void cephx_build_service_ticket_request(CephContext *cct,
+ uint32_t keys,
+ ceph::buffer::list& request);
+
+extern bool cephx_build_service_ticket_reply(CephContext *cct,
+ CryptoKey& principal_secret,
+ std::vector<CephXSessionAuthInfo> ticket_info,
+ bool should_encrypt_ticket,
+ CryptoKey& ticket_enc_key,
+ ceph::buffer::list& reply);
+
+struct CephXServiceTicketRequest {
+ uint32_t keys;
+
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ __u8 struct_v = 1;
+ encode(struct_v, bl);
+ encode(keys, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ __u8 struct_v;
+ decode(struct_v, bl);
+ decode(keys, bl);
+ }
+};
+WRITE_CLASS_ENCODER(CephXServiceTicketRequest)
+
+
+/*
+ * Authorize
+ */
+
+struct CephXAuthorizeReply {
+ uint64_t nonce_plus_one;
+ std::string connection_secret;
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ __u8 struct_v = 1;
+ if (connection_secret.size()) {
+ struct_v = 2;
+ }
+ encode(struct_v, bl);
+ encode(nonce_plus_one, bl);
+ if (struct_v >= 2) {
+ struct_v = 2;
+ encode(connection_secret, bl);
+ }
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ __u8 struct_v;
+ decode(struct_v, bl);
+ decode(nonce_plus_one, bl);
+ if (struct_v >= 2) {
+ decode(connection_secret, bl);
+ }
+ }
+};
+WRITE_CLASS_ENCODER(CephXAuthorizeReply)
+
+
+struct CephXAuthorizer : public AuthAuthorizer {
+private:
+ CephContext *cct;
+public:
+ uint64_t nonce;
+ ceph::buffer::list base_bl;
+
+ explicit CephXAuthorizer(CephContext *cct_)
+ : AuthAuthorizer(CEPH_AUTH_CEPHX), cct(cct_), nonce(0) {}
+
+ bool build_authorizer();
+ bool verify_reply(ceph::buffer::list::const_iterator& reply,
+ std::string *connection_secret) override;
+ bool add_challenge(CephContext *cct, const ceph::buffer::list& challenge) override;
+};
+
+
+
+/*
+ * TicketHandler
+ */
+struct CephXTicketHandler {
+ uint32_t service_id;
+ CryptoKey session_key;
+ CephXTicketBlob ticket; // opaque to us
+ utime_t renew_after, expires;
+ bool have_key_flag;
+
+ CephXTicketHandler(CephContext *cct_, uint32_t service_id_)
+ : service_id(service_id_), have_key_flag(false), cct(cct_) { }
+
+ // to build our ServiceTicket
+ bool verify_service_ticket_reply(CryptoKey& principal_secret,
+ ceph::buffer::list::const_iterator& indata);
+ // to access the service
+ CephXAuthorizer *build_authorizer(uint64_t global_id) const;
+
+ bool have_key();
+ bool need_key() const;
+
+ void invalidate_ticket() {
+ have_key_flag = false;
+ }
+private:
+ CephContext *cct;
+};
+
+struct CephXTicketManager {
+ typedef std::map<uint32_t, CephXTicketHandler> tickets_map_t;
+ tickets_map_t tickets_map;
+ uint64_t global_id;
+
+ explicit CephXTicketManager(CephContext *cct_) : global_id(0), cct(cct_) {}
+
+ bool verify_service_ticket_reply(CryptoKey& principal_secret,
+ ceph::buffer::list::const_iterator& indata);
+
+ CephXTicketHandler& get_handler(uint32_t type) {
+ tickets_map_t::iterator i = tickets_map.find(type);
+ if (i != tickets_map.end())
+ return i->second;
+ CephXTicketHandler newTicketHandler(cct, type);
+ std::pair < tickets_map_t::iterator, bool > res =
+ tickets_map.insert(std::make_pair(type, newTicketHandler));
+ ceph_assert(res.second);
+ return res.first->second;
+ }
+ CephXAuthorizer *build_authorizer(uint32_t service_id) const;
+ bool have_key(uint32_t service_id);
+ bool need_key(uint32_t service_id) const;
+ void set_have_need_key(uint32_t service_id, uint32_t& have, uint32_t& need);
+ void validate_tickets(uint32_t mask, uint32_t& have, uint32_t& need);
+ void invalidate_ticket(uint32_t service_id);
+
+private:
+ CephContext *cct;
+};
+
+
+/* A */
+struct CephXServiceTicket {
+ CryptoKey session_key;
+ utime_t validity;
+
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ __u8 struct_v = 1;
+ encode(struct_v, bl);
+ encode(session_key, bl);
+ encode(validity, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ __u8 struct_v;
+ decode(struct_v, bl);
+ decode(session_key, bl);
+ decode(validity, bl);
+ }
+};
+WRITE_CLASS_ENCODER(CephXServiceTicket)
+
+/* B */
+struct CephXServiceTicketInfo {
+ AuthTicket ticket;
+ CryptoKey session_key;
+
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ __u8 struct_v = 1;
+ encode(struct_v, bl);
+ encode(ticket, bl);
+ encode(session_key, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ __u8 struct_v;
+ decode(struct_v, bl);
+ decode(ticket, bl);
+ decode(session_key, bl);
+ }
+};
+WRITE_CLASS_ENCODER(CephXServiceTicketInfo)
+
+struct CephXAuthorizeChallenge : public AuthAuthorizerChallenge {
+ uint64_t server_challenge;
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ __u8 struct_v = 1;
+ encode(struct_v, bl);
+ encode(server_challenge, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ __u8 struct_v;
+ decode(struct_v, bl);
+ decode(server_challenge, bl);
+ }
+};
+WRITE_CLASS_ENCODER(CephXAuthorizeChallenge)
+
+struct CephXAuthorize {
+ uint64_t nonce;
+ bool have_challenge = false;
+ uint64_t server_challenge_plus_one = 0;
+ void encode(ceph::buffer::list& bl) const {
+ using ceph::encode;
+ __u8 struct_v = 2;
+ encode(struct_v, bl);
+ encode(nonce, bl);
+ encode(have_challenge, bl);
+ encode(server_challenge_plus_one, bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ using ceph::decode;
+ __u8 struct_v;
+ decode(struct_v, bl);
+ decode(nonce, bl);
+ if (struct_v >= 2) {
+ decode(have_challenge, bl);
+ decode(server_challenge_plus_one, bl);
+ }
+ }
+};
+WRITE_CLASS_ENCODER(CephXAuthorize)
+
+/*
+ * Decode an extract ticket
+ */
+bool cephx_decode_ticket(CephContext *cct, KeyStore *keys,
+ uint32_t service_id,
+ const CephXTicketBlob& ticket_blob,
+ CephXServiceTicketInfo& ticket_info);
+
+/*
+ * Verify authorizer and generate reply authorizer
+ */
+extern bool cephx_verify_authorizer(
+ CephContext *cct,
+ const KeyStore& keys,
+ ceph::buffer::list::const_iterator& indata,
+ size_t connection_secret_required_len,
+ CephXServiceTicketInfo& ticket_info,
+ std::unique_ptr<AuthAuthorizerChallenge> *challenge,
+ std::string *connection_secret,
+ ceph::buffer::list *reply_bl);
+
+
+
+
+
+
+/*
+ * encode+encrypt macros
+ */
+static constexpr uint64_t AUTH_ENC_MAGIC = 0xff009cad8826aa55ull;
+
+template <typename T>
+void decode_decrypt_enc_bl(CephContext *cct, T& t, CryptoKey key,
+ const ceph::buffer::list& bl_enc,
+ std::string &error)
+{
+ uint64_t magic;
+ ceph::buffer::list bl;
+
+ if (key.decrypt(cct, bl_enc, bl, &error) < 0)
+ return;
+
+ auto iter2 = bl.cbegin();
+ __u8 struct_v;
+ using ceph::decode;
+ decode(struct_v, iter2);
+ decode(magic, iter2);
+ if (magic != AUTH_ENC_MAGIC) {
+ std::ostringstream oss;
+ oss << "bad magic in decode_decrypt, " << magic << " != " << AUTH_ENC_MAGIC;
+ error = oss.str();
+ return;
+ }
+
+ decode(t, iter2);
+}
+
+template <typename T>
+void encode_encrypt_enc_bl(CephContext *cct, const T& t, const CryptoKey& key,
+ ceph::buffer::list& out, std::string &error)
+{
+ ceph::buffer::list bl;
+ __u8 struct_v = 1;
+ using ceph::encode;
+ encode(struct_v, bl);
+ uint64_t magic = AUTH_ENC_MAGIC;
+ encode(magic, bl);
+ encode(t, bl);
+
+ key.encrypt(cct, bl, out, &error);
+}
+
+template <typename T>
+int decode_decrypt(CephContext *cct, T& t, const CryptoKey& key,
+ ceph::buffer::list::const_iterator& iter, std::string &error)
+{
+ ceph::buffer::list bl_enc;
+ using ceph::decode;
+ try {
+ decode(bl_enc, iter);
+ decode_decrypt_enc_bl(cct, t, key, bl_enc, error);
+ }
+ catch (ceph::buffer::error &e) {
+ error = "error decoding block for decryption";
+ }
+ if (!error.empty())
+ return CEPHX_CRYPT_ERR;
+ return 0;
+}
+
+template <typename T>
+int encode_encrypt(CephContext *cct, const T& t, const CryptoKey& key,
+ ceph::buffer::list& out, std::string &error)
+{
+ using ceph::encode;
+ ceph::buffer::list bl_enc;
+ encode_encrypt_enc_bl(cct, t, key, bl_enc, error);
+ if (!error.empty()){
+ return CEPHX_CRYPT_ERR;
+ }
+ encode(bl_enc, out);
+ return 0;
+}
+
+#endif
diff --git a/src/auth/cephx/CephxServiceHandler.cc b/src/auth/cephx/CephxServiceHandler.cc
new file mode 100644
index 000000000..977a43ad6
--- /dev/null
+++ b/src/auth/cephx/CephxServiceHandler.cc
@@ -0,0 +1,421 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+
+#include "CephxServiceHandler.h"
+#include "CephxProtocol.h"
+#include "CephxKeyServer.h"
+#include <errno.h>
+#include <sstream>
+
+#include "include/random.h"
+#include "common/config.h"
+#include "common/debug.h"
+
+#define dout_subsys ceph_subsys_auth
+#undef dout_prefix
+#define dout_prefix *_dout << "cephx server " << entity_name << ": "
+
+using std::dec;
+using std::hex;
+using std::vector;
+
+using ceph::bufferlist;
+using ceph::decode;
+using ceph::encode;
+
+int CephxServiceHandler::do_start_session(
+ bool is_new_global_id,
+ bufferlist *result_bl,
+ AuthCapsInfo *caps)
+{
+ global_id_status = is_new_global_id ? global_id_status_t::NEW_PENDING :
+ global_id_status_t::RECLAIM_PENDING;
+
+ uint64_t min = 1; // always non-zero
+ uint64_t max = std::numeric_limits<uint64_t>::max();
+ server_challenge = ceph::util::generate_random_number<uint64_t>(min, max);
+ ldout(cct, 10) << "start_session server_challenge "
+ << hex << server_challenge << dec << dendl;
+
+ CephXServerChallenge ch;
+ ch.server_challenge = server_challenge;
+ encode(ch, *result_bl);
+ return 0;
+}
+
+int CephxServiceHandler::verify_old_ticket(
+ const CephXAuthenticate& req,
+ CephXServiceTicketInfo& old_ticket_info,
+ bool& should_enc_ticket)
+{
+ ldout(cct, 20) << " checking old_ticket: secret_id="
+ << req.old_ticket.secret_id
+ << " len=" << req.old_ticket.blob.length()
+ << ", old_ticket_may_be_omitted="
+ << req.old_ticket_may_be_omitted << dendl;
+ ceph_assert(global_id_status != global_id_status_t::NONE);
+ if (global_id_status == global_id_status_t::NEW_PENDING) {
+ // old ticket is not needed
+ if (req.old_ticket.blob.length()) {
+ ldout(cct, 0) << " superfluous ticket presented" << dendl;
+ return -EINVAL;
+ }
+ if (req.old_ticket_may_be_omitted) {
+ ldout(cct, 10) << " new global_id " << global_id
+ << " (unexposed legacy client)" << dendl;
+ global_id_status = global_id_status_t::NEW_NOT_EXPOSED;
+ } else {
+ ldout(cct, 10) << " new global_id " << global_id << dendl;
+ global_id_status = global_id_status_t::NEW_OK;
+ }
+ return 0;
+ }
+
+ if (!req.old_ticket.blob.length()) {
+ // old ticket is needed but not presented
+ if (cct->_conf->auth_allow_insecure_global_id_reclaim &&
+ req.old_ticket_may_be_omitted) {
+ ldout(cct, 10) << " allowing reclaim of global_id " << global_id
+ << " with no ticket presented (legacy client, auth_allow_insecure_global_id_reclaim=true)"
+ << dendl;
+ global_id_status = global_id_status_t::RECLAIM_INSECURE;
+ return 0;
+ }
+ ldout(cct, 0) << " attempt to reclaim global_id " << global_id
+ << " without presenting ticket" << dendl;
+ return -EACCES;
+ }
+
+ if (!cephx_decode_ticket(cct, key_server, CEPH_ENTITY_TYPE_AUTH,
+ req.old_ticket, old_ticket_info)) {
+ if (cct->_conf->auth_allow_insecure_global_id_reclaim &&
+ req.old_ticket_may_be_omitted) {
+ ldout(cct, 10) << " allowing reclaim of global_id " << global_id
+ << " using bad ticket (legacy client, auth_allow_insecure_global_id_reclaim=true)"
+ << dendl;
+ global_id_status = global_id_status_t::RECLAIM_INSECURE;
+ return 0;
+ }
+ ldout(cct, 0) << " attempt to reclaim global_id " << global_id
+ << " using bad ticket" << dendl;
+ return -EACCES;
+ }
+ ldout(cct, 20) << " decoded old_ticket: global_id="
+ << old_ticket_info.ticket.global_id << dendl;
+ if (global_id != old_ticket_info.ticket.global_id) {
+ if (cct->_conf->auth_allow_insecure_global_id_reclaim &&
+ req.old_ticket_may_be_omitted) {
+ ldout(cct, 10) << " allowing reclaim of global_id " << global_id
+ << " using mismatching ticket (legacy client, auth_allow_insecure_global_id_reclaim=true)"
+ << dendl;
+ global_id_status = global_id_status_t::RECLAIM_INSECURE;
+ return 0;
+ }
+ ldout(cct, 0) << " attempt to reclaim global_id " << global_id
+ << " using mismatching ticket" << dendl;
+ return -EACCES;
+ }
+ ldout(cct, 10) << " allowing reclaim of global_id " << global_id
+ << " (valid ticket presented, will encrypt new ticket)"
+ << dendl;
+ global_id_status = global_id_status_t::RECLAIM_OK;
+ should_enc_ticket = true;
+ return 0;
+}
+
+int CephxServiceHandler::handle_request(
+ bufferlist::const_iterator& indata,
+ size_t connection_secret_required_len,
+ bufferlist *result_bl,
+ AuthCapsInfo *caps,
+ CryptoKey *psession_key,
+ std::string *pconnection_secret)
+{
+ int ret = 0;
+
+ struct CephXRequestHeader cephx_header;
+ try {
+ decode(cephx_header, indata);
+ } catch (ceph::buffer::error& e) {
+ ldout(cct, 0) << __func__ << " failed to decode CephXRequestHeader: "
+ << e.what() << dendl;
+ return -EPERM;
+ }
+
+ switch (cephx_header.request_type) {
+ case CEPHX_GET_AUTH_SESSION_KEY:
+ {
+ ldout(cct, 10) << "handle_request get_auth_session_key for "
+ << entity_name << dendl;
+
+ CephXAuthenticate req;
+ try {
+ decode(req, indata);
+ } catch (ceph::buffer::error& e) {
+ ldout(cct, 0) << __func__ << " failed to decode CephXAuthenticate: "
+ << e.what() << dendl;
+ ret = -EPERM;
+ break;
+ }
+
+ EntityAuth eauth;
+ if (!key_server->get_auth(entity_name, eauth)) {
+ ldout(cct, 0) << "couldn't find entity name: " << entity_name << dendl;
+ ret = -EACCES;
+ break;
+ }
+
+ if (!server_challenge) {
+ ret = -EACCES;
+ break;
+ }
+
+ uint64_t expected_key;
+ CryptoKey *used_key = &eauth.key;
+ std::string error;
+ cephx_calc_client_server_challenge(cct, eauth.key, server_challenge,
+ req.client_challenge, &expected_key, error);
+ if ((!error.empty() || req.key != expected_key) &&
+ !eauth.pending_key.empty()) {
+ ldout(cct, 10) << "normal key failed for " << entity_name
+ << ", trying pending_key" << dendl;
+ // try pending_key instead
+ error.clear();
+ cephx_calc_client_server_challenge(cct, eauth.pending_key,
+ server_challenge,
+ req.client_challenge, &expected_key,
+ error);
+ if (error.empty()) {
+ used_key = &eauth.pending_key;
+ key_server->note_used_pending_key(entity_name, eauth.pending_key);
+ }
+ }
+ if (!error.empty()) {
+ ldout(cct, 0) << " cephx_calc_client_server_challenge error: " << error << dendl;
+ ret = -EACCES;
+ break;
+ }
+
+ ldout(cct, 20) << " checking key: req.key=" << hex << req.key
+ << " expected_key=" << expected_key << dec << dendl;
+ if (req.key != expected_key) {
+ ldout(cct, 0) << " unexpected key: req.key=" << hex << req.key
+ << " expected_key=" << expected_key << dec << dendl;
+ ret = -EACCES;
+ break;
+ }
+
+ CryptoKey session_key;
+ CephXSessionAuthInfo info;
+ bool should_enc_ticket = false;
+
+ CephXServiceTicketInfo old_ticket_info;
+ ret = verify_old_ticket(req, old_ticket_info, should_enc_ticket);
+ if (ret) {
+ ldout(cct, 0) << " could not verify old ticket" << dendl;
+ break;
+ }
+
+ double ttl;
+ if (!key_server->get_service_secret(CEPH_ENTITY_TYPE_AUTH,
+ info.service_secret, info.secret_id,
+ ttl)) {
+ ldout(cct, 0) << " could not get service secret for auth subsystem" << dendl;
+ ret = -EIO;
+ break;
+ }
+
+ info.service_id = CEPH_ENTITY_TYPE_AUTH;
+ info.ticket.name = entity_name;
+ info.ticket.global_id = global_id;
+ info.ticket.init_timestamps(ceph_clock_now(), ttl);
+ info.validity.set_from_double(ttl);
+
+ key_server->generate_secret(session_key);
+
+ info.session_key = session_key;
+ if (psession_key) {
+ *psession_key = session_key;
+ }
+
+ vector<CephXSessionAuthInfo> info_vec;
+ info_vec.push_back(info);
+
+ build_cephx_response_header(cephx_header.request_type, 0, *result_bl);
+ if (!cephx_build_service_ticket_reply(
+ cct, *used_key, info_vec, should_enc_ticket,
+ old_ticket_info.session_key, *result_bl)) {
+ ret = -EIO;
+ break;
+ }
+
+ if (!key_server->get_service_caps(entity_name, CEPH_ENTITY_TYPE_MON,
+ *caps)) {
+ ldout(cct, 0) << " could not get mon caps for " << entity_name << dendl;
+ ret = -EACCES;
+ break;
+ } else {
+ char *caps_str = caps->caps.c_str();
+ if (!caps_str || !caps_str[0]) {
+ ldout(cct,0) << "mon caps null for " << entity_name << dendl;
+ ret = -EACCES;
+ break;
+ }
+
+ if (req.other_keys) {
+ // nautilus+ client
+ // generate a connection_secret
+ bufferlist cbl;
+ if (pconnection_secret) {
+ pconnection_secret->resize(connection_secret_required_len);
+ if (connection_secret_required_len) {
+ cct->random()->get_bytes(pconnection_secret->data(),
+ connection_secret_required_len);
+ }
+ std::string err;
+ if (encode_encrypt(cct, *pconnection_secret, session_key, cbl,
+ err)) {
+ lderr(cct) << __func__ << " failed to encrypt connection secret, "
+ << err << dendl;
+ ret = -EACCES;
+ break;
+ }
+ }
+ encode(cbl, *result_bl);
+ // provide requested service tickets at the same time
+ vector<CephXSessionAuthInfo> info_vec;
+ for (uint32_t service_id = 1; service_id <= req.other_keys;
+ service_id <<= 1) {
+ // skip CEPH_ENTITY_TYPE_AUTH: auth ticket is already encoded
+ // (possibly encrypted with the old session key)
+ if ((req.other_keys & service_id) &&
+ service_id != CEPH_ENTITY_TYPE_AUTH) {
+ ldout(cct, 10) << " adding key for service "
+ << ceph_entity_type_name(service_id) << dendl;
+ CephXSessionAuthInfo svc_info;
+ key_server->build_session_auth_info(
+ service_id,
+ info.ticket,
+ svc_info);
+ info_vec.push_back(svc_info);
+ }
+ }
+ bufferlist extra;
+ if (!info_vec.empty()) {
+ CryptoKey no_key;
+ cephx_build_service_ticket_reply(
+ cct, session_key, info_vec, false, no_key, extra);
+ }
+ encode(extra, *result_bl);
+ }
+
+ // caller should try to finish authentication
+ ret = 1;
+ }
+ }
+ break;
+
+ case CEPHX_GET_PRINCIPAL_SESSION_KEY:
+ {
+ ldout(cct, 10) << "handle_request get_principal_session_key" << dendl;
+
+ bufferlist tmp_bl;
+ CephXServiceTicketInfo auth_ticket_info;
+ // note: no challenge here.
+ if (!cephx_verify_authorizer(
+ cct, *key_server, indata, 0, auth_ticket_info, nullptr,
+ nullptr,
+ &tmp_bl)) {
+ ret = -EACCES;
+ break;
+ }
+
+ CephXServiceTicketRequest ticket_req;
+ try {
+ decode(ticket_req, indata);
+ } catch (ceph::buffer::error& e) {
+ ldout(cct, 0) << __func__
+ << " failed to decode CephXServiceTicketRequest: "
+ << e.what() << dendl;
+ ret = -EPERM;
+ break;
+ }
+ ldout(cct, 10) << " ticket_req.keys = " << ticket_req.keys << dendl;
+
+ ret = 0;
+ vector<CephXSessionAuthInfo> info_vec;
+ int found_services = 0;
+ int service_err = 0;
+ for (uint32_t service_id = 1; service_id <= ticket_req.keys;
+ service_id <<= 1) {
+ // skip CEPH_ENTITY_TYPE_AUTH: auth ticket must be obtained with
+ // CEPHX_GET_AUTH_SESSION_KEY
+ if ((ticket_req.keys & service_id) &&
+ service_id != CEPH_ENTITY_TYPE_AUTH) {
+ ldout(cct, 10) << " adding key for service "
+ << ceph_entity_type_name(service_id) << dendl;
+ CephXSessionAuthInfo info;
+ int r = key_server->build_session_auth_info(
+ service_id,
+ auth_ticket_info.ticket, // parent ticket (client's auth ticket)
+ info);
+ // tolerate missing MGR rotating key for the purposes of upgrades.
+ if (r < 0) {
+ ldout(cct, 10) << " missing key for service "
+ << ceph_entity_type_name(service_id) << dendl;
+ service_err = r;
+ continue;
+ }
+ info_vec.push_back(info);
+ ++found_services;
+ }
+ }
+ if (!found_services && service_err) {
+ ldout(cct, 10) << __func__ << " did not find any service keys" << dendl;
+ ret = service_err;
+ }
+ CryptoKey no_key;
+ build_cephx_response_header(cephx_header.request_type, ret, *result_bl);
+ cephx_build_service_ticket_reply(cct, auth_ticket_info.session_key,
+ info_vec, false, no_key, *result_bl);
+ }
+ break;
+
+ case CEPHX_GET_ROTATING_KEY:
+ {
+ ldout(cct, 10) << "handle_request getting rotating secret for "
+ << entity_name << dendl;
+ build_cephx_response_header(cephx_header.request_type, 0, *result_bl);
+ if (!key_server->get_rotating_encrypted(entity_name, *result_bl)) {
+ ret = -EACCES;
+ break;
+ }
+ }
+ break;
+
+ default:
+ ldout(cct, 10) << "handle_request unknown op " << cephx_header.request_type << dendl;
+ return -EINVAL;
+ }
+ return ret;
+}
+
+void CephxServiceHandler::build_cephx_response_header(int request_type, int status, bufferlist& bl)
+{
+ struct CephXResponseHeader header;
+ header.request_type = request_type;
+ header.status = status;
+ encode(header, bl);
+}
diff --git a/src/auth/cephx/CephxServiceHandler.h b/src/auth/cephx/CephxServiceHandler.h
new file mode 100644
index 000000000..e6e093ee4
--- /dev/null
+++ b/src/auth/cephx/CephxServiceHandler.h
@@ -0,0 +1,54 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef CEPH_CEPHXSERVICEHANDLER_H
+#define CEPH_CEPHXSERVICEHANDLER_H
+
+#include "auth/AuthServiceHandler.h"
+#include "auth/Auth.h"
+
+class KeyServer;
+struct CephXAuthenticate;
+struct CephXServiceTicketInfo;
+
+class CephxServiceHandler : public AuthServiceHandler {
+ KeyServer *key_server;
+ uint64_t server_challenge;
+
+public:
+ CephxServiceHandler(CephContext *cct_, KeyServer *ks)
+ : AuthServiceHandler(cct_), key_server(ks), server_challenge(0) {}
+ ~CephxServiceHandler() override {}
+
+ int handle_request(
+ ceph::buffer::list::const_iterator& indata,
+ size_t connection_secret_required_length,
+ ceph::buffer::list *result_bl,
+ AuthCapsInfo *caps,
+ CryptoKey *session_key,
+ std::string *connection_secret) override;
+
+private:
+ int do_start_session(bool is_new_global_id,
+ ceph::buffer::list *result_bl,
+ AuthCapsInfo *caps) override;
+
+ int verify_old_ticket(const CephXAuthenticate& req,
+ CephXServiceTicketInfo& old_ticket_info,
+ bool& should_enc_ticket);
+ void build_cephx_response_header(int request_type, int status,
+ ceph::buffer::list& bl);
+};
+
+#endif
diff --git a/src/auth/cephx/CephxSessionHandler.cc b/src/auth/cephx/CephxSessionHandler.cc
new file mode 100644
index 000000000..6b2712568
--- /dev/null
+++ b/src/auth/cephx/CephxSessionHandler.cc
@@ -0,0 +1,194 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "CephxSessionHandler.h"
+#include "CephxProtocol.h"
+
+#include <errno.h>
+#include <sstream>
+
+#include "common/config.h"
+#include "include/ceph_features.h"
+#include "msg/Message.h"
+
+#define dout_subsys ceph_subsys_auth
+
+namespace {
+#ifdef WITH_SEASTAR
+ crimson::common::ConfigProxy& conf(CephContext*) {
+ return crimson::common::local_conf();
+ }
+#else
+ ConfigProxy& conf(CephContext* cct) {
+ return cct->_conf;
+ }
+#endif
+}
+
+int CephxSessionHandler::_calc_signature(Message *m, uint64_t *psig)
+{
+ const ceph_msg_header& header = m->get_header();
+ const ceph_msg_footer& footer = m->get_footer();
+
+ if (!HAVE_FEATURE(features, CEPHX_V2)) {
+ // legacy pre-mimic behavior for compatibility
+
+ // optimized signature calculation
+ // - avoid temporary allocated buffers from encode_encrypt[_enc_bl]
+ // - skip the leading 4 byte wrapper from encode_encrypt
+ struct {
+ __u8 v;
+ ceph_le64 magic;
+ ceph_le32 len;
+ ceph_le32 header_crc;
+ ceph_le32 front_crc;
+ ceph_le32 middle_crc;
+ ceph_le32 data_crc;
+ } __attribute__ ((packed)) sigblock = {
+ 1, ceph_le64(AUTH_ENC_MAGIC), ceph_le32(4 * 4),
+ ceph_le32(header.crc), ceph_le32(footer.front_crc),
+ ceph_le32(footer.middle_crc), ceph_le32(footer.data_crc)
+ };
+
+ char exp_buf[CryptoKey::get_max_outbuf_size(sizeof(sigblock))];
+
+ try {
+ const CryptoKey::in_slice_t in {
+ sizeof(sigblock),
+ reinterpret_cast<const unsigned char*>(&sigblock)
+ };
+ const CryptoKey::out_slice_t out {
+ sizeof(exp_buf),
+ reinterpret_cast<unsigned char*>(&exp_buf)
+ };
+ key.encrypt(cct, in, out);
+ } catch (std::exception& e) {
+ lderr(cct) << __func__ << " failed to encrypt signature block" << dendl;
+ return -1;
+ }
+
+ *psig = *reinterpret_cast<ceph_le64*>(exp_buf);
+ } else {
+ // newer mimic+ signatures
+ struct {
+ ceph_le32 header_crc;
+ ceph_le32 front_crc;
+ ceph_le32 front_len;
+ ceph_le32 middle_crc;
+ ceph_le32 middle_len;
+ ceph_le32 data_crc;
+ ceph_le32 data_len;
+ ceph_le32 seq_lower_word;
+ } __attribute__ ((packed)) sigblock = {
+ ceph_le32(header.crc),
+ ceph_le32(footer.front_crc),
+ ceph_le32(header.front_len),
+ ceph_le32(footer.middle_crc),
+ ceph_le32(header.middle_len),
+ ceph_le32(footer.data_crc),
+ ceph_le32(header.data_len),
+ ceph_le32(header.seq)
+ };
+
+ char exp_buf[CryptoKey::get_max_outbuf_size(sizeof(sigblock))];
+
+ try {
+ const CryptoKey::in_slice_t in {
+ sizeof(sigblock),
+ reinterpret_cast<const unsigned char*>(&sigblock)
+ };
+ const CryptoKey::out_slice_t out {
+ sizeof(exp_buf),
+ reinterpret_cast<unsigned char*>(&exp_buf)
+ };
+ key.encrypt(cct, in, out);
+ } catch (std::exception& e) {
+ lderr(cct) << __func__ << " failed to encrypt signature block" << dendl;
+ return -1;
+ }
+
+ struct enc {
+ ceph_le64 a, b, c, d;
+ } *penc = reinterpret_cast<enc*>(exp_buf);
+ *psig = penc->a ^ penc->b ^ penc->c ^ penc->d;
+ }
+
+ ldout(cct, 10) << __func__ << " seq " << m->get_seq()
+ << " front_crc_ = " << footer.front_crc
+ << " middle_crc = " << footer.middle_crc
+ << " data_crc = " << footer.data_crc
+ << " sig = " << *psig
+ << dendl;
+ return 0;
+}
+
+int CephxSessionHandler::sign_message(Message *m)
+{
+ // If runtime signing option is off, just return success without signing.
+ if (!conf(cct)->cephx_sign_messages) {
+ return 0;
+ }
+
+ uint64_t sig;
+ int r = _calc_signature(m, &sig);
+ if (r < 0)
+ return r;
+
+ ceph_msg_footer& f = m->get_footer();
+ f.sig = sig;
+ f.flags = (unsigned)f.flags | CEPH_MSG_FOOTER_SIGNED;
+ ldout(cct, 20) << "Putting signature in client message(seq # " << m->get_seq()
+ << "): sig = " << sig << dendl;
+ return 0;
+}
+
+int CephxSessionHandler::check_message_signature(Message *m)
+{
+ // If runtime signing option is off, just return success without checking signature.
+ if (!conf(cct)->cephx_sign_messages) {
+ return 0;
+ }
+ if ((features & CEPH_FEATURE_MSG_AUTH) == 0) {
+ // it's fine, we didn't negotiate this feature.
+ return 0;
+ }
+
+ uint64_t sig;
+ int r = _calc_signature(m, &sig);
+ if (r < 0)
+ return r;
+
+ if (sig != m->get_footer().sig) {
+ // Should have been signed, but signature check failed. PLR
+ if (!(m->get_footer().flags & CEPH_MSG_FOOTER_SIGNED)) {
+ ldout(cct, 0) << "SIGN: MSG " << m->get_seq() << " Sender did not set CEPH_MSG_FOOTER_SIGNED." << dendl;
+ }
+ ldout(cct, 0) << "SIGN: MSG " << m->get_seq() << " Message signature does not match contents." << dendl;
+ ldout(cct, 0) << "SIGN: MSG " << m->get_seq() << "Signature on message:" << dendl;
+ ldout(cct, 0) << "SIGN: MSG " << m->get_seq() << " sig: " << m->get_footer().sig << dendl;
+ ldout(cct, 0) << "SIGN: MSG " << m->get_seq() << "Locally calculated signature:" << dendl;
+ ldout(cct, 0) << "SIGN: MSG " << m->get_seq() << " sig_check:" << sig << dendl;
+
+ // For the moment, printing an error message to the log and
+ // returning failure is sufficient. In the long term, we should
+ // probably have code parsing the log looking for this kind of
+ // security failure, particularly when there are large numbers of
+ // them, since the latter is a potential sign of an attack. PLR
+
+ ldout(cct, 0) << "Signature failed." << dendl;
+ return (SESSION_SIGNATURE_FAILURE);
+ }
+
+ return 0;
+}
diff --git a/src/auth/cephx/CephxSessionHandler.h b/src/auth/cephx/CephxSessionHandler.h
new file mode 100644
index 000000000..e09426f1a
--- /dev/null
+++ b/src/auth/cephx/CephxSessionHandler.h
@@ -0,0 +1,44 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+
+#include "auth/AuthSessionHandler.h"
+#include "auth/Auth.h"
+#include "include/common_fwd.h"
+
+class Message;
+
+class CephxSessionHandler : public AuthSessionHandler {
+ CephContext *cct;
+ int protocol;
+ CryptoKey key; // per mon authentication
+ uint64_t features;
+
+ int _calc_signature(Message *m, uint64_t *psig);
+
+public:
+ CephxSessionHandler(CephContext *cct,
+ const CryptoKey& session_key,
+ const uint64_t features)
+ : cct(cct),
+ protocol(CEPH_AUTH_CEPHX),
+ key(session_key),
+ features(features) {
+ }
+ ~CephxSessionHandler() override = default;
+
+ int sign_message(Message *m) override;
+ int check_message_signature(Message *m) override ;
+};
+