summaryrefslogtreecommitdiffstats
path: root/src/auth/cephx/CephxProtocol.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/auth/cephx/CephxProtocol.h')
-rw-r--r--src/auth/cephx/CephxProtocol.h527
1 files changed, 527 insertions, 0 deletions
diff --git a/src/auth/cephx/CephxProtocol.h b/src/auth/cephx/CephxProtocol.h
new file mode 100644
index 000000000..134df6b66
--- /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 = 0;
+ }
+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