]>
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 | #include "common/config.h" | |
16 | #include "CephxKeyServer.h" | |
17 | #include "common/dout.h" | |
18 | #include <sstream> | |
19 | ||
20 | #define dout_subsys ceph_subsys_auth | |
21 | #undef dout_prefix | |
22 | #define dout_prefix *_dout << "cephx keyserverdata: " | |
23 | ||
f67539c2 TL |
24 | using std::ostringstream; |
25 | using std::string; | |
26 | using std::stringstream; | |
27 | ||
28 | using ceph::bufferptr; | |
29 | using ceph::bufferlist; | |
30 | using ceph::Formatter; | |
31 | ||
7c673cae | 32 | bool KeyServerData::get_service_secret(CephContext *cct, uint32_t service_id, |
c5c27e9a TL |
33 | CryptoKey& secret, uint64_t& secret_id, |
34 | double& ttl) const | |
7c673cae | 35 | { |
f67539c2 | 36 | auto iter = rotating_secrets.find(service_id); |
7c673cae FG |
37 | if (iter == rotating_secrets.end()) { |
38 | ldout(cct, 10) << "get_service_secret service " << ceph_entity_type_name(service_id) << " not found " << dendl; | |
39 | return false; | |
40 | } | |
41 | ||
42 | const RotatingSecrets& secrets = iter->second; | |
43 | ||
44 | // second to oldest, unless it's expired | |
f67539c2 | 45 | auto riter = secrets.secrets.begin(); |
7c673cae FG |
46 | if (secrets.secrets.size() > 1) |
47 | ++riter; | |
48 | ||
c5c27e9a TL |
49 | utime_t now = ceph_clock_now(); |
50 | if (riter->second.expiration < now) | |
7c673cae FG |
51 | ++riter; // "current" key has expired, use "next" key instead |
52 | ||
53 | secret_id = riter->first; | |
c5c27e9a | 54 | secret = riter->second.key; |
7c673cae | 55 | |
c5c27e9a TL |
56 | // ttl may have just been increased by the user |
57 | // cap it by expiration of "next" key to prevent handing out a ticket | |
58 | // with a bogus, possibly way into the future, validity | |
59 | ttl = service_id == CEPH_ENTITY_TYPE_AUTH ? | |
60 | cct->_conf->auth_mon_ticket_ttl : cct->_conf->auth_service_ticket_ttl; | |
20effc67 | 61 | ttl = std::min(ttl, static_cast<double>( |
c5c27e9a TL |
62 | secrets.secrets.rbegin()->second.expiration - now)); |
63 | ||
64 | ldout(cct, 30) << __func__ << " service " | |
65 | << ceph_entity_type_name(service_id) << " secret_id " | |
66 | << secret_id << " " << riter->second << " ttl " << ttl | |
67 | << dendl; | |
7c673cae FG |
68 | return true; |
69 | } | |
70 | ||
71 | bool KeyServerData::get_service_secret(CephContext *cct, uint32_t service_id, | |
72 | uint64_t secret_id, CryptoKey& secret) const | |
73 | { | |
f67539c2 | 74 | auto iter = rotating_secrets.find(service_id); |
a4b75251 TL |
75 | if (iter == rotating_secrets.end()) { |
76 | ldout(cct, 10) << __func__ << " no rotating_secrets for service " << service_id | |
77 | << " " << ceph_entity_type_name(service_id) << dendl; | |
7c673cae | 78 | return false; |
a4b75251 | 79 | } |
7c673cae FG |
80 | |
81 | const RotatingSecrets& secrets = iter->second; | |
f67539c2 | 82 | auto riter = secrets.secrets.find(secret_id); |
7c673cae FG |
83 | |
84 | if (riter == secrets.secrets.end()) { | |
85 | ldout(cct, 10) << "get_service_secret service " << ceph_entity_type_name(service_id) | |
86 | << " secret " << secret_id << " not found" << dendl; | |
87 | ldout(cct, 30) << " I have:" << dendl; | |
f67539c2 TL |
88 | for (auto iter = secrets.secrets.begin(); |
89 | iter != secrets.secrets.end(); | |
90 | ++iter) | |
7c673cae FG |
91 | ldout(cct, 30) << " id " << iter->first << " " << iter->second << dendl; |
92 | return false; | |
93 | } | |
94 | ||
95 | secret = riter->second.key; | |
96 | ||
97 | return true; | |
98 | } | |
99 | bool KeyServerData::get_auth(const EntityName& name, EntityAuth& auth) const { | |
f67539c2 | 100 | auto iter = secrets.find(name); |
7c673cae FG |
101 | if (iter != secrets.end()) { |
102 | auth = iter->second; | |
103 | return true; | |
104 | } | |
105 | return extra_secrets->get_auth(name, auth); | |
106 | } | |
107 | ||
108 | bool KeyServerData::get_secret(const EntityName& name, CryptoKey& secret) const { | |
f67539c2 | 109 | auto iter = secrets.find(name); |
7c673cae FG |
110 | if (iter != secrets.end()) { |
111 | secret = iter->second.key; | |
112 | return true; | |
113 | } | |
114 | return extra_secrets->get_secret(name, secret); | |
115 | } | |
116 | ||
117 | bool KeyServerData::get_caps(CephContext *cct, const EntityName& name, | |
118 | const string& type, AuthCapsInfo& caps_info) const | |
119 | { | |
120 | caps_info.allow_all = false; | |
121 | ||
122 | ldout(cct, 10) << "get_caps: name=" << name.to_str() << dendl; | |
f67539c2 | 123 | auto iter = secrets.find(name); |
7c673cae FG |
124 | if (iter != secrets.end()) { |
125 | ldout(cct, 10) << "get_secret: num of caps=" << iter->second.caps.size() << dendl; | |
f67539c2 | 126 | auto capsiter = iter->second.caps.find(type); |
7c673cae FG |
127 | if (capsiter != iter->second.caps.end()) { |
128 | caps_info.caps = capsiter->second; | |
129 | } | |
130 | return true; | |
131 | } | |
132 | ||
133 | return extra_secrets->get_caps(name, type, caps_info); | |
134 | } | |
135 | ||
136 | ||
137 | #undef dout_prefix | |
138 | #define dout_prefix *_dout << "cephx keyserver: " | |
139 | ||
140 | ||
141 | KeyServer::KeyServer(CephContext *cct_, KeyRing *extra_secrets) | |
142 | : cct(cct_), | |
143 | data(extra_secrets), | |
11fdf7f2 | 144 | lock{ceph::make_mutex("KeyServer::lock")} |
7c673cae FG |
145 | { |
146 | } | |
147 | ||
148 | int KeyServer::start_server() | |
149 | { | |
11fdf7f2 | 150 | std::scoped_lock l{lock}; |
7c673cae | 151 | |
7c673cae FG |
152 | _dump_rotating_secrets(); |
153 | return 0; | |
154 | } | |
155 | ||
a4b75251 | 156 | void KeyServer::dump() |
7c673cae | 157 | { |
a4b75251 | 158 | _dump_rotating_secrets(); |
7c673cae FG |
159 | } |
160 | ||
161 | void KeyServer::_dump_rotating_secrets() | |
162 | { | |
163 | ldout(cct, 30) << "_dump_rotating_secrets" << dendl; | |
f67539c2 | 164 | for (auto iter = data.rotating_secrets.begin(); |
7c673cae FG |
165 | iter != data.rotating_secrets.end(); |
166 | ++iter) { | |
167 | RotatingSecrets& key = iter->second; | |
f67539c2 | 168 | for (auto mapiter = key.secrets.begin(); |
7c673cae FG |
169 | mapiter != key.secrets.end(); |
170 | ++mapiter) | |
171 | ldout(cct, 30) << "service " << ceph_entity_type_name(iter->first) | |
172 | << " id " << mapiter->first | |
173 | << " key " << mapiter->second << dendl; | |
174 | } | |
175 | } | |
176 | ||
a4b75251 | 177 | int KeyServer::_rotate_secret(uint32_t service_id, KeyServerData &pending_data) |
7c673cae | 178 | { |
a4b75251 | 179 | RotatingSecrets& r = pending_data.rotating_secrets[service_id]; |
7c673cae FG |
180 | int added = 0; |
181 | utime_t now = ceph_clock_now(); | |
182 | double ttl = service_id == CEPH_ENTITY_TYPE_AUTH ? cct->_conf->auth_mon_ticket_ttl : cct->_conf->auth_service_ticket_ttl; | |
183 | ||
184 | while (r.need_new_secrets(now)) { | |
185 | ExpiringCryptoKey ek; | |
186 | generate_secret(ek.key); | |
187 | if (r.empty()) { | |
188 | ek.expiration = now; | |
189 | } else { | |
190 | utime_t next_ttl = now; | |
191 | next_ttl += ttl; | |
11fdf7f2 | 192 | ek.expiration = std::max(next_ttl, r.next().expiration); |
7c673cae FG |
193 | } |
194 | ek.expiration += ttl; | |
195 | uint64_t secret_id = r.add(ek); | |
196 | ldout(cct, 10) << "_rotate_secret adding " << ceph_entity_type_name(service_id) << dendl; | |
197 | ldout(cct, 30) << "_rotate_secret adding " << ceph_entity_type_name(service_id) | |
198 | << " id " << secret_id << " " << ek | |
199 | << dendl; | |
200 | added++; | |
201 | } | |
202 | return added; | |
203 | } | |
204 | ||
205 | bool KeyServer::get_secret(const EntityName& name, CryptoKey& secret) const | |
206 | { | |
11fdf7f2 | 207 | std::scoped_lock l{lock}; |
7c673cae FG |
208 | return data.get_secret(name, secret); |
209 | } | |
210 | ||
211 | bool KeyServer::get_auth(const EntityName& name, EntityAuth& auth) const | |
212 | { | |
11fdf7f2 | 213 | std::scoped_lock l{lock}; |
7c673cae FG |
214 | return data.get_auth(name, auth); |
215 | } | |
216 | ||
217 | bool KeyServer::get_caps(const EntityName& name, const string& type, | |
218 | AuthCapsInfo& caps_info) const | |
219 | { | |
11fdf7f2 | 220 | std::scoped_lock l{lock}; |
7c673cae FG |
221 | |
222 | return data.get_caps(cct, name, type, caps_info); | |
223 | } | |
224 | ||
c5c27e9a TL |
225 | bool KeyServer::get_service_secret(uint32_t service_id, CryptoKey& secret, |
226 | uint64_t& secret_id, double& ttl) const | |
7c673cae | 227 | { |
11fdf7f2 | 228 | std::scoped_lock l{lock}; |
7c673cae | 229 | |
c5c27e9a | 230 | return data.get_service_secret(cct, service_id, secret, secret_id, ttl); |
7c673cae FG |
231 | } |
232 | ||
233 | bool KeyServer::get_service_secret(uint32_t service_id, | |
234 | uint64_t secret_id, CryptoKey& secret) const | |
235 | { | |
11fdf7f2 | 236 | std::scoped_lock l{lock}; |
7c673cae FG |
237 | |
238 | return data.get_service_secret(cct, service_id, secret_id, secret); | |
239 | } | |
240 | ||
241 | bool KeyServer::generate_secret(CryptoKey& secret) | |
242 | { | |
243 | bufferptr bp; | |
244 | CryptoHandler *crypto = cct->get_crypto_handler(CEPH_CRYPTO_AES); | |
245 | if (!crypto) | |
246 | return false; | |
247 | ||
11fdf7f2 | 248 | if (crypto->create(cct->random(), bp) < 0) |
7c673cae FG |
249 | return false; |
250 | ||
251 | secret.set_secret(CEPH_CRYPTO_AES, bp, ceph_clock_now()); | |
252 | ||
253 | return true; | |
254 | } | |
255 | ||
256 | bool KeyServer::generate_secret(EntityName& name, CryptoKey& secret) | |
257 | { | |
258 | if (!generate_secret(secret)) | |
259 | return false; | |
260 | ||
11fdf7f2 | 261 | std::scoped_lock l{lock}; |
7c673cae FG |
262 | |
263 | EntityAuth auth; | |
264 | auth.key = secret; | |
265 | ||
266 | data.add_auth(name, auth); | |
267 | ||
268 | return true; | |
269 | } | |
270 | ||
271 | bool KeyServer::contains(const EntityName& name) const | |
272 | { | |
11fdf7f2 | 273 | std::scoped_lock l{lock}; |
7c673cae FG |
274 | |
275 | return data.contains(name); | |
276 | } | |
277 | ||
278 | int KeyServer::encode_secrets(Formatter *f, stringstream *ds) const | |
279 | { | |
11fdf7f2 | 280 | std::scoped_lock l{lock}; |
f67539c2 | 281 | auto mapiter = data.secrets_begin(); |
7c673cae FG |
282 | |
283 | if (mapiter == data.secrets_end()) | |
284 | return -ENOENT; | |
285 | ||
286 | if (f) | |
287 | f->open_array_section("auth_dump"); | |
288 | ||
289 | while (mapiter != data.secrets_end()) { | |
290 | const EntityName& name = mapiter->first; | |
291 | if (ds) { | |
292 | *ds << name.to_str() << std::endl; | |
293 | *ds << "\tkey: " << mapiter->second.key << std::endl; | |
7c673cae FG |
294 | } |
295 | if (f) { | |
296 | f->open_object_section("auth_entities"); | |
297 | f->dump_string("entity", name.to_str()); | |
298 | f->dump_stream("key") << mapiter->second.key; | |
7c673cae FG |
299 | f->open_object_section("caps"); |
300 | } | |
301 | ||
f67539c2 | 302 | auto capsiter = mapiter->second.caps.begin(); |
7c673cae FG |
303 | for (; capsiter != mapiter->second.caps.end(); ++capsiter) { |
304 | // FIXME: need a const_iterator for bufferlist, but it doesn't exist yet. | |
305 | bufferlist *bl = const_cast<bufferlist*>(&capsiter->second); | |
11fdf7f2 | 306 | auto dataiter = bl->cbegin(); |
7c673cae | 307 | string caps; |
11fdf7f2 TL |
308 | using ceph::decode; |
309 | decode(caps, dataiter); | |
7c673cae FG |
310 | if (ds) |
311 | *ds << "\tcaps: [" << capsiter->first << "] " << caps << std::endl; | |
312 | if (f) | |
313 | f->dump_string(capsiter->first.c_str(), caps); | |
314 | } | |
315 | if (f) { | |
316 | f->close_section(); // caps | |
317 | f->close_section(); // auth_entities | |
318 | } | |
319 | ||
320 | ++mapiter; | |
321 | } | |
322 | ||
323 | if (f) | |
324 | f->close_section(); // auth_dump | |
325 | return 0; | |
326 | } | |
327 | ||
328 | void KeyServer::encode_formatted(string label, Formatter *f, bufferlist &bl) | |
329 | { | |
11fdf7f2 | 330 | ceph_assert(f != NULL); |
7c673cae FG |
331 | f->open_object_section(label.c_str()); |
332 | encode_secrets(f, NULL); | |
333 | f->close_section(); | |
334 | f->flush(bl); | |
335 | } | |
336 | ||
337 | void KeyServer::encode_plaintext(bufferlist &bl) | |
338 | { | |
339 | stringstream os; | |
340 | encode_secrets(NULL, &os); | |
341 | bl.append(os.str()); | |
342 | } | |
343 | ||
a4b75251 | 344 | bool KeyServer::prepare_rotating_update(bufferlist& rotating_bl) |
7c673cae | 345 | { |
11fdf7f2 | 346 | std::scoped_lock l{lock}; |
a4b75251 TL |
347 | ldout(cct, 20) << __func__ << " before: data.rotating_ver=" << data.rotating_ver |
348 | << dendl; | |
7c673cae | 349 | |
a4b75251 TL |
350 | KeyServerData pending_data(nullptr); |
351 | pending_data.rotating_ver = data.rotating_ver + 1; | |
352 | pending_data.rotating_secrets = data.rotating_secrets; | |
7c673cae | 353 | |
a4b75251 TL |
354 | int added = 0; |
355 | added += _rotate_secret(CEPH_ENTITY_TYPE_AUTH, pending_data); | |
356 | added += _rotate_secret(CEPH_ENTITY_TYPE_MON, pending_data); | |
357 | added += _rotate_secret(CEPH_ENTITY_TYPE_OSD, pending_data); | |
358 | added += _rotate_secret(CEPH_ENTITY_TYPE_MDS, pending_data); | |
359 | added += _rotate_secret(CEPH_ENTITY_TYPE_MGR, pending_data); | |
360 | if (!added) { | |
7c673cae | 361 | return false; |
a4b75251 | 362 | } |
7c673cae | 363 | |
a4b75251 TL |
364 | ldout(cct, 20) << __func__ << " after: pending_data.rotating_ver=" |
365 | << pending_data.rotating_ver | |
366 | << dendl; | |
367 | pending_data.encode_rotating(rotating_bl); | |
7c673cae FG |
368 | return true; |
369 | } | |
370 | ||
371 | bool KeyServer::get_rotating_encrypted(const EntityName& name, | |
372 | bufferlist& enc_bl) const | |
373 | { | |
11fdf7f2 | 374 | std::scoped_lock l{lock}; |
7c673cae | 375 | |
f67539c2 | 376 | auto mapiter = data.find_name(name); |
7c673cae FG |
377 | if (mapiter == data.secrets_end()) |
378 | return false; | |
379 | ||
380 | const CryptoKey& specific_key = mapiter->second.key; | |
381 | ||
f67539c2 | 382 | auto rotate_iter = data.rotating_secrets.find(name.get_type()); |
7c673cae FG |
383 | if (rotate_iter == data.rotating_secrets.end()) |
384 | return false; | |
385 | ||
386 | RotatingSecrets secrets = rotate_iter->second; | |
387 | ||
388 | std::string error; | |
389 | if (encode_encrypt(cct, secrets, specific_key, enc_bl, error)) | |
390 | return false; | |
391 | ||
392 | return true; | |
393 | } | |
394 | ||
395 | bool KeyServer::_get_service_caps(const EntityName& name, uint32_t service_id, | |
396 | AuthCapsInfo& caps_info) const | |
397 | { | |
398 | string s = ceph_entity_type_name(service_id); | |
399 | ||
400 | return data.get_caps(cct, name, s, caps_info); | |
401 | } | |
402 | ||
403 | bool KeyServer::get_service_caps(const EntityName& name, uint32_t service_id, | |
404 | AuthCapsInfo& caps_info) const | |
405 | { | |
11fdf7f2 | 406 | std::scoped_lock l{lock}; |
7c673cae FG |
407 | return _get_service_caps(name, service_id, caps_info); |
408 | } | |
409 | ||
410 | ||
11fdf7f2 TL |
411 | int KeyServer::_build_session_auth_info(uint32_t service_id, |
412 | const AuthTicket& parent_ticket, | |
c5c27e9a TL |
413 | CephXSessionAuthInfo& info, |
414 | double ttl) | |
7c673cae FG |
415 | { |
416 | info.service_id = service_id; | |
11fdf7f2 | 417 | info.ticket = parent_ticket; |
c5c27e9a TL |
418 | info.ticket.init_timestamps(ceph_clock_now(), ttl); |
419 | info.validity.set_from_double(ttl); | |
7c673cae FG |
420 | |
421 | generate_secret(info.session_key); | |
422 | ||
423 | // mon keys are stored externally. and the caps are blank anyway. | |
424 | if (service_id != CEPH_ENTITY_TYPE_MON) { | |
425 | string s = ceph_entity_type_name(service_id); | |
426 | if (!data.get_caps(cct, info.ticket.name, s, info.ticket.caps)) { | |
427 | return -EINVAL; | |
428 | } | |
429 | } | |
430 | return 0; | |
431 | } | |
432 | ||
11fdf7f2 TL |
433 | int KeyServer::build_session_auth_info(uint32_t service_id, |
434 | const AuthTicket& parent_ticket, | |
7c673cae FG |
435 | CephXSessionAuthInfo& info) |
436 | { | |
c5c27e9a TL |
437 | double ttl; |
438 | if (!get_service_secret(service_id, info.service_secret, info.secret_id, | |
439 | ttl)) { | |
9f95a23c | 440 | return -EACCES; |
7c673cae FG |
441 | } |
442 | ||
11fdf7f2 | 443 | std::scoped_lock l{lock}; |
c5c27e9a | 444 | return _build_session_auth_info(service_id, parent_ticket, info, ttl); |
7c673cae FG |
445 | } |
446 | ||
11fdf7f2 TL |
447 | int KeyServer::build_session_auth_info(uint32_t service_id, |
448 | const AuthTicket& parent_ticket, | |
c5c27e9a TL |
449 | const CryptoKey& service_secret, |
450 | uint64_t secret_id, | |
451 | CephXSessionAuthInfo& info) | |
7c673cae FG |
452 | { |
453 | info.service_secret = service_secret; | |
454 | info.secret_id = secret_id; | |
455 | ||
11fdf7f2 | 456 | std::scoped_lock l{lock}; |
c5c27e9a TL |
457 | return _build_session_auth_info(service_id, parent_ticket, info, |
458 | cct->_conf->auth_service_ticket_ttl); | |
7c673cae FG |
459 | } |
460 |