]>
Commit | Line | Data |
---|---|---|
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) 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" | |
11fdf7f2 TL |
22 | #include "include/random.h" |
23 | #include "common/ceph_context.h" | |
7c673cae FG |
24 | #include "common/config.h" |
25 | #include "common/dout.h" | |
26 | ||
27 | #define dout_subsys ceph_subsys_auth | |
28 | #undef dout_prefix | |
29 | #define dout_prefix *_dout << "cephx client: " | |
30 | ||
f67539c2 TL |
31 | using std::string; |
32 | ||
33 | using ceph::bufferlist; | |
34 | ||
11fdf7f2 TL |
35 | void CephxClientHandler::reset() |
36 | { | |
37 | ldout(cct,10) << __func__ << dendl; | |
38 | starting = true; | |
39 | server_challenge = 0; | |
40 | } | |
7c673cae FG |
41 | |
42 | int CephxClientHandler::build_request(bufferlist& bl) const | |
43 | { | |
44 | ldout(cct, 10) << "build_request" << dendl; | |
45 | ||
7c673cae FG |
46 | if (need & CEPH_ENTITY_TYPE_AUTH) { |
47 | /* authenticate */ | |
48 | CephXRequestHeader header; | |
49 | header.request_type = CEPHX_GET_AUTH_SESSION_KEY; | |
11fdf7f2 | 50 | encode(header, bl); |
7c673cae FG |
51 | |
52 | CryptoKey secret; | |
53 | const bool got = keyring->get_secret(cct->_conf->name, secret); | |
54 | if (!got) { | |
55 | ldout(cct, 20) << "no secret found for entity: " << cct->_conf->name << dendl; | |
56 | return -ENOENT; | |
57 | } | |
58 | ||
59 | // is the key OK? | |
60 | if (!secret.get_secret().length()) { | |
61 | ldout(cct, 20) << "secret for entity " << cct->_conf->name << " is invalid" << dendl; | |
62 | return -EINVAL; | |
63 | } | |
64 | ||
65 | CephXAuthenticate req; | |
11fdf7f2 | 66 | req.client_challenge = ceph::util::generate_random_number<uint64_t>(); |
7c673cae FG |
67 | std::string error; |
68 | cephx_calc_client_server_challenge(cct, secret, server_challenge, | |
69 | req.client_challenge, &req.key, error); | |
70 | if (!error.empty()) { | |
71 | ldout(cct, 20) << "cephx_calc_client_server_challenge error: " << error << dendl; | |
72 | return -EIO; | |
73 | } | |
74 | ||
75 | req.old_ticket = ticket_handler->ticket; | |
76 | ||
11fdf7f2 TL |
77 | // for nautilus+ servers: request other keys at the same time |
78 | req.other_keys = need; | |
79 | ||
7c673cae FG |
80 | if (req.old_ticket.blob.length()) { |
81 | ldout(cct, 20) << "old ticket len=" << req.old_ticket.blob.length() << dendl; | |
82 | } | |
83 | ||
11fdf7f2 | 84 | encode(req, bl); |
7c673cae FG |
85 | |
86 | ldout(cct, 10) << "get auth session key: client_challenge " | |
11fdf7f2 | 87 | << std::hex << req.client_challenge << std::dec << dendl; |
7c673cae FG |
88 | return 0; |
89 | } | |
90 | ||
91 | if (_need_tickets()) { | |
92 | /* get service tickets */ | |
93 | ldout(cct, 10) << "get service keys: want=" << want << " need=" << need << " have=" << have << dendl; | |
94 | ||
95 | CephXRequestHeader header; | |
96 | header.request_type = CEPHX_GET_PRINCIPAL_SESSION_KEY; | |
11fdf7f2 | 97 | encode(header, bl); |
7c673cae FG |
98 | |
99 | CephXAuthorizer *authorizer = ticket_handler->build_authorizer(global_id); | |
100 | if (!authorizer) | |
101 | return -EINVAL; | |
102 | bl.claim_append(authorizer->bl); | |
103 | delete authorizer; | |
104 | ||
105 | CephXServiceTicketRequest req; | |
106 | req.keys = need; | |
11fdf7f2 | 107 | encode(req, bl); |
7c673cae FG |
108 | } |
109 | ||
110 | return 0; | |
111 | } | |
112 | ||
113 | bool CephxClientHandler::_need_tickets() const | |
114 | { | |
115 | // do not bother (re)requesting tickets if we *only* need the MGR | |
116 | // ticket; that can happen during an upgrade and we want to avoid a | |
117 | // loop. we'll end up re-requesting it later when the secrets | |
118 | // rotating. | |
119 | return need && need != CEPH_ENTITY_TYPE_MGR; | |
120 | } | |
121 | ||
11fdf7f2 TL |
122 | int CephxClientHandler::handle_response( |
123 | int ret, | |
124 | bufferlist::const_iterator& indata, | |
125 | CryptoKey *session_key, | |
126 | std::string *connection_secret) | |
7c673cae | 127 | { |
11fdf7f2 | 128 | ldout(cct, 10) << this << " handle_response ret = " << ret << dendl; |
7c673cae FG |
129 | |
130 | if (ret < 0) | |
131 | return ret; // hrm! | |
132 | ||
133 | if (starting) { | |
134 | CephXServerChallenge ch; | |
eafe8130 TL |
135 | try { |
136 | decode(ch, indata); | |
f67539c2 | 137 | } catch (ceph::buffer::error& e) { |
eafe8130 TL |
138 | ldout(cct, 1) << __func__ << " failed to decode CephXServerChallenge: " |
139 | << e.what() << dendl; | |
140 | return -EPERM; | |
141 | } | |
7c673cae FG |
142 | server_challenge = ch.server_challenge; |
143 | ldout(cct, 10) << " got initial server challenge " | |
11fdf7f2 | 144 | << std::hex << server_challenge << std::dec << dendl; |
7c673cae FG |
145 | starting = false; |
146 | ||
147 | tickets.invalidate_ticket(CEPH_ENTITY_TYPE_AUTH); | |
148 | return -EAGAIN; | |
149 | } | |
150 | ||
151 | struct CephXResponseHeader header; | |
eafe8130 TL |
152 | try { |
153 | decode(header, indata); | |
f67539c2 | 154 | } catch (ceph::buffer::error& e) { |
eafe8130 TL |
155 | ldout(cct, 1) << __func__ << " failed to decode CephXResponseHeader: " |
156 | << e.what() << dendl; | |
157 | return -EPERM; | |
158 | } | |
7c673cae FG |
159 | |
160 | switch (header.request_type) { | |
161 | case CEPHX_GET_AUTH_SESSION_KEY: | |
162 | { | |
163 | ldout(cct, 10) << " get_auth_session_key" << dendl; | |
164 | CryptoKey secret; | |
165 | const bool got = keyring->get_secret(cct->_conf->name, secret); | |
166 | if (!got) { | |
167 | ldout(cct, 0) << "key not found for " << cct->_conf->name << dendl; | |
168 | return -ENOENT; | |
169 | } | |
170 | ||
171 | if (!tickets.verify_service_ticket_reply(secret, indata)) { | |
172 | ldout(cct, 0) << "could not verify service_ticket reply" << dendl; | |
9f95a23c | 173 | return -EACCES; |
7c673cae FG |
174 | } |
175 | ldout(cct, 10) << " want=" << want << " need=" << need << " have=" << have << dendl; | |
11fdf7f2 TL |
176 | if (!indata.end()) { |
177 | bufferlist cbl, extra_tickets; | |
f67539c2 | 178 | using ceph::decode; |
eafe8130 TL |
179 | try { |
180 | decode(cbl, indata); | |
181 | decode(extra_tickets, indata); | |
f67539c2 | 182 | } catch (ceph::buffer::error& e) { |
eafe8130 TL |
183 | ldout(cct, 1) << __func__ << " failed to decode tickets: " |
184 | << e.what() << dendl; | |
185 | return -EPERM; | |
186 | } | |
11fdf7f2 TL |
187 | ldout(cct, 10) << " got connection bl " << cbl.length() |
188 | << " and extra tickets " << extra_tickets.length() | |
189 | << dendl; | |
20effc67 TL |
190 | // for msgr1, both session_key and connection_secret are NULL |
191 | // so we skip extra_tickets and incur an additional round-trip | |
192 | // to get service tickets via CEPHX_GET_PRINCIPAL_SESSION_KEY | |
193 | // as if talking to a pre-nautilus mon | |
194 | // this wasn't intended but turns out to be needed because in | |
195 | // msgr1 case MonClient doesn't explicitly wait for the monmap | |
196 | // (which is shared together with CEPHX_GET_AUTH_SESSION_KEY | |
197 | // reply) | |
198 | // instead, it waits for CEPHX_GET_PRINCIPAL_SESSION_KEY reply | |
199 | // which comes after the monmap and hence the monmap is always | |
200 | // handled by the time authentication is considered finished | |
201 | // if we start to always process extra_tickets here, MonClient | |
202 | // would have no reason to send CEPHX_GET_PRINCIPAL_SESSION_KEY | |
203 | // and RadosClient::connect() or similar could return with no | |
204 | // actual monmap but just an initial bootstrap stub, leading | |
205 | // to mon commands going out with zero fsid and other issues | |
11fdf7f2 TL |
206 | if (session_key && connection_secret) { |
207 | CephXTicketHandler& ticket_handler = | |
208 | tickets.get_handler(CEPH_ENTITY_TYPE_AUTH); | |
209 | if (session_key) { | |
210 | *session_key = ticket_handler.session_key; | |
211 | } | |
212 | if (cbl.length() && connection_secret) { | |
213 | auto p = cbl.cbegin(); | |
214 | string err; | |
215 | if (decode_decrypt(cct, *connection_secret, *session_key, p, | |
216 | err)) { | |
217 | lderr(cct) << __func__ << " failed to decrypt connection_secret" | |
218 | << dendl; | |
219 | } else { | |
220 | ldout(cct, 10) << " got connection_secret " | |
221 | << connection_secret->size() << " bytes" << dendl; | |
222 | } | |
223 | } | |
224 | if (extra_tickets.length()) { | |
225 | auto p = extra_tickets.cbegin(); | |
226 | if (!tickets.verify_service_ticket_reply( | |
227 | *session_key, p)) { | |
228 | lderr(cct) << "could not verify extra service_tickets" << dendl; | |
229 | } else { | |
230 | ldout(cct, 10) << " got extra service_tickets" << dendl; | |
231 | } | |
232 | } | |
233 | } | |
234 | } | |
7c673cae FG |
235 | validate_tickets(); |
236 | if (_need_tickets()) | |
237 | ret = -EAGAIN; | |
238 | else | |
239 | ret = 0; | |
11fdf7f2 | 240 | } |
7c673cae FG |
241 | break; |
242 | ||
243 | case CEPHX_GET_PRINCIPAL_SESSION_KEY: | |
244 | { | |
245 | CephXTicketHandler& ticket_handler = tickets.get_handler(CEPH_ENTITY_TYPE_AUTH); | |
246 | ldout(cct, 10) << " get_principal_session_key session_key " << ticket_handler.session_key << dendl; | |
247 | ||
248 | if (!tickets.verify_service_ticket_reply(ticket_handler.session_key, indata)) { | |
249 | ldout(cct, 0) << "could not verify service_ticket reply" << dendl; | |
9f95a23c | 250 | return -EACCES; |
7c673cae FG |
251 | } |
252 | validate_tickets(); | |
253 | if (!_need_tickets()) { | |
254 | ret = 0; | |
255 | } | |
256 | } | |
257 | break; | |
258 | ||
259 | case CEPHX_GET_ROTATING_KEY: | |
260 | { | |
261 | ldout(cct, 10) << " get_rotating_key" << dendl; | |
262 | if (rotating_secrets) { | |
263 | RotatingSecrets secrets; | |
264 | CryptoKey secret_key; | |
265 | const bool got = keyring->get_secret(cct->_conf->name, secret_key); | |
266 | if (!got) { | |
267 | ldout(cct, 0) << "key not found for " << cct->_conf->name << dendl; | |
268 | return -ENOENT; | |
269 | } | |
270 | std::string error; | |
271 | if (decode_decrypt(cct, secrets, secret_key, indata, error)) { | |
272 | ldout(cct, 0) << "could not set rotating key: decode_decrypt failed. error:" | |
273 | << error << dendl; | |
274 | return -EINVAL; | |
275 | } else { | |
224ce89b | 276 | rotating_secrets->set_secrets(std::move(secrets)); |
7c673cae FG |
277 | } |
278 | } | |
279 | } | |
280 | break; | |
281 | ||
282 | default: | |
283 | ldout(cct, 0) << " unknown request_type " << header.request_type << dendl; | |
284 | ceph_abort(); | |
285 | } | |
286 | return ret; | |
287 | } | |
288 | ||
289 | ||
7c673cae FG |
290 | AuthAuthorizer *CephxClientHandler::build_authorizer(uint32_t service_id) const |
291 | { | |
7c673cae FG |
292 | ldout(cct, 10) << "build_authorizer for service " << ceph_entity_type_name(service_id) << dendl; |
293 | return tickets.build_authorizer(service_id); | |
294 | } | |
295 | ||
296 | ||
297 | bool CephxClientHandler::build_rotating_request(bufferlist& bl) const | |
298 | { | |
299 | ldout(cct, 10) << "build_rotating_request" << dendl; | |
300 | CephXRequestHeader header; | |
301 | header.request_type = CEPHX_GET_ROTATING_KEY; | |
11fdf7f2 | 302 | encode(header, bl); |
7c673cae FG |
303 | return true; |
304 | } | |
305 | ||
306 | void CephxClientHandler::prepare_build_request() | |
307 | { | |
7c673cae FG |
308 | ldout(cct, 10) << "validate_tickets: want=" << want << " need=" << need |
309 | << " have=" << have << dendl; | |
310 | validate_tickets(); | |
311 | ldout(cct, 10) << "want=" << want << " need=" << need << " have=" << have | |
312 | << dendl; | |
313 | ||
314 | ticket_handler = &(tickets.get_handler(CEPH_ENTITY_TYPE_AUTH)); | |
315 | } | |
316 | ||
317 | void CephxClientHandler::validate_tickets() | |
318 | { | |
319 | // lock should be held for write | |
320 | tickets.validate_tickets(want, have, need); | |
321 | } | |
322 | ||
323 | bool CephxClientHandler::need_tickets() | |
324 | { | |
7c673cae FG |
325 | validate_tickets(); |
326 | ||
327 | ldout(cct, 20) << "need_tickets: want=" << want | |
328 | << " have=" << have | |
329 | << " need=" << need | |
330 | << dendl; | |
331 | ||
332 | return _need_tickets(); | |
333 | } |