]> git.proxmox.com Git - ceph.git/blame - ceph/src/auth/cephx/CephxProtocol.cc
update sources to 12.2.7
[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"
17#include "common/config.h"
18#include "common/debug.h"
19#include "include/buffer.h"
20
21#define dout_subsys ceph_subsys_auth
22#undef dout_prefix
23#define dout_prefix *_dout << "cephx: "
24
25
26
27void cephx_calc_client_server_challenge(CephContext *cct, CryptoKey& secret, uint64_t server_challenge,
28 uint64_t client_challenge, uint64_t *key, std::string &error)
29{
30 CephXChallengeBlob b;
31 b.server_challenge = server_challenge;
32 b.client_challenge = client_challenge;
33
34 bufferlist enc;
35 if (encode_encrypt(cct, b, secret, enc, error))
36 return;
37
38 uint64_t k = 0;
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++)
41 k ^= mswab(*p);
42 *key = k;
43}
44
45
46/*
47 * Authentication
48 */
49
50bool cephx_build_service_ticket_blob(CephContext *cct, CephXSessionAuthInfo& info,
51 CephXTicketBlob& blob)
52{
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;
57
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;
62 std::string error;
63 if (!info.service_secret.get_secret().length())
64 error = "invalid key"; // Bad key?
65 else
66 encode_encrypt_enc_bl(cct, ticket_info, info.service_secret, blob.blob, error);
67 if (!error.empty()) {
68 ldout(cct, -1) << "cephx_build_service_ticket_blob failed with error "
69 << error << dendl;
70 return false;
71 }
72 return true;
73}
74
75/*
76 * AUTH SERVER: authenticate
77 *
78 * Authenticate principal, respond with AuthServiceTicketInfo
79 *
80 * {session key, validity}^principal_secret
81 * {principal_ticket, session key}^service_secret ... "enc_ticket"
82 */
83bool 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,
88 bufferlist& reply)
89{
90 __u8 service_ticket_reply_v = 1;
91 ::encode(service_ticket_reply_v, reply);
92
93 uint32_t num = ticket_info_vec.size();
94 ::encode(num, reply);
95 ldout(cct, 10) << "build_service_ticket_reply encoding " << num
96 << " tickets with secret " << principal_secret << dendl;
97
98 for (vector<CephXSessionAuthInfo>::iterator ticket_iter = ticket_info_vec.begin();
99 ticket_iter != ticket_info_vec.end();
100 ++ticket_iter) {
101 CephXSessionAuthInfo& info = *ticket_iter;
102 ::encode(info.service_id, reply);
103
104 __u8 service_ticket_v = 1;
105 ::encode(service_ticket_v, reply);
106
107 CephXServiceTicket msg_a;
108 msg_a.session_key = info.session_key;
109 msg_a.validity = info.validity;
110 std::string error;
111 if (encode_encrypt(cct, msg_a, principal_secret, reply, error)) {
112 ldout(cct, -1) << "error encoding encrypted: " << error << dendl;
113 return false;
114 }
115
116 bufferlist service_ticket_bl;
117 CephXTicketBlob blob;
118 if (!cephx_build_service_ticket_blob(cct, info, blob)) {
119 return false;
120 }
121 ::encode(blob, service_ticket_bl);
122
123 ldout(cct, 30) << "service_ticket_blob is ";
124 service_ticket_bl.hexdump(*_dout);
125 *_dout << dendl;
126
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;
131 return false;
132 }
133 } else {
134 ::encode(service_ticket_bl, reply);
135 }
136 }
137 return true;
138}
139
140/*
141 * PRINCIPAL: verify our attempt to authenticate succeeded. fill out
142 * this ServiceTicket with the result.
143 */
144bool CephXTicketHandler::verify_service_ticket_reply(CryptoKey& secret,
145 bufferlist::iterator& indata)
146{
147 __u8 service_ticket_v;
148 ::decode(service_ticket_v, indata);
149
150 CephXServiceTicket msg_a;
151 std::string error;
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;
154 return false;
155 }
156
157 __u8 ticket_enc;
158 ::decode(ticket_enc, indata);
159
160 bufferlist service_ticket_bl;
161 if (ticket_enc) {
162 ldout(cct, 10) << " got encrypted ticket" << dendl;
163 std::string error;
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;
167 return false;
168 }
169 } else {
170 ::decode(service_ticket_bl, indata);
171 }
172 bufferlist::iterator iter = service_ticket_bl.begin();
173 ::decode(ticket, iter);
174 ldout(cct, 10) << " ticket.secret_id=" << ticket.secret_id << dendl;
175
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;
187 }
188
189 have_key_flag = true;
190 return true;
191}
192
193bool CephXTicketHandler::have_key()
194{
195 if (have_key_flag) {
196 have_key_flag = ceph_clock_now() < expires;
197 }
198
199 return have_key_flag;
200}
201
202bool CephXTicketHandler::need_key() const
203{
204 if (have_key_flag) {
205 return (!expires.is_zero()) && (ceph_clock_now() >= renew_after);
206 }
207
208 return true;
209}
210
211bool CephXTicketManager::have_key(uint32_t service_id)
212{
213 map<uint32_t, CephXTicketHandler>::iterator iter = tickets_map.find(service_id);
214 if (iter == tickets_map.end())
215 return false;
216 return iter->second.have_key();
217}
218
219bool CephXTicketManager::need_key(uint32_t service_id) const
220{
221 map<uint32_t, CephXTicketHandler>::const_iterator iter = tickets_map.find(service_id);
222 if (iter == tickets_map.end())
223 return true;
224 return iter->second.need_key();
225}
226
227void CephXTicketManager::set_have_need_key(uint32_t service_id, uint32_t& have, uint32_t& need)
228{
229 map<uint32_t, CephXTicketHandler>::iterator iter = tickets_map.find(service_id);
230 if (iter == tickets_map.end()) {
231 have &= ~service_id;
232 need |= service_id;
233 ldout(cct, 10) << "set_have_need_key no handler for service "
234 << ceph_entity_type_name(service_id) << dendl;
235 return;
236 }
237
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())
242 need |= service_id;
243 else
244 need &= ~service_id;
245
246 if (iter->second.have_key())
247 have |= service_id;
248 else
249 have &= ~service_id;
250}
251
252void CephXTicketManager::invalidate_ticket(uint32_t service_id)
253{
254 map<uint32_t, CephXTicketHandler>::iterator iter = tickets_map.find(service_id);
255 if (iter != tickets_map.end())
256 iter->second.invalidate_ticket();
257}
258
259/*
260 * PRINCIPAL: verify our attempt to authenticate succeeded. fill out
261 * this ServiceTicket with the result.
262 */
263bool CephXTicketManager::verify_service_ticket_reply(CryptoKey& secret,
264 bufferlist::iterator& indata)
265{
266 __u8 service_ticket_reply_v;
267 ::decode(service_ticket_reply_v, indata);
268
269 uint32_t num;
270 ::decode(num, indata);
271 ldout(cct, 10) << "verify_service_ticket_reply got " << num << " keys" << dendl;
272
273 for (int i=0; i<(int)num; i++) {
274 uint32_t type;
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)) {
279 return false;
280 }
281 handler.service_id = type;
282 }
283
284 if (!indata.end())
285 return false;
286
287 return true;
288}
289
290/*
291 * PRINCIPAL: build authorizer to access the service.
292 *
293 * ticket, {timestamp}^session_key
294 */
295CephXAuthorizer *CephXTicketHandler::build_authorizer(uint64_t global_id) const
296{
297 CephXAuthorizer *a = new CephXAuthorizer(cct);
298 a->session_key = session_key;
28e407b8 299 get_random_bytes((char*)&a->nonce, sizeof(a->nonce));
7c673cae
FG
300
301 __u8 authorizer_v = 1;
302 ::encode(authorizer_v, a->bl);
303 ::encode(global_id, a->bl);
304 ::encode(service_id, a->bl);
305
306 ::encode(ticket, a->bl);
28e407b8 307 a->base_bl = a->bl;
7c673cae
FG
308
309 CephXAuthorize msg;
310 msg.nonce = a->nonce;
311
312 std::string error;
313 if (encode_encrypt(cct, msg, session_key, a->bl, error)) {
314 ldout(cct, 0) << "failed to encrypt authorizer: " << error << dendl;
315 delete a;
316 return 0;
317 }
318 return a;
319}
320
321/*
322 * PRINCIPAL: build authorizer to access the service.
323 *
324 * ticket, {timestamp}^session_key
325 */
326CephXAuthorizer *CephXTicketManager::build_authorizer(uint32_t service_id) const
327{
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;
332 return NULL;
333 }
334
335 const CephXTicketHandler& handler = iter->second;
336 return handler.build_authorizer(global_id);
337}
338
339void CephXTicketManager::validate_tickets(uint32_t mask, uint32_t& have, uint32_t& need)
340{
341 uint32_t i;
342 need = 0;
343 for (i = 1; i<=mask; i<<=1) {
344 if (mask & i) {
345 set_have_need_key(i, have, need);
346 }
347 }
348 ldout(cct, 10) << "validate_tickets want " << mask << " have " << have
349 << " need " << need << dendl;
350}
351
352bool cephx_decode_ticket(CephContext *cct, KeyStore *keys, uint32_t service_id,
353 CephXTicketBlob& ticket_blob, CephXServiceTicketInfo& ticket_info)
354{
355 uint64_t secret_id = ticket_blob.secret_id;
356 CryptoKey service_secret;
357
358 if (!ticket_blob.blob.length()) {
359 return false;
360 }
361
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;
366 return false;
367 }
368 } else {
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;
372 return false;
373 }
374 }
375
376 std::string error;
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:"
380 << error << dendl;
381 return false;
382 }
383
384 return true;
385}
386
387/*
388 * SERVICE: verify authorizer and generate reply authorizer
389 *
390 * {timestamp + 1}^session_key
391 */
392bool cephx_verify_authorizer(CephContext *cct, KeyStore *keys,
393 bufferlist::iterator& indata,
28e407b8
AA
394 CephXServiceTicketInfo& ticket_info,
395 std::unique_ptr<AuthAuthorizerChallenge> *challenge,
396 bufferlist& reply_bl)
7c673cae
FG
397{
398 __u8 authorizer_v;
399 uint32_t service_id;
400 uint64_t global_id;
401 CryptoKey service_secret;
402 // ticket blob
403 CephXTicketBlob ticket;
404
405
406 try {
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) {
412 // Unable to decode!
413 return false;
414 }
415 ldout(cct, 10) << "verify_authorizer decrypted service "
416 << ceph_entity_type_name(service_id)
417 << " secret_id=" << ticket.secret_id << dendl;
418
419 if (ticket.secret_id == (uint64_t)-1) {
420 EntityName name;
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;
425 return false;
426 }
427 } else {
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");
433 return false;
434 }
435 }
436 std::string error;
437 if (!service_secret.get_secret().length())
438 error = "invalid key"; // Bad key?
439 else
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: "
443 << error << dendl;
444 return false;
445 }
446
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;
450 return false;
451 }
452
453 ldout(cct, 10) << "verify_authorizer global_id=" << global_id << dendl;
454
455 // CephXAuthorize
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: "
459 << error << dendl;
460 return false;
461 }
462
28e407b8
AA
463 if (challenge) {
464 auto *c = static_cast<CephXAuthorizeChallenge*>(challenge->get());
465 if (!auth_msg.have_challenge || !c) {
466 c = new CephXAuthorizeChallenge;
467 challenge->reset(c);
468 get_random_bytes((char*)&c->server_challenge, sizeof(c->server_challenge));
469 ldout(cct,10) << __func__ << " adding server_challenge " << c->server_challenge
470 << dendl;
471
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;
475 return false;
476 }
477 return false;
478 }
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) {
483 return false;
484 }
485 }
486
7c673cae
FG
487 /*
488 * Reply authorizer:
489 * {timestamp + 1}^session_key
490 */
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;
496 return false;
497 }
498
499 ldout(cct, 10) << "verify_authorizer ok nonce " << hex << auth_msg.nonce << dec
500 << " reply_bl.length()=" << reply_bl.length() << dendl;
501 return true;
502}
503
504bool CephXAuthorizer::verify_reply(bufferlist::iterator& indata)
505{
506 CephXAuthorizeReply reply;
507
508 std::string error;
509 if (decode_decrypt(cct, reply, session_key, indata, error)) {
510 ldout(cct, 0) << "verify_reply couldn't decrypt with error: " << error << dendl;
511 return false;
512 }
513
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;
518 return false;
519 }
520 return true;
521}
522
28e407b8
AA
523bool CephXAuthorizer::add_challenge(CephContext *cct, bufferlist& challenge)
524{
525 bl = base_bl;
526
527 CephXAuthorize msg;
528 msg.nonce = nonce;
529
530 auto p = challenge.begin();
531 if (!p.end()) {
532 std::string error;
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): "
537 << error << dendl;
538 return false;
539 }
540 msg.have_challenge = true;
541 msg.server_challenge_plus_one = ch.server_challenge + 1;
542 }
543
544 std::string error;
545 if (encode_encrypt(cct, msg, session_key, bl, error)) {
546 ldout(cct, 0) << __func__ << " failed to encrypt authorizer: " << error << dendl;
547 return false;
548 }
549 return true;
550}