1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net>
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
18 #include "CephxClientHandler.h"
19 #include "CephxProtocol.h"
21 #include "auth/KeyRing.h"
22 #include "include/random.h"
23 #include "common/ceph_context.h"
24 #include "common/config.h"
25 #include "common/dout.h"
27 #define dout_subsys ceph_subsys_auth
29 #define dout_prefix *_dout << "cephx client: "
33 using ceph::bufferlist
;
35 void CephxClientHandler::reset()
37 ldout(cct
,10) << __func__
<< dendl
;
42 int CephxClientHandler::build_request(bufferlist
& bl
) const
44 ldout(cct
, 10) << "build_request" << dendl
;
46 if (need
& CEPH_ENTITY_TYPE_AUTH
) {
48 CephXRequestHeader header
;
49 header
.request_type
= CEPHX_GET_AUTH_SESSION_KEY
;
53 const bool got
= keyring
->get_secret(cct
->_conf
->name
, secret
);
55 ldout(cct
, 20) << "no secret found for entity: " << cct
->_conf
->name
<< dendl
;
60 if (!secret
.get_secret().length()) {
61 ldout(cct
, 20) << "secret for entity " << cct
->_conf
->name
<< " is invalid" << dendl
;
65 CephXAuthenticate req
;
66 req
.client_challenge
= ceph::util::generate_random_number
<uint64_t>();
68 cephx_calc_client_server_challenge(cct
, secret
, server_challenge
,
69 req
.client_challenge
, &req
.key
, error
);
71 ldout(cct
, 20) << "cephx_calc_client_server_challenge error: " << error
<< dendl
;
75 req
.old_ticket
= ticket_handler
->ticket
;
77 // for nautilus+ servers: request other keys at the same time
78 req
.other_keys
= need
;
80 if (req
.old_ticket
.blob
.length()) {
81 ldout(cct
, 20) << "old ticket len=" << req
.old_ticket
.blob
.length() << dendl
;
86 ldout(cct
, 10) << "get auth session key: client_challenge "
87 << std::hex
<< req
.client_challenge
<< std::dec
<< dendl
;
91 if (_need_tickets()) {
92 /* get service tickets */
93 ldout(cct
, 10) << "get service keys: want=" << want
<< " need=" << need
<< " have=" << have
<< dendl
;
95 CephXRequestHeader header
;
96 header
.request_type
= CEPHX_GET_PRINCIPAL_SESSION_KEY
;
99 CephXAuthorizer
*authorizer
= ticket_handler
->build_authorizer(global_id
);
102 bl
.claim_append(authorizer
->bl
);
105 CephXServiceTicketRequest req
;
113 bool CephxClientHandler::_need_tickets() const
115 // do not bother (re)requesting tickets if we *only* need the MGR
116 // ticket; that can happen during an upgrade and we want to avoid a
117 // loop. we'll end up re-requesting it later when the secrets
119 return need
&& need
!= CEPH_ENTITY_TYPE_MGR
;
122 int CephxClientHandler::handle_response(
124 bufferlist::const_iterator
& indata
,
125 CryptoKey
*session_key
,
126 std::string
*connection_secret
)
128 ldout(cct
, 10) << this << " handle_response ret = " << ret
<< dendl
;
134 CephXServerChallenge ch
;
137 } catch (ceph::buffer::error
& e
) {
138 ldout(cct
, 1) << __func__
<< " failed to decode CephXServerChallenge: "
139 << e
.what() << dendl
;
142 server_challenge
= ch
.server_challenge
;
143 ldout(cct
, 10) << " got initial server challenge "
144 << std::hex
<< server_challenge
<< std::dec
<< dendl
;
147 tickets
.invalidate_ticket(CEPH_ENTITY_TYPE_AUTH
);
151 struct CephXResponseHeader header
;
153 decode(header
, indata
);
154 } catch (ceph::buffer::error
& e
) {
155 ldout(cct
, 1) << __func__
<< " failed to decode CephXResponseHeader: "
156 << e
.what() << dendl
;
160 switch (header
.request_type
) {
161 case CEPHX_GET_AUTH_SESSION_KEY
:
163 ldout(cct
, 10) << " get_auth_session_key" << dendl
;
165 const bool got
= keyring
->get_secret(cct
->_conf
->name
, secret
);
167 ldout(cct
, 0) << "key not found for " << cct
->_conf
->name
<< dendl
;
171 if (!tickets
.verify_service_ticket_reply(secret
, indata
)) {
172 ldout(cct
, 0) << "could not verify service_ticket reply" << dendl
;
175 ldout(cct
, 10) << " want=" << want
<< " need=" << need
<< " have=" << have
<< dendl
;
177 bufferlist cbl
, extra_tickets
;
181 decode(extra_tickets
, indata
);
182 } catch (ceph::buffer::error
& e
) {
183 ldout(cct
, 1) << __func__
<< " failed to decode tickets: "
184 << e
.what() << dendl
;
187 ldout(cct
, 10) << " got connection bl " << cbl
.length()
188 << " and extra tickets " << extra_tickets
.length()
190 // for msgr1, both session_key and connection_secret are NULL
191 // so we skip extra_tickets and incur an additional round-trip
192 // to get service tickets via CEPHX_GET_PRINCIPAL_SESSION_KEY
193 // as if talking to a pre-nautilus mon
194 // this wasn't intended but turns out to be needed because in
195 // msgr1 case MonClient doesn't explicitly wait for the monmap
196 // (which is shared together with CEPHX_GET_AUTH_SESSION_KEY
198 // instead, it waits for CEPHX_GET_PRINCIPAL_SESSION_KEY reply
199 // which comes after the monmap and hence the monmap is always
200 // handled by the time authentication is considered finished
201 // if we start to always process extra_tickets here, MonClient
202 // would have no reason to send CEPHX_GET_PRINCIPAL_SESSION_KEY
203 // and RadosClient::connect() or similar could return with no
204 // actual monmap but just an initial bootstrap stub, leading
205 // to mon commands going out with zero fsid and other issues
206 if (session_key
&& connection_secret
) {
207 CephXTicketHandler
& ticket_handler
=
208 tickets
.get_handler(CEPH_ENTITY_TYPE_AUTH
);
210 *session_key
= ticket_handler
.session_key
;
212 if (cbl
.length() && connection_secret
) {
213 auto p
= cbl
.cbegin();
215 if (decode_decrypt(cct
, *connection_secret
, *session_key
, p
,
217 lderr(cct
) << __func__
<< " failed to decrypt connection_secret"
220 ldout(cct
, 10) << " got connection_secret "
221 << connection_secret
->size() << " bytes" << dendl
;
224 if (extra_tickets
.length()) {
225 auto p
= extra_tickets
.cbegin();
226 if (!tickets
.verify_service_ticket_reply(
228 lderr(cct
) << "could not verify extra service_tickets" << dendl
;
230 ldout(cct
, 10) << " got extra service_tickets" << dendl
;
243 case CEPHX_GET_PRINCIPAL_SESSION_KEY
:
245 CephXTicketHandler
& ticket_handler
= tickets
.get_handler(CEPH_ENTITY_TYPE_AUTH
);
246 ldout(cct
, 10) << " get_principal_session_key session_key " << ticket_handler
.session_key
<< dendl
;
248 if (!tickets
.verify_service_ticket_reply(ticket_handler
.session_key
, indata
)) {
249 ldout(cct
, 0) << "could not verify service_ticket reply" << dendl
;
253 if (!_need_tickets()) {
259 case CEPHX_GET_ROTATING_KEY
:
261 ldout(cct
, 10) << " get_rotating_key" << dendl
;
262 if (rotating_secrets
) {
263 RotatingSecrets secrets
;
264 CryptoKey secret_key
;
265 const bool got
= keyring
->get_secret(cct
->_conf
->name
, secret_key
);
267 ldout(cct
, 0) << "key not found for " << cct
->_conf
->name
<< dendl
;
271 if (decode_decrypt(cct
, secrets
, secret_key
, indata
, error
)) {
272 ldout(cct
, 0) << "could not set rotating key: decode_decrypt failed. error:"
276 rotating_secrets
->set_secrets(std::move(secrets
));
283 ldout(cct
, 0) << " unknown request_type " << header
.request_type
<< dendl
;
290 AuthAuthorizer
*CephxClientHandler::build_authorizer(uint32_t service_id
) const
292 ldout(cct
, 10) << "build_authorizer for service " << ceph_entity_type_name(service_id
) << dendl
;
293 return tickets
.build_authorizer(service_id
);
297 bool CephxClientHandler::build_rotating_request(bufferlist
& bl
) const
299 ldout(cct
, 10) << "build_rotating_request" << dendl
;
300 CephXRequestHeader header
;
301 header
.request_type
= CEPHX_GET_ROTATING_KEY
;
306 void CephxClientHandler::prepare_build_request()
308 ldout(cct
, 10) << "validate_tickets: want=" << want
<< " need=" << need
309 << " have=" << have
<< dendl
;
311 ldout(cct
, 10) << "want=" << want
<< " need=" << need
<< " have=" << have
314 ticket_handler
= &(tickets
.get_handler(CEPH_ENTITY_TYPE_AUTH
));
317 void CephxClientHandler::validate_tickets()
319 // lock should be held for write
320 tickets
.validate_tickets(want
, have
, need
);
323 bool CephxClientHandler::need_tickets()
327 ldout(cct
, 20) << "need_tickets: want=" << want
332 return _need_tickets();