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