Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / auth / cephx / CephxClientHandler.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) 2004-2009 Sage Weil <sage@newdream.net>
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
16 #include <errno.h>
17
18 #include "CephxClientHandler.h"
19 #include "CephxProtocol.h"
20
21 #include "auth/KeyRing.h"
22 #include "common/config.h"
23 #include "common/dout.h"
24
25 #define dout_subsys ceph_subsys_auth
26 #undef dout_prefix
27 #define dout_prefix *_dout << "cephx client: "
28
29
30 int CephxClientHandler::build_request(bufferlist& bl) const
31 {
32   ldout(cct, 10) << "build_request" << dendl;
33
34   RWLock::RLocker l(lock);
35
36   if (need & CEPH_ENTITY_TYPE_AUTH) {
37     /* authenticate */
38     CephXRequestHeader header;
39     header.request_type = CEPHX_GET_AUTH_SESSION_KEY;
40     ::encode(header, bl);
41
42     CryptoKey secret;
43     const bool got = keyring->get_secret(cct->_conf->name, secret);
44     if (!got) {
45       ldout(cct, 20) << "no secret found for entity: " << cct->_conf->name << dendl;
46       return -ENOENT;
47     }
48
49     // is the key OK?
50     if (!secret.get_secret().length()) {
51       ldout(cct, 20) << "secret for entity " << cct->_conf->name << " is invalid" << dendl;
52       return -EINVAL;
53     }
54
55     CephXAuthenticate req;
56     get_random_bytes((char *)&req.client_challenge, sizeof(req.client_challenge));
57     std::string error;
58     cephx_calc_client_server_challenge(cct, secret, server_challenge,
59                                        req.client_challenge, &req.key, error);
60     if (!error.empty()) {
61       ldout(cct, 20) << "cephx_calc_client_server_challenge error: " << error << dendl;
62       return -EIO;
63     }
64
65     req.old_ticket = ticket_handler->ticket;
66
67     if (req.old_ticket.blob.length()) {
68       ldout(cct, 20) << "old ticket len=" << req.old_ticket.blob.length() << dendl;
69     }
70
71     ::encode(req, bl);
72
73     ldout(cct, 10) << "get auth session key: client_challenge "
74                    << hex << req.client_challenge << dendl;
75     return 0;
76   }
77
78   if (_need_tickets()) {
79     /* get service tickets */
80     ldout(cct, 10) << "get service keys: want=" << want << " need=" << need << " have=" << have << dendl;
81
82     CephXRequestHeader header;
83     header.request_type = CEPHX_GET_PRINCIPAL_SESSION_KEY;
84     ::encode(header, bl);
85
86     CephXAuthorizer *authorizer = ticket_handler->build_authorizer(global_id);
87     if (!authorizer)
88       return -EINVAL;
89     bl.claim_append(authorizer->bl);
90     delete authorizer;
91
92     CephXServiceTicketRequest req;
93     req.keys = need;
94     ::encode(req, bl);
95   }
96
97   return 0;
98 }
99
100 bool CephxClientHandler::_need_tickets() const
101 {
102   // do not bother (re)requesting tickets if we *only* need the MGR
103   // ticket; that can happen during an upgrade and we want to avoid a
104   // loop.  we'll end up re-requesting it later when the secrets
105   // rotating.
106   return need && need != CEPH_ENTITY_TYPE_MGR;
107 }
108
109 int CephxClientHandler::handle_response(int ret, bufferlist::iterator& indata)
110 {
111   ldout(cct, 10) << "handle_response ret = " << ret << dendl;
112   RWLock::WLocker l(lock);
113   
114   if (ret < 0)
115     return ret; // hrm!
116
117   if (starting) {
118     CephXServerChallenge ch;
119     ::decode(ch, indata);
120     server_challenge = ch.server_challenge;
121     ldout(cct, 10) << " got initial server challenge "
122                    << hex << server_challenge << dendl;
123     starting = false;
124
125     tickets.invalidate_ticket(CEPH_ENTITY_TYPE_AUTH);
126     return -EAGAIN;
127   }
128
129   struct CephXResponseHeader header;
130   ::decode(header, indata);
131
132   switch (header.request_type) {
133   case CEPHX_GET_AUTH_SESSION_KEY:
134     {
135       ldout(cct, 10) << " get_auth_session_key" << dendl;
136       CryptoKey secret;
137       const bool got = keyring->get_secret(cct->_conf->name, secret);
138       if (!got) {
139         ldout(cct, 0) << "key not found for " << cct->_conf->name << dendl;
140         return -ENOENT;
141       }
142         
143       if (!tickets.verify_service_ticket_reply(secret, indata)) {
144         ldout(cct, 0) << "could not verify service_ticket reply" << dendl;
145         return -EPERM;
146       }
147       ldout(cct, 10) << " want=" << want << " need=" << need << " have=" << have << dendl;
148       validate_tickets();
149       if (_need_tickets())
150         ret = -EAGAIN;
151       else
152         ret = 0;
153     }
154     break;
155
156   case CEPHX_GET_PRINCIPAL_SESSION_KEY:
157     {
158       CephXTicketHandler& ticket_handler = tickets.get_handler(CEPH_ENTITY_TYPE_AUTH);
159       ldout(cct, 10) << " get_principal_session_key session_key " << ticket_handler.session_key << dendl;
160   
161       if (!tickets.verify_service_ticket_reply(ticket_handler.session_key, indata)) {
162         ldout(cct, 0) << "could not verify service_ticket reply" << dendl;
163         return -EPERM;
164       }
165       validate_tickets();
166       if (!_need_tickets()) {
167         ret = 0;
168       }
169     }
170     break;
171
172   case CEPHX_GET_ROTATING_KEY:
173     {
174       ldout(cct, 10) << " get_rotating_key" << dendl;
175       if (rotating_secrets) {
176         RotatingSecrets secrets;
177         CryptoKey secret_key;
178         const bool got = keyring->get_secret(cct->_conf->name, secret_key);
179         if (!got) {
180           ldout(cct, 0) << "key not found for " << cct->_conf->name << dendl;
181           return -ENOENT;
182         }
183         std::string error;
184         if (decode_decrypt(cct, secrets, secret_key, indata, error)) {
185           ldout(cct, 0) << "could not set rotating key: decode_decrypt failed. error:"
186             << error << dendl;
187           return -EINVAL;
188         } else {
189           rotating_secrets->set_secrets(std::move(secrets));
190         }
191       }
192     }
193     break;
194
195   default:
196    ldout(cct, 0) << " unknown request_type " << header.request_type << dendl;
197    ceph_abort();
198   }
199   return ret;
200 }
201
202
203
204 AuthAuthorizer *CephxClientHandler::build_authorizer(uint32_t service_id) const
205 {
206   RWLock::RLocker l(lock);
207   ldout(cct, 10) << "build_authorizer for service " << ceph_entity_type_name(service_id) << dendl;
208   return tickets.build_authorizer(service_id);
209 }
210
211
212 bool CephxClientHandler::build_rotating_request(bufferlist& bl) const
213 {
214   ldout(cct, 10) << "build_rotating_request" << dendl;
215   CephXRequestHeader header;
216   header.request_type = CEPHX_GET_ROTATING_KEY;
217   ::encode(header, bl);
218   return true;
219 }
220
221 void CephxClientHandler::prepare_build_request()
222 {
223   RWLock::WLocker l(lock);
224   ldout(cct, 10) << "validate_tickets: want=" << want << " need=" << need
225                  << " have=" << have << dendl;
226   validate_tickets();
227   ldout(cct, 10) << "want=" << want << " need=" << need << " have=" << have
228                  << dendl;
229
230   ticket_handler = &(tickets.get_handler(CEPH_ENTITY_TYPE_AUTH));
231 }
232
233 void CephxClientHandler::validate_tickets()
234 {
235   // lock should be held for write
236   tickets.validate_tickets(want, have, need);
237 }
238
239 bool CephxClientHandler::need_tickets()
240 {
241   RWLock::WLocker l(lock);
242   validate_tickets();
243
244   ldout(cct, 20) << "need_tickets: want=" << want
245                  << " have=" << have
246                  << " need=" << need
247                  << dendl;
248
249   return _need_tickets();
250 }
251