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