]> git.proxmox.com Git - ceph.git/blob - ceph/src/mon/AuthMonitor.cc
e6473c5e41769b57fe4ca2c5d0748bbad1c1bcd3
[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 if (f) {
625 mon->key_server.encode_formatted("auth", f.get(), rdata);
626 } else {
627 mon->key_server.encode_plaintext(rdata);
628 if (rdata.length() > 0)
629 ss << "installed auth entries:" << std::endl;
630 else
631 ss << "no installed auth entries!" << std::endl;
632 }
633 r = 0;
634 goto done;
635 } else {
636 ss << "invalid command";
637 r = -EINVAL;
638 }
639
640 done:
641 rdata.append(ds);
642 string rs;
643 getline(ss, rs, '\0');
644 mon->reply_command(op, r, rs, rdata, get_last_committed());
645 return true;
646 }
647
648 void AuthMonitor::export_keyring(KeyRing& keyring)
649 {
650 mon->key_server.export_keyring(keyring);
651 }
652
653 int AuthMonitor::import_keyring(KeyRing& keyring)
654 {
655 for (map<EntityName, EntityAuth>::iterator p = keyring.get_keys().begin();
656 p != keyring.get_keys().end();
657 ++p) {
658 if (p->second.caps.empty()) {
659 dout(0) << "import: no caps supplied" << dendl;
660 return -EINVAL;
661 }
662 int err = add_entity(p->first, p->second);
663 assert(err == 0);
664 }
665 return 0;
666 }
667
668 int AuthMonitor::remove_entity(const EntityName &entity)
669 {
670 dout(10) << __func__ << " " << entity << dendl;
671 if (!mon->key_server.contains(entity))
672 return -ENOENT;
673
674 KeyServerData::Incremental auth_inc;
675 auth_inc.name = entity;
676 auth_inc.op = KeyServerData::AUTH_INC_DEL;
677 push_cephx_inc(auth_inc);
678
679 return 0;
680 }
681
682 bool AuthMonitor::entity_is_pending(EntityName& entity)
683 {
684 // are we about to have it?
685 for (auto& p : pending_auth) {
686 if (p.inc_type == AUTH_DATA) {
687 KeyServerData::Incremental inc;
688 bufferlist::iterator q = p.auth_data.begin();
689 ::decode(inc, q);
690 if (inc.op == KeyServerData::AUTH_INC_ADD &&
691 inc.name == entity) {
692 return true;
693 }
694 }
695 }
696 return false;
697 }
698
699 int AuthMonitor::exists_and_matches_entity(
700 const auth_entity_t& entity,
701 bool has_secret,
702 stringstream& ss)
703 {
704 return exists_and_matches_entity(entity.name, entity.auth,
705 entity.auth.caps, has_secret, ss);
706 }
707
708 int AuthMonitor::exists_and_matches_entity(
709 const EntityName& name,
710 const EntityAuth& auth,
711 const map<string,bufferlist>& caps,
712 bool has_secret,
713 stringstream& ss)
714 {
715
716 dout(20) << __func__ << " entity " << name << " auth " << auth
717 << " caps " << caps << " has_secret " << has_secret << dendl;
718
719 EntityAuth existing_auth;
720 // does entry already exist?
721 if (mon->key_server.get_auth(name, existing_auth)) {
722 // key match?
723 if (has_secret) {
724 if (existing_auth.key.get_secret().cmp(auth.key.get_secret())) {
725 ss << "entity " << name << " exists but key does not match";
726 return -EEXIST;
727 }
728 }
729
730 // caps match?
731 if (caps.size() != existing_auth.caps.size()) {
732 ss << "entity " << name << " exists but caps do not match";
733 return -EINVAL;
734 }
735 for (auto& it : caps) {
736 if (existing_auth.caps.count(it.first) == 0 ||
737 !existing_auth.caps[it.first].contents_equal(it.second)) {
738 ss << "entity " << name << " exists but cap "
739 << it.first << " does not match";
740 return -EINVAL;
741 }
742 }
743
744 // they match, no-op
745 return 0;
746 }
747 return -ENOENT;
748 }
749
750 int AuthMonitor::add_entity(
751 const EntityName& name,
752 const EntityAuth& auth)
753 {
754
755 // okay, add it.
756 KeyServerData::Incremental auth_inc;
757 auth_inc.op = KeyServerData::AUTH_INC_ADD;
758 auth_inc.name = name;
759 auth_inc.auth = auth;
760
761 dout(10) << " importing " << auth_inc.name << dendl;
762 dout(30) << " " << auth_inc.auth << dendl;
763 push_cephx_inc(auth_inc);
764 return 0;
765 }
766
767 int AuthMonitor::validate_osd_destroy(
768 int32_t id,
769 const uuid_d& uuid,
770 EntityName& cephx_entity,
771 EntityName& lockbox_entity,
772 stringstream& ss)
773 {
774 assert(paxos->is_plugged());
775
776 dout(10) << __func__ << " id " << id << " uuid " << uuid << dendl;
777
778 string cephx_str = "osd." + stringify(id);
779 string lockbox_str = "client.osd-lockbox." + stringify(uuid);
780
781 if (!cephx_entity.from_str(cephx_str)) {
782 dout(10) << __func__ << " invalid cephx entity '"
783 << cephx_str << "'" << dendl;
784 ss << "invalid cephx key entity '" << cephx_str << "'";
785 return -EINVAL;
786 }
787
788 if (!lockbox_entity.from_str(lockbox_str)) {
789 dout(10) << __func__ << " invalid lockbox entity '"
790 << lockbox_str << "'" << dendl;
791 ss << "invalid lockbox key entity '" << lockbox_str << "'";
792 return -EINVAL;
793 }
794
795 if (!mon->key_server.contains(cephx_entity) &&
796 !mon->key_server.contains(lockbox_entity)) {
797 return -ENOENT;
798 }
799
800 return 0;
801 }
802
803 int AuthMonitor::do_osd_destroy(
804 const EntityName& cephx_entity,
805 const EntityName& lockbox_entity)
806 {
807 assert(paxos->is_plugged());
808
809 dout(10) << __func__ << " cephx " << cephx_entity
810 << " lockbox " << lockbox_entity << dendl;
811
812 bool removed = false;
813
814 int err = remove_entity(cephx_entity);
815 if (err == -ENOENT) {
816 dout(10) << __func__ << " " << cephx_entity << " does not exist" << dendl;
817 } else {
818 removed = true;
819 }
820
821 err = remove_entity(lockbox_entity);
822 if (err == -ENOENT) {
823 dout(10) << __func__ << " " << lockbox_entity << " does not exist" << dendl;
824 } else {
825 removed = true;
826 }
827
828 if (!removed) {
829 dout(10) << __func__ << " entities do not exist -- no-op." << dendl;
830 return 0;
831 }
832
833 // given we have paxos plugged, this will not result in a proposal
834 // being triggered, but it will still be needed so that we get our
835 // pending state encoded into the paxos' pending transaction.
836 propose_pending();
837 return 0;
838 }
839
840 bufferlist _encode_cap(const string& cap)
841 {
842 bufferlist bl;
843 ::encode(cap, bl);
844 return bl;
845 }
846
847 int _create_auth(
848 EntityAuth& auth,
849 const string& key,
850 const map<string,bufferlist>& caps)
851 {
852 if (key.empty())
853 return -EINVAL;
854 try {
855 auth.key.decode_base64(key);
856 } catch (buffer::error& e) {
857 return -EINVAL;
858 }
859 auth.caps = caps;
860 return 0;
861 }
862
863 int AuthMonitor::validate_osd_new(
864 int32_t id,
865 const uuid_d& uuid,
866 const string& cephx_secret,
867 const string& lockbox_secret,
868 auth_entity_t& cephx_entity,
869 auth_entity_t& lockbox_entity,
870 stringstream& ss)
871 {
872
873 dout(10) << __func__ << " osd." << id << " uuid " << uuid << dendl;
874
875 map<string,bufferlist> cephx_caps = {
876 { "osd", _encode_cap("allow *") },
877 { "mon", _encode_cap("allow profile osd") },
878 { "mgr", _encode_cap("allow profile osd") }
879 };
880 map<string,bufferlist> lockbox_caps = {
881 { "mon", _encode_cap("allow command \"config-key get\" "
882 "with key=\"dm-crypt/osd/" +
883 stringify(uuid) +
884 "/luks\"") }
885 };
886
887 bool has_lockbox = !lockbox_secret.empty();
888
889 string cephx_name = "osd." + stringify(id);
890 string lockbox_name = "client.osd-lockbox." + stringify(uuid);
891
892 if (!cephx_entity.name.from_str(cephx_name)) {
893 dout(10) << __func__ << " invalid cephx entity '"
894 << cephx_name << "'" << dendl;
895 ss << "invalid cephx key entity '" << cephx_name << "'";
896 return -EINVAL;
897 }
898
899 if (has_lockbox) {
900 if (!lockbox_entity.name.from_str(lockbox_name)) {
901 dout(10) << __func__ << " invalid cephx lockbox entity '"
902 << lockbox_name << "'" << dendl;
903 ss << "invalid cephx lockbox entity '" << lockbox_name << "'";
904 return -EINVAL;
905 }
906 }
907
908 if (entity_is_pending(cephx_entity.name) ||
909 (has_lockbox && entity_is_pending(lockbox_entity.name))) {
910 // If we have pending entities for either the cephx secret or the
911 // lockbox secret, then our safest bet is to retry the command at
912 // a later time. These entities may be pending because an `osd new`
913 // command has been run (which is unlikely, due to the nature of
914 // the operation, which will force a paxos proposal), or (more likely)
915 // because a competing client created those entities before we handled
916 // the `osd new` command. Regardless, let's wait and see.
917 return -EAGAIN;
918 }
919
920 if (!is_valid_cephx_key(cephx_secret)) {
921 ss << "invalid cephx secret.";
922 return -EINVAL;
923 }
924
925 if (has_lockbox && !is_valid_cephx_key(lockbox_secret)) {
926 ss << "invalid cephx lockbox secret.";
927 return -EINVAL;
928 }
929
930 int err = _create_auth(cephx_entity.auth, cephx_secret, cephx_caps);
931 assert(0 == err);
932
933 bool cephx_is_idempotent = false, lockbox_is_idempotent = false;
934 err = exists_and_matches_entity(cephx_entity, true, ss);
935
936 if (err != -ENOENT) {
937 if (err < 0) {
938 return err;
939 }
940 assert(0 == err);
941 cephx_is_idempotent = true;
942 }
943
944 if (has_lockbox) {
945 err = _create_auth(lockbox_entity.auth, lockbox_secret, lockbox_caps);
946 assert(err == 0);
947 err = exists_and_matches_entity(lockbox_entity, true, ss);
948 if (err != -ENOENT) {
949 if (err < 0) {
950 return err;
951 }
952 assert(0 == err);
953 lockbox_is_idempotent = true;
954 }
955 }
956
957 if (cephx_is_idempotent && (!has_lockbox || lockbox_is_idempotent)) {
958 return EEXIST;
959 }
960
961 return 0;
962 }
963
964 int AuthMonitor::do_osd_new(
965 const auth_entity_t& cephx_entity,
966 const auth_entity_t& lockbox_entity,
967 bool has_lockbox)
968 {
969 assert(paxos->is_plugged());
970
971 dout(10) << __func__ << " cephx " << cephx_entity.name
972 << " lockbox ";
973 if (has_lockbox) {
974 *_dout << lockbox_entity.name;
975 } else {
976 *_dout << "n/a";
977 }
978 *_dout << dendl;
979
980 // we must have validated before reaching this point.
981 // if keys exist, then this means they also match; otherwise we would
982 // have failed before calling this function.
983 bool cephx_exists = mon->key_server.contains(cephx_entity.name);
984
985 if (!cephx_exists) {
986 int err = add_entity(cephx_entity.name, cephx_entity.auth);
987 assert(0 == err);
988 }
989
990 if (has_lockbox &&
991 !mon->key_server.contains(lockbox_entity.name)) {
992 int err = add_entity(lockbox_entity.name, lockbox_entity.auth);
993 assert(0 == err);
994 }
995
996 // given we have paxos plugged, this will not result in a proposal
997 // being triggered, but it will still be needed so that we get our
998 // pending state encoded into the paxos' pending transaction.
999 propose_pending();
1000 return 0;
1001 }
1002
1003 bool AuthMonitor::prepare_command(MonOpRequestRef op)
1004 {
1005 MMonCommand *m = static_cast<MMonCommand*>(op->get_req());
1006 stringstream ss, ds;
1007 bufferlist rdata;
1008 string rs;
1009 int err = -EINVAL;
1010
1011 map<string, cmd_vartype> cmdmap;
1012 if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
1013 // ss has reason for failure
1014 string rs = ss.str();
1015 mon->reply_command(op, -EINVAL, rs, rdata, get_last_committed());
1016 return true;
1017 }
1018
1019 string prefix;
1020 vector<string>caps_vec;
1021 string entity_name;
1022 EntityName entity;
1023
1024 cmd_getval(g_ceph_context, cmdmap, "prefix", prefix);
1025
1026 string format;
1027 cmd_getval(g_ceph_context, cmdmap, "format", format, string("plain"));
1028 boost::scoped_ptr<Formatter> f(Formatter::create(format));
1029
1030 MonSession *session = m->get_session();
1031 if (!session) {
1032 mon->reply_command(op, -EACCES, "access denied", rdata, get_last_committed());
1033 return true;
1034 }
1035
1036 cmd_getval(g_ceph_context, cmdmap, "caps", caps_vec);
1037 if ((caps_vec.size() % 2) != 0) {
1038 ss << "bad capabilities request; odd number of arguments";
1039 err = -EINVAL;
1040 goto done;
1041 }
1042
1043 cmd_getval(g_ceph_context, cmdmap, "entity", entity_name);
1044 if (!entity_name.empty() && !entity.from_str(entity_name)) {
1045 ss << "bad entity name";
1046 err = -EINVAL;
1047 goto done;
1048 }
1049
1050 if (prefix == "auth import") {
1051 bufferlist bl = m->get_data();
1052 if (bl.length() == 0) {
1053 ss << "auth import: no data supplied";
1054 getline(ss, rs);
1055 mon->reply_command(op, -EINVAL, rs, get_last_committed());
1056 return true;
1057 }
1058 bufferlist::iterator iter = bl.begin();
1059 KeyRing keyring;
1060 try {
1061 ::decode(keyring, iter);
1062 } catch (const buffer::error &ex) {
1063 ss << "error decoding keyring" << " " << ex.what();
1064 err = -EINVAL;
1065 goto done;
1066 }
1067 err = import_keyring(keyring);
1068 if (err < 0) {
1069 ss << "auth import: no caps supplied";
1070 getline(ss, rs);
1071 mon->reply_command(op, -EINVAL, rs, get_last_committed());
1072 return true;
1073 }
1074 ss << "imported keyring";
1075 getline(ss, rs);
1076 err = 0;
1077 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1078 get_last_committed() + 1));
1079 return true;
1080 } else if (prefix == "auth add" && !entity_name.empty()) {
1081 /* expected behavior:
1082 * - if command reproduces current state, return 0.
1083 * - if command adds brand new entity, handle it.
1084 * - if command adds new state to existing entity, return error.
1085 */
1086 KeyServerData::Incremental auth_inc;
1087 auth_inc.name = entity;
1088 bufferlist bl = m->get_data();
1089 bool has_keyring = (bl.length() > 0);
1090 map<string,bufferlist> new_caps;
1091
1092 KeyRing new_keyring;
1093 if (has_keyring) {
1094 bufferlist::iterator iter = bl.begin();
1095 try {
1096 ::decode(new_keyring, iter);
1097 } catch (const buffer::error &ex) {
1098 ss << "error decoding keyring";
1099 err = -EINVAL;
1100 goto done;
1101 }
1102 }
1103
1104 // are we about to have it?
1105 if (entity_is_pending(entity)) {
1106 wait_for_finished_proposal(op,
1107 new Monitor::C_Command(mon, op, 0, rs, get_last_committed() + 1));
1108 return true;
1109 }
1110
1111 // build new caps from provided arguments (if available)
1112 for (vector<string>::iterator it = caps_vec.begin();
1113 it != caps_vec.end() && (it + 1) != caps_vec.end();
1114 it += 2) {
1115 string sys = *it;
1116 bufferlist cap;
1117 ::encode(*(it+1), cap);
1118 new_caps[sys] = cap;
1119 }
1120
1121 // pull info out of provided keyring
1122 EntityAuth new_inc;
1123 if (has_keyring) {
1124 if (!new_keyring.get_auth(auth_inc.name, new_inc)) {
1125 ss << "key for " << auth_inc.name
1126 << " not found in provided keyring";
1127 err = -EINVAL;
1128 goto done;
1129 }
1130 if (!new_caps.empty() && !new_inc.caps.empty()) {
1131 ss << "caps cannot be specified both in keyring and in command";
1132 err = -EINVAL;
1133 goto done;
1134 }
1135 if (new_caps.empty()) {
1136 new_caps = new_inc.caps;
1137 }
1138 }
1139
1140 err = exists_and_matches_entity(auth_inc.name, new_inc,
1141 new_caps, has_keyring, ss);
1142 // if entity/key/caps do not exist in the keyring, just fall through
1143 // and add the entity; otherwise, make sure everything matches (in
1144 // which case it's a no-op), because if not we must fail.
1145 if (err != -ENOENT) {
1146 if (err < 0) {
1147 goto done;
1148 }
1149 // no-op.
1150 assert(err == 0);
1151 goto done;
1152 }
1153 err = 0;
1154
1155 // okay, add it.
1156 if (!has_keyring) {
1157 dout(10) << "AuthMonitor::prepare_command generating random key for "
1158 << auth_inc.name << dendl;
1159 new_inc.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1160 }
1161 new_inc.caps = new_caps;
1162
1163 err = add_entity(auth_inc.name, new_inc);
1164 assert(err == 0);
1165
1166 ss << "added key for " << auth_inc.name;
1167 getline(ss, rs);
1168 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1169 get_last_committed() + 1));
1170 return true;
1171 } else if ((prefix == "auth get-or-create-key" ||
1172 prefix == "auth get-or-create") &&
1173 !entity_name.empty()) {
1174 // auth get-or-create <name> [mon osdcapa osd osdcapb ...]
1175
1176 if (!valid_caps(caps_vec, &ss)) {
1177 err = -EINVAL;
1178 goto done;
1179 }
1180
1181 // Parse the list of caps into a map
1182 std::map<std::string, bufferlist> wanted_caps;
1183 for (vector<string>::const_iterator it = caps_vec.begin();
1184 it != caps_vec.end() && (it + 1) != caps_vec.end();
1185 it += 2) {
1186 const std::string &sys = *it;
1187 bufferlist cap;
1188 ::encode(*(it+1), cap);
1189 wanted_caps[sys] = cap;
1190 }
1191
1192 // do we have it?
1193 EntityAuth entity_auth;
1194 if (mon->key_server.get_auth(entity, entity_auth)) {
1195 for (const auto &sys_cap : wanted_caps) {
1196 if (entity_auth.caps.count(sys_cap.first) == 0 ||
1197 !entity_auth.caps[sys_cap.first].contents_equal(sys_cap.second)) {
1198 ss << "key for " << entity << " exists but cap " << sys_cap.first
1199 << " does not match";
1200 err = -EINVAL;
1201 goto done;
1202 }
1203 }
1204
1205 if (prefix == "auth get-or-create-key") {
1206 if (f) {
1207 entity_auth.key.encode_formatted("auth", f.get(), rdata);
1208 } else {
1209 ds << entity_auth.key;
1210 }
1211 } else {
1212 KeyRing kr;
1213 kr.add(entity, entity_auth.key);
1214 if (f) {
1215 kr.set_caps(entity, entity_auth.caps);
1216 kr.encode_formatted("auth", f.get(), rdata);
1217 } else {
1218 kr.encode_plaintext(rdata);
1219 }
1220 }
1221 err = 0;
1222 goto done;
1223 }
1224
1225 // ...or are we about to?
1226 for (vector<Incremental>::iterator p = pending_auth.begin();
1227 p != pending_auth.end();
1228 ++p) {
1229 if (p->inc_type == AUTH_DATA) {
1230 KeyServerData::Incremental auth_inc;
1231 bufferlist::iterator q = p->auth_data.begin();
1232 ::decode(auth_inc, q);
1233 if (auth_inc.op == KeyServerData::AUTH_INC_ADD &&
1234 auth_inc.name == entity) {
1235 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1236 get_last_committed() + 1));
1237 return true;
1238 }
1239 }
1240 }
1241
1242 // create it
1243 KeyServerData::Incremental auth_inc;
1244 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1245 auth_inc.name = entity;
1246 auth_inc.auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1247 auth_inc.auth.caps = wanted_caps;
1248
1249 push_cephx_inc(auth_inc);
1250
1251 if (prefix == "auth get-or-create-key") {
1252 if (f) {
1253 auth_inc.auth.key.encode_formatted("auth", f.get(), rdata);
1254 } else {
1255 ds << auth_inc.auth.key;
1256 }
1257 } else {
1258 KeyRing kr;
1259 kr.add(entity, auth_inc.auth.key);
1260 if (f) {
1261 kr.set_caps(entity, wanted_caps);
1262 kr.encode_formatted("auth", f.get(), rdata);
1263 } else {
1264 kr.encode_plaintext(rdata);
1265 }
1266 }
1267
1268 rdata.append(ds);
1269 getline(ss, rs);
1270 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs, rdata,
1271 get_last_committed() + 1));
1272 return true;
1273 } else if (prefix == "auth caps" && !entity_name.empty()) {
1274 KeyServerData::Incremental auth_inc;
1275 auth_inc.name = entity;
1276 if (!mon->key_server.get_auth(auth_inc.name, auth_inc.auth)) {
1277 ss << "couldn't find entry " << auth_inc.name;
1278 err = -ENOENT;
1279 goto done;
1280 }
1281
1282 if (!valid_caps(caps_vec, &ss)) {
1283 err = -EINVAL;
1284 goto done;
1285 }
1286
1287 map<string,bufferlist> newcaps;
1288 for (vector<string>::iterator it = caps_vec.begin();
1289 it != caps_vec.end(); it += 2)
1290 ::encode(*(it+1), newcaps[*it]);
1291
1292 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1293 auth_inc.auth.caps = newcaps;
1294 push_cephx_inc(auth_inc);
1295
1296 ss << "updated caps for " << auth_inc.name;
1297 getline(ss, rs);
1298 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1299 get_last_committed() + 1));
1300 return true;
1301 } else if ((prefix == "auth del" || prefix == "auth rm") &&
1302 !entity_name.empty()) {
1303 KeyServerData::Incremental auth_inc;
1304 auth_inc.name = entity;
1305 if (!mon->key_server.contains(auth_inc.name)) {
1306 ss << "entity " << entity << " does not exist";
1307 err = 0;
1308 goto done;
1309 }
1310 auth_inc.op = KeyServerData::AUTH_INC_DEL;
1311 push_cephx_inc(auth_inc);
1312
1313 ss << "updated";
1314 getline(ss, rs);
1315 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1316 get_last_committed() + 1));
1317 return true;
1318 }
1319 done:
1320 rdata.append(ds);
1321 getline(ss, rs, '\0');
1322 mon->reply_command(op, err, rs, rdata, get_last_committed());
1323 return false;
1324 }
1325
1326 bool AuthMonitor::prepare_global_id(MonOpRequestRef op)
1327 {
1328 dout(10) << "AuthMonitor::prepare_global_id" << dendl;
1329 increase_max_global_id();
1330
1331 return true;
1332 }
1333
1334 void AuthMonitor::upgrade_format()
1335 {
1336 unsigned int current = 2;
1337 if (!mon->get_quorum_mon_features().contains_all(
1338 ceph::features::mon::FEATURE_LUMINOUS)) {
1339 current = 1;
1340 }
1341 if (format_version >= current) {
1342 dout(20) << __func__ << " format " << format_version << " is current" << dendl;
1343 return;
1344 }
1345
1346 bool changed = false;
1347 if (format_version == 0) {
1348 dout(1) << __func__ << " upgrading from format 0 to 1" << dendl;
1349 map<EntityName, EntityAuth>::iterator p;
1350 for (p = mon->key_server.secrets_begin();
1351 p != mon->key_server.secrets_end();
1352 ++p) {
1353 // grab mon caps, if any
1354 string mon_caps;
1355 if (p->second.caps.count("mon") == 0)
1356 continue;
1357 try {
1358 bufferlist::iterator it = p->second.caps["mon"].begin();
1359 ::decode(mon_caps, it);
1360 }
1361 catch (buffer::error) {
1362 dout(10) << __func__ << " unable to parse mon cap for "
1363 << p->first << dendl;
1364 continue;
1365 }
1366
1367 string n = p->first.to_str();
1368 string new_caps;
1369
1370 // set daemon profiles
1371 if ((p->first.is_osd() || p->first.is_mds()) &&
1372 mon_caps == "allow rwx") {
1373 new_caps = string("allow profile ") + string(p->first.get_type_name());
1374 }
1375
1376 // update bootstrap keys
1377 if (n == "client.bootstrap-osd") {
1378 new_caps = "allow profile bootstrap-osd";
1379 }
1380 if (n == "client.bootstrap-mds") {
1381 new_caps = "allow profile bootstrap-mds";
1382 }
1383
1384 if (new_caps.length() > 0) {
1385 dout(5) << __func__ << " updating " << p->first << " mon cap from "
1386 << mon_caps << " to " << new_caps << dendl;
1387
1388 bufferlist bl;
1389 ::encode(new_caps, bl);
1390
1391 KeyServerData::Incremental auth_inc;
1392 auth_inc.name = p->first;
1393 auth_inc.auth = p->second;
1394 auth_inc.auth.caps["mon"] = bl;
1395 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1396 push_cephx_inc(auth_inc);
1397 changed = true;
1398 }
1399 }
1400 }
1401
1402 if (format_version == 1) {
1403 dout(1) << __func__ << " upgrading from format 1 to 2" << dendl;
1404 map<EntityName, EntityAuth>::iterator p;
1405 for (p = mon->key_server.secrets_begin();
1406 p != mon->key_server.secrets_end();
1407 ++p) {
1408 string n = p->first.to_str();
1409
1410 string newcap;
1411 if (n == "client.admin") {
1412 // admin gets it all
1413 newcap = "allow *";
1414 } else if (n.find("osd.") == 0 ||
1415 n.find("mds.") == 0 ||
1416 n.find("mon.") == 0) {
1417 // daemons follow their profile
1418 string type = n.substr(0, 3);
1419 newcap = "allow profile " + type;
1420 } else if (p->second.caps.count("mon")) {
1421 // if there are any mon caps, give them 'r' mgr caps
1422 newcap = "allow r";
1423 }
1424
1425 if (newcap.length() > 0) {
1426 dout(5) << " giving " << n << " mgr '" << newcap << "'" << dendl;
1427 bufferlist bl;
1428 ::encode(newcap, bl);
1429
1430 KeyServerData::Incremental auth_inc;
1431 auth_inc.name = p->first;
1432 auth_inc.auth = p->second;
1433 auth_inc.auth.caps["mgr"] = bl;
1434 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1435 push_cephx_inc(auth_inc);
1436 }
1437
1438 if (n.find("mgr.") == 0 &&
1439 p->second.caps.count("mon")) {
1440 // the kraken ceph-mgr@.service set the mon cap to 'allow *'.
1441 auto blp = p->second.caps["mon"].begin();
1442 string oldcaps;
1443 ::decode(oldcaps, blp);
1444 if (oldcaps == "allow *") {
1445 dout(5) << " fixing " << n << " mon cap to 'allow profile mgr'"
1446 << dendl;
1447 bufferlist bl;
1448 ::encode("allow profile mgr", bl);
1449 KeyServerData::Incremental auth_inc;
1450 auth_inc.name = p->first;
1451 auth_inc.auth = p->second;
1452 auth_inc.auth.caps["mon"] = bl;
1453 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1454 push_cephx_inc(auth_inc);
1455 }
1456 }
1457 }
1458
1459 // add bootstrap key
1460 {
1461 KeyServerData::Incremental auth_inc;
1462 bool r = auth_inc.name.from_str("client.bootstrap-mgr");
1463 assert(r);
1464 ::encode("allow profile bootstrap-mgr", auth_inc.auth.caps["mon"]);
1465 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1466 push_cephx_inc(auth_inc);
1467 }
1468 changed = true;
1469 }
1470
1471 if (changed) {
1472 // note new format
1473 dout(10) << __func__ << " proposing update from format " << format_version
1474 << " -> " << current << dendl;
1475 format_version = current;
1476 propose_pending();
1477 }
1478 }
1479
1480 void AuthMonitor::dump_info(Formatter *f)
1481 {
1482 /*** WARNING: do not include any privileged information here! ***/
1483 f->open_object_section("auth");
1484 f->dump_unsigned("first_committed", get_first_committed());
1485 f->dump_unsigned("last_committed", get_last_committed());
1486 f->dump_unsigned("num_secrets", mon->key_server.get_num_secrets());
1487 f->close_section();
1488 }