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.
16 #include "CephxServiceHandler.h"
17 #include "CephxProtocol.h"
18 #include "CephxKeyServer.h"
22 #include "include/random.h"
23 #include "common/config.h"
24 #include "common/debug.h"
26 #define dout_subsys ceph_subsys_auth
28 #define dout_prefix *_dout << "cephx server " << entity_name << ": "
34 using ceph::bufferlist
;
38 int CephxServiceHandler::do_start_session(
39 bool is_new_global_id
,
40 bufferlist
*result_bl
,
43 global_id_status
= is_new_global_id
? global_id_status_t::NEW_PENDING
:
44 global_id_status_t::RECLAIM_PENDING
;
46 uint64_t min
= 1; // always non-zero
47 uint64_t max
= std::numeric_limits
<uint64_t>::max();
48 server_challenge
= ceph::util::generate_random_number
<uint64_t>(min
, max
);
49 ldout(cct
, 10) << "start_session server_challenge "
50 << hex
<< server_challenge
<< dec
<< dendl
;
52 CephXServerChallenge ch
;
53 ch
.server_challenge
= server_challenge
;
54 encode(ch
, *result_bl
);
58 int CephxServiceHandler::verify_old_ticket(
59 const CephXAuthenticate
& req
,
60 CephXServiceTicketInfo
& old_ticket_info
,
61 bool& should_enc_ticket
)
63 ldout(cct
, 20) << " checking old_ticket: secret_id="
64 << req
.old_ticket
.secret_id
65 << " len=" << req
.old_ticket
.blob
.length()
66 << ", old_ticket_may_be_omitted="
67 << req
.old_ticket_may_be_omitted
<< dendl
;
68 ceph_assert(global_id_status
!= global_id_status_t::NONE
);
69 if (global_id_status
== global_id_status_t::NEW_PENDING
) {
70 // old ticket is not needed
71 if (req
.old_ticket
.blob
.length()) {
72 ldout(cct
, 0) << " superfluous ticket presented" << dendl
;
75 if (req
.old_ticket_may_be_omitted
) {
76 ldout(cct
, 10) << " new global_id " << global_id
77 << " (unexposed legacy client)" << dendl
;
78 global_id_status
= global_id_status_t::NEW_NOT_EXPOSED
;
80 ldout(cct
, 10) << " new global_id " << global_id
<< dendl
;
81 global_id_status
= global_id_status_t::NEW_OK
;
86 if (!req
.old_ticket
.blob
.length()) {
87 // old ticket is needed but not presented
88 if (cct
->_conf
->auth_allow_insecure_global_id_reclaim
&&
89 req
.old_ticket_may_be_omitted
) {
90 ldout(cct
, 10) << " allowing reclaim of global_id " << global_id
91 << " with no ticket presented (legacy client, auth_allow_insecure_global_id_reclaim=true)"
93 global_id_status
= global_id_status_t::RECLAIM_INSECURE
;
96 ldout(cct
, 0) << " attempt to reclaim global_id " << global_id
97 << " without presenting ticket" << dendl
;
101 if (!cephx_decode_ticket(cct
, key_server
, CEPH_ENTITY_TYPE_AUTH
,
102 req
.old_ticket
, old_ticket_info
)) {
103 if (cct
->_conf
->auth_allow_insecure_global_id_reclaim
&&
104 req
.old_ticket_may_be_omitted
) {
105 ldout(cct
, 10) << " allowing reclaim of global_id " << global_id
106 << " using bad ticket (legacy client, auth_allow_insecure_global_id_reclaim=true)"
108 global_id_status
= global_id_status_t::RECLAIM_INSECURE
;
111 ldout(cct
, 0) << " attempt to reclaim global_id " << global_id
112 << " using bad ticket" << dendl
;
115 ldout(cct
, 20) << " decoded old_ticket: global_id="
116 << old_ticket_info
.ticket
.global_id
<< dendl
;
117 if (global_id
!= old_ticket_info
.ticket
.global_id
) {
118 if (cct
->_conf
->auth_allow_insecure_global_id_reclaim
&&
119 req
.old_ticket_may_be_omitted
) {
120 ldout(cct
, 10) << " allowing reclaim of global_id " << global_id
121 << " using mismatching ticket (legacy client, auth_allow_insecure_global_id_reclaim=true)"
123 global_id_status
= global_id_status_t::RECLAIM_INSECURE
;
126 ldout(cct
, 0) << " attempt to reclaim global_id " << global_id
127 << " using mismatching ticket" << dendl
;
130 ldout(cct
, 10) << " allowing reclaim of global_id " << global_id
131 << " (valid ticket presented, will encrypt new ticket)"
133 global_id_status
= global_id_status_t::RECLAIM_OK
;
134 should_enc_ticket
= true;
138 int CephxServiceHandler::handle_request(
139 bufferlist::const_iterator
& indata
,
140 size_t connection_secret_required_len
,
141 bufferlist
*result_bl
,
143 CryptoKey
*psession_key
,
144 std::string
*pconnection_secret
)
148 struct CephXRequestHeader cephx_header
;
150 decode(cephx_header
, indata
);
151 } catch (ceph::buffer::error
& e
) {
152 ldout(cct
, 0) << __func__
<< " failed to decode CephXRequestHeader: "
153 << e
.what() << dendl
;
157 switch (cephx_header
.request_type
) {
158 case CEPHX_GET_AUTH_SESSION_KEY
:
160 ldout(cct
, 10) << "handle_request get_auth_session_key for "
161 << entity_name
<< dendl
;
163 CephXAuthenticate req
;
166 } catch (ceph::buffer::error
& e
) {
167 ldout(cct
, 0) << __func__
<< " failed to decode CephXAuthenticate: "
168 << e
.what() << dendl
;
174 if (!key_server
->get_secret(entity_name
, secret
)) {
175 ldout(cct
, 0) << "couldn't find entity name: " << entity_name
<< dendl
;
180 if (!server_challenge
) {
185 uint64_t expected_key
;
187 cephx_calc_client_server_challenge(cct
, secret
, server_challenge
,
188 req
.client_challenge
, &expected_key
, error
);
189 if (!error
.empty()) {
190 ldout(cct
, 0) << " cephx_calc_client_server_challenge error: " << error
<< dendl
;
195 ldout(cct
, 20) << " checking key: req.key=" << hex
<< req
.key
196 << " expected_key=" << expected_key
<< dec
<< dendl
;
197 if (req
.key
!= expected_key
) {
198 ldout(cct
, 0) << " unexpected key: req.key=" << hex
<< req
.key
199 << " expected_key=" << expected_key
<< dec
<< dendl
;
204 CryptoKey session_key
;
205 CephXSessionAuthInfo info
;
206 bool should_enc_ticket
= false;
209 if (! key_server
->get_auth(entity_name
, eauth
)) {
214 CephXServiceTicketInfo old_ticket_info
;
215 ret
= verify_old_ticket(req
, old_ticket_info
, should_enc_ticket
);
217 ldout(cct
, 0) << " could not verify old ticket" << dendl
;
222 if (!key_server
->get_service_secret(CEPH_ENTITY_TYPE_AUTH
,
223 info
.service_secret
, info
.secret_id
,
225 ldout(cct
, 0) << " could not get service secret for auth subsystem" << dendl
;
230 info
.service_id
= CEPH_ENTITY_TYPE_AUTH
;
231 info
.ticket
.name
= entity_name
;
232 info
.ticket
.global_id
= global_id
;
233 info
.ticket
.init_timestamps(ceph_clock_now(), ttl
);
234 info
.validity
.set_from_double(ttl
);
236 key_server
->generate_secret(session_key
);
238 info
.session_key
= session_key
;
240 *psession_key
= session_key
;
243 vector
<CephXSessionAuthInfo
> info_vec
;
244 info_vec
.push_back(info
);
246 build_cephx_response_header(cephx_header
.request_type
, 0, *result_bl
);
247 if (!cephx_build_service_ticket_reply(
248 cct
, eauth
.key
, info_vec
, should_enc_ticket
,
249 old_ticket_info
.session_key
, *result_bl
)) {
254 if (!key_server
->get_service_caps(entity_name
, CEPH_ENTITY_TYPE_MON
,
256 ldout(cct
, 0) << " could not get mon caps for " << entity_name
<< dendl
;
260 char *caps_str
= caps
->caps
.c_str();
261 if (!caps_str
|| !caps_str
[0]) {
262 ldout(cct
,0) << "mon caps null for " << entity_name
<< dendl
;
267 if (req
.other_keys
) {
269 // generate a connection_secret
271 if (pconnection_secret
) {
272 pconnection_secret
->resize(connection_secret_required_len
);
273 if (connection_secret_required_len
) {
274 cct
->random()->get_bytes(pconnection_secret
->data(),
275 connection_secret_required_len
);
278 if (encode_encrypt(cct
, *pconnection_secret
, session_key
, cbl
,
280 lderr(cct
) << __func__
<< " failed to encrypt connection secret, "
286 encode(cbl
, *result_bl
);
287 // provide requested service tickets at the same time
288 vector
<CephXSessionAuthInfo
> info_vec
;
289 for (uint32_t service_id
= 1; service_id
<= req
.other_keys
;
291 // skip CEPH_ENTITY_TYPE_AUTH: auth ticket is already encoded
292 // (possibly encrypted with the old session key)
293 if ((req
.other_keys
& service_id
) &&
294 service_id
!= CEPH_ENTITY_TYPE_AUTH
) {
295 ldout(cct
, 10) << " adding key for service "
296 << ceph_entity_type_name(service_id
) << dendl
;
297 CephXSessionAuthInfo svc_info
;
298 key_server
->build_session_auth_info(
302 info_vec
.push_back(svc_info
);
306 if (!info_vec
.empty()) {
308 cephx_build_service_ticket_reply(
309 cct
, session_key
, info_vec
, false, no_key
, extra
);
311 encode(extra
, *result_bl
);
314 // caller should try to finish authentication
320 case CEPHX_GET_PRINCIPAL_SESSION_KEY
:
322 ldout(cct
, 10) << "handle_request get_principal_session_key" << dendl
;
325 CephXServiceTicketInfo auth_ticket_info
;
326 // note: no challenge here.
327 if (!cephx_verify_authorizer(
328 cct
, *key_server
, indata
, 0, auth_ticket_info
, nullptr,
335 CephXServiceTicketRequest ticket_req
;
337 decode(ticket_req
, indata
);
338 } catch (ceph::buffer::error
& e
) {
339 ldout(cct
, 0) << __func__
340 << " failed to decode CephXServiceTicketRequest: "
341 << e
.what() << dendl
;
345 ldout(cct
, 10) << " ticket_req.keys = " << ticket_req
.keys
<< dendl
;
348 vector
<CephXSessionAuthInfo
> info_vec
;
349 int found_services
= 0;
351 for (uint32_t service_id
= 1; service_id
<= ticket_req
.keys
;
353 // skip CEPH_ENTITY_TYPE_AUTH: auth ticket must be obtained with
354 // CEPHX_GET_AUTH_SESSION_KEY
355 if ((ticket_req
.keys
& service_id
) &&
356 service_id
!= CEPH_ENTITY_TYPE_AUTH
) {
357 ldout(cct
, 10) << " adding key for service "
358 << ceph_entity_type_name(service_id
) << dendl
;
359 CephXSessionAuthInfo info
;
360 int r
= key_server
->build_session_auth_info(
362 auth_ticket_info
.ticket
, // parent ticket (client's auth ticket)
364 // tolerate missing MGR rotating key for the purposes of upgrades.
366 ldout(cct
, 10) << " missing key for service "
367 << ceph_entity_type_name(service_id
) << dendl
;
371 info_vec
.push_back(info
);
375 if (!found_services
&& service_err
) {
376 ldout(cct
, 10) << __func__
<< " did not find any service keys" << dendl
;
380 build_cephx_response_header(cephx_header
.request_type
, ret
, *result_bl
);
381 cephx_build_service_ticket_reply(cct
, auth_ticket_info
.session_key
,
382 info_vec
, false, no_key
, *result_bl
);
386 case CEPHX_GET_ROTATING_KEY
:
388 ldout(cct
, 10) << "handle_request getting rotating secret for "
389 << entity_name
<< dendl
;
390 build_cephx_response_header(cephx_header
.request_type
, 0, *result_bl
);
391 if (!key_server
->get_rotating_encrypted(entity_name
, *result_bl
)) {
399 ldout(cct
, 10) << "handle_request unknown op " << cephx_header
.request_type
<< dendl
;
405 void CephxServiceHandler::build_cephx_response_header(int request_type
, int status
, bufferlist
& bl
)
407 struct CephXResponseHeader header
;
408 header
.request_type
= request_type
;
409 header
.status
= status
;