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