Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / auth / cephx / CephxProtocol.cc
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
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)
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
50 bool 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  */
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,
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  */
144 bool 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
193 bool 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
202 bool 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
211 bool 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
219 bool 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
227 void 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
252 void 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  */
263 bool 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  */
295 CephXAuthorizer *CephXTicketHandler::build_authorizer(uint64_t global_id) const
296 {
297   CephXAuthorizer *a = new CephXAuthorizer(cct);
298   a->session_key = session_key;
299   a->nonce = ((uint64_t)rand() << 32) + rand();
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);
307
308   CephXAuthorize msg;
309   msg.nonce = a->nonce;
310
311   std::string error;
312   if (encode_encrypt(cct, msg, session_key, a->bl, error)) {
313     ldout(cct, 0) << "failed to encrypt authorizer: " << error << dendl;
314     delete a;
315     return 0;
316   }
317   return a;
318 }
319
320 /*
321  * PRINCIPAL: build authorizer to access the service.
322  *
323  * ticket, {timestamp}^session_key
324  */
325 CephXAuthorizer *CephXTicketManager::build_authorizer(uint32_t service_id) const
326 {
327   map<uint32_t, CephXTicketHandler>::const_iterator iter = tickets_map.find(service_id);
328   if (iter == tickets_map.end()) {
329     ldout(cct, 0) << "no TicketHandler for service "
330                   << ceph_entity_type_name(service_id) << dendl;
331     return NULL;
332   }
333
334   const CephXTicketHandler& handler = iter->second;
335   return handler.build_authorizer(global_id);
336 }
337
338 void CephXTicketManager::validate_tickets(uint32_t mask, uint32_t& have, uint32_t& need)
339 {
340   uint32_t i;
341   need = 0;
342   for (i = 1; i<=mask; i<<=1) {
343     if (mask & i) {
344       set_have_need_key(i, have, need);
345     }
346   }
347   ldout(cct, 10) << "validate_tickets want " << mask << " have " << have
348                  << " need " << need << dendl;
349 }
350
351 bool cephx_decode_ticket(CephContext *cct, KeyStore *keys, uint32_t service_id,
352               CephXTicketBlob& ticket_blob, CephXServiceTicketInfo& ticket_info)
353 {
354   uint64_t secret_id = ticket_blob.secret_id;
355   CryptoKey service_secret;
356
357   if (!ticket_blob.blob.length()) {
358     return false;
359   }
360
361   if (secret_id == (uint64_t)-1) {
362     if (!keys->get_secret(cct->_conf->name, service_secret)) {
363       ldout(cct, 0) << "ceph_decode_ticket could not get general service secret for service_id="
364               << ceph_entity_type_name(service_id) << " secret_id=" << secret_id << dendl;
365       return false;
366     }
367   } else {
368     if (!keys->get_service_secret(service_id, secret_id, service_secret)) {
369       ldout(cct, 0) << "ceph_decode_ticket could not get service secret for service_id=" 
370               << ceph_entity_type_name(service_id) << " secret_id=" << secret_id << dendl;
371       return false;
372     }
373   }
374
375   std::string error;
376   decode_decrypt_enc_bl(cct, ticket_info, service_secret, ticket_blob.blob, error);
377   if (!error.empty()) {
378     ldout(cct, 0) << "ceph_decode_ticket could not decrypt ticket info. error:" 
379         << error << dendl;
380     return false;
381   }
382
383   return true;
384 }
385
386 /*
387  * SERVICE: verify authorizer and generate reply authorizer
388  *
389  * {timestamp + 1}^session_key
390  */
391 bool cephx_verify_authorizer(CephContext *cct, KeyStore *keys,
392                              bufferlist::iterator& indata,
393                              CephXServiceTicketInfo& ticket_info, bufferlist& reply_bl)
394 {
395   __u8 authorizer_v;
396   uint32_t service_id;
397   uint64_t global_id;
398   CryptoKey service_secret;
399   // ticket blob
400   CephXTicketBlob ticket;
401
402
403   try {
404     ::decode(authorizer_v, indata);
405     ::decode(global_id, indata);
406     ::decode(service_id, indata);
407     ::decode(ticket, indata);
408   } catch (buffer::end_of_buffer &e) {
409     // Unable to decode!
410     return false;
411   }
412   ldout(cct, 10) << "verify_authorizer decrypted service "
413            << ceph_entity_type_name(service_id)
414            << " secret_id=" << ticket.secret_id << dendl;
415
416   if (ticket.secret_id == (uint64_t)-1) {
417     EntityName name;
418     name.set_type(service_id);
419     if (!keys->get_secret(name, service_secret)) {
420       ldout(cct, 0) << "verify_authorizer could not get general service secret for service "
421               << ceph_entity_type_name(service_id) << " secret_id=" << ticket.secret_id << dendl;
422       return false;
423     }
424   } else {
425     if (!keys->get_service_secret(service_id, ticket.secret_id, service_secret)) {
426       ldout(cct, 0) << "verify_authorizer could not get service secret for service "
427               << ceph_entity_type_name(service_id) << " secret_id=" << ticket.secret_id << dendl;
428       if (cct->_conf->auth_debug && ticket.secret_id == 0)
429         assert(0 == "got secret_id=0");
430       return false;
431     }
432   }
433   std::string error;
434   if (!service_secret.get_secret().length())
435     error = "invalid key";  // Bad key?
436   else
437     decode_decrypt_enc_bl(cct, ticket_info, service_secret, ticket.blob, error);
438   if (!error.empty()) {
439     ldout(cct, 0) << "verify_authorizer could not decrypt ticket info: error: "
440       << error << dendl;
441     return false;
442   }
443
444   if (ticket_info.ticket.global_id != global_id) {
445     ldout(cct, 0) << "verify_authorizer global_id mismatch: declared id=" << global_id
446             << " ticket_id=" << ticket_info.ticket.global_id << dendl;
447     return false;
448   }
449
450   ldout(cct, 10) << "verify_authorizer global_id=" << global_id << dendl;
451
452   // CephXAuthorize
453   CephXAuthorize auth_msg;
454   if (decode_decrypt(cct, auth_msg, ticket_info.session_key, indata, error)) {
455     ldout(cct, 0) << "verify_authorizercould not decrypt authorize request with error: "
456       << error << dendl;
457     return false;
458   }
459
460   /*
461    * Reply authorizer:
462    *  {timestamp + 1}^session_key
463    */
464   CephXAuthorizeReply reply;
465   // reply.trans_id = auth_msg.trans_id;
466   reply.nonce_plus_one = auth_msg.nonce + 1;
467   if (encode_encrypt(cct, reply, ticket_info.session_key, reply_bl, error)) {
468     ldout(cct, 10) << "verify_authorizer: encode_encrypt error: " << error << dendl;
469     return false;
470   }
471
472   ldout(cct, 10) << "verify_authorizer ok nonce " << hex << auth_msg.nonce << dec
473            << " reply_bl.length()=" << reply_bl.length() <<  dendl;
474   return true;
475 }
476
477 bool CephXAuthorizer::verify_reply(bufferlist::iterator& indata)
478 {
479   CephXAuthorizeReply reply;
480
481   std::string error;
482   if (decode_decrypt(cct, reply, session_key, indata, error)) {
483       ldout(cct, 0) << "verify_reply couldn't decrypt with error: " << error << dendl;
484       return false;
485   }
486
487   uint64_t expect = nonce + 1;
488   if (expect != reply.nonce_plus_one) {
489     ldout(cct, 0) << "verify_authorizer_reply bad nonce got " << reply.nonce_plus_one << " expected " << expect
490             << " sent " << nonce << dendl;
491     return false;
492   }
493   return true;
494 }
495