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) 2009-2011 New Dream Network
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.
15 #include "CephxProtocol.h"
16 #include "common/Clock.h"
17 #include "common/config.h"
18 #include "common/debug.h"
19 #include "include/buffer.h"
21 #define dout_subsys ceph_subsys_auth
23 #define dout_prefix *_dout << "cephx: "
27 void cephx_calc_client_server_challenge(CephContext
*cct
, CryptoKey
& secret
, uint64_t server_challenge
,
28 uint64_t client_challenge
, uint64_t *key
, std::string
&error
)
31 b
.server_challenge
= server_challenge
;
32 b
.client_challenge
= client_challenge
;
35 if (encode_encrypt(cct
, b
, secret
, enc
, error
))
39 const uint64_t *p
= (const uint64_t *)enc
.c_str();
40 for (int pos
= 0; pos
+ sizeof(k
) <= enc
.length(); pos
+=sizeof(k
), p
++)
50 bool cephx_build_service_ticket_blob(CephContext
*cct
, CephXSessionAuthInfo
& info
,
51 CephXTicketBlob
& blob
)
53 CephXServiceTicketInfo ticket_info
;
54 ticket_info
.session_key
= info
.session_key
;
55 ticket_info
.ticket
= info
.ticket
;
56 ticket_info
.ticket
.caps
= info
.ticket
.caps
;
58 ldout(cct
, 10) << "build_service_ticket service " << ceph_entity_type_name(info
.service_id
)
59 << " secret_id " << info
.secret_id
60 << " ticket_info.ticket.name=" << ticket_info
.ticket
.name
.to_str() << dendl
;
61 blob
.secret_id
= info
.secret_id
;
63 if (!info
.service_secret
.get_secret().length())
64 error
= "invalid key"; // Bad key?
66 encode_encrypt_enc_bl(cct
, ticket_info
, info
.service_secret
, blob
.blob
, error
);
68 ldout(cct
, -1) << "cephx_build_service_ticket_blob failed with error "
76 * AUTH SERVER: authenticate
78 * Authenticate principal, respond with AuthServiceTicketInfo
80 * {session key, validity}^principal_secret
81 * {principal_ticket, session key}^service_secret ... "enc_ticket"
83 bool cephx_build_service_ticket_reply(CephContext
*cct
,
84 CryptoKey
& principal_secret
,
85 vector
<CephXSessionAuthInfo
> ticket_info_vec
,
86 bool should_encrypt_ticket
,
87 CryptoKey
& ticket_enc_key
,
90 __u8 service_ticket_reply_v
= 1;
91 ::encode(service_ticket_reply_v
, reply
);
93 uint32_t num
= ticket_info_vec
.size();
95 ldout(cct
, 10) << "build_service_ticket_reply encoding " << num
96 << " tickets with secret " << principal_secret
<< dendl
;
98 for (vector
<CephXSessionAuthInfo
>::iterator ticket_iter
= ticket_info_vec
.begin();
99 ticket_iter
!= ticket_info_vec
.end();
101 CephXSessionAuthInfo
& info
= *ticket_iter
;
102 ::encode(info
.service_id
, reply
);
104 __u8 service_ticket_v
= 1;
105 ::encode(service_ticket_v
, reply
);
107 CephXServiceTicket msg_a
;
108 msg_a
.session_key
= info
.session_key
;
109 msg_a
.validity
= info
.validity
;
111 if (encode_encrypt(cct
, msg_a
, principal_secret
, reply
, error
)) {
112 ldout(cct
, -1) << "error encoding encrypted: " << error
<< dendl
;
116 bufferlist service_ticket_bl
;
117 CephXTicketBlob blob
;
118 if (!cephx_build_service_ticket_blob(cct
, info
, blob
)) {
121 ::encode(blob
, service_ticket_bl
);
123 ldout(cct
, 30) << "service_ticket_blob is ";
124 service_ticket_bl
.hexdump(*_dout
);
127 ::encode((__u8
)should_encrypt_ticket
, reply
);
128 if (should_encrypt_ticket
) {
129 if (encode_encrypt(cct
, service_ticket_bl
, ticket_enc_key
, reply
, error
)) {
130 ldout(cct
, -1) << "error encoding encrypted ticket: " << error
<< dendl
;
134 ::encode(service_ticket_bl
, reply
);
141 * PRINCIPAL: verify our attempt to authenticate succeeded. fill out
142 * this ServiceTicket with the result.
144 bool CephXTicketHandler::verify_service_ticket_reply(CryptoKey
& secret
,
145 bufferlist::iterator
& indata
)
147 __u8 service_ticket_v
;
148 ::decode(service_ticket_v
, indata
);
150 CephXServiceTicket msg_a
;
152 if (decode_decrypt(cct
, msg_a
, secret
, indata
, error
)) {
153 ldout(cct
, 0) << "verify_service_ticket_reply: failed decode_decrypt, error is: " << error
<< dendl
;
158 ::decode(ticket_enc
, indata
);
160 bufferlist service_ticket_bl
;
162 ldout(cct
, 10) << " got encrypted ticket" << dendl
;
164 if (decode_decrypt(cct
, service_ticket_bl
, session_key
, indata
, error
)) {
165 ldout(cct
, 10) << "verify_service_ticket_reply: decode_decrypt failed "
166 << "with " << error
<< dendl
;
170 ::decode(service_ticket_bl
, indata
);
172 bufferlist::iterator iter
= service_ticket_bl
.begin();
173 ::decode(ticket
, iter
);
174 ldout(cct
, 10) << " ticket.secret_id=" << ticket
.secret_id
<< dendl
;
176 ldout(cct
, 10) << "verify_service_ticket_reply service " << ceph_entity_type_name(service_id
)
177 << " secret_id " << ticket
.secret_id
178 << " session_key " << msg_a
.session_key
179 << " validity=" << msg_a
.validity
<< dendl
;
180 session_key
= msg_a
.session_key
;
181 if (!msg_a
.validity
.is_zero()) {
182 expires
= ceph_clock_now();
183 expires
+= msg_a
.validity
;
184 renew_after
= expires
;
185 renew_after
-= ((double)msg_a
.validity
.sec() / 4);
186 ldout(cct
, 10) << "ticket expires=" << expires
<< " renew_after=" << renew_after
<< dendl
;
189 have_key_flag
= true;
193 bool CephXTicketHandler::have_key()
196 have_key_flag
= ceph_clock_now() < expires
;
199 return have_key_flag
;
202 bool CephXTicketHandler::need_key() const
205 return (!expires
.is_zero()) && (ceph_clock_now() >= renew_after
);
211 bool CephXTicketManager::have_key(uint32_t service_id
)
213 map
<uint32_t, CephXTicketHandler
>::iterator iter
= tickets_map
.find(service_id
);
214 if (iter
== tickets_map
.end())
216 return iter
->second
.have_key();
219 bool CephXTicketManager::need_key(uint32_t service_id
) const
221 map
<uint32_t, CephXTicketHandler
>::const_iterator iter
= tickets_map
.find(service_id
);
222 if (iter
== tickets_map
.end())
224 return iter
->second
.need_key();
227 void CephXTicketManager::set_have_need_key(uint32_t service_id
, uint32_t& have
, uint32_t& need
)
229 map
<uint32_t, CephXTicketHandler
>::iterator iter
= tickets_map
.find(service_id
);
230 if (iter
== tickets_map
.end()) {
233 ldout(cct
, 10) << "set_have_need_key no handler for service "
234 << ceph_entity_type_name(service_id
) << dendl
;
238 //ldout(cct, 10) << "set_have_need_key service " << ceph_entity_type_name(service_id)
239 //<< " (" << service_id << ")"
240 //<< " need=" << iter->second.need_key() << " have=" << iter->second.have_key() << dendl;
241 if (iter
->second
.need_key())
246 if (iter
->second
.have_key())
252 void CephXTicketManager::invalidate_ticket(uint32_t service_id
)
254 map
<uint32_t, CephXTicketHandler
>::iterator iter
= tickets_map
.find(service_id
);
255 if (iter
!= tickets_map
.end())
256 iter
->second
.invalidate_ticket();
260 * PRINCIPAL: verify our attempt to authenticate succeeded. fill out
261 * this ServiceTicket with the result.
263 bool CephXTicketManager::verify_service_ticket_reply(CryptoKey
& secret
,
264 bufferlist::iterator
& indata
)
266 __u8 service_ticket_reply_v
;
267 ::decode(service_ticket_reply_v
, indata
);
270 ::decode(num
, indata
);
271 ldout(cct
, 10) << "verify_service_ticket_reply got " << num
<< " keys" << dendl
;
273 for (int i
=0; i
<(int)num
; i
++) {
275 ::decode(type
, indata
);
276 ldout(cct
, 10) << "got key for service_id " << ceph_entity_type_name(type
) << dendl
;
277 CephXTicketHandler
& handler
= get_handler(type
);
278 if (!handler
.verify_service_ticket_reply(secret
, indata
)) {
281 handler
.service_id
= type
;
291 * PRINCIPAL: build authorizer to access the service.
293 * ticket, {timestamp}^session_key
295 CephXAuthorizer
*CephXTicketHandler::build_authorizer(uint64_t global_id
) const
297 CephXAuthorizer
*a
= new CephXAuthorizer(cct
);
298 a
->session_key
= session_key
;
299 get_random_bytes((char*)&a
->nonce
, sizeof(a
->nonce
));
301 __u8 authorizer_v
= 1;
302 ::encode(authorizer_v
, a
->bl
);
303 ::encode(global_id
, a
->bl
);
304 ::encode(service_id
, a
->bl
);
306 ::encode(ticket
, a
->bl
);
310 msg
.nonce
= a
->nonce
;
313 if (encode_encrypt(cct
, msg
, session_key
, a
->bl
, error
)) {
314 ldout(cct
, 0) << "failed to encrypt authorizer: " << error
<< dendl
;
322 * PRINCIPAL: build authorizer to access the service.
324 * ticket, {timestamp}^session_key
326 CephXAuthorizer
*CephXTicketManager::build_authorizer(uint32_t service_id
) const
328 map
<uint32_t, CephXTicketHandler
>::const_iterator iter
= tickets_map
.find(service_id
);
329 if (iter
== tickets_map
.end()) {
330 ldout(cct
, 0) << "no TicketHandler for service "
331 << ceph_entity_type_name(service_id
) << dendl
;
335 const CephXTicketHandler
& handler
= iter
->second
;
336 return handler
.build_authorizer(global_id
);
339 void CephXTicketManager::validate_tickets(uint32_t mask
, uint32_t& have
, uint32_t& need
)
343 for (i
= 1; i
<=mask
; i
<<=1) {
345 set_have_need_key(i
, have
, need
);
348 ldout(cct
, 10) << "validate_tickets want " << mask
<< " have " << have
349 << " need " << need
<< dendl
;
352 bool cephx_decode_ticket(CephContext
*cct
, KeyStore
*keys
, uint32_t service_id
,
353 CephXTicketBlob
& ticket_blob
, CephXServiceTicketInfo
& ticket_info
)
355 uint64_t secret_id
= ticket_blob
.secret_id
;
356 CryptoKey service_secret
;
358 if (!ticket_blob
.blob
.length()) {
362 if (secret_id
== (uint64_t)-1) {
363 if (!keys
->get_secret(cct
->_conf
->name
, service_secret
)) {
364 ldout(cct
, 0) << "ceph_decode_ticket could not get general service secret for service_id="
365 << ceph_entity_type_name(service_id
) << " secret_id=" << secret_id
<< dendl
;
369 if (!keys
->get_service_secret(service_id
, secret_id
, service_secret
)) {
370 ldout(cct
, 0) << "ceph_decode_ticket could not get service secret for service_id="
371 << ceph_entity_type_name(service_id
) << " secret_id=" << secret_id
<< dendl
;
377 decode_decrypt_enc_bl(cct
, ticket_info
, service_secret
, ticket_blob
.blob
, error
);
378 if (!error
.empty()) {
379 ldout(cct
, 0) << "ceph_decode_ticket could not decrypt ticket info. error:"
388 * SERVICE: verify authorizer and generate reply authorizer
390 * {timestamp + 1}^session_key
392 bool cephx_verify_authorizer(CephContext
*cct
, KeyStore
*keys
,
393 bufferlist::iterator
& indata
,
394 CephXServiceTicketInfo
& ticket_info
,
395 std::unique_ptr
<AuthAuthorizerChallenge
> *challenge
,
396 bufferlist
& reply_bl
)
401 CryptoKey service_secret
;
403 CephXTicketBlob ticket
;
407 ::decode(authorizer_v
, indata
);
408 ::decode(global_id
, indata
);
409 ::decode(service_id
, indata
);
410 ::decode(ticket
, indata
);
411 } catch (buffer::end_of_buffer
&e
) {
415 ldout(cct
, 10) << "verify_authorizer decrypted service "
416 << ceph_entity_type_name(service_id
)
417 << " secret_id=" << ticket
.secret_id
<< dendl
;
419 if (ticket
.secret_id
== (uint64_t)-1) {
421 name
.set_type(service_id
);
422 if (!keys
->get_secret(name
, service_secret
)) {
423 ldout(cct
, 0) << "verify_authorizer could not get general service secret for service "
424 << ceph_entity_type_name(service_id
) << " secret_id=" << ticket
.secret_id
<< dendl
;
428 if (!keys
->get_service_secret(service_id
, ticket
.secret_id
, service_secret
)) {
429 ldout(cct
, 0) << "verify_authorizer could not get service secret for service "
430 << ceph_entity_type_name(service_id
) << " secret_id=" << ticket
.secret_id
<< dendl
;
431 if (cct
->_conf
->auth_debug
&& ticket
.secret_id
== 0)
432 assert(0 == "got secret_id=0");
437 if (!service_secret
.get_secret().length())
438 error
= "invalid key"; // Bad key?
440 decode_decrypt_enc_bl(cct
, ticket_info
, service_secret
, ticket
.blob
, error
);
441 if (!error
.empty()) {
442 ldout(cct
, 0) << "verify_authorizer could not decrypt ticket info: error: "
447 if (ticket_info
.ticket
.global_id
!= global_id
) {
448 ldout(cct
, 0) << "verify_authorizer global_id mismatch: declared id=" << global_id
449 << " ticket_id=" << ticket_info
.ticket
.global_id
<< dendl
;
453 ldout(cct
, 10) << "verify_authorizer global_id=" << global_id
<< dendl
;
456 CephXAuthorize auth_msg
;
457 if (decode_decrypt(cct
, auth_msg
, ticket_info
.session_key
, indata
, error
)) {
458 ldout(cct
, 0) << "verify_authorizercould not decrypt authorize request with error: "
464 auto *c
= static_cast<CephXAuthorizeChallenge
*>(challenge
->get());
465 if (!auth_msg
.have_challenge
|| !c
) {
466 c
= new CephXAuthorizeChallenge
;
468 get_random_bytes((char*)&c
->server_challenge
, sizeof(c
->server_challenge
));
469 ldout(cct
,10) << __func__
<< " adding server_challenge " << c
->server_challenge
472 encode_encrypt_enc_bl(cct
, *c
, ticket_info
.session_key
, reply_bl
, error
);
473 if (!error
.empty()) {
474 ldout(cct
, 10) << "verify_authorizer: encode_encrypt error: " << error
<< dendl
;
479 ldout(cct
, 10) << __func__
<< " got server_challenge+1 "
480 << auth_msg
.server_challenge_plus_one
481 << " expecting " << c
->server_challenge
+ 1 << dendl
;
482 if (c
->server_challenge
+ 1 != auth_msg
.server_challenge_plus_one
) {
489 * {timestamp + 1}^session_key
491 CephXAuthorizeReply reply
;
492 // reply.trans_id = auth_msg.trans_id;
493 reply
.nonce_plus_one
= auth_msg
.nonce
+ 1;
494 if (encode_encrypt(cct
, reply
, ticket_info
.session_key
, reply_bl
, error
)) {
495 ldout(cct
, 10) << "verify_authorizer: encode_encrypt error: " << error
<< dendl
;
499 ldout(cct
, 10) << "verify_authorizer ok nonce " << hex
<< auth_msg
.nonce
<< dec
500 << " reply_bl.length()=" << reply_bl
.length() << dendl
;
504 bool CephXAuthorizer::verify_reply(bufferlist::iterator
& indata
)
506 CephXAuthorizeReply reply
;
509 if (decode_decrypt(cct
, reply
, session_key
, indata
, error
)) {
510 ldout(cct
, 0) << "verify_reply couldn't decrypt with error: " << error
<< dendl
;
514 uint64_t expect
= nonce
+ 1;
515 if (expect
!= reply
.nonce_plus_one
) {
516 ldout(cct
, 0) << "verify_authorizer_reply bad nonce got " << reply
.nonce_plus_one
<< " expected " << expect
517 << " sent " << nonce
<< dendl
;
523 bool CephXAuthorizer::add_challenge(CephContext
*cct
, bufferlist
& challenge
)
530 auto p
= challenge
.begin();
533 CephXAuthorizeChallenge ch
;
534 decode_decrypt_enc_bl(cct
, ch
, session_key
, challenge
, error
);
535 if (!error
.empty()) {
536 ldout(cct
, 0) << "failed to decrypt challenge (" << challenge
.length() << " bytes): "
540 msg
.have_challenge
= true;
541 msg
.server_challenge_plus_one
= ch
.server_challenge
+ 1;
545 if (encode_encrypt(cct
, msg
, session_key
, bl
, error
)) {
546 ldout(cct
, 0) << __func__
<< " failed to encrypt authorizer: " << error
<< dendl
;