]> git.proxmox.com Git - ceph.git/blame - ceph/src/auth/cephx/CephxProtocol.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / auth / cephx / CephxProtocol.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2009-2011 New Dream Network
7 *
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.
12 *
13 */
14
15#include "CephxProtocol.h"
16#include "common/Clock.h"
11fdf7f2 17#include "common/ceph_context.h"
7c673cae
FG
18#include "common/config.h"
19#include "common/debug.h"
20#include "include/buffer.h"
21
22#define dout_subsys ceph_subsys_auth
23#undef dout_prefix
24#define dout_prefix *_dout << "cephx: "
25
f67539c2
TL
26using std::dec;
27using std::hex;
28using std::vector;
7c673cae 29
f67539c2
TL
30using ceph::bufferlist;
31using ceph::decode;
32using ceph::encode;
7c673cae
FG
33
34void cephx_calc_client_server_challenge(CephContext *cct, CryptoKey& secret, uint64_t server_challenge,
35 uint64_t client_challenge, uint64_t *key, std::string &error)
36{
37 CephXChallengeBlob b;
38 b.server_challenge = server_challenge;
39 b.client_challenge = client_challenge;
40
41 bufferlist enc;
42 if (encode_encrypt(cct, b, secret, enc, error))
43 return;
44
45 uint64_t k = 0;
eafe8130 46 const ceph_le64 *p = (const ceph_le64 *)enc.c_str();
7c673cae 47 for (int pos = 0; pos + sizeof(k) <= enc.length(); pos+=sizeof(k), p++)
eafe8130 48 k ^= *p;
7c673cae
FG
49 *key = k;
50}
51
52
53/*
54 * Authentication
55 */
56
57bool cephx_build_service_ticket_blob(CephContext *cct, CephXSessionAuthInfo& info,
58 CephXTicketBlob& blob)
59{
60 CephXServiceTicketInfo ticket_info;
61 ticket_info.session_key = info.session_key;
62 ticket_info.ticket = info.ticket;
63 ticket_info.ticket.caps = info.ticket.caps;
64
11fdf7f2
TL
65 ldout(cct, 10) << "build_service_ticket service "
66 << ceph_entity_type_name(info.service_id)
67 << " secret_id " << info.secret_id
68 << " ticket_info.ticket.name="
69 << ticket_info.ticket.name.to_str()
70 << " ticket.global_id " << info.ticket.global_id << dendl;
7c673cae
FG
71 blob.secret_id = info.secret_id;
72 std::string error;
73 if (!info.service_secret.get_secret().length())
74 error = "invalid key"; // Bad key?
75 else
76 encode_encrypt_enc_bl(cct, ticket_info, info.service_secret, blob.blob, error);
77 if (!error.empty()) {
78 ldout(cct, -1) << "cephx_build_service_ticket_blob failed with error "
79 << error << dendl;
80 return false;
81 }
82 return true;
83}
84
85/*
86 * AUTH SERVER: authenticate
87 *
88 * Authenticate principal, respond with AuthServiceTicketInfo
89 *
90 * {session key, validity}^principal_secret
91 * {principal_ticket, session key}^service_secret ... "enc_ticket"
92 */
93bool cephx_build_service_ticket_reply(CephContext *cct,
94 CryptoKey& principal_secret,
95 vector<CephXSessionAuthInfo> ticket_info_vec,
96 bool should_encrypt_ticket,
97 CryptoKey& ticket_enc_key,
98 bufferlist& reply)
99{
100 __u8 service_ticket_reply_v = 1;
f67539c2 101 using ceph::encode;
11fdf7f2 102 encode(service_ticket_reply_v, reply);
7c673cae
FG
103
104 uint32_t num = ticket_info_vec.size();
11fdf7f2 105 encode(num, reply);
7c673cae
FG
106 ldout(cct, 10) << "build_service_ticket_reply encoding " << num
107 << " tickets with secret " << principal_secret << dendl;
108
f67539c2 109 for (auto ticket_iter = ticket_info_vec.begin();
7c673cae
FG
110 ticket_iter != ticket_info_vec.end();
111 ++ticket_iter) {
112 CephXSessionAuthInfo& info = *ticket_iter;
11fdf7f2 113 encode(info.service_id, reply);
7c673cae
FG
114
115 __u8 service_ticket_v = 1;
11fdf7f2 116 encode(service_ticket_v, reply);
7c673cae
FG
117
118 CephXServiceTicket msg_a;
119 msg_a.session_key = info.session_key;
120 msg_a.validity = info.validity;
121 std::string error;
122 if (encode_encrypt(cct, msg_a, principal_secret, reply, error)) {
123 ldout(cct, -1) << "error encoding encrypted: " << error << dendl;
124 return false;
125 }
126
127 bufferlist service_ticket_bl;
128 CephXTicketBlob blob;
129 if (!cephx_build_service_ticket_blob(cct, info, blob)) {
130 return false;
131 }
11fdf7f2 132 encode(blob, service_ticket_bl);
7c673cae
FG
133
134 ldout(cct, 30) << "service_ticket_blob is ";
135 service_ticket_bl.hexdump(*_dout);
136 *_dout << dendl;
137
11fdf7f2 138 encode((__u8)should_encrypt_ticket, reply);
7c673cae
FG
139 if (should_encrypt_ticket) {
140 if (encode_encrypt(cct, service_ticket_bl, ticket_enc_key, reply, error)) {
141 ldout(cct, -1) << "error encoding encrypted ticket: " << error << dendl;
142 return false;
143 }
144 } else {
11fdf7f2 145 encode(service_ticket_bl, reply);
7c673cae
FG
146 }
147 }
148 return true;
149}
150
151/*
152 * PRINCIPAL: verify our attempt to authenticate succeeded. fill out
153 * this ServiceTicket with the result.
154 */
11fdf7f2
TL
155bool CephXTicketHandler::verify_service_ticket_reply(
156 CryptoKey& secret,
157 bufferlist::const_iterator& indata)
7c673cae 158{
f67539c2 159 using ceph::decode;
eafe8130
TL
160 try {
161 __u8 service_ticket_v;
162 decode(service_ticket_v, indata);
7c673cae 163
eafe8130 164 CephXServiceTicket msg_a;
7c673cae 165 std::string error;
eafe8130
TL
166 if (decode_decrypt(cct, msg_a, secret, indata, error)) {
167 ldout(cct, 0) << __func__ << " failed decode_decrypt, error is: " << error
168 << dendl;
7c673cae
FG
169 return false;
170 }
7c673cae 171
eafe8130
TL
172 __u8 ticket_enc;
173 decode(ticket_enc, indata);
174
175 bufferlist service_ticket_bl;
176 if (ticket_enc) {
177 ldout(cct, 10) << __func__ << " got encrypted ticket" << dendl;
178 std::string error;
179 if (decode_decrypt(cct, service_ticket_bl, session_key, indata, error)) {
180 ldout(cct, 10) << __func__ << " decode_decrypt failed "
181 << "with " << error << dendl;
182 return false;
183 }
184 } else {
185 decode(service_ticket_bl, indata);
186 }
187 auto iter = service_ticket_bl.cbegin();
188 decode(ticket, iter);
189 ldout(cct, 10) << __func__ << " ticket.secret_id=" << ticket.secret_id
190 << dendl;
191
192 ldout(cct, 10) << __func__ << " service "
193 << ceph_entity_type_name(service_id)
194 << " secret_id " << ticket.secret_id
195 << " session_key " << msg_a.session_key
196 << " validity=" << msg_a.validity << dendl;
197 session_key = msg_a.session_key;
198 if (!msg_a.validity.is_zero()) {
199 expires = ceph_clock_now();
200 expires += msg_a.validity;
201 renew_after = expires;
202 renew_after -= ((double)msg_a.validity.sec() / 4);
203 ldout(cct, 10) << __func__ << " ticket expires=" << expires
204 << " renew_after=" << renew_after << dendl;
205 }
206
207 have_key_flag = true;
208 return true;
f67539c2 209 } catch (ceph::buffer::error& e) {
eafe8130
TL
210 ldout(cct, 1) << __func__ << " decode error: " << e.what() << dendl;
211 return false;
212 }
7c673cae
FG
213}
214
215bool CephXTicketHandler::have_key()
216{
217 if (have_key_flag) {
218 have_key_flag = ceph_clock_now() < expires;
219 }
220
221 return have_key_flag;
222}
223
224bool CephXTicketHandler::need_key() const
225{
226 if (have_key_flag) {
227 return (!expires.is_zero()) && (ceph_clock_now() >= renew_after);
228 }
229
230 return true;
231}
232
233bool CephXTicketManager::have_key(uint32_t service_id)
234{
f67539c2 235 auto iter = tickets_map.find(service_id);
7c673cae
FG
236 if (iter == tickets_map.end())
237 return false;
238 return iter->second.have_key();
239}
240
241bool CephXTicketManager::need_key(uint32_t service_id) const
242{
f67539c2 243 auto iter = tickets_map.find(service_id);
7c673cae
FG
244 if (iter == tickets_map.end())
245 return true;
246 return iter->second.need_key();
247}
248
249void CephXTicketManager::set_have_need_key(uint32_t service_id, uint32_t& have, uint32_t& need)
250{
f67539c2 251 auto iter = tickets_map.find(service_id);
7c673cae
FG
252 if (iter == tickets_map.end()) {
253 have &= ~service_id;
254 need |= service_id;
255 ldout(cct, 10) << "set_have_need_key no handler for service "
256 << ceph_entity_type_name(service_id) << dendl;
257 return;
258 }
259
260 //ldout(cct, 10) << "set_have_need_key service " << ceph_entity_type_name(service_id)
261 //<< " (" << service_id << ")"
262 //<< " need=" << iter->second.need_key() << " have=" << iter->second.have_key() << dendl;
263 if (iter->second.need_key())
264 need |= service_id;
265 else
266 need &= ~service_id;
267
268 if (iter->second.have_key())
269 have |= service_id;
270 else
271 have &= ~service_id;
272}
273
274void CephXTicketManager::invalidate_ticket(uint32_t service_id)
275{
f67539c2 276 auto iter = tickets_map.find(service_id);
7c673cae
FG
277 if (iter != tickets_map.end())
278 iter->second.invalidate_ticket();
279}
280
281/*
282 * PRINCIPAL: verify our attempt to authenticate succeeded. fill out
283 * this ServiceTicket with the result.
284 */
285bool CephXTicketManager::verify_service_ticket_reply(CryptoKey& secret,
11fdf7f2 286 bufferlist::const_iterator& indata)
7c673cae
FG
287{
288 __u8 service_ticket_reply_v;
9f95a23c 289 uint32_t num = 0;
eafe8130
TL
290 try {
291 decode(service_ticket_reply_v, indata);
292 decode(num, indata);
f67539c2 293 } catch (ceph::buffer::error& e) {
eafe8130
TL
294 ldout(cct, 10) << __func__ << " failed to decode ticket v or count: "
295 << e.what() << dendl;
296 }
7c673cae
FG
297 ldout(cct, 10) << "verify_service_ticket_reply got " << num << " keys" << dendl;
298
299 for (int i=0; i<(int)num; i++) {
9f95a23c 300 uint32_t type = 0;
eafe8130
TL
301 try {
302 decode(type, indata);
f67539c2 303 } catch (ceph::buffer::error& e) {
eafe8130
TL
304 ldout(cct, 10) << __func__ << " failed to decode ticket type: " << e.what()
305 << dendl;
306 }
7c673cae
FG
307 ldout(cct, 10) << "got key for service_id " << ceph_entity_type_name(type) << dendl;
308 CephXTicketHandler& handler = get_handler(type);
309 if (!handler.verify_service_ticket_reply(secret, indata)) {
310 return false;
311 }
312 handler.service_id = type;
313 }
314
7c673cae
FG
315 return true;
316}
317
318/*
319 * PRINCIPAL: build authorizer to access the service.
320 *
321 * ticket, {timestamp}^session_key
322 */
323CephXAuthorizer *CephXTicketHandler::build_authorizer(uint64_t global_id) const
324{
325 CephXAuthorizer *a = new CephXAuthorizer(cct);
326 a->session_key = session_key;
11fdf7f2 327 cct->random()->get_bytes((char*)&a->nonce, sizeof(a->nonce));
7c673cae 328
11fdf7f2
TL
329 __u8 authorizer_v = 1; // see AUTH_MODE_* in Auth.h
330 encode(authorizer_v, a->bl);
331 encode(global_id, a->bl);
332 encode(service_id, a->bl);
7c673cae 333
11fdf7f2 334 encode(ticket, a->bl);
28e407b8 335 a->base_bl = a->bl;
7c673cae
FG
336
337 CephXAuthorize msg;
338 msg.nonce = a->nonce;
339
340 std::string error;
341 if (encode_encrypt(cct, msg, session_key, a->bl, error)) {
342 ldout(cct, 0) << "failed to encrypt authorizer: " << error << dendl;
343 delete a;
344 return 0;
345 }
346 return a;
347}
348
349/*
350 * PRINCIPAL: build authorizer to access the service.
351 *
352 * ticket, {timestamp}^session_key
353 */
354CephXAuthorizer *CephXTicketManager::build_authorizer(uint32_t service_id) const
355{
f67539c2 356 auto iter = tickets_map.find(service_id);
7c673cae
FG
357 if (iter == tickets_map.end()) {
358 ldout(cct, 0) << "no TicketHandler for service "
359 << ceph_entity_type_name(service_id) << dendl;
360 return NULL;
361 }
362
363 const CephXTicketHandler& handler = iter->second;
364 return handler.build_authorizer(global_id);
365}
366
367void CephXTicketManager::validate_tickets(uint32_t mask, uint32_t& have, uint32_t& need)
368{
369 uint32_t i;
370 need = 0;
371 for (i = 1; i<=mask; i<<=1) {
372 if (mask & i) {
373 set_have_need_key(i, have, need);
374 }
375 }
376 ldout(cct, 10) << "validate_tickets want " << mask << " have " << have
377 << " need " << need << dendl;
378}
379
c5c27e9a
TL
380bool cephx_decode_ticket(CephContext *cct, KeyStore *keys,
381 uint32_t service_id,
382 const CephXTicketBlob& ticket_blob,
383 CephXServiceTicketInfo& ticket_info)
7c673cae
FG
384{
385 uint64_t secret_id = ticket_blob.secret_id;
386 CryptoKey service_secret;
387
388 if (!ticket_blob.blob.length()) {
389 return false;
390 }
391
392 if (secret_id == (uint64_t)-1) {
393 if (!keys->get_secret(cct->_conf->name, service_secret)) {
394 ldout(cct, 0) << "ceph_decode_ticket could not get general service secret for service_id="
395 << ceph_entity_type_name(service_id) << " secret_id=" << secret_id << dendl;
396 return false;
397 }
398 } else {
399 if (!keys->get_service_secret(service_id, secret_id, service_secret)) {
400 ldout(cct, 0) << "ceph_decode_ticket could not get service secret for service_id="
401 << ceph_entity_type_name(service_id) << " secret_id=" << secret_id << dendl;
402 return false;
403 }
404 }
405
406 std::string error;
407 decode_decrypt_enc_bl(cct, ticket_info, service_secret, ticket_blob.blob, error);
408 if (!error.empty()) {
409 ldout(cct, 0) << "ceph_decode_ticket could not decrypt ticket info. error:"
410 << error << dendl;
411 return false;
412 }
413
414 return true;
415}
416
417/*
418 * SERVICE: verify authorizer and generate reply authorizer
419 *
420 * {timestamp + 1}^session_key
421 */
9f95a23c 422bool cephx_verify_authorizer(CephContext *cct, const KeyStore& keys,
11fdf7f2
TL
423 bufferlist::const_iterator& indata,
424 size_t connection_secret_required_len,
28e407b8
AA
425 CephXServiceTicketInfo& ticket_info,
426 std::unique_ptr<AuthAuthorizerChallenge> *challenge,
11fdf7f2
TL
427 std::string *connection_secret,
428 bufferlist *reply_bl)
7c673cae
FG
429{
430 __u8 authorizer_v;
431 uint32_t service_id;
432 uint64_t global_id;
433 CryptoKey service_secret;
434 // ticket blob
435 CephXTicketBlob ticket;
436
7c673cae 437 try {
11fdf7f2
TL
438 decode(authorizer_v, indata);
439 decode(global_id, indata);
440 decode(service_id, indata);
441 decode(ticket, indata);
f67539c2 442 } catch (ceph::buffer::end_of_buffer &e) {
7c673cae
FG
443 // Unable to decode!
444 return false;
445 }
446 ldout(cct, 10) << "verify_authorizer decrypted service "
447 << ceph_entity_type_name(service_id)
448 << " secret_id=" << ticket.secret_id << dendl;
449
450 if (ticket.secret_id == (uint64_t)-1) {
451 EntityName name;
452 name.set_type(service_id);
9f95a23c 453 if (!keys.get_secret(name, service_secret)) {
7c673cae
FG
454 ldout(cct, 0) << "verify_authorizer could not get general service secret for service "
455 << ceph_entity_type_name(service_id) << " secret_id=" << ticket.secret_id << dendl;
456 return false;
457 }
458 } else {
9f95a23c 459 if (!keys.get_service_secret(service_id, ticket.secret_id, service_secret)) {
7c673cae
FG
460 ldout(cct, 0) << "verify_authorizer could not get service secret for service "
461 << ceph_entity_type_name(service_id) << " secret_id=" << ticket.secret_id << dendl;
462 if (cct->_conf->auth_debug && ticket.secret_id == 0)
11fdf7f2 463 ceph_abort_msg("got secret_id=0");
7c673cae
FG
464 return false;
465 }
466 }
467 std::string error;
468 if (!service_secret.get_secret().length())
469 error = "invalid key"; // Bad key?
470 else
471 decode_decrypt_enc_bl(cct, ticket_info, service_secret, ticket.blob, error);
472 if (!error.empty()) {
473 ldout(cct, 0) << "verify_authorizer could not decrypt ticket info: error: "
474 << error << dendl;
475 return false;
476 }
477
478 if (ticket_info.ticket.global_id != global_id) {
479 ldout(cct, 0) << "verify_authorizer global_id mismatch: declared id=" << global_id
480 << " ticket_id=" << ticket_info.ticket.global_id << dendl;
481 return false;
482 }
483
484 ldout(cct, 10) << "verify_authorizer global_id=" << global_id << dendl;
485
486 // CephXAuthorize
487 CephXAuthorize auth_msg;
488 if (decode_decrypt(cct, auth_msg, ticket_info.session_key, indata, error)) {
489 ldout(cct, 0) << "verify_authorizercould not decrypt authorize request with error: "
490 << error << dendl;
491 return false;
492 }
493
28e407b8
AA
494 if (challenge) {
495 auto *c = static_cast<CephXAuthorizeChallenge*>(challenge->get());
496 if (!auth_msg.have_challenge || !c) {
497 c = new CephXAuthorizeChallenge;
498 challenge->reset(c);
11fdf7f2 499 cct->random()->get_bytes((char*)&c->server_challenge, sizeof(c->server_challenge));
28e407b8
AA
500 ldout(cct,10) << __func__ << " adding server_challenge " << c->server_challenge
501 << dendl;
502
11fdf7f2 503 encode_encrypt_enc_bl(cct, *c, ticket_info.session_key, *reply_bl, error);
28e407b8
AA
504 if (!error.empty()) {
505 ldout(cct, 10) << "verify_authorizer: encode_encrypt error: " << error << dendl;
506 return false;
507 }
508 return false;
509 }
510 ldout(cct, 10) << __func__ << " got server_challenge+1 "
511 << auth_msg.server_challenge_plus_one
512 << " expecting " << c->server_challenge + 1 << dendl;
513 if (c->server_challenge + 1 != auth_msg.server_challenge_plus_one) {
514 return false;
515 }
516 }
517
7c673cae
FG
518 /*
519 * Reply authorizer:
520 * {timestamp + 1}^session_key
521 */
522 CephXAuthorizeReply reply;
523 // reply.trans_id = auth_msg.trans_id;
524 reply.nonce_plus_one = auth_msg.nonce + 1;
11fdf7f2
TL
525 if (connection_secret) {
526 // generate a connection secret
527 connection_secret->resize(connection_secret_required_len);
528 if (connection_secret_required_len) {
f67539c2
TL
529#ifdef WITH_SEASTAR
530 std::random_device rd;
531 std::generate_n(connection_secret->data(),
532 connection_secret_required_len,
533 std::default_random_engine{rd()});
534#else
11fdf7f2
TL
535 cct->random()->get_bytes(connection_secret->data(),
536 connection_secret_required_len);
f67539c2 537#endif
11fdf7f2
TL
538 }
539 reply.connection_secret = *connection_secret;
540 }
11fdf7f2 541 if (encode_encrypt(cct, reply, ticket_info.session_key, *reply_bl, error)) {
7c673cae
FG
542 ldout(cct, 10) << "verify_authorizer: encode_encrypt error: " << error << dendl;
543 return false;
544 }
545
546 ldout(cct, 10) << "verify_authorizer ok nonce " << hex << auth_msg.nonce << dec
11fdf7f2 547 << " reply_bl.length()=" << reply_bl->length() << dendl;
7c673cae
FG
548 return true;
549}
550
11fdf7f2
TL
551bool CephXAuthorizer::verify_reply(bufferlist::const_iterator& indata,
552 std::string *connection_secret)
7c673cae
FG
553{
554 CephXAuthorizeReply reply;
555
556 std::string error;
557 if (decode_decrypt(cct, reply, session_key, indata, error)) {
558 ldout(cct, 0) << "verify_reply couldn't decrypt with error: " << error << dendl;
559 return false;
560 }
561
562 uint64_t expect = nonce + 1;
563 if (expect != reply.nonce_plus_one) {
564 ldout(cct, 0) << "verify_authorizer_reply bad nonce got " << reply.nonce_plus_one << " expected " << expect
565 << " sent " << nonce << dendl;
566 return false;
567 }
11fdf7f2
TL
568
569 if (connection_secret &&
570 reply.connection_secret.size()) {
571 *connection_secret = reply.connection_secret;
572 }
7c673cae
FG
573 return true;
574}
575
11fdf7f2
TL
576bool CephXAuthorizer::add_challenge(CephContext *cct,
577 const bufferlist& challenge)
28e407b8
AA
578{
579 bl = base_bl;
580
581 CephXAuthorize msg;
582 msg.nonce = nonce;
583
584 auto p = challenge.begin();
585 if (!p.end()) {
586 std::string error;
587 CephXAuthorizeChallenge ch;
588 decode_decrypt_enc_bl(cct, ch, session_key, challenge, error);
589 if (!error.empty()) {
590 ldout(cct, 0) << "failed to decrypt challenge (" << challenge.length() << " bytes): "
591 << error << dendl;
592 return false;
593 }
594 msg.have_challenge = true;
595 msg.server_challenge_plus_one = ch.server_challenge + 1;
596 }
597
598 std::string error;
599 if (encode_encrypt(cct, msg, session_key, bl, error)) {
600 ldout(cct, 0) << __func__ << " failed to encrypt authorizer: " << error << dendl;
601 return false;
602 }
603 return true;
604}