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