X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Fauth%2Fcephx%2FCephxProtocol.cc;fp=src%2Fceph%2Fsrc%2Fauth%2Fcephx%2FCephxProtocol.cc;h=5836a33bd531e7a31e662d6d93a5098150a4625b;hb=812ff6ca9fcd3e629e49d4328905f33eee8ca3f5;hp=0000000000000000000000000000000000000000;hpb=15280273faafb77777eab341909a3f495cf248d9;p=stor4nfv.git diff --git a/src/ceph/src/auth/cephx/CephxProtocol.cc b/src/ceph/src/auth/cephx/CephxProtocol.cc new file mode 100644 index 0000000..5836a33 --- /dev/null +++ b/src/ceph/src/auth/cephx/CephxProtocol.cc @@ -0,0 +1,495 @@ +// -*- 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/config.h" +#include "common/debug.h" +#include "include/buffer.h" + +#define dout_subsys ceph_subsys_auth +#undef dout_prefix +#define dout_prefix *_dout << "cephx: " + + + +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 uint64_t *p = (const uint64_t *)enc.c_str(); + for (int pos = 0; pos + sizeof(k) <= enc.length(); pos+=sizeof(k), p++) + k ^= mswab(*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() << 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 ticket_info_vec, + bool should_encrypt_ticket, + CryptoKey& ticket_enc_key, + bufferlist& reply) +{ + __u8 service_ticket_reply_v = 1; + ::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 (vector::iterator 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::iterator& indata) +{ + __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) << "verify_service_ticket_reply: 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) << " got encrypted ticket" << dendl; + std::string error; + if (decode_decrypt(cct, service_ticket_bl, session_key, indata, error)) { + ldout(cct, 10) << "verify_service_ticket_reply: decode_decrypt failed " + << "with " << error << dendl; + return false; + } + } else { + ::decode(service_ticket_bl, indata); + } + bufferlist::iterator iter = service_ticket_bl.begin(); + ::decode(ticket, iter); + ldout(cct, 10) << " ticket.secret_id=" << ticket.secret_id << dendl; + + ldout(cct, 10) << "verify_service_ticket_reply 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) << "ticket expires=" << expires << " renew_after=" << renew_after << dendl; + } + + have_key_flag = true; + return true; +} + +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) +{ + map::iterator 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 +{ + map::const_iterator 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) +{ + map::iterator 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) +{ + map::iterator 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::iterator& indata) +{ + __u8 service_ticket_reply_v; + ::decode(service_ticket_reply_v, indata); + + uint32_t num; + ::decode(num, indata); + ldout(cct, 10) << "verify_service_ticket_reply got " << num << " keys" << dendl; + + for (int i=0; i<(int)num; i++) { + uint32_t type; + ::decode(type, indata); + 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; + } + + if (!indata.end()) + return false; + + 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; + a->nonce = ((uint64_t)rand() << 32) + rand(); + + __u8 authorizer_v = 1; + ::encode(authorizer_v, a->bl); + ::encode(global_id, a->bl); + ::encode(service_id, a->bl); + + ::encode(ticket, 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 +{ + map::const_iterator 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, + 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, KeyStore *keys, + bufferlist::iterator& indata, + CephXServiceTicketInfo& ticket_info, 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 (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) + assert(0 == "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; + } + + /* + * Reply authorizer: + * {timestamp + 1}^session_key + */ + CephXAuthorizeReply reply; + // reply.trans_id = auth_msg.trans_id; + reply.nonce_plus_one = auth_msg.nonce + 1; + 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::iterator& indata) +{ + 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; + } + return true; +} +