]> git.proxmox.com Git - ceph.git/blob - ceph/src/mon/AuthMonitor.cc
add stop-gap to fix compat with CPUs not supporting SSE 4.1
[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/OSDMonitor.h"
21 #include "mon/MDSMonitor.h"
22 #include "mon/ConfigMonitor.h"
23
24 #include "messages/MMonCommand.h"
25 #include "messages/MAuth.h"
26 #include "messages/MAuthReply.h"
27 #include "messages/MMonGlobalID.h"
28 #include "messages/MMonUsedPendingKeys.h"
29 #include "msg/Messenger.h"
30
31 #include "auth/AuthServiceHandler.h"
32 #include "auth/KeyRing.h"
33 #include "include/stringify.h"
34 #include "include/ceph_assert.h"
35
36 #include "mds/MDSAuthCaps.h"
37 #include "mgr/MgrCap.h"
38 #include "osd/OSDCap.h"
39
40 #define dout_subsys ceph_subsys_mon
41 #undef dout_prefix
42 #define dout_prefix _prefix(_dout, mon, get_last_committed())
43 using namespace TOPNSPC::common;
44
45 using std::cerr;
46 using std::cout;
47 using std::dec;
48 using std::hex;
49 using std::list;
50 using std::map;
51 using std::make_pair;
52 using std::ostream;
53 using std::ostringstream;
54 using std::pair;
55 using std::set;
56 using std::setfill;
57 using std::string;
58 using std::stringstream;
59 using std::to_string;
60 using std::vector;
61 using std::unique_ptr;
62
63 using ceph::bufferlist;
64 using ceph::decode;
65 using ceph::encode;
66 using ceph::Formatter;
67 using ceph::JSONFormatter;
68 using ceph::make_message;
69 using ceph::mono_clock;
70 using ceph::mono_time;
71 using ceph::timespan_str;
72 static ostream& _prefix(std::ostream *_dout, Monitor &mon, version_t v) {
73 return *_dout << "mon." << mon.name << "@" << mon.rank
74 << "(" << mon.get_state_name()
75 << ").auth v" << v << " ";
76 }
77
78 ostream& operator<<(ostream &out, const AuthMonitor &pm)
79 {
80 return out << "auth";
81 }
82
83 bool AuthMonitor::check_rotate()
84 {
85 KeyServerData::Incremental rot_inc;
86 rot_inc.op = KeyServerData::AUTH_INC_SET_ROTATING;
87 if (mon.key_server.prepare_rotating_update(rot_inc.rotating_bl)) {
88 dout(10) << __func__ << " updating rotating" << dendl;
89 push_cephx_inc(rot_inc);
90 return true;
91 }
92 return false;
93 }
94
95 void AuthMonitor::process_used_pending_keys(
96 const std::map<EntityName,CryptoKey>& used_pending_keys)
97 {
98 for (auto& [name, used_key] : used_pending_keys) {
99 dout(10) << __func__ << " used pending_key for " << name << dendl;
100 KeyServerData::Incremental inc;
101 inc.op = KeyServerData::AUTH_INC_ADD;
102 inc.name = name;
103
104 mon.key_server.get_auth(name, inc.auth);
105 for (auto& p : pending_auth) {
106 if (p.inc_type == AUTH_DATA) {
107 KeyServerData::Incremental auth_inc;
108 auto q = p.auth_data.cbegin();
109 decode(auth_inc, q);
110 if (auth_inc.op == KeyServerData::AUTH_INC_ADD &&
111 auth_inc.name == name) {
112 dout(10) << __func__ << " starting with pending uncommitted" << dendl;
113 inc.auth = auth_inc.auth;
114 }
115 }
116 }
117 if (stringify(inc.auth.pending_key) == stringify(used_key)) {
118 dout(10) << __func__ << " committing pending_key -> key for "
119 << name << dendl;
120 inc.auth.key = inc.auth.pending_key;
121 inc.auth.pending_key.clear();
122 push_cephx_inc(inc);
123 }
124 }
125 }
126
127 /*
128 Tick function to update the map based on performance every N seconds
129 */
130
131 void AuthMonitor::tick()
132 {
133 if (!is_active()) return;
134
135 dout(10) << *this << dendl;
136
137 // increase global_id?
138 bool propose = false;
139 bool increase;
140 {
141 std::lock_guard l(mon.auth_lock);
142 increase = _should_increase_max_global_id();
143 }
144 if (increase) {
145 if (mon.is_leader()) {
146 increase_max_global_id();
147 propose = true;
148 } else {
149 dout(10) << __func__ << "requesting more ids from leader" << dendl;
150 MMonGlobalID *req = new MMonGlobalID();
151 req->old_max_id = max_global_id;
152 mon.send_mon_message(req, mon.get_leader());
153 }
154 }
155
156 if (mon.monmap->min_mon_release >= ceph_release_t::quincy) {
157 auto used_pending_keys = mon.key_server.get_used_pending_keys();
158 if (!used_pending_keys.empty()) {
159 dout(10) << __func__ << " " << used_pending_keys.size() << " used pending_keys"
160 << dendl;
161 if (mon.is_leader()) {
162 process_used_pending_keys(used_pending_keys);
163 propose = true;
164 } else {
165 MMonUsedPendingKeys *req = new MMonUsedPendingKeys();
166 req->used_pending_keys = used_pending_keys;
167 mon.send_mon_message(req, mon.get_leader());
168 }
169 }
170 }
171
172 if (!mon.is_leader()) {
173 return;
174 }
175
176 if (check_rotate()) {
177 propose = true;
178 }
179
180 if (propose) {
181 propose_pending();
182 }
183 }
184
185 void AuthMonitor::on_active()
186 {
187 dout(10) << "AuthMonitor::on_active()" << dendl;
188
189 if (!mon.is_leader())
190 return;
191
192 mon.key_server.start_server();
193 mon.key_server.clear_used_pending_keys();
194
195 if (is_writeable()) {
196 bool propose = false;
197 if (check_rotate()) {
198 propose = true;
199 }
200 bool increase;
201 {
202 std::lock_guard l(mon.auth_lock);
203 increase = _should_increase_max_global_id();
204 }
205 if (increase) {
206 increase_max_global_id();
207 propose = true;
208 }
209 if (propose) {
210 propose_pending();
211 }
212 }
213 }
214
215 bufferlist _encode_cap(const string& cap)
216 {
217 bufferlist bl;
218 encode(cap, bl);
219 return bl;
220 }
221
222 void AuthMonitor::get_initial_keyring(KeyRing *keyring)
223 {
224 dout(10) << __func__ << dendl;
225 ceph_assert(keyring != nullptr);
226
227 bufferlist bl;
228 int ret = mon.store->get("mkfs", "keyring", bl);
229 if (ret == -ENOENT) {
230 return;
231 }
232 // fail hard only if there's an error we're not expecting to see
233 ceph_assert(ret == 0);
234
235 auto p = bl.cbegin();
236 decode(*keyring, p);
237 }
238
239 void _generate_bootstrap_keys(
240 list<pair<EntityName,EntityAuth> >* auth_lst)
241 {
242 ceph_assert(auth_lst != nullptr);
243
244 map<string,map<string,bufferlist> > bootstrap = {
245 { "admin", {
246 { "mon", _encode_cap("allow *") },
247 { "osd", _encode_cap("allow *") },
248 { "mds", _encode_cap("allow *") },
249 { "mgr", _encode_cap("allow *") }
250 } },
251 { "bootstrap-osd", {
252 { "mon", _encode_cap("allow profile bootstrap-osd") }
253 } },
254 { "bootstrap-rgw", {
255 { "mon", _encode_cap("allow profile bootstrap-rgw") }
256 } },
257 { "bootstrap-mds", {
258 { "mon", _encode_cap("allow profile bootstrap-mds") }
259 } },
260 { "bootstrap-mgr", {
261 { "mon", _encode_cap("allow profile bootstrap-mgr") }
262 } },
263 { "bootstrap-rbd", {
264 { "mon", _encode_cap("allow profile bootstrap-rbd") }
265 } },
266 { "bootstrap-rbd-mirror", {
267 { "mon", _encode_cap("allow profile bootstrap-rbd-mirror") }
268 } }
269 };
270
271 for (auto &p : bootstrap) {
272 EntityName name;
273 name.from_str("client." + p.first);
274 EntityAuth auth;
275 auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
276 auth.caps = p.second;
277
278 auth_lst->push_back(make_pair(name, auth));
279 }
280 }
281
282 void AuthMonitor::create_initial_keys(KeyRing *keyring)
283 {
284 dout(10) << __func__ << " with keyring" << dendl;
285 ceph_assert(keyring != nullptr);
286
287 list<pair<EntityName,EntityAuth> > auth_lst;
288 _generate_bootstrap_keys(&auth_lst);
289
290 for (auto &p : auth_lst) {
291 if (keyring->exists(p.first)) {
292 continue;
293 }
294 keyring->add(p.first, p.second);
295 }
296 }
297
298 void AuthMonitor::create_initial()
299 {
300 dout(10) << "create_initial -- creating initial map" << dendl;
301
302 // initialize rotating keys
303 mon.key_server.clear_secrets();
304 check_rotate();
305 ceph_assert(pending_auth.size() == 1);
306
307 if (mon.is_keyring_required()) {
308 KeyRing keyring;
309 // attempt to obtain an existing mkfs-time keyring
310 get_initial_keyring(&keyring);
311 // create missing keys in the keyring
312 create_initial_keys(&keyring);
313 // import the resulting keyring
314 import_keyring(keyring);
315 }
316
317 max_global_id = MIN_GLOBAL_ID;
318
319 Incremental inc;
320 inc.inc_type = GLOBAL_ID;
321 inc.max_global_id = max_global_id;
322 pending_auth.push_back(inc);
323
324 format_version = 3;
325 }
326
327 void AuthMonitor::update_from_paxos(bool *need_bootstrap)
328 {
329 dout(10) << __func__ << dendl;
330 load_health();
331
332 version_t version = get_last_committed();
333 version_t keys_ver = mon.key_server.get_ver();
334 if (version == keys_ver)
335 return;
336 ceph_assert(version > keys_ver);
337
338 version_t latest_full = get_version_latest_full();
339
340 dout(10) << __func__ << " version " << version << " keys ver " << keys_ver
341 << " latest " << latest_full << dendl;
342
343 if ((latest_full > 0) && (latest_full > keys_ver)) {
344 bufferlist latest_bl;
345 int err = get_version_full(latest_full, latest_bl);
346 ceph_assert(err == 0);
347 ceph_assert(latest_bl.length() != 0);
348 dout(7) << __func__ << " loading summary e " << latest_full << dendl;
349 dout(7) << __func__ << " latest length " << latest_bl.length() << dendl;
350 auto p = latest_bl.cbegin();
351 __u8 struct_v;
352 decode(struct_v, p);
353 decode(max_global_id, p);
354 decode(mon.key_server, p);
355 mon.key_server.set_ver(latest_full);
356 keys_ver = latest_full;
357 }
358
359 dout(10) << __func__ << " key server version " << mon.key_server.get_ver() << dendl;
360
361 // walk through incrementals
362 while (version > keys_ver) {
363 bufferlist bl;
364 int ret = get_version(keys_ver+1, bl);
365 ceph_assert(ret == 0);
366 ceph_assert(bl.length());
367
368 // reset if we are moving to initial state. we will normally have
369 // keys in here temporarily for bootstrapping that we need to
370 // clear out.
371 if (keys_ver == 0)
372 mon.key_server.clear_secrets();
373
374 dout(20) << __func__ << " walking through version " << (keys_ver+1)
375 << " len " << bl.length() << dendl;
376
377 auto p = bl.cbegin();
378 __u8 v;
379 decode(v, p);
380 while (!p.end()) {
381 Incremental inc;
382 decode(inc, p);
383 switch (inc.inc_type) {
384 case GLOBAL_ID:
385 max_global_id = inc.max_global_id;
386 break;
387
388 case AUTH_DATA:
389 {
390 KeyServerData::Incremental auth_inc;
391 auto iter = inc.auth_data.cbegin();
392 decode(auth_inc, iter);
393 mon.key_server.apply_data_incremental(auth_inc);
394 break;
395 }
396 }
397 }
398
399 keys_ver++;
400 mon.key_server.set_ver(keys_ver);
401
402 if (keys_ver == 1 && mon.is_keyring_required()) {
403 auto t(std::make_shared<MonitorDBStore::Transaction>());
404 t->erase("mkfs", "keyring");
405 mon.store->apply_transaction(t);
406 }
407 }
408
409 {
410 std::lock_guard l(mon.auth_lock);
411 if (last_allocated_id == 0) {
412 last_allocated_id = max_global_id;
413 dout(10) << __func__ << " last_allocated_id initialized to "
414 << max_global_id << dendl;
415 }
416 }
417
418 dout(10) << __func__ << " max_global_id=" << max_global_id
419 << " format_version " << format_version
420 << dendl;
421
422 mon.key_server.dump();
423 }
424
425 bool AuthMonitor::_should_increase_max_global_id()
426 {
427 ceph_assert(ceph_mutex_is_locked(mon.auth_lock));
428 auto num_prealloc = g_conf()->mon_globalid_prealloc;
429 if (max_global_id < num_prealloc ||
430 (last_allocated_id + 1) >= max_global_id - num_prealloc / 2) {
431 return true;
432 }
433 return false;
434 }
435
436 void AuthMonitor::increase_max_global_id()
437 {
438 ceph_assert(mon.is_leader());
439
440 Incremental inc;
441 inc.inc_type = GLOBAL_ID;
442 inc.max_global_id = max_global_id + g_conf()->mon_globalid_prealloc;
443 dout(10) << "increasing max_global_id to " << inc.max_global_id << dendl;
444 pending_auth.push_back(inc);
445 }
446
447 bool AuthMonitor::should_propose(double& delay)
448 {
449 return (!pending_auth.empty());
450 }
451
452 void AuthMonitor::create_pending()
453 {
454 pending_auth.clear();
455 dout(10) << "create_pending v " << (get_last_committed() + 1) << dendl;
456 }
457
458 void AuthMonitor::encode_pending(MonitorDBStore::TransactionRef t)
459 {
460 dout(10) << __func__ << " v " << (get_last_committed() + 1) << dendl;
461
462 bufferlist bl;
463
464 __u8 v = 1;
465 encode(v, bl);
466 vector<Incremental>::iterator p;
467 for (p = pending_auth.begin(); p != pending_auth.end(); ++p)
468 p->encode(bl, mon.get_quorum_con_features());
469
470 version_t version = get_last_committed() + 1;
471 put_version(t, version, bl);
472 put_last_committed(t, version);
473
474 // health
475 health_check_map_t next;
476 map<string,list<string>> bad_detail; // entity -> details
477 for (auto i = mon.key_server.secrets_begin();
478 i != mon.key_server.secrets_end();
479 ++i) {
480 for (auto& p : i->second.caps) {
481 ostringstream ss;
482 if (!valid_caps(p.first, p.second, &ss)) {
483 ostringstream ss2;
484 ss2 << i->first << " " << ss.str();
485 bad_detail[i->first.to_str()].push_back(ss2.str());
486 }
487 }
488 }
489 for (auto& inc : pending_auth) {
490 if (inc.inc_type == AUTH_DATA) {
491 KeyServerData::Incremental auth_inc;
492 auto iter = inc.auth_data.cbegin();
493 decode(auth_inc, iter);
494 if (auth_inc.op == KeyServerData::AUTH_INC_DEL) {
495 bad_detail.erase(auth_inc.name.to_str());
496 } else if (auth_inc.op == KeyServerData::AUTH_INC_ADD) {
497 for (auto& p : auth_inc.auth.caps) {
498 ostringstream ss;
499 if (!valid_caps(p.first, p.second, &ss)) {
500 ostringstream ss2;
501 ss2 << auth_inc.name << " " << ss.str();
502 bad_detail[auth_inc.name.to_str()].push_back(ss2.str());
503 }
504 }
505 }
506 }
507 }
508 if (bad_detail.size()) {
509 ostringstream ss;
510 ss << bad_detail.size() << " auth entities have invalid capabilities";
511 health_check_t *check = &next.add("AUTH_BAD_CAPS", HEALTH_ERR, ss.str(),
512 bad_detail.size());
513 for (auto& i : bad_detail) {
514 for (auto& j : i.second) {
515 check->detail.push_back(j);
516 }
517 }
518 }
519 encode_health(next, t);
520 }
521
522 void AuthMonitor::encode_full(MonitorDBStore::TransactionRef t)
523 {
524 version_t version = mon.key_server.get_ver();
525 // do not stash full version 0 as it will never be removed nor read
526 if (version == 0)
527 return;
528
529 dout(10) << __func__ << " auth v " << version << dendl;
530 ceph_assert(get_last_committed() == version);
531
532 bufferlist full_bl;
533 std::scoped_lock l{mon.key_server.get_lock()};
534 dout(20) << __func__ << " key server has "
535 << (mon.key_server.has_secrets() ? "" : "no ")
536 << "secrets!" << dendl;
537 __u8 v = 1;
538 encode(v, full_bl);
539 encode(max_global_id, full_bl);
540 encode(mon.key_server, full_bl);
541
542 put_version_full(t, version, full_bl);
543 put_version_latest_full(t, version);
544 }
545
546 version_t AuthMonitor::get_trim_to() const
547 {
548 unsigned max = g_conf()->paxos_max_join_drift * 2;
549 version_t version = get_last_committed();
550 if (mon.is_leader() && (version > max))
551 return version - max;
552 return 0;
553 }
554
555 bool AuthMonitor::preprocess_query(MonOpRequestRef op)
556 {
557 auto m = op->get_req<PaxosServiceMessage>();
558 dout(10) << "preprocess_query " << *m << " from " << m->get_orig_source_inst() << dendl;
559 switch (m->get_type()) {
560 case MSG_MON_COMMAND:
561 try {
562 return preprocess_command(op);
563 } catch (const bad_cmd_get& e) {
564 bufferlist bl;
565 mon.reply_command(op, -EINVAL, e.what(), bl, get_last_committed());
566 return true;
567 }
568
569 case CEPH_MSG_AUTH:
570 return prep_auth(op, false);
571
572 case MSG_MON_GLOBAL_ID:
573 return false;
574
575 case MSG_MON_USED_PENDING_KEYS:
576 return false;
577
578 default:
579 ceph_abort();
580 return true;
581 }
582 }
583
584 bool AuthMonitor::prepare_update(MonOpRequestRef op)
585 {
586 auto m = op->get_req<PaxosServiceMessage>();
587 dout(10) << "prepare_update " << *m << " from " << m->get_orig_source_inst() << dendl;
588 switch (m->get_type()) {
589 case MSG_MON_COMMAND:
590 try {
591 return prepare_command(op);
592 } catch (const bad_cmd_get& e) {
593 bufferlist bl;
594 mon.reply_command(op, -EINVAL, e.what(), bl, get_last_committed());
595 return true;
596 }
597 case MSG_MON_GLOBAL_ID:
598 return prepare_global_id(op);
599 case MSG_MON_USED_PENDING_KEYS:
600 return prepare_used_pending_keys(op);
601 case CEPH_MSG_AUTH:
602 return prep_auth(op, true);
603 default:
604 ceph_abort();
605 return false;
606 }
607 }
608
609 void AuthMonitor::_set_mon_num_rank(int num, int rank)
610 {
611 dout(10) << __func__ << " num " << num << " rank " << rank << dendl;
612 ceph_assert(ceph_mutex_is_locked(mon.auth_lock));
613 mon_num = num;
614 mon_rank = rank;
615 }
616
617 uint64_t AuthMonitor::_assign_global_id()
618 {
619 ceph_assert(ceph_mutex_is_locked(mon.auth_lock));
620 if (mon_num < 1 || mon_rank < 0) {
621 dout(10) << __func__ << " inactive (num_mon " << mon_num
622 << " rank " << mon_rank << ")" << dendl;
623 return 0;
624 }
625 if (!last_allocated_id) {
626 dout(10) << __func__ << " last_allocated_id == 0" << dendl;
627 return 0;
628 }
629
630 uint64_t id = last_allocated_id + 1;
631 int remainder = id % mon_num;
632 if (remainder) {
633 remainder = mon_num - remainder;
634 }
635 id += remainder + mon_rank;
636
637 if (id >= max_global_id) {
638 dout(10) << __func__ << " failed (max " << max_global_id << ")" << dendl;
639 return 0;
640 }
641
642 last_allocated_id = id;
643 dout(10) << __func__ << " " << id << " (max " << max_global_id << ")"
644 << dendl;
645 return id;
646 }
647
648 uint64_t AuthMonitor::assign_global_id(bool should_increase_max)
649 {
650 uint64_t id;
651 {
652 std::lock_guard l(mon.auth_lock);
653 id =_assign_global_id();
654 if (should_increase_max) {
655 should_increase_max = _should_increase_max_global_id();
656 }
657 }
658 if (mon.is_leader() &&
659 should_increase_max) {
660 increase_max_global_id();
661 }
662 return id;
663 }
664
665 bool AuthMonitor::prep_auth(MonOpRequestRef op, bool paxos_writable)
666 {
667 auto m = op->get_req<MAuth>();
668 dout(10) << "prep_auth() blob_size=" << m->get_auth_payload().length() << dendl;
669
670 MonSession *s = op->get_session();
671 if (!s) {
672 dout(10) << "no session, dropping" << dendl;
673 return true;
674 }
675
676 int ret = 0;
677 MAuthReply *reply;
678 bufferlist response_bl;
679 auto indata = m->auth_payload.cbegin();
680 __u32 proto = m->protocol;
681 bool start = false;
682 bool finished = false;
683 EntityName entity_name;
684 bool is_new_global_id = false;
685
686 // set up handler?
687 if (m->protocol == 0 && !s->auth_handler) {
688 set<__u32> supported;
689
690 try {
691 __u8 struct_v = 1;
692 decode(struct_v, indata);
693 decode(supported, indata);
694 decode(entity_name, indata);
695 decode(s->con->peer_global_id, indata);
696 } catch (const ceph::buffer::error &e) {
697 dout(10) << "failed to decode initial auth message" << dendl;
698 ret = -EINVAL;
699 goto reply;
700 }
701
702 // do we require cephx signatures?
703
704 if (!m->get_connection()->has_feature(CEPH_FEATURE_MSG_AUTH)) {
705 if (entity_name.get_type() == CEPH_ENTITY_TYPE_MON ||
706 entity_name.get_type() == CEPH_ENTITY_TYPE_OSD ||
707 entity_name.get_type() == CEPH_ENTITY_TYPE_MDS ||
708 entity_name.get_type() == CEPH_ENTITY_TYPE_MGR) {
709 if (g_conf()->cephx_cluster_require_signatures ||
710 g_conf()->cephx_require_signatures) {
711 dout(1) << m->get_source_inst()
712 << " supports cephx but not signatures and"
713 << " 'cephx [cluster] require signatures = true';"
714 << " disallowing cephx" << dendl;
715 supported.erase(CEPH_AUTH_CEPHX);
716 }
717 } else {
718 if (g_conf()->cephx_service_require_signatures ||
719 g_conf()->cephx_require_signatures) {
720 dout(1) << m->get_source_inst()
721 << " supports cephx but not signatures and"
722 << " 'cephx [service] require signatures = true';"
723 << " disallowing cephx" << dendl;
724 supported.erase(CEPH_AUTH_CEPHX);
725 }
726 }
727 } else if (!m->get_connection()->has_feature(CEPH_FEATURE_CEPHX_V2)) {
728 if (entity_name.get_type() == CEPH_ENTITY_TYPE_MON ||
729 entity_name.get_type() == CEPH_ENTITY_TYPE_OSD ||
730 entity_name.get_type() == CEPH_ENTITY_TYPE_MDS ||
731 entity_name.get_type() == CEPH_ENTITY_TYPE_MGR) {
732 if (g_conf()->cephx_cluster_require_version >= 2 ||
733 g_conf()->cephx_require_version >= 2) {
734 dout(1) << m->get_source_inst()
735 << " supports cephx but not v2 and"
736 << " 'cephx [cluster] require version >= 2';"
737 << " disallowing cephx" << dendl;
738 supported.erase(CEPH_AUTH_CEPHX);
739 }
740 } else {
741 if (g_conf()->cephx_service_require_version >= 2 ||
742 g_conf()->cephx_require_version >= 2) {
743 dout(1) << m->get_source_inst()
744 << " supports cephx but not v2 and"
745 << " 'cephx [service] require version >= 2';"
746 << " disallowing cephx" << dendl;
747 supported.erase(CEPH_AUTH_CEPHX);
748 }
749 }
750 }
751
752 int type;
753 if (entity_name.get_type() == CEPH_ENTITY_TYPE_MON ||
754 entity_name.get_type() == CEPH_ENTITY_TYPE_OSD ||
755 entity_name.get_type() == CEPH_ENTITY_TYPE_MDS ||
756 entity_name.get_type() == CEPH_ENTITY_TYPE_MGR)
757 type = mon.auth_cluster_required.pick(supported);
758 else
759 type = mon.auth_service_required.pick(supported);
760
761 s->auth_handler = get_auth_service_handler(type, g_ceph_context, &mon.key_server);
762 if (!s->auth_handler) {
763 dout(1) << "client did not provide supported auth type" << dendl;
764 ret = -ENOTSUP;
765 goto reply;
766 }
767 start = true;
768 proto = type;
769 } else if (!s->auth_handler) {
770 dout(10) << "protocol specified but no s->auth_handler" << dendl;
771 ret = -EINVAL;
772 goto reply;
773 }
774
775 /* assign a new global_id? we assume this should only happen on the first
776 request. If a client tries to send it later, it'll screw up its auth
777 session */
778 if (!s->con->peer_global_id) {
779 s->con->peer_global_id = assign_global_id(paxos_writable);
780 if (!s->con->peer_global_id) {
781
782 delete s->auth_handler;
783 s->auth_handler = NULL;
784
785 if (mon.is_leader() && paxos_writable) {
786 dout(10) << "increasing global id, waitlisting message" << dendl;
787 wait_for_active(op, new C_RetryMessage(this, op));
788 goto done;
789 }
790
791 if (!mon.is_leader()) {
792 dout(10) << "not the leader, requesting more ids from leader" << dendl;
793 int leader = mon.get_leader();
794 MMonGlobalID *req = new MMonGlobalID();
795 req->old_max_id = max_global_id;
796 mon.send_mon_message(req, leader);
797 wait_for_finished_proposal(op, new C_RetryMessage(this, op));
798 return true;
799 }
800
801 ceph_assert(!paxos_writable);
802 return false;
803 }
804 is_new_global_id = true;
805 }
806
807 try {
808 if (start) {
809 // new session
810 ret = s->auth_handler->start_session(entity_name,
811 s->con->peer_global_id,
812 is_new_global_id,
813 &response_bl,
814 &s->con->peer_caps_info);
815 } else {
816 // request
817 ret = s->auth_handler->handle_request(
818 indata,
819 0, // no connection_secret needed
820 &response_bl,
821 &s->con->peer_caps_info,
822 nullptr, nullptr);
823 }
824 if (ret == -EIO) {
825 wait_for_active(op, new C_RetryMessage(this,op));
826 goto done;
827 }
828 if (ret > 0) {
829 if (!s->authenticated &&
830 mon.ms_handle_authentication(s->con.get()) > 0) {
831 finished = true;
832 }
833 ret = 0;
834 }
835 } catch (const ceph::buffer::error &err) {
836 ret = -EINVAL;
837 dout(0) << "caught error when trying to handle auth request, probably malformed request" << dendl;
838 }
839
840 reply:
841 reply = new MAuthReply(proto, &response_bl, ret, s->con->peer_global_id);
842 mon.send_reply(op, reply);
843 if (finished) {
844 // always send the latest monmap.
845 if (m->monmap_epoch < mon.monmap->get_epoch())
846 mon.send_latest_monmap(m->get_connection().get());
847
848 mon.configmon()->check_sub(s);
849 }
850 done:
851 return true;
852 }
853
854 bool AuthMonitor::preprocess_command(MonOpRequestRef op)
855 {
856 auto m = op->get_req<MMonCommand>();
857 int r = -1;
858 bufferlist rdata;
859 stringstream ss, ds;
860
861 cmdmap_t cmdmap;
862 if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
863 // ss has reason for failure
864 string rs = ss.str();
865 mon.reply_command(op, -EINVAL, rs, rdata, get_last_committed());
866 return true;
867 }
868
869 string prefix;
870 cmd_getval(cmdmap, "prefix", prefix);
871 if (prefix == "auth add" ||
872 prefix == "auth del" ||
873 prefix == "auth rm" ||
874 prefix == "auth get-or-create" ||
875 prefix == "auth get-or-create-key" ||
876 prefix == "auth get-or-create-pending" ||
877 prefix == "auth clear-pending" ||
878 prefix == "auth commit-pending" ||
879 prefix == "fs authorize" ||
880 prefix == "auth import" ||
881 prefix == "auth caps") {
882 return false;
883 }
884
885 MonSession *session = op->get_session();
886 if (!session) {
887 mon.reply_command(op, -EACCES, "access denied", rdata, get_last_committed());
888 return true;
889 }
890
891 // entity might not be supplied, but if it is, it should be valid
892 string entity_name;
893 cmd_getval(cmdmap, "entity", entity_name);
894 EntityName entity;
895 if (!entity_name.empty() && !entity.from_str(entity_name)) {
896 ss << "invalid entity_auth " << entity_name;
897 mon.reply_command(op, -EINVAL, ss.str(), get_last_committed());
898 return true;
899 }
900
901 string format = cmd_getval_or<string>(cmdmap, "format", "plain");
902 boost::scoped_ptr<Formatter> f(Formatter::create(format));
903
904 if (prefix == "auth export") {
905 KeyRing keyring;
906 export_keyring(keyring);
907 if (!entity_name.empty()) {
908 EntityAuth eauth;
909 if (keyring.get_auth(entity, eauth)) {
910 KeyRing kr;
911 kr.add(entity, eauth);
912 if (f)
913 kr.encode_formatted("auth", f.get(), rdata);
914 else
915 kr.encode_plaintext(rdata);
916 r = 0;
917 } else {
918 ss << "no key for " << eauth;
919 r = -ENOENT;
920 }
921 } else {
922 if (f)
923 keyring.encode_formatted("auth", f.get(), rdata);
924 else
925 keyring.encode_plaintext(rdata);
926 r = 0;
927 }
928 } else if (prefix == "auth get" && !entity_name.empty()) {
929 KeyRing keyring;
930 EntityAuth entity_auth;
931 if (!mon.key_server.get_auth(entity, entity_auth)) {
932 ss << "failed to find " << entity_name << " in keyring";
933 r = -ENOENT;
934 } else {
935 keyring.add(entity, entity_auth);
936 if (f)
937 keyring.encode_formatted("auth", f.get(), rdata);
938 else
939 keyring.encode_plaintext(rdata);
940 r = 0;
941 }
942 } else if (prefix == "auth print-key" ||
943 prefix == "auth print_key" ||
944 prefix == "auth get-key") {
945 EntityAuth auth;
946 if (!mon.key_server.get_auth(entity, auth)) {
947 ss << "don't have " << entity;
948 r = -ENOENT;
949 goto done;
950 }
951 if (f) {
952 auth.key.encode_formatted("auth", f.get(), rdata);
953 } else {
954 auth.key.encode_plaintext(rdata);
955 }
956 r = 0;
957 } else if (prefix == "auth list" ||
958 prefix == "auth ls") {
959 if (f) {
960 mon.key_server.encode_formatted("auth", f.get(), rdata);
961 } else {
962 mon.key_server.encode_plaintext(rdata);
963 }
964 r = 0;
965 goto done;
966 } else {
967 ss << "invalid command";
968 r = -EINVAL;
969 }
970
971 done:
972 rdata.append(ds);
973 string rs;
974 getline(ss, rs, '\0');
975 mon.reply_command(op, r, rs, rdata, get_last_committed());
976 return true;
977 }
978
979 void AuthMonitor::export_keyring(KeyRing& keyring)
980 {
981 mon.key_server.export_keyring(keyring);
982 }
983
984 int AuthMonitor::import_keyring(KeyRing& keyring)
985 {
986 dout(10) << __func__ << " " << keyring.size() << " keys" << dendl;
987
988 for (map<EntityName, EntityAuth>::iterator p = keyring.get_keys().begin();
989 p != keyring.get_keys().end();
990 ++p) {
991 if (p->second.caps.empty()) {
992 dout(0) << "import: no caps supplied" << dendl;
993 return -EINVAL;
994 }
995 int err = add_entity(p->first, p->second);
996 ceph_assert(err == 0);
997 }
998 return 0;
999 }
1000
1001 int AuthMonitor::remove_entity(const EntityName &entity)
1002 {
1003 dout(10) << __func__ << " " << entity << dendl;
1004 if (!mon.key_server.contains(entity))
1005 return -ENOENT;
1006
1007 KeyServerData::Incremental auth_inc;
1008 auth_inc.name = entity;
1009 auth_inc.op = KeyServerData::AUTH_INC_DEL;
1010 push_cephx_inc(auth_inc);
1011
1012 return 0;
1013 }
1014
1015 bool AuthMonitor::entity_is_pending(EntityName& entity)
1016 {
1017 // are we about to have it?
1018 for (auto& p : pending_auth) {
1019 if (p.inc_type == AUTH_DATA) {
1020 KeyServerData::Incremental inc;
1021 auto q = p.auth_data.cbegin();
1022 decode(inc, q);
1023 if (inc.op == KeyServerData::AUTH_INC_ADD &&
1024 inc.name == entity) {
1025 return true;
1026 }
1027 }
1028 }
1029 return false;
1030 }
1031
1032 int AuthMonitor::exists_and_matches_entity(
1033 const auth_entity_t& entity,
1034 bool has_secret,
1035 stringstream& ss)
1036 {
1037 return exists_and_matches_entity(entity.name, entity.auth,
1038 entity.auth.caps, has_secret, ss);
1039 }
1040
1041 int AuthMonitor::exists_and_matches_entity(
1042 const EntityName& name,
1043 const EntityAuth& auth,
1044 const map<string,bufferlist>& caps,
1045 bool has_secret,
1046 stringstream& ss)
1047 {
1048
1049 dout(20) << __func__ << " entity " << name << " auth " << auth
1050 << " caps " << caps << " has_secret " << has_secret << dendl;
1051
1052 EntityAuth existing_auth;
1053 // does entry already exist?
1054 if (mon.key_server.get_auth(name, existing_auth)) {
1055 // key match?
1056 if (has_secret) {
1057 if (existing_auth.key.get_secret().cmp(auth.key.get_secret())) {
1058 ss << "entity " << name << " exists but key does not match";
1059 return -EEXIST;
1060 }
1061 }
1062
1063 // caps match?
1064 if (caps.size() != existing_auth.caps.size()) {
1065 ss << "entity " << name << " exists but caps do not match";
1066 return -EINVAL;
1067 }
1068 for (auto& it : caps) {
1069 if (existing_auth.caps.count(it.first) == 0 ||
1070 !existing_auth.caps[it.first].contents_equal(it.second)) {
1071 ss << "entity " << name << " exists but cap "
1072 << it.first << " does not match";
1073 return -EINVAL;
1074 }
1075 }
1076
1077 // they match, no-op
1078 return 0;
1079 }
1080 return -ENOENT;
1081 }
1082
1083 int AuthMonitor::add_entity(
1084 const EntityName& name,
1085 const EntityAuth& auth)
1086 {
1087
1088 // okay, add it.
1089 KeyServerData::Incremental auth_inc;
1090 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1091 auth_inc.name = name;
1092 auth_inc.auth = auth;
1093
1094 dout(10) << " add auth entity " << auth_inc.name << dendl;
1095 dout(30) << " " << auth_inc.auth << dendl;
1096 push_cephx_inc(auth_inc);
1097 return 0;
1098 }
1099
1100 int AuthMonitor::validate_osd_destroy(
1101 int32_t id,
1102 const uuid_d& uuid,
1103 EntityName& cephx_entity,
1104 EntityName& lockbox_entity,
1105 stringstream& ss)
1106 {
1107 ceph_assert(paxos.is_plugged());
1108
1109 dout(10) << __func__ << " id " << id << " uuid " << uuid << dendl;
1110
1111 string cephx_str = "osd." + stringify(id);
1112 string lockbox_str = "client.osd-lockbox." + stringify(uuid);
1113
1114 if (!cephx_entity.from_str(cephx_str)) {
1115 dout(10) << __func__ << " invalid cephx entity '"
1116 << cephx_str << "'" << dendl;
1117 ss << "invalid cephx key entity '" << cephx_str << "'";
1118 return -EINVAL;
1119 }
1120
1121 if (!lockbox_entity.from_str(lockbox_str)) {
1122 dout(10) << __func__ << " invalid lockbox entity '"
1123 << lockbox_str << "'" << dendl;
1124 ss << "invalid lockbox key entity '" << lockbox_str << "'";
1125 return -EINVAL;
1126 }
1127
1128 if (!mon.key_server.contains(cephx_entity) &&
1129 !mon.key_server.contains(lockbox_entity)) {
1130 return -ENOENT;
1131 }
1132
1133 return 0;
1134 }
1135
1136 int AuthMonitor::do_osd_destroy(
1137 const EntityName& cephx_entity,
1138 const EntityName& lockbox_entity)
1139 {
1140 ceph_assert(paxos.is_plugged());
1141
1142 dout(10) << __func__ << " cephx " << cephx_entity
1143 << " lockbox " << lockbox_entity << dendl;
1144
1145 bool removed = false;
1146
1147 int err = remove_entity(cephx_entity);
1148 if (err == -ENOENT) {
1149 dout(10) << __func__ << " " << cephx_entity << " does not exist" << dendl;
1150 } else {
1151 removed = true;
1152 }
1153
1154 err = remove_entity(lockbox_entity);
1155 if (err == -ENOENT) {
1156 dout(10) << __func__ << " " << lockbox_entity << " does not exist" << dendl;
1157 } else {
1158 removed = true;
1159 }
1160
1161 if (!removed) {
1162 dout(10) << __func__ << " entities do not exist -- no-op." << dendl;
1163 return 0;
1164 }
1165
1166 // given we have paxos plugged, this will not result in a proposal
1167 // being triggered, but it will still be needed so that we get our
1168 // pending state encoded into the paxos' pending transaction.
1169 propose_pending();
1170 return 0;
1171 }
1172
1173 int _create_auth(
1174 EntityAuth& auth,
1175 const string& key,
1176 const map<string,bufferlist>& caps)
1177 {
1178 if (key.empty())
1179 return -EINVAL;
1180 try {
1181 auth.key.decode_base64(key);
1182 } catch (ceph::buffer::error& e) {
1183 return -EINVAL;
1184 }
1185 auth.caps = caps;
1186 return 0;
1187 }
1188
1189 int AuthMonitor::validate_osd_new(
1190 int32_t id,
1191 const uuid_d& uuid,
1192 const string& cephx_secret,
1193 const string& lockbox_secret,
1194 auth_entity_t& cephx_entity,
1195 auth_entity_t& lockbox_entity,
1196 stringstream& ss)
1197 {
1198
1199 dout(10) << __func__ << " osd." << id << " uuid " << uuid << dendl;
1200
1201 map<string,bufferlist> cephx_caps = {
1202 { "osd", _encode_cap("allow *") },
1203 { "mon", _encode_cap("allow profile osd") },
1204 { "mgr", _encode_cap("allow profile osd") }
1205 };
1206 map<string,bufferlist> lockbox_caps = {
1207 { "mon", _encode_cap("allow command \"config-key get\" "
1208 "with key=\"dm-crypt/osd/" +
1209 stringify(uuid) +
1210 "/luks\"") }
1211 };
1212
1213 bool has_lockbox = !lockbox_secret.empty();
1214
1215 string cephx_name = "osd." + stringify(id);
1216 string lockbox_name = "client.osd-lockbox." + stringify(uuid);
1217
1218 if (!cephx_entity.name.from_str(cephx_name)) {
1219 dout(10) << __func__ << " invalid cephx entity '"
1220 << cephx_name << "'" << dendl;
1221 ss << "invalid cephx key entity '" << cephx_name << "'";
1222 return -EINVAL;
1223 }
1224
1225 if (has_lockbox) {
1226 if (!lockbox_entity.name.from_str(lockbox_name)) {
1227 dout(10) << __func__ << " invalid cephx lockbox entity '"
1228 << lockbox_name << "'" << dendl;
1229 ss << "invalid cephx lockbox entity '" << lockbox_name << "'";
1230 return -EINVAL;
1231 }
1232 }
1233
1234 if (entity_is_pending(cephx_entity.name) ||
1235 (has_lockbox && entity_is_pending(lockbox_entity.name))) {
1236 // If we have pending entities for either the cephx secret or the
1237 // lockbox secret, then our safest bet is to retry the command at
1238 // a later time. These entities may be pending because an `osd new`
1239 // command has been run (which is unlikely, due to the nature of
1240 // the operation, which will force a paxos proposal), or (more likely)
1241 // because a competing client created those entities before we handled
1242 // the `osd new` command. Regardless, let's wait and see.
1243 return -EAGAIN;
1244 }
1245
1246 if (!is_valid_cephx_key(cephx_secret)) {
1247 ss << "invalid cephx secret.";
1248 return -EINVAL;
1249 }
1250
1251 if (has_lockbox && !is_valid_cephx_key(lockbox_secret)) {
1252 ss << "invalid cephx lockbox secret.";
1253 return -EINVAL;
1254 }
1255
1256 int err = _create_auth(cephx_entity.auth, cephx_secret, cephx_caps);
1257 ceph_assert(0 == err);
1258
1259 bool cephx_is_idempotent = false, lockbox_is_idempotent = false;
1260 err = exists_and_matches_entity(cephx_entity, true, ss);
1261
1262 if (err != -ENOENT) {
1263 if (err < 0) {
1264 return err;
1265 }
1266 ceph_assert(0 == err);
1267 cephx_is_idempotent = true;
1268 }
1269
1270 if (has_lockbox) {
1271 err = _create_auth(lockbox_entity.auth, lockbox_secret, lockbox_caps);
1272 ceph_assert(err == 0);
1273 err = exists_and_matches_entity(lockbox_entity, true, ss);
1274 if (err != -ENOENT) {
1275 if (err < 0) {
1276 return err;
1277 }
1278 ceph_assert(0 == err);
1279 lockbox_is_idempotent = true;
1280 }
1281 }
1282
1283 if (cephx_is_idempotent && (!has_lockbox || lockbox_is_idempotent)) {
1284 return EEXIST;
1285 }
1286
1287 return 0;
1288 }
1289
1290 int AuthMonitor::do_osd_new(
1291 const auth_entity_t& cephx_entity,
1292 const auth_entity_t& lockbox_entity,
1293 bool has_lockbox)
1294 {
1295 ceph_assert(paxos.is_plugged());
1296
1297 dout(10) << __func__ << " cephx " << cephx_entity.name
1298 << " lockbox ";
1299 if (has_lockbox) {
1300 *_dout << lockbox_entity.name;
1301 } else {
1302 *_dout << "n/a";
1303 }
1304 *_dout << dendl;
1305
1306 // we must have validated before reaching this point.
1307 // if keys exist, then this means they also match; otherwise we would
1308 // have failed before calling this function.
1309 bool cephx_exists = mon.key_server.contains(cephx_entity.name);
1310
1311 if (!cephx_exists) {
1312 int err = add_entity(cephx_entity.name, cephx_entity.auth);
1313 ceph_assert(0 == err);
1314 }
1315
1316 if (has_lockbox &&
1317 !mon.key_server.contains(lockbox_entity.name)) {
1318 int err = add_entity(lockbox_entity.name, lockbox_entity.auth);
1319 ceph_assert(0 == err);
1320 }
1321
1322 // given we have paxos plugged, this will not result in a proposal
1323 // being triggered, but it will still be needed so that we get our
1324 // pending state encoded into the paxos' pending transaction.
1325 propose_pending();
1326 return 0;
1327 }
1328
1329 bool AuthMonitor::valid_caps(
1330 const string& type,
1331 const string& caps,
1332 ostream *out)
1333 {
1334 if (type == "mon") {
1335 MonCap moncap;
1336 if (!moncap.parse(caps, out)) {
1337 return false;
1338 }
1339 return true;
1340 }
1341
1342 if (!g_conf().get_val<bool>("mon_auth_validate_all_caps")) {
1343 return true;
1344 }
1345
1346 if (type == "mgr") {
1347 MgrCap mgrcap;
1348 if (!mgrcap.parse(caps, out)) {
1349 return false;
1350 }
1351 } else if (type == "osd") {
1352 OSDCap ocap;
1353 if (!ocap.parse(caps, out)) {
1354 return false;
1355 }
1356 } else if (type == "mds") {
1357 MDSAuthCaps mdscap;
1358 if (!mdscap.parse(g_ceph_context, caps, out)) {
1359 return false;
1360 }
1361 } else {
1362 if (out) {
1363 *out << "unknown cap type '" << type << "'";
1364 }
1365 return false;
1366 }
1367 return true;
1368 }
1369
1370 bool AuthMonitor::valid_caps(const vector<string>& caps, ostream *out)
1371 {
1372 for (vector<string>::const_iterator p = caps.begin();
1373 p != caps.end(); p += 2) {
1374 if ((p+1) == caps.end()) {
1375 *out << "cap '" << *p << "' has no value";
1376 return false;
1377 }
1378 if (!valid_caps(*p, *(p+1), out)) {
1379 return false;
1380 }
1381 }
1382 return true;
1383 }
1384
1385 bool AuthMonitor::prepare_command(MonOpRequestRef op)
1386 {
1387 auto m = op->get_req<MMonCommand>();
1388 stringstream ss, ds;
1389 bufferlist rdata;
1390 string rs;
1391 int err = -EINVAL;
1392
1393 cmdmap_t cmdmap;
1394 if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
1395 // ss has reason for failure
1396 string rs = ss.str();
1397 mon.reply_command(op, -EINVAL, rs, rdata, get_last_committed());
1398 return true;
1399 }
1400
1401 string prefix;
1402 vector<string>caps_vec;
1403 string entity_name;
1404 EntityName entity;
1405
1406 cmd_getval(cmdmap, "prefix", prefix);
1407
1408 string format = cmd_getval_or<string>(cmdmap, "format", "plain");
1409 boost::scoped_ptr<Formatter> f(Formatter::create(format));
1410
1411 MonSession *session = op->get_session();
1412 if (!session) {
1413 mon.reply_command(op, -EACCES, "access denied", rdata, get_last_committed());
1414 return true;
1415 }
1416
1417 cmd_getval(cmdmap, "caps", caps_vec);
1418 // fs authorize command's can have odd number of caps arguments
1419 if ((prefix != "fs authorize") && (caps_vec.size() % 2) != 0) {
1420 ss << "bad capabilities request; odd number of arguments";
1421 err = -EINVAL;
1422 goto done;
1423 }
1424
1425 cmd_getval(cmdmap, "entity", entity_name);
1426 if (!entity_name.empty() && !entity.from_str(entity_name)) {
1427 ss << "bad entity name";
1428 err = -EINVAL;
1429 goto done;
1430 }
1431
1432 if (prefix == "auth import") {
1433 bufferlist bl = m->get_data();
1434 if (bl.length() == 0) {
1435 ss << "auth import: no data supplied";
1436 getline(ss, rs);
1437 mon.reply_command(op, -EINVAL, rs, get_last_committed());
1438 return true;
1439 }
1440 auto iter = bl.cbegin();
1441 KeyRing keyring;
1442 try {
1443 decode(keyring, iter);
1444 } catch (const ceph::buffer::error &ex) {
1445 ss << "error decoding keyring" << " " << ex.what();
1446 err = -EINVAL;
1447 goto done;
1448 }
1449 err = import_keyring(keyring);
1450 if (err < 0) {
1451 ss << "auth import: no caps supplied";
1452 getline(ss, rs);
1453 mon.reply_command(op, -EINVAL, rs, get_last_committed());
1454 return true;
1455 }
1456 err = 0;
1457 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1458 get_last_committed() + 1));
1459 return true;
1460 } else if (prefix == "auth add" && !entity_name.empty()) {
1461 /* expected behavior:
1462 * - if command reproduces current state, return 0.
1463 * - if command adds brand new entity, handle it.
1464 * - if command adds new state to existing entity, return error.
1465 */
1466 KeyServerData::Incremental auth_inc;
1467 auth_inc.name = entity;
1468 bufferlist bl = m->get_data();
1469 bool has_keyring = (bl.length() > 0);
1470 map<string,bufferlist> new_caps;
1471
1472 KeyRing new_keyring;
1473 if (has_keyring) {
1474 auto iter = bl.cbegin();
1475 try {
1476 decode(new_keyring, iter);
1477 } catch (const ceph::buffer::error &ex) {
1478 ss << "error decoding keyring";
1479 err = -EINVAL;
1480 goto done;
1481 }
1482 }
1483
1484 if (!valid_caps(caps_vec, &ss)) {
1485 err = -EINVAL;
1486 goto done;
1487 }
1488
1489 // are we about to have it?
1490 if (entity_is_pending(entity)) {
1491 wait_for_finished_proposal(op,
1492 new Monitor::C_Command(mon, op, 0, rs, get_last_committed() + 1));
1493 return true;
1494 }
1495
1496 // build new caps from provided arguments (if available)
1497 for (vector<string>::iterator it = caps_vec.begin();
1498 it != caps_vec.end() && (it + 1) != caps_vec.end();
1499 it += 2) {
1500 string sys = *it;
1501 bufferlist cap;
1502 encode(*(it+1), cap);
1503 new_caps[sys] = cap;
1504 }
1505
1506 // pull info out of provided keyring
1507 EntityAuth new_inc;
1508 if (has_keyring) {
1509 if (!new_keyring.get_auth(auth_inc.name, new_inc)) {
1510 ss << "key for " << auth_inc.name
1511 << " not found in provided keyring";
1512 err = -EINVAL;
1513 goto done;
1514 }
1515 if (!new_caps.empty() && !new_inc.caps.empty()) {
1516 ss << "caps cannot be specified both in keyring and in command";
1517 err = -EINVAL;
1518 goto done;
1519 }
1520 if (new_caps.empty()) {
1521 new_caps = new_inc.caps;
1522 }
1523 }
1524
1525 err = exists_and_matches_entity(auth_inc.name, new_inc,
1526 new_caps, has_keyring, ss);
1527 // if entity/key/caps do not exist in the keyring, just fall through
1528 // and add the entity; otherwise, make sure everything matches (in
1529 // which case it's a no-op), because if not we must fail.
1530 if (err != -ENOENT) {
1531 if (err < 0) {
1532 goto done;
1533 }
1534 // no-op.
1535 ceph_assert(err == 0);
1536 goto done;
1537 }
1538 err = 0;
1539
1540 // okay, add it.
1541 if (!has_keyring) {
1542 dout(10) << "AuthMonitor::prepare_command generating random key for "
1543 << auth_inc.name << dendl;
1544 new_inc.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1545 }
1546 new_inc.caps = new_caps;
1547
1548 err = add_entity(auth_inc.name, new_inc);
1549 ceph_assert(err == 0);
1550
1551 ss << "added key for " << auth_inc.name;
1552 getline(ss, rs);
1553 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1554 get_last_committed() + 1));
1555 return true;
1556 } else if ((prefix == "auth get-or-create-pending" ||
1557 prefix == "auth clear-pending" ||
1558 prefix == "auth commit-pending")) {
1559 if (mon.monmap->min_mon_release < ceph_release_t::quincy) {
1560 err = -EPERM;
1561 ss << "pending_keys are not available until after upgrading to quincy";
1562 goto done;
1563 }
1564
1565 EntityAuth entity_auth;
1566 if (!mon.key_server.get_auth(entity, entity_auth)) {
1567 ss << "entity " << entity << " does not exist";
1568 err = -ENOENT;
1569 goto done;
1570 }
1571
1572 // is there an uncommitted pending_key? (or any change for this entity)
1573 for (auto& p : pending_auth) {
1574 if (p.inc_type == AUTH_DATA) {
1575 KeyServerData::Incremental auth_inc;
1576 auto q = p.auth_data.cbegin();
1577 decode(auth_inc, q);
1578 if (auth_inc.op == KeyServerData::AUTH_INC_ADD &&
1579 auth_inc.name == entity) {
1580 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1581 get_last_committed() + 1));
1582 return true;
1583 }
1584 }
1585 }
1586
1587 if (prefix == "auth get-or-create-pending") {
1588 KeyRing kr;
1589 bool exists = false;
1590 if (!entity_auth.pending_key.empty()) {
1591 kr.add(entity, entity_auth.key, entity_auth.pending_key);
1592 err = 0;
1593 exists = true;
1594 } else {
1595 KeyServerData::Incremental auth_inc;
1596 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1597 auth_inc.name = entity;
1598 auth_inc.auth = entity_auth;
1599 auth_inc.auth.pending_key.create(g_ceph_context, CEPH_CRYPTO_AES);
1600 push_cephx_inc(auth_inc);
1601 kr.add(entity, auth_inc.auth.key, auth_inc.auth.pending_key);
1602 push_cephx_inc(auth_inc);
1603 }
1604 if (f) {
1605 kr.encode_formatted("auth", f.get(), rdata);
1606 } else {
1607 kr.encode_plaintext(rdata);
1608 }
1609 if (exists) {
1610 goto done;
1611 }
1612 } else if (prefix == "auth clear-pending") {
1613 if (entity_auth.pending_key.empty()) {
1614 err = 0;
1615 goto done;
1616 }
1617 KeyServerData::Incremental auth_inc;
1618 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1619 auth_inc.name = entity;
1620 auth_inc.auth = entity_auth;
1621 auth_inc.auth.pending_key.clear();
1622 push_cephx_inc(auth_inc);
1623 } else if (prefix == "auth commit-pending") {
1624 if (entity_auth.pending_key.empty()) {
1625 err = 0;
1626 ss << "no pending key";
1627 goto done;
1628 }
1629 KeyServerData::Incremental auth_inc;
1630 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1631 auth_inc.name = entity;
1632 auth_inc.auth = entity_auth;
1633 auth_inc.auth.key = auth_inc.auth.pending_key;
1634 auth_inc.auth.pending_key.clear();
1635 push_cephx_inc(auth_inc);
1636 }
1637 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs, rdata,
1638 get_last_committed() + 1));
1639 return true;
1640 } else if ((prefix == "auth get-or-create-key" ||
1641 prefix == "auth get-or-create") &&
1642 !entity_name.empty()) {
1643 // auth get-or-create <name> [mon osdcapa osd osdcapb ...]
1644
1645 if (!valid_caps(caps_vec, &ss)) {
1646 err = -EINVAL;
1647 goto done;
1648 }
1649
1650 // Parse the list of caps into a map
1651 std::map<std::string, bufferlist> wanted_caps;
1652 for (vector<string>::const_iterator it = caps_vec.begin();
1653 it != caps_vec.end() && (it + 1) != caps_vec.end();
1654 it += 2) {
1655 const std::string &sys = *it;
1656 bufferlist cap;
1657 encode(*(it+1), cap);
1658 wanted_caps[sys] = cap;
1659 }
1660
1661 // do we have it?
1662 EntityAuth entity_auth;
1663 if (mon.key_server.get_auth(entity, entity_auth)) {
1664 for (const auto &sys_cap : wanted_caps) {
1665 if (entity_auth.caps.count(sys_cap.first) == 0 ||
1666 !entity_auth.caps[sys_cap.first].contents_equal(sys_cap.second)) {
1667 ss << "key for " << entity << " exists but cap " << sys_cap.first
1668 << " does not match";
1669 err = -EINVAL;
1670 goto done;
1671 }
1672 }
1673
1674 if (prefix == "auth get-or-create-key") {
1675 if (f) {
1676 entity_auth.key.encode_formatted("auth", f.get(), rdata);
1677 } else {
1678 ds << entity_auth.key;
1679 }
1680 } else {
1681 KeyRing kr;
1682 kr.add(entity, entity_auth.key, entity_auth.pending_key);
1683 if (f) {
1684 kr.set_caps(entity, entity_auth.caps);
1685 kr.encode_formatted("auth", f.get(), rdata);
1686 } else {
1687 kr.encode_plaintext(rdata);
1688 }
1689 }
1690 err = 0;
1691 goto done;
1692 }
1693
1694 // ...or are we about to?
1695 for (vector<Incremental>::iterator p = pending_auth.begin();
1696 p != pending_auth.end();
1697 ++p) {
1698 if (p->inc_type == AUTH_DATA) {
1699 KeyServerData::Incremental auth_inc;
1700 auto q = p->auth_data.cbegin();
1701 decode(auth_inc, q);
1702 if (auth_inc.op == KeyServerData::AUTH_INC_ADD &&
1703 auth_inc.name == entity) {
1704 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1705 get_last_committed() + 1));
1706 return true;
1707 }
1708 }
1709 }
1710
1711 // create it
1712 KeyServerData::Incremental auth_inc;
1713 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1714 auth_inc.name = entity;
1715 auth_inc.auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1716 auth_inc.auth.caps = wanted_caps;
1717
1718 push_cephx_inc(auth_inc);
1719
1720 if (prefix == "auth get-or-create-key") {
1721 if (f) {
1722 auth_inc.auth.key.encode_formatted("auth", f.get(), rdata);
1723 } else {
1724 ds << auth_inc.auth.key;
1725 }
1726 } else {
1727 KeyRing kr;
1728 kr.add(entity, auth_inc.auth.key);
1729 if (f) {
1730 kr.set_caps(entity, wanted_caps);
1731 kr.encode_formatted("auth", f.get(), rdata);
1732 } else {
1733 kr.encode_plaintext(rdata);
1734 }
1735 }
1736
1737 rdata.append(ds);
1738 getline(ss, rs);
1739 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs, rdata,
1740 get_last_committed() + 1));
1741 return true;
1742 } else if (prefix == "fs authorize") {
1743 string filesystem;
1744 cmd_getval(cmdmap, "filesystem", filesystem);
1745 string mon_cap_string = "allow r";
1746 string mds_cap_string, osd_cap_string;
1747 string osd_cap_wanted = "r";
1748
1749 std::shared_ptr<const Filesystem> fs;
1750 if (filesystem != "*" && filesystem != "all") {
1751 fs = mon.mdsmon()->get_fsmap().get_filesystem(filesystem);
1752 if (fs == nullptr) {
1753 ss << "filesystem " << filesystem << " does not exist.";
1754 err = -EINVAL;
1755 goto done;
1756 } else {
1757 mon_cap_string += " fsname=" + std::string(fs->mds_map.get_fs_name());
1758 }
1759 }
1760
1761 for (auto it = caps_vec.begin();
1762 it != caps_vec.end() && (it + 1) != caps_vec.end();
1763 it += 2) {
1764 const string &path = *it;
1765 const string &cap = *(it+1);
1766 bool root_squash = false;
1767 if ((it + 2) != caps_vec.end() && *(it+2) == "root_squash") {
1768 root_squash = true;
1769 ++it;
1770 }
1771
1772 if (cap != "r" && cap.compare(0, 2, "rw")) {
1773 ss << "Permission flags must start with 'r' or 'rw'.";
1774 err = -EINVAL;
1775 goto done;
1776 }
1777 if (cap.compare(0, 2, "rw") == 0)
1778 osd_cap_wanted = "rw";
1779
1780 char last='\0';
1781 for (size_t i = 2; i < cap.size(); ++i) {
1782 char c = cap.at(i);
1783 if (last >= c) {
1784 ss << "Permission flags (except 'rw') must be specified in alphabetical order.";
1785 err = -EINVAL;
1786 goto done;
1787 }
1788 switch (c) {
1789 case 'p':
1790 break;
1791 case 's':
1792 break;
1793 default:
1794 ss << "Unknown permission flag '" << c << "'.";
1795 err = -EINVAL;
1796 goto done;
1797 }
1798 }
1799
1800 mds_cap_string += mds_cap_string.empty() ? "" : ", ";
1801 mds_cap_string += "allow " + cap;
1802
1803 if (filesystem != "*" && filesystem != "all" && fs != nullptr) {
1804 mds_cap_string += " fsname=" + std::string(fs->mds_map.get_fs_name());
1805 }
1806
1807 if (path != "/") {
1808 mds_cap_string += " path=" + path;
1809 }
1810
1811 if (root_squash) {
1812 mds_cap_string += " root_squash";
1813 }
1814 }
1815
1816 osd_cap_string += osd_cap_string.empty() ? "" : ", ";
1817 osd_cap_string += "allow " + osd_cap_wanted
1818 + " tag " + pg_pool_t::APPLICATION_NAME_CEPHFS
1819 + " data=" + filesystem;
1820
1821 std::map<string, bufferlist> wanted_caps = {
1822 { "mon", _encode_cap(mon_cap_string) },
1823 { "osd", _encode_cap(osd_cap_string) },
1824 { "mds", _encode_cap(mds_cap_string) }
1825 };
1826
1827 if (!valid_caps("mon", mon_cap_string, &ss) ||
1828 !valid_caps("osd", osd_cap_string, &ss) ||
1829 !valid_caps("mds", mds_cap_string, &ss)) {
1830 err = -EINVAL;
1831 goto done;
1832 }
1833
1834 EntityAuth entity_auth;
1835 if (mon.key_server.get_auth(entity, entity_auth)) {
1836 for (const auto &sys_cap : wanted_caps) {
1837 if (entity_auth.caps.count(sys_cap.first) == 0 ||
1838 !entity_auth.caps[sys_cap.first].contents_equal(sys_cap.second)) {
1839 ss << entity << " already has fs capabilities that differ from "
1840 << "those supplied. To generate a new auth key for " << entity
1841 << ", first remove " << entity << " from configuration files, "
1842 << "execute 'ceph auth rm " << entity << "', then execute this "
1843 << "command again.";
1844 err = -EINVAL;
1845 goto done;
1846 }
1847 }
1848
1849 KeyRing kr;
1850 kr.add(entity, entity_auth.key);
1851 if (f) {
1852 kr.set_caps(entity, entity_auth.caps);
1853 kr.encode_formatted("auth", f.get(), rdata);
1854 } else {
1855 kr.encode_plaintext(rdata);
1856 }
1857 err = 0;
1858 goto done;
1859 }
1860
1861 KeyServerData::Incremental auth_inc;
1862 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1863 auth_inc.name = entity;
1864 auth_inc.auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1865 auth_inc.auth.caps = wanted_caps;
1866
1867 push_cephx_inc(auth_inc);
1868 KeyRing kr;
1869 kr.add(entity, auth_inc.auth.key);
1870 if (f) {
1871 kr.set_caps(entity, wanted_caps);
1872 kr.encode_formatted("auth", f.get(), rdata);
1873 } else {
1874 kr.encode_plaintext(rdata);
1875 }
1876
1877 rdata.append(ds);
1878 getline(ss, rs);
1879 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs, rdata,
1880 get_last_committed() + 1));
1881 return true;
1882 } else if (prefix == "auth caps" && !entity_name.empty()) {
1883 KeyServerData::Incremental auth_inc;
1884 auth_inc.name = entity;
1885 if (!mon.key_server.get_auth(auth_inc.name, auth_inc.auth)) {
1886 ss << "couldn't find entry " << auth_inc.name;
1887 err = -ENOENT;
1888 goto done;
1889 }
1890
1891 if (!valid_caps(caps_vec, &ss)) {
1892 err = -EINVAL;
1893 goto done;
1894 }
1895
1896 map<string,bufferlist> newcaps;
1897 for (vector<string>::iterator it = caps_vec.begin();
1898 it != caps_vec.end(); it += 2)
1899 encode(*(it+1), newcaps[*it]);
1900
1901 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1902 auth_inc.auth.caps = newcaps;
1903 push_cephx_inc(auth_inc);
1904
1905 ss << "updated caps for " << auth_inc.name;
1906 getline(ss, rs);
1907 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1908 get_last_committed() + 1));
1909 return true;
1910 } else if ((prefix == "auth del" || prefix == "auth rm") &&
1911 !entity_name.empty()) {
1912 KeyServerData::Incremental auth_inc;
1913 auth_inc.name = entity;
1914 if (!mon.key_server.contains(auth_inc.name)) {
1915 err = 0;
1916 goto done;
1917 }
1918 auth_inc.op = KeyServerData::AUTH_INC_DEL;
1919 push_cephx_inc(auth_inc);
1920
1921 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1922 get_last_committed() + 1));
1923 return true;
1924 }
1925 done:
1926 rdata.append(ds);
1927 getline(ss, rs, '\0');
1928 mon.reply_command(op, err, rs, rdata, get_last_committed());
1929 return false;
1930 }
1931
1932 bool AuthMonitor::prepare_global_id(MonOpRequestRef op)
1933 {
1934 dout(10) << "AuthMonitor::prepare_global_id" << dendl;
1935 increase_max_global_id();
1936 return true;
1937 }
1938
1939 bool AuthMonitor::prepare_used_pending_keys(MonOpRequestRef op)
1940 {
1941 dout(10) << __func__ << " " << op << dendl;
1942 auto m = op->get_req<MMonUsedPendingKeys>();
1943 process_used_pending_keys(m->used_pending_keys);
1944 return true;
1945 }
1946
1947 bool AuthMonitor::_upgrade_format_to_dumpling()
1948 {
1949 dout(1) << __func__ << " upgrading from format 0 to 1" << dendl;
1950 ceph_assert(format_version == 0);
1951
1952 bool changed = false;
1953 map<EntityName, EntityAuth>::iterator p;
1954 for (p = mon.key_server.secrets_begin();
1955 p != mon.key_server.secrets_end();
1956 ++p) {
1957 // grab mon caps, if any
1958 string mon_caps;
1959 if (p->second.caps.count("mon") == 0)
1960 continue;
1961 try {
1962 auto it = p->second.caps["mon"].cbegin();
1963 decode(mon_caps, it);
1964 }
1965 catch (const ceph::buffer::error&) {
1966 dout(10) << __func__ << " unable to parse mon cap for "
1967 << p->first << dendl;
1968 continue;
1969 }
1970
1971 string n = p->first.to_str();
1972 string new_caps;
1973
1974 // set daemon profiles
1975 if ((p->first.is_osd() || p->first.is_mds()) &&
1976 mon_caps == "allow rwx") {
1977 new_caps = string("allow profile ") + std::string(p->first.get_type_name());
1978 }
1979
1980 // update bootstrap keys
1981 if (n == "client.bootstrap-osd") {
1982 new_caps = "allow profile bootstrap-osd";
1983 }
1984 if (n == "client.bootstrap-mds") {
1985 new_caps = "allow profile bootstrap-mds";
1986 }
1987
1988 if (new_caps.length() > 0) {
1989 dout(5) << __func__ << " updating " << p->first << " mon cap from "
1990 << mon_caps << " to " << new_caps << dendl;
1991
1992 bufferlist bl;
1993 encode(new_caps, bl);
1994
1995 KeyServerData::Incremental auth_inc;
1996 auth_inc.name = p->first;
1997 auth_inc.auth = p->second;
1998 auth_inc.auth.caps["mon"] = bl;
1999 auth_inc.op = KeyServerData::AUTH_INC_ADD;
2000 push_cephx_inc(auth_inc);
2001 changed = true;
2002 }
2003 }
2004 return changed;
2005 }
2006
2007 bool AuthMonitor::_upgrade_format_to_luminous()
2008 {
2009 dout(1) << __func__ << " upgrading from format 1 to 2" << dendl;
2010 ceph_assert(format_version == 1);
2011
2012 bool changed = false;
2013 map<EntityName, EntityAuth>::iterator p;
2014 for (p = mon.key_server.secrets_begin();
2015 p != mon.key_server.secrets_end();
2016 ++p) {
2017 string n = p->first.to_str();
2018
2019 string newcap;
2020 if (n == "client.admin") {
2021 // admin gets it all
2022 newcap = "allow *";
2023 } else if (n.find("osd.") == 0 ||
2024 n.find("mds.") == 0 ||
2025 n.find("mon.") == 0) {
2026 // daemons follow their profile
2027 string type = n.substr(0, 3);
2028 newcap = "allow profile " + type;
2029 } else if (p->second.caps.count("mon")) {
2030 // if there are any mon caps, give them 'r' mgr caps
2031 newcap = "allow r";
2032 }
2033
2034 if (newcap.length() > 0) {
2035 dout(5) << " giving " << n << " mgr '" << newcap << "'" << dendl;
2036 bufferlist bl;
2037 encode(newcap, bl);
2038
2039 EntityAuth auth = p->second;
2040 auth.caps["mgr"] = bl;
2041
2042 add_entity(p->first, auth);
2043 changed = true;
2044 }
2045
2046 if (n.find("mgr.") == 0 &&
2047 p->second.caps.count("mon")) {
2048 // the kraken ceph-mgr@.service set the mon cap to 'allow *'.
2049 auto blp = p->second.caps["mon"].cbegin();
2050 string oldcaps;
2051 decode(oldcaps, blp);
2052 if (oldcaps == "allow *") {
2053 dout(5) << " fixing " << n << " mon cap to 'allow profile mgr'"
2054 << dendl;
2055 bufferlist bl;
2056 encode("allow profile mgr", bl);
2057
2058 EntityAuth auth = p->second;
2059 auth.caps["mon"] = bl;
2060 add_entity(p->first, p->second);
2061 changed = true;
2062 }
2063 }
2064 }
2065
2066 // add bootstrap key if it does not already exist
2067 // (might have already been get-or-create'd by
2068 // ceph-create-keys)
2069 EntityName bootstrap_mgr_name;
2070 int r = bootstrap_mgr_name.from_str("client.bootstrap-mgr");
2071 ceph_assert(r);
2072 if (!mon.key_server.contains(bootstrap_mgr_name)) {
2073
2074 EntityName name = bootstrap_mgr_name;
2075 EntityAuth auth;
2076 encode("allow profile bootstrap-mgr", auth.caps["mon"]);
2077 auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
2078 add_entity(name, auth);
2079 changed = true;
2080 }
2081 return changed;
2082 }
2083
2084 bool AuthMonitor::_upgrade_format_to_mimic()
2085 {
2086 dout(1) << __func__ << " upgrading from format 2 to 3" << dendl;
2087 ceph_assert(format_version == 2);
2088
2089 list<pair<EntityName,EntityAuth> > auth_lst;
2090 _generate_bootstrap_keys(&auth_lst);
2091
2092 bool changed = false;
2093 for (auto &p : auth_lst) {
2094 if (mon.key_server.contains(p.first)) {
2095 continue;
2096 }
2097 int err = add_entity(p.first, p.second);
2098 ceph_assert(err == 0);
2099 changed = true;
2100 }
2101
2102 return changed;
2103 }
2104
2105 void AuthMonitor::upgrade_format()
2106 {
2107 constexpr unsigned int FORMAT_NONE = 0;
2108 constexpr unsigned int FORMAT_DUMPLING = 1;
2109 constexpr unsigned int FORMAT_LUMINOUS = 2;
2110 constexpr unsigned int FORMAT_MIMIC = 3;
2111
2112 // when upgrading from the current format to a new format, ensure that
2113 // the new format doesn't break the older format. I.e., if a given format N
2114 // changes or adds something, ensure that when upgrading from N-1 to N+1, we
2115 // still observe the changes for format N if those have not been superseded
2116 // by N+1.
2117
2118 unsigned int current = FORMAT_MIMIC;
2119 if (!mon.get_quorum_mon_features().contains_all(
2120 ceph::features::mon::FEATURE_LUMINOUS)) {
2121 // pre-luminous quorum
2122 current = FORMAT_DUMPLING;
2123 } else if (!mon.get_quorum_mon_features().contains_all(
2124 ceph::features::mon::FEATURE_MIMIC)) {
2125 // pre-mimic quorum
2126 current = FORMAT_LUMINOUS;
2127 }
2128 if (format_version >= current) {
2129 dout(20) << __func__ << " format " << format_version
2130 << " is current" << dendl;
2131 return;
2132 }
2133
2134 // perform a rolling upgrade of the new format, if necessary.
2135 // i.e., if we are moving from format NONE to MIMIC, we will first upgrade
2136 // to DUMPLING, then to LUMINOUS, and finally to MIMIC, in several different
2137 // proposals.
2138
2139 bool changed = false;
2140 if (format_version == FORMAT_NONE) {
2141 changed = _upgrade_format_to_dumpling();
2142
2143 } else if (format_version == FORMAT_DUMPLING) {
2144 changed = _upgrade_format_to_luminous();
2145 } else if (format_version == FORMAT_LUMINOUS) {
2146 changed = _upgrade_format_to_mimic();
2147 }
2148
2149 if (changed) {
2150 // note new format
2151 dout(10) << __func__ << " proposing update from format " << format_version
2152 << " -> " << current << dendl;
2153 format_version = current;
2154 propose_pending();
2155 }
2156 }
2157
2158 void AuthMonitor::dump_info(Formatter *f)
2159 {
2160 /*** WARNING: do not include any privileged information here! ***/
2161 f->open_object_section("auth");
2162 f->dump_unsigned("first_committed", get_first_committed());
2163 f->dump_unsigned("last_committed", get_last_committed());
2164 f->dump_unsigned("num_secrets", mon.key_server.get_num_secrets());
2165 f->close_section();
2166 }