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