]> git.proxmox.com Git - ceph.git/blob - ceph/src/mon/AuthMonitor.cc
817e13d05542fbf4b3f3284466be8414693b6cb5
[ceph.git] / ceph / src / mon / AuthMonitor.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-2006 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 <sstream>
16
17 #include "mon/AuthMonitor.h"
18 #include "mon/Monitor.h"
19 #include "mon/MonitorDBStore.h"
20 #include "mon/ConfigKeyService.h"
21 #include "mon/OSDMonitor.h"
22
23 #include "messages/MMonCommand.h"
24 #include "messages/MAuth.h"
25 #include "messages/MAuthReply.h"
26 #include "messages/MMonGlobalID.h"
27 #include "msg/Messenger.h"
28
29 #include "auth/AuthServiceHandler.h"
30 #include "auth/KeyRing.h"
31 #include "include/stringify.h"
32 #include "include/assert.h"
33
34 #define dout_subsys ceph_subsys_mon
35 #undef dout_prefix
36 #define dout_prefix _prefix(_dout, mon, get_last_committed())
37 static ostream& _prefix(std::ostream *_dout, Monitor *mon, version_t v) {
38 return *_dout << "mon." << mon->name << "@" << mon->rank
39 << "(" << mon->get_state_name()
40 << ").auth v" << v << " ";
41 }
42
43 ostream& operator<<(ostream &out, const AuthMonitor &pm)
44 {
45 return out << "auth";
46 }
47
48 bool AuthMonitor::check_rotate()
49 {
50 KeyServerData::Incremental rot_inc;
51 rot_inc.op = KeyServerData::AUTH_INC_SET_ROTATING;
52 if (!mon->key_server.updated_rotating(rot_inc.rotating_bl, last_rotating_ver))
53 return false;
54 dout(10) << __func__ << " updated rotating" << dendl;
55 push_cephx_inc(rot_inc);
56 return true;
57 }
58
59 /*
60 Tick function to update the map based on performance every N seconds
61 */
62
63 void AuthMonitor::tick()
64 {
65 if (!is_active()) return;
66
67 dout(10) << *this << dendl;
68
69 if (!mon->is_leader()) return;
70
71 if (check_rotate())
72 propose_pending();
73 }
74
75 void AuthMonitor::on_active()
76 {
77 dout(10) << "AuthMonitor::on_active()" << dendl;
78
79 if (!mon->is_leader())
80 return;
81 mon->key_server.start_server();
82 }
83
84 void AuthMonitor::create_initial()
85 {
86 dout(10) << "create_initial -- creating initial map" << dendl;
87
88 // initialize rotating keys
89 last_rotating_ver = 0;
90 check_rotate();
91 assert(pending_auth.size() == 1);
92
93 if (mon->is_keyring_required()) {
94 KeyRing keyring;
95 bufferlist bl;
96 int ret = mon->store->get("mkfs", "keyring", bl);
97 // fail hard only if there's an error we're not expecting to see
98 assert((ret == 0) || (ret == -ENOENT));
99
100 // try importing only if there's a key
101 if (ret == 0) {
102 KeyRing keyring;
103 bufferlist::iterator p = bl.begin();
104
105 ::decode(keyring, p);
106 import_keyring(keyring);
107 }
108 }
109
110 max_global_id = MIN_GLOBAL_ID;
111
112 Incremental inc;
113 inc.inc_type = GLOBAL_ID;
114 inc.max_global_id = max_global_id;
115 pending_auth.push_back(inc);
116
117 format_version = 2;
118 }
119
120 void AuthMonitor::update_from_paxos(bool *need_bootstrap)
121 {
122 dout(10) << __func__ << dendl;
123 version_t version = get_last_committed();
124 version_t keys_ver = mon->key_server.get_ver();
125 if (version == keys_ver)
126 return;
127 assert(version > keys_ver);
128
129 version_t latest_full = get_version_latest_full();
130
131 dout(10) << __func__ << " version " << version << " keys ver " << keys_ver
132 << " latest " << latest_full << dendl;
133
134 if ((latest_full > 0) && (latest_full > keys_ver)) {
135 bufferlist latest_bl;
136 int err = get_version_full(latest_full, latest_bl);
137 assert(err == 0);
138 assert(latest_bl.length() != 0);
139 dout(7) << __func__ << " loading summary e " << latest_full << dendl;
140 dout(7) << __func__ << " latest length " << latest_bl.length() << dendl;
141 bufferlist::iterator p = latest_bl.begin();
142 __u8 struct_v;
143 ::decode(struct_v, p);
144 ::decode(max_global_id, p);
145 ::decode(mon->key_server, p);
146 mon->key_server.set_ver(latest_full);
147 keys_ver = latest_full;
148 }
149
150 dout(10) << __func__ << " key server version " << mon->key_server.get_ver() << dendl;
151
152 // walk through incrementals
153 while (version > keys_ver) {
154 bufferlist bl;
155 int ret = get_version(keys_ver+1, bl);
156 assert(ret == 0);
157 assert(bl.length());
158
159 // reset if we are moving to initial state. we will normally have
160 // keys in here temporarily for bootstrapping that we need to
161 // clear out.
162 if (keys_ver == 0)
163 mon->key_server.clear_secrets();
164
165 dout(20) << __func__ << " walking through version " << (keys_ver+1)
166 << " len " << bl.length() << dendl;
167
168 bufferlist::iterator p = bl.begin();
169 __u8 v;
170 ::decode(v, p);
171 while (!p.end()) {
172 Incremental inc;
173 ::decode(inc, p);
174 switch (inc.inc_type) {
175 case GLOBAL_ID:
176 max_global_id = inc.max_global_id;
177 break;
178
179 case AUTH_DATA:
180 {
181 KeyServerData::Incremental auth_inc;
182 bufferlist::iterator iter = inc.auth_data.begin();
183 ::decode(auth_inc, iter);
184 mon->key_server.apply_data_incremental(auth_inc);
185 break;
186 }
187 }
188 }
189
190 keys_ver++;
191 mon->key_server.set_ver(keys_ver);
192
193 if (keys_ver == 1 && mon->is_keyring_required()) {
194 auto t(std::make_shared<MonitorDBStore::Transaction>());
195 t->erase("mkfs", "keyring");
196 mon->store->apply_transaction(t);
197 }
198 }
199
200 if (last_allocated_id == 0)
201 last_allocated_id = max_global_id;
202
203 dout(10) << "update_from_paxos() last_allocated_id=" << last_allocated_id
204 << " max_global_id=" << max_global_id
205 << " format_version " << format_version
206 << dendl;
207 }
208
209 void AuthMonitor::increase_max_global_id()
210 {
211 assert(mon->is_leader());
212
213 max_global_id += g_conf->mon_globalid_prealloc;
214 dout(10) << "increasing max_global_id to " << max_global_id << dendl;
215 Incremental inc;
216 inc.inc_type = GLOBAL_ID;
217 inc.max_global_id = max_global_id;
218 pending_auth.push_back(inc);
219 }
220
221 bool AuthMonitor::should_propose(double& delay)
222 {
223 return (!pending_auth.empty());
224 }
225
226 void AuthMonitor::create_pending()
227 {
228 pending_auth.clear();
229 dout(10) << "create_pending v " << (get_last_committed() + 1) << dendl;
230 }
231
232 void AuthMonitor::encode_pending(MonitorDBStore::TransactionRef t)
233 {
234 dout(10) << __func__ << " v " << (get_last_committed() + 1) << dendl;
235
236 bufferlist bl;
237
238 __u8 v = 1;
239 ::encode(v, bl);
240 vector<Incremental>::iterator p;
241 for (p = pending_auth.begin(); p != pending_auth.end(); ++p)
242 p->encode(bl, mon->get_quorum_con_features());
243
244 version_t version = get_last_committed() + 1;
245 put_version(t, version, bl);
246 put_last_committed(t, version);
247 }
248
249 void AuthMonitor::encode_full(MonitorDBStore::TransactionRef t)
250 {
251 version_t version = mon->key_server.get_ver();
252 // do not stash full version 0 as it will never be removed nor read
253 if (version == 0)
254 return;
255
256 dout(10) << __func__ << " auth v " << version << dendl;
257 assert(get_last_committed() == version);
258
259 bufferlist full_bl;
260 Mutex::Locker l(mon->key_server.get_lock());
261 dout(20) << __func__ << " key server has "
262 << (mon->key_server.has_secrets() ? "" : "no ")
263 << "secrets!" << dendl;
264 __u8 v = 1;
265 ::encode(v, full_bl);
266 ::encode(max_global_id, full_bl);
267 ::encode(mon->key_server, full_bl);
268
269 put_version_full(t, version, full_bl);
270 put_version_latest_full(t, version);
271 }
272
273 version_t AuthMonitor::get_trim_to()
274 {
275 unsigned max = g_conf->paxos_max_join_drift * 2;
276 version_t version = get_last_committed();
277 if (mon->is_leader() && (version > max))
278 return version - max;
279 return 0;
280 }
281
282 bool AuthMonitor::preprocess_query(MonOpRequestRef op)
283 {
284 PaxosServiceMessage *m = static_cast<PaxosServiceMessage*>(op->get_req());
285 dout(10) << "preprocess_query " << *m << " from " << m->get_orig_source_inst() << dendl;
286 switch (m->get_type()) {
287 case MSG_MON_COMMAND:
288 return preprocess_command(op);
289
290 case CEPH_MSG_AUTH:
291 return prep_auth(op, false);
292
293 case MSG_MON_GLOBAL_ID:
294 return false;
295
296 default:
297 ceph_abort();
298 return true;
299 }
300 }
301
302 bool AuthMonitor::prepare_update(MonOpRequestRef op)
303 {
304 PaxosServiceMessage *m = static_cast<PaxosServiceMessage*>(op->get_req());
305 dout(10) << "prepare_update " << *m << " from " << m->get_orig_source_inst() << dendl;
306 switch (m->get_type()) {
307 case MSG_MON_COMMAND:
308 return prepare_command(op);
309 case MSG_MON_GLOBAL_ID:
310 return prepare_global_id(op);
311 case CEPH_MSG_AUTH:
312 return prep_auth(op, true);
313 default:
314 ceph_abort();
315 return false;
316 }
317 }
318
319 uint64_t AuthMonitor::assign_global_id(MonOpRequestRef op, bool should_increase_max)
320 {
321 MAuth *m = static_cast<MAuth*>(op->get_req());
322 int total_mon = mon->monmap->size();
323 dout(10) << "AuthMonitor::assign_global_id m=" << *m << " mon=" << mon->rank << "/" << total_mon
324 << " last_allocated=" << last_allocated_id << " max_global_id=" << max_global_id << dendl;
325
326 uint64_t next_global_id = last_allocated_id + 1;
327 int remainder = next_global_id % total_mon;
328 if (remainder)
329 remainder = total_mon - remainder;
330 next_global_id += remainder + mon->rank;
331 dout(10) << "next_global_id should be " << next_global_id << dendl;
332
333 // if we can't bump the max, bail out now on an out-of-bounds gid
334 if (next_global_id > max_global_id &&
335 (!mon->is_leader() || !should_increase_max)) {
336 return 0;
337 }
338
339 // can we return a gid?
340 bool return_next = (next_global_id <= max_global_id);
341
342 // bump the max?
343 while (mon->is_leader() &&
344 (max_global_id < g_conf->mon_globalid_prealloc ||
345 next_global_id >= max_global_id - g_conf->mon_globalid_prealloc / 2)) {
346 increase_max_global_id();
347 }
348
349 if (return_next) {
350 last_allocated_id = next_global_id;
351 return next_global_id;
352 } else {
353 return 0;
354 }
355 }
356
357
358 bool AuthMonitor::prep_auth(MonOpRequestRef op, bool paxos_writable)
359 {
360 MAuth *m = static_cast<MAuth*>(op->get_req());
361 dout(10) << "prep_auth() blob_size=" << m->get_auth_payload().length() << dendl;
362
363 MonSession *s = op->get_session();
364 if (!s) {
365 dout(10) << "no session, dropping" << dendl;
366 return true;
367 }
368
369 int ret = 0;
370 AuthCapsInfo caps_info;
371 MAuthReply *reply;
372 bufferlist response_bl;
373 bufferlist::iterator indata = m->auth_payload.begin();
374 __u32 proto = m->protocol;
375 bool start = false;
376 EntityName entity_name;
377
378 // set up handler?
379 if (m->protocol == 0 && !s->auth_handler) {
380 set<__u32> supported;
381
382 try {
383 __u8 struct_v = 1;
384 ::decode(struct_v, indata);
385 ::decode(supported, indata);
386 ::decode(entity_name, indata);
387 ::decode(s->global_id, indata);
388 } catch (const buffer::error &e) {
389 dout(10) << "failed to decode initial auth message" << dendl;
390 ret = -EINVAL;
391 goto reply;
392 }
393
394 // do we require cephx signatures?
395
396 if (!m->get_connection()->has_feature(CEPH_FEATURE_MSG_AUTH)) {
397 if (entity_name.get_type() == CEPH_ENTITY_TYPE_MON ||
398 entity_name.get_type() == CEPH_ENTITY_TYPE_OSD ||
399 entity_name.get_type() == CEPH_ENTITY_TYPE_MDS ||
400 entity_name.get_type() == CEPH_ENTITY_TYPE_MGR) {
401 if (g_conf->cephx_cluster_require_signatures ||
402 g_conf->cephx_require_signatures) {
403 dout(1) << m->get_source_inst()
404 << " supports cephx but not signatures and"
405 << " 'cephx [cluster] require signatures = true';"
406 << " disallowing cephx" << dendl;
407 supported.erase(CEPH_AUTH_CEPHX);
408 }
409 } else {
410 if (g_conf->cephx_service_require_signatures ||
411 g_conf->cephx_require_signatures) {
412 dout(1) << m->get_source_inst()
413 << " supports cephx but not signatures and"
414 << " 'cephx [service] require signatures = true';"
415 << " disallowing cephx" << dendl;
416 supported.erase(CEPH_AUTH_CEPHX);
417 }
418 }
419 }
420
421 int type;
422 if (entity_name.get_type() == CEPH_ENTITY_TYPE_MON ||
423 entity_name.get_type() == CEPH_ENTITY_TYPE_OSD ||
424 entity_name.get_type() == CEPH_ENTITY_TYPE_MDS ||
425 entity_name.get_type() == CEPH_ENTITY_TYPE_MGR)
426 type = mon->auth_cluster_required.pick(supported);
427 else
428 type = mon->auth_service_required.pick(supported);
429
430 s->auth_handler = get_auth_service_handler(type, g_ceph_context, &mon->key_server);
431 if (!s->auth_handler) {
432 dout(1) << "client did not provide supported auth type" << dendl;
433 ret = -ENOTSUP;
434 goto reply;
435 }
436 start = true;
437 } else if (!s->auth_handler) {
438 dout(10) << "protocol specified but no s->auth_handler" << dendl;
439 ret = -EINVAL;
440 goto reply;
441 }
442
443 /* assign a new global_id? we assume this should only happen on the first
444 request. If a client tries to send it later, it'll screw up its auth
445 session */
446 if (!s->global_id) {
447 s->global_id = assign_global_id(op, paxos_writable);
448 if (!s->global_id) {
449
450 delete s->auth_handler;
451 s->auth_handler = NULL;
452
453 if (mon->is_leader() && paxos_writable) {
454 dout(10) << "increasing global id, waitlisting message" << dendl;
455 wait_for_active(op, new C_RetryMessage(this, op));
456 goto done;
457 }
458
459 if (!mon->is_leader()) {
460 dout(10) << "not the leader, requesting more ids from leader" << dendl;
461 int leader = mon->get_leader();
462 MMonGlobalID *req = new MMonGlobalID();
463 req->old_max_id = max_global_id;
464 mon->messenger->send_message(req, mon->monmap->get_inst(leader));
465 wait_for_finished_proposal(op, new C_RetryMessage(this, op));
466 return true;
467 }
468
469 assert(!paxos_writable);
470 return false;
471 }
472 }
473
474 try {
475 uint64_t auid = 0;
476 if (start) {
477 // new session
478
479 // always send the latest monmap.
480 if (m->monmap_epoch < mon->monmap->get_epoch())
481 mon->send_latest_monmap(m->get_connection().get());
482
483 proto = s->auth_handler->start_session(entity_name, indata, response_bl, caps_info);
484 ret = 0;
485 if (caps_info.allow_all)
486 s->caps.set_allow_all();
487 } else {
488 // request
489 ret = s->auth_handler->handle_request(indata, response_bl, s->global_id, caps_info, &auid);
490 }
491 if (ret == -EIO) {
492 wait_for_active(op, new C_RetryMessage(this,op));
493 goto done;
494 }
495 if (caps_info.caps.length()) {
496 bufferlist::iterator p = caps_info.caps.begin();
497 string str;
498 try {
499 ::decode(str, p);
500 } catch (const buffer::error &err) {
501 derr << "corrupt cap data for " << entity_name << " in auth db" << dendl;
502 str.clear();
503 }
504 s->caps.parse(str, NULL);
505 s->auid = auid;
506 }
507 } catch (const buffer::error &err) {
508 ret = -EINVAL;
509 dout(0) << "caught error when trying to handle auth request, probably malformed request" << dendl;
510 }
511
512 reply:
513 reply = new MAuthReply(proto, &response_bl, ret, s->global_id);
514 mon->send_reply(op, reply);
515 done:
516 return true;
517 }
518
519 bool AuthMonitor::preprocess_command(MonOpRequestRef op)
520 {
521 MMonCommand *m = static_cast<MMonCommand*>(op->get_req());
522 int r = -1;
523 bufferlist rdata;
524 stringstream ss, ds;
525
526 map<string, cmd_vartype> cmdmap;
527 if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
528 // ss has reason for failure
529 string rs = ss.str();
530 mon->reply_command(op, -EINVAL, rs, rdata, get_last_committed());
531 return true;
532 }
533
534 string prefix;
535 cmd_getval(g_ceph_context, cmdmap, "prefix", prefix);
536 if (prefix == "auth add" ||
537 prefix == "auth del" ||
538 prefix == "auth rm" ||
539 prefix == "auth get-or-create" ||
540 prefix == "auth get-or-create-key" ||
541 prefix == "auth import" ||
542 prefix == "auth caps") {
543 return false;
544 }
545
546 MonSession *session = m->get_session();
547 if (!session) {
548 mon->reply_command(op, -EACCES, "access denied", rdata, get_last_committed());
549 return true;
550 }
551
552 // entity might not be supplied, but if it is, it should be valid
553 string entity_name;
554 cmd_getval(g_ceph_context, cmdmap, "entity", entity_name);
555 EntityName entity;
556 if (!entity_name.empty() && !entity.from_str(entity_name)) {
557 ss << "invalid entity_auth " << entity_name;
558 mon->reply_command(op, -EINVAL, ss.str(), get_last_committed());
559 return true;
560 }
561
562 string format;
563 cmd_getval(g_ceph_context, cmdmap, "format", format, string("plain"));
564 boost::scoped_ptr<Formatter> f(Formatter::create(format));
565
566 if (prefix == "auth export") {
567 KeyRing keyring;
568 export_keyring(keyring);
569 if (!entity_name.empty()) {
570 EntityAuth eauth;
571 if (keyring.get_auth(entity, eauth)) {
572 KeyRing kr;
573 kr.add(entity, eauth);
574 if (f)
575 kr.encode_formatted("auth", f.get(), rdata);
576 else
577 kr.encode_plaintext(rdata);
578 ss << "export " << eauth;
579 r = 0;
580 } else {
581 ss << "no key for " << eauth;
582 r = -ENOENT;
583 }
584 } else {
585 if (f)
586 keyring.encode_formatted("auth", f.get(), rdata);
587 else
588 keyring.encode_plaintext(rdata);
589
590 ss << "exported master keyring";
591 r = 0;
592 }
593 } else if (prefix == "auth get" && !entity_name.empty()) {
594 KeyRing keyring;
595 EntityAuth entity_auth;
596 if(!mon->key_server.get_auth(entity, entity_auth)) {
597 ss << "failed to find " << entity_name << " in keyring";
598 r = -ENOENT;
599 } else {
600 keyring.add(entity, entity_auth);
601 if (f)
602 keyring.encode_formatted("auth", f.get(), rdata);
603 else
604 keyring.encode_plaintext(rdata);
605 ss << "exported keyring for " << entity_name;
606 r = 0;
607 }
608 } else if (prefix == "auth print-key" ||
609 prefix == "auth print_key" ||
610 prefix == "auth get-key") {
611 EntityAuth auth;
612 if (!mon->key_server.get_auth(entity, auth)) {
613 ss << "don't have " << entity;
614 r = -ENOENT;
615 goto done;
616 }
617 if (f) {
618 auth.key.encode_formatted("auth", f.get(), rdata);
619 } else {
620 auth.key.encode_plaintext(rdata);
621 }
622 r = 0;
623 } else if (prefix == "auth list" ||
624 prefix == "auth ls") {
625 if (f) {
626 mon->key_server.encode_formatted("auth", f.get(), rdata);
627 } else {
628 mon->key_server.encode_plaintext(rdata);
629 if (rdata.length() > 0)
630 ss << "installed auth entries:" << std::endl;
631 else
632 ss << "no installed auth entries!" << std::endl;
633 }
634 r = 0;
635 goto done;
636 } else {
637 ss << "invalid command";
638 r = -EINVAL;
639 }
640
641 done:
642 rdata.append(ds);
643 string rs;
644 getline(ss, rs, '\0');
645 mon->reply_command(op, r, rs, rdata, get_last_committed());
646 return true;
647 }
648
649 void AuthMonitor::export_keyring(KeyRing& keyring)
650 {
651 mon->key_server.export_keyring(keyring);
652 }
653
654 int AuthMonitor::import_keyring(KeyRing& keyring)
655 {
656 for (map<EntityName, EntityAuth>::iterator p = keyring.get_keys().begin();
657 p != keyring.get_keys().end();
658 ++p) {
659 if (p->second.caps.empty()) {
660 dout(0) << "import: no caps supplied" << dendl;
661 return -EINVAL;
662 }
663 int err = add_entity(p->first, p->second);
664 assert(err == 0);
665 }
666 return 0;
667 }
668
669 int AuthMonitor::remove_entity(const EntityName &entity)
670 {
671 dout(10) << __func__ << " " << entity << dendl;
672 if (!mon->key_server.contains(entity))
673 return -ENOENT;
674
675 KeyServerData::Incremental auth_inc;
676 auth_inc.name = entity;
677 auth_inc.op = KeyServerData::AUTH_INC_DEL;
678 push_cephx_inc(auth_inc);
679
680 return 0;
681 }
682
683 bool AuthMonitor::entity_is_pending(EntityName& entity)
684 {
685 // are we about to have it?
686 for (auto& p : pending_auth) {
687 if (p.inc_type == AUTH_DATA) {
688 KeyServerData::Incremental inc;
689 bufferlist::iterator q = p.auth_data.begin();
690 ::decode(inc, q);
691 if (inc.op == KeyServerData::AUTH_INC_ADD &&
692 inc.name == entity) {
693 return true;
694 }
695 }
696 }
697 return false;
698 }
699
700 int AuthMonitor::exists_and_matches_entity(
701 const auth_entity_t& entity,
702 bool has_secret,
703 stringstream& ss)
704 {
705 return exists_and_matches_entity(entity.name, entity.auth,
706 entity.auth.caps, has_secret, ss);
707 }
708
709 int AuthMonitor::exists_and_matches_entity(
710 const EntityName& name,
711 const EntityAuth& auth,
712 const map<string,bufferlist>& caps,
713 bool has_secret,
714 stringstream& ss)
715 {
716
717 dout(20) << __func__ << " entity " << name << " auth " << auth
718 << " caps " << caps << " has_secret " << has_secret << dendl;
719
720 EntityAuth existing_auth;
721 // does entry already exist?
722 if (mon->key_server.get_auth(name, existing_auth)) {
723 // key match?
724 if (has_secret) {
725 if (existing_auth.key.get_secret().cmp(auth.key.get_secret())) {
726 ss << "entity " << name << " exists but key does not match";
727 return -EEXIST;
728 }
729 }
730
731 // caps match?
732 if (caps.size() != existing_auth.caps.size()) {
733 ss << "entity " << name << " exists but caps do not match";
734 return -EINVAL;
735 }
736 for (auto& it : caps) {
737 if (existing_auth.caps.count(it.first) == 0 ||
738 !existing_auth.caps[it.first].contents_equal(it.second)) {
739 ss << "entity " << name << " exists but cap "
740 << it.first << " does not match";
741 return -EINVAL;
742 }
743 }
744
745 // they match, no-op
746 return 0;
747 }
748 return -ENOENT;
749 }
750
751 int AuthMonitor::add_entity(
752 const EntityName& name,
753 const EntityAuth& auth)
754 {
755
756 // okay, add it.
757 KeyServerData::Incremental auth_inc;
758 auth_inc.op = KeyServerData::AUTH_INC_ADD;
759 auth_inc.name = name;
760 auth_inc.auth = auth;
761
762 dout(10) << " importing " << auth_inc.name << dendl;
763 dout(30) << " " << auth_inc.auth << dendl;
764 push_cephx_inc(auth_inc);
765 return 0;
766 }
767
768 int AuthMonitor::validate_osd_destroy(
769 int32_t id,
770 const uuid_d& uuid,
771 EntityName& cephx_entity,
772 EntityName& lockbox_entity,
773 stringstream& ss)
774 {
775 assert(paxos->is_plugged());
776
777 dout(10) << __func__ << " id " << id << " uuid " << uuid << dendl;
778
779 string cephx_str = "osd." + stringify(id);
780 string lockbox_str = "client.osd-lockbox." + stringify(uuid);
781
782 if (!cephx_entity.from_str(cephx_str)) {
783 dout(10) << __func__ << " invalid cephx entity '"
784 << cephx_str << "'" << dendl;
785 ss << "invalid cephx key entity '" << cephx_str << "'";
786 return -EINVAL;
787 }
788
789 if (!lockbox_entity.from_str(lockbox_str)) {
790 dout(10) << __func__ << " invalid lockbox entity '"
791 << lockbox_str << "'" << dendl;
792 ss << "invalid lockbox key entity '" << lockbox_str << "'";
793 return -EINVAL;
794 }
795
796 if (!mon->key_server.contains(cephx_entity) &&
797 !mon->key_server.contains(lockbox_entity)) {
798 return -ENOENT;
799 }
800
801 return 0;
802 }
803
804 int AuthMonitor::do_osd_destroy(
805 const EntityName& cephx_entity,
806 const EntityName& lockbox_entity)
807 {
808 assert(paxos->is_plugged());
809
810 dout(10) << __func__ << " cephx " << cephx_entity
811 << " lockbox " << lockbox_entity << dendl;
812
813 bool removed = false;
814
815 int err = remove_entity(cephx_entity);
816 if (err == -ENOENT) {
817 dout(10) << __func__ << " " << cephx_entity << " does not exist" << dendl;
818 } else {
819 removed = true;
820 }
821
822 err = remove_entity(lockbox_entity);
823 if (err == -ENOENT) {
824 dout(10) << __func__ << " " << lockbox_entity << " does not exist" << dendl;
825 } else {
826 removed = true;
827 }
828
829 if (!removed) {
830 dout(10) << __func__ << " entities do not exist -- no-op." << dendl;
831 return 0;
832 }
833
834 // given we have paxos plugged, this will not result in a proposal
835 // being triggered, but it will still be needed so that we get our
836 // pending state encoded into the paxos' pending transaction.
837 propose_pending();
838 return 0;
839 }
840
841 bufferlist _encode_cap(const string& cap)
842 {
843 bufferlist bl;
844 ::encode(cap, bl);
845 return bl;
846 }
847
848 int _create_auth(
849 EntityAuth& auth,
850 const string& key,
851 const map<string,bufferlist>& caps)
852 {
853 if (key.empty())
854 return -EINVAL;
855 try {
856 auth.key.decode_base64(key);
857 } catch (buffer::error& e) {
858 return -EINVAL;
859 }
860 auth.caps = caps;
861 return 0;
862 }
863
864 int AuthMonitor::validate_osd_new(
865 int32_t id,
866 const uuid_d& uuid,
867 const string& cephx_secret,
868 const string& lockbox_secret,
869 auth_entity_t& cephx_entity,
870 auth_entity_t& lockbox_entity,
871 stringstream& ss)
872 {
873
874 dout(10) << __func__ << " osd." << id << " uuid " << uuid << dendl;
875
876 map<string,bufferlist> cephx_caps = {
877 { "osd", _encode_cap("allow *") },
878 { "mon", _encode_cap("allow profile osd") },
879 { "mgr", _encode_cap("allow profile osd") }
880 };
881 map<string,bufferlist> lockbox_caps = {
882 { "mon", _encode_cap("allow command \"config-key get\" "
883 "with key=\"dm-crypt/osd/" +
884 stringify(uuid) +
885 "/luks\"") }
886 };
887
888 bool has_lockbox = !lockbox_secret.empty();
889
890 string cephx_name = "osd." + stringify(id);
891 string lockbox_name = "client.osd-lockbox." + stringify(uuid);
892
893 if (!cephx_entity.name.from_str(cephx_name)) {
894 dout(10) << __func__ << " invalid cephx entity '"
895 << cephx_name << "'" << dendl;
896 ss << "invalid cephx key entity '" << cephx_name << "'";
897 return -EINVAL;
898 }
899
900 if (has_lockbox) {
901 if (!lockbox_entity.name.from_str(lockbox_name)) {
902 dout(10) << __func__ << " invalid cephx lockbox entity '"
903 << lockbox_name << "'" << dendl;
904 ss << "invalid cephx lockbox entity '" << lockbox_name << "'";
905 return -EINVAL;
906 }
907 }
908
909 if (entity_is_pending(cephx_entity.name) ||
910 (has_lockbox && entity_is_pending(lockbox_entity.name))) {
911 // If we have pending entities for either the cephx secret or the
912 // lockbox secret, then our safest bet is to retry the command at
913 // a later time. These entities may be pending because an `osd new`
914 // command has been run (which is unlikely, due to the nature of
915 // the operation, which will force a paxos proposal), or (more likely)
916 // because a competing client created those entities before we handled
917 // the `osd new` command. Regardless, let's wait and see.
918 return -EAGAIN;
919 }
920
921 if (!is_valid_cephx_key(cephx_secret)) {
922 ss << "invalid cephx secret.";
923 return -EINVAL;
924 }
925
926 if (has_lockbox && !is_valid_cephx_key(lockbox_secret)) {
927 ss << "invalid cephx lockbox secret.";
928 return -EINVAL;
929 }
930
931 int err = _create_auth(cephx_entity.auth, cephx_secret, cephx_caps);
932 assert(0 == err);
933
934 bool cephx_is_idempotent = false, lockbox_is_idempotent = false;
935 err = exists_and_matches_entity(cephx_entity, true, ss);
936
937 if (err != -ENOENT) {
938 if (err < 0) {
939 return err;
940 }
941 assert(0 == err);
942 cephx_is_idempotent = true;
943 }
944
945 if (has_lockbox) {
946 err = _create_auth(lockbox_entity.auth, lockbox_secret, lockbox_caps);
947 assert(err == 0);
948 err = exists_and_matches_entity(lockbox_entity, true, ss);
949 if (err != -ENOENT) {
950 if (err < 0) {
951 return err;
952 }
953 assert(0 == err);
954 lockbox_is_idempotent = true;
955 }
956 }
957
958 if (cephx_is_idempotent && (!has_lockbox || lockbox_is_idempotent)) {
959 return EEXIST;
960 }
961
962 return 0;
963 }
964
965 int AuthMonitor::do_osd_new(
966 const auth_entity_t& cephx_entity,
967 const auth_entity_t& lockbox_entity,
968 bool has_lockbox)
969 {
970 assert(paxos->is_plugged());
971
972 dout(10) << __func__ << " cephx " << cephx_entity.name
973 << " lockbox ";
974 if (has_lockbox) {
975 *_dout << lockbox_entity.name;
976 } else {
977 *_dout << "n/a";
978 }
979 *_dout << dendl;
980
981 // we must have validated before reaching this point.
982 // if keys exist, then this means they also match; otherwise we would
983 // have failed before calling this function.
984 bool cephx_exists = mon->key_server.contains(cephx_entity.name);
985
986 if (!cephx_exists) {
987 int err = add_entity(cephx_entity.name, cephx_entity.auth);
988 assert(0 == err);
989 }
990
991 if (has_lockbox &&
992 !mon->key_server.contains(lockbox_entity.name)) {
993 int err = add_entity(lockbox_entity.name, lockbox_entity.auth);
994 assert(0 == err);
995 }
996
997 // given we have paxos plugged, this will not result in a proposal
998 // being triggered, but it will still be needed so that we get our
999 // pending state encoded into the paxos' pending transaction.
1000 propose_pending();
1001 return 0;
1002 }
1003
1004 bool AuthMonitor::prepare_command(MonOpRequestRef op)
1005 {
1006 MMonCommand *m = static_cast<MMonCommand*>(op->get_req());
1007 stringstream ss, ds;
1008 bufferlist rdata;
1009 string rs;
1010 int err = -EINVAL;
1011
1012 map<string, cmd_vartype> cmdmap;
1013 if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
1014 // ss has reason for failure
1015 string rs = ss.str();
1016 mon->reply_command(op, -EINVAL, rs, rdata, get_last_committed());
1017 return true;
1018 }
1019
1020 string prefix;
1021 vector<string>caps_vec;
1022 string entity_name;
1023 EntityName entity;
1024
1025 cmd_getval(g_ceph_context, cmdmap, "prefix", prefix);
1026
1027 string format;
1028 cmd_getval(g_ceph_context, cmdmap, "format", format, string("plain"));
1029 boost::scoped_ptr<Formatter> f(Formatter::create(format));
1030
1031 MonSession *session = m->get_session();
1032 if (!session) {
1033 mon->reply_command(op, -EACCES, "access denied", rdata, get_last_committed());
1034 return true;
1035 }
1036
1037 cmd_getval(g_ceph_context, cmdmap, "caps", caps_vec);
1038 if ((caps_vec.size() % 2) != 0) {
1039 ss << "bad capabilities request; odd number of arguments";
1040 err = -EINVAL;
1041 goto done;
1042 }
1043
1044 cmd_getval(g_ceph_context, cmdmap, "entity", entity_name);
1045 if (!entity_name.empty() && !entity.from_str(entity_name)) {
1046 ss << "bad entity name";
1047 err = -EINVAL;
1048 goto done;
1049 }
1050
1051 if (prefix == "auth import") {
1052 bufferlist bl = m->get_data();
1053 if (bl.length() == 0) {
1054 ss << "auth import: no data supplied";
1055 getline(ss, rs);
1056 mon->reply_command(op, -EINVAL, rs, get_last_committed());
1057 return true;
1058 }
1059 bufferlist::iterator iter = bl.begin();
1060 KeyRing keyring;
1061 try {
1062 ::decode(keyring, iter);
1063 } catch (const buffer::error &ex) {
1064 ss << "error decoding keyring" << " " << ex.what();
1065 err = -EINVAL;
1066 goto done;
1067 }
1068 err = import_keyring(keyring);
1069 if (err < 0) {
1070 ss << "auth import: no caps supplied";
1071 getline(ss, rs);
1072 mon->reply_command(op, -EINVAL, rs, get_last_committed());
1073 return true;
1074 }
1075 ss << "imported keyring";
1076 getline(ss, rs);
1077 err = 0;
1078 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1079 get_last_committed() + 1));
1080 return true;
1081 } else if (prefix == "auth add" && !entity_name.empty()) {
1082 /* expected behavior:
1083 * - if command reproduces current state, return 0.
1084 * - if command adds brand new entity, handle it.
1085 * - if command adds new state to existing entity, return error.
1086 */
1087 KeyServerData::Incremental auth_inc;
1088 auth_inc.name = entity;
1089 bufferlist bl = m->get_data();
1090 bool has_keyring = (bl.length() > 0);
1091 map<string,bufferlist> new_caps;
1092
1093 KeyRing new_keyring;
1094 if (has_keyring) {
1095 bufferlist::iterator iter = bl.begin();
1096 try {
1097 ::decode(new_keyring, iter);
1098 } catch (const buffer::error &ex) {
1099 ss << "error decoding keyring";
1100 err = -EINVAL;
1101 goto done;
1102 }
1103 }
1104
1105 // are we about to have it?
1106 if (entity_is_pending(entity)) {
1107 wait_for_finished_proposal(op,
1108 new Monitor::C_Command(mon, op, 0, rs, get_last_committed() + 1));
1109 return true;
1110 }
1111
1112 // build new caps from provided arguments (if available)
1113 for (vector<string>::iterator it = caps_vec.begin();
1114 it != caps_vec.end() && (it + 1) != caps_vec.end();
1115 it += 2) {
1116 string sys = *it;
1117 bufferlist cap;
1118 ::encode(*(it+1), cap);
1119 new_caps[sys] = cap;
1120 }
1121
1122 // pull info out of provided keyring
1123 EntityAuth new_inc;
1124 if (has_keyring) {
1125 if (!new_keyring.get_auth(auth_inc.name, new_inc)) {
1126 ss << "key for " << auth_inc.name
1127 << " not found in provided keyring";
1128 err = -EINVAL;
1129 goto done;
1130 }
1131 if (!new_caps.empty() && !new_inc.caps.empty()) {
1132 ss << "caps cannot be specified both in keyring and in command";
1133 err = -EINVAL;
1134 goto done;
1135 }
1136 if (new_caps.empty()) {
1137 new_caps = new_inc.caps;
1138 }
1139 }
1140
1141 err = exists_and_matches_entity(auth_inc.name, new_inc,
1142 new_caps, has_keyring, ss);
1143 // if entity/key/caps do not exist in the keyring, just fall through
1144 // and add the entity; otherwise, make sure everything matches (in
1145 // which case it's a no-op), because if not we must fail.
1146 if (err != -ENOENT) {
1147 if (err < 0) {
1148 goto done;
1149 }
1150 // no-op.
1151 assert(err == 0);
1152 goto done;
1153 }
1154 err = 0;
1155
1156 // okay, add it.
1157 if (!has_keyring) {
1158 dout(10) << "AuthMonitor::prepare_command generating random key for "
1159 << auth_inc.name << dendl;
1160 new_inc.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1161 }
1162 new_inc.caps = new_caps;
1163
1164 err = add_entity(auth_inc.name, new_inc);
1165 assert(err == 0);
1166
1167 ss << "added key for " << auth_inc.name;
1168 getline(ss, rs);
1169 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1170 get_last_committed() + 1));
1171 return true;
1172 } else if ((prefix == "auth get-or-create-key" ||
1173 prefix == "auth get-or-create") &&
1174 !entity_name.empty()) {
1175 // auth get-or-create <name> [mon osdcapa osd osdcapb ...]
1176
1177 if (!valid_caps(caps_vec, &ss)) {
1178 err = -EINVAL;
1179 goto done;
1180 }
1181
1182 // Parse the list of caps into a map
1183 std::map<std::string, bufferlist> wanted_caps;
1184 for (vector<string>::const_iterator it = caps_vec.begin();
1185 it != caps_vec.end() && (it + 1) != caps_vec.end();
1186 it += 2) {
1187 const std::string &sys = *it;
1188 bufferlist cap;
1189 ::encode(*(it+1), cap);
1190 wanted_caps[sys] = cap;
1191 }
1192
1193 // do we have it?
1194 EntityAuth entity_auth;
1195 if (mon->key_server.get_auth(entity, entity_auth)) {
1196 for (const auto &sys_cap : wanted_caps) {
1197 if (entity_auth.caps.count(sys_cap.first) == 0 ||
1198 !entity_auth.caps[sys_cap.first].contents_equal(sys_cap.second)) {
1199 ss << "key for " << entity << " exists but cap " << sys_cap.first
1200 << " does not match";
1201 err = -EINVAL;
1202 goto done;
1203 }
1204 }
1205
1206 if (prefix == "auth get-or-create-key") {
1207 if (f) {
1208 entity_auth.key.encode_formatted("auth", f.get(), rdata);
1209 } else {
1210 ds << entity_auth.key;
1211 }
1212 } else {
1213 KeyRing kr;
1214 kr.add(entity, entity_auth.key);
1215 if (f) {
1216 kr.set_caps(entity, entity_auth.caps);
1217 kr.encode_formatted("auth", f.get(), rdata);
1218 } else {
1219 kr.encode_plaintext(rdata);
1220 }
1221 }
1222 err = 0;
1223 goto done;
1224 }
1225
1226 // ...or are we about to?
1227 for (vector<Incremental>::iterator p = pending_auth.begin();
1228 p != pending_auth.end();
1229 ++p) {
1230 if (p->inc_type == AUTH_DATA) {
1231 KeyServerData::Incremental auth_inc;
1232 bufferlist::iterator q = p->auth_data.begin();
1233 ::decode(auth_inc, q);
1234 if (auth_inc.op == KeyServerData::AUTH_INC_ADD &&
1235 auth_inc.name == entity) {
1236 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1237 get_last_committed() + 1));
1238 return true;
1239 }
1240 }
1241 }
1242
1243 // create it
1244 KeyServerData::Incremental auth_inc;
1245 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1246 auth_inc.name = entity;
1247 auth_inc.auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1248 auth_inc.auth.caps = wanted_caps;
1249
1250 push_cephx_inc(auth_inc);
1251
1252 if (prefix == "auth get-or-create-key") {
1253 if (f) {
1254 auth_inc.auth.key.encode_formatted("auth", f.get(), rdata);
1255 } else {
1256 ds << auth_inc.auth.key;
1257 }
1258 } else {
1259 KeyRing kr;
1260 kr.add(entity, auth_inc.auth.key);
1261 if (f) {
1262 kr.set_caps(entity, wanted_caps);
1263 kr.encode_formatted("auth", f.get(), rdata);
1264 } else {
1265 kr.encode_plaintext(rdata);
1266 }
1267 }
1268
1269 rdata.append(ds);
1270 getline(ss, rs);
1271 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs, rdata,
1272 get_last_committed() + 1));
1273 return true;
1274 } else if (prefix == "auth caps" && !entity_name.empty()) {
1275 KeyServerData::Incremental auth_inc;
1276 auth_inc.name = entity;
1277 if (!mon->key_server.get_auth(auth_inc.name, auth_inc.auth)) {
1278 ss << "couldn't find entry " << auth_inc.name;
1279 err = -ENOENT;
1280 goto done;
1281 }
1282
1283 if (!valid_caps(caps_vec, &ss)) {
1284 err = -EINVAL;
1285 goto done;
1286 }
1287
1288 map<string,bufferlist> newcaps;
1289 for (vector<string>::iterator it = caps_vec.begin();
1290 it != caps_vec.end(); it += 2)
1291 ::encode(*(it+1), newcaps[*it]);
1292
1293 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1294 auth_inc.auth.caps = newcaps;
1295 push_cephx_inc(auth_inc);
1296
1297 ss << "updated caps for " << auth_inc.name;
1298 getline(ss, rs);
1299 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1300 get_last_committed() + 1));
1301 return true;
1302 } else if ((prefix == "auth del" || prefix == "auth rm") &&
1303 !entity_name.empty()) {
1304 KeyServerData::Incremental auth_inc;
1305 auth_inc.name = entity;
1306 if (!mon->key_server.contains(auth_inc.name)) {
1307 ss << "entity " << entity << " does not exist";
1308 err = 0;
1309 goto done;
1310 }
1311 auth_inc.op = KeyServerData::AUTH_INC_DEL;
1312 push_cephx_inc(auth_inc);
1313
1314 ss << "updated";
1315 getline(ss, rs);
1316 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1317 get_last_committed() + 1));
1318 return true;
1319 }
1320 done:
1321 rdata.append(ds);
1322 getline(ss, rs, '\0');
1323 mon->reply_command(op, err, rs, rdata, get_last_committed());
1324 return false;
1325 }
1326
1327 bool AuthMonitor::prepare_global_id(MonOpRequestRef op)
1328 {
1329 dout(10) << "AuthMonitor::prepare_global_id" << dendl;
1330 increase_max_global_id();
1331
1332 return true;
1333 }
1334
1335 void AuthMonitor::upgrade_format()
1336 {
1337 unsigned int current = 2;
1338 if (!mon->get_quorum_mon_features().contains_all(
1339 ceph::features::mon::FEATURE_LUMINOUS)) {
1340 current = 1;
1341 }
1342 if (format_version >= current) {
1343 dout(20) << __func__ << " format " << format_version << " is current" << dendl;
1344 return;
1345 }
1346
1347 bool changed = false;
1348 if (format_version == 0) {
1349 dout(1) << __func__ << " upgrading from format 0 to 1" << dendl;
1350 map<EntityName, EntityAuth>::iterator p;
1351 for (p = mon->key_server.secrets_begin();
1352 p != mon->key_server.secrets_end();
1353 ++p) {
1354 // grab mon caps, if any
1355 string mon_caps;
1356 if (p->second.caps.count("mon") == 0)
1357 continue;
1358 try {
1359 bufferlist::iterator it = p->second.caps["mon"].begin();
1360 ::decode(mon_caps, it);
1361 }
1362 catch (buffer::error) {
1363 dout(10) << __func__ << " unable to parse mon cap for "
1364 << p->first << dendl;
1365 continue;
1366 }
1367
1368 string n = p->first.to_str();
1369 string new_caps;
1370
1371 // set daemon profiles
1372 if ((p->first.is_osd() || p->first.is_mds()) &&
1373 mon_caps == "allow rwx") {
1374 new_caps = string("allow profile ") + string(p->first.get_type_name());
1375 }
1376
1377 // update bootstrap keys
1378 if (n == "client.bootstrap-osd") {
1379 new_caps = "allow profile bootstrap-osd";
1380 }
1381 if (n == "client.bootstrap-mds") {
1382 new_caps = "allow profile bootstrap-mds";
1383 }
1384
1385 if (new_caps.length() > 0) {
1386 dout(5) << __func__ << " updating " << p->first << " mon cap from "
1387 << mon_caps << " to " << new_caps << dendl;
1388
1389 bufferlist bl;
1390 ::encode(new_caps, bl);
1391
1392 KeyServerData::Incremental auth_inc;
1393 auth_inc.name = p->first;
1394 auth_inc.auth = p->second;
1395 auth_inc.auth.caps["mon"] = bl;
1396 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1397 push_cephx_inc(auth_inc);
1398 changed = true;
1399 }
1400 }
1401 }
1402
1403 if (format_version == 1) {
1404 dout(1) << __func__ << " upgrading from format 1 to 2" << dendl;
1405 map<EntityName, EntityAuth>::iterator p;
1406 for (p = mon->key_server.secrets_begin();
1407 p != mon->key_server.secrets_end();
1408 ++p) {
1409 string n = p->first.to_str();
1410
1411 string newcap;
1412 if (n == "client.admin") {
1413 // admin gets it all
1414 newcap = "allow *";
1415 } else if (n.find("osd.") == 0 ||
1416 n.find("mds.") == 0 ||
1417 n.find("mon.") == 0) {
1418 // daemons follow their profile
1419 string type = n.substr(0, 3);
1420 newcap = "allow profile " + type;
1421 } else if (p->second.caps.count("mon")) {
1422 // if there are any mon caps, give them 'r' mgr caps
1423 newcap = "allow r";
1424 }
1425
1426 if (newcap.length() > 0) {
1427 dout(5) << " giving " << n << " mgr '" << newcap << "'" << dendl;
1428 bufferlist bl;
1429 ::encode(newcap, bl);
1430
1431 KeyServerData::Incremental auth_inc;
1432 auth_inc.name = p->first;
1433 auth_inc.auth = p->second;
1434 auth_inc.auth.caps["mgr"] = bl;
1435 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1436 push_cephx_inc(auth_inc);
1437 }
1438
1439 if (n.find("mgr.") == 0 &&
1440 p->second.caps.count("mon")) {
1441 // the kraken ceph-mgr@.service set the mon cap to 'allow *'.
1442 auto blp = p->second.caps["mon"].begin();
1443 string oldcaps;
1444 ::decode(oldcaps, blp);
1445 if (oldcaps == "allow *") {
1446 dout(5) << " fixing " << n << " mon cap to 'allow profile mgr'"
1447 << dendl;
1448 bufferlist bl;
1449 ::encode("allow profile mgr", bl);
1450 KeyServerData::Incremental auth_inc;
1451 auth_inc.name = p->first;
1452 auth_inc.auth = p->second;
1453 auth_inc.auth.caps["mon"] = bl;
1454 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1455 push_cephx_inc(auth_inc);
1456 }
1457 }
1458 }
1459
1460 // add bootstrap key
1461 {
1462 KeyServerData::Incremental auth_inc;
1463 bool r = auth_inc.name.from_str("client.bootstrap-mgr");
1464 assert(r);
1465 ::encode("allow profile bootstrap-mgr", auth_inc.auth.caps["mon"]);
1466 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1467 // generate key
1468 auth_inc.auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1469 push_cephx_inc(auth_inc);
1470 }
1471 changed = true;
1472 }
1473
1474 if (changed) {
1475 // note new format
1476 dout(10) << __func__ << " proposing update from format " << format_version
1477 << " -> " << current << dendl;
1478 format_version = current;
1479 propose_pending();
1480 }
1481 }
1482
1483 void AuthMonitor::dump_info(Formatter *f)
1484 {
1485 /*** WARNING: do not include any privileged information here! ***/
1486 f->open_object_section("auth");
1487 f->dump_unsigned("first_committed", get_first_committed());
1488 f->dump_unsigned("last_committed", get_last_committed());
1489 f->dump_unsigned("num_secrets", mon->key_server.get_num_secrets());
1490 f->close_section();
1491 }