]> git.proxmox.com Git - ceph.git/blob - ceph/src/mon/AuthMonitor.cc
f6b035fb2eb35e70222482af0b5176c31e2ff32c
[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 = cmd_getval_or<string>(cmdmap, "format", "plain");
845 boost::scoped_ptr<Formatter> f(Formatter::create(format));
846
847 if (prefix == "auth export") {
848 KeyRing keyring;
849 export_keyring(keyring);
850 if (!entity_name.empty()) {
851 EntityAuth eauth;
852 if (keyring.get_auth(entity, eauth)) {
853 KeyRing kr;
854 kr.add(entity, eauth);
855 if (f)
856 kr.encode_formatted("auth", f.get(), rdata);
857 else
858 kr.encode_plaintext(rdata);
859 ss << "export " << eauth;
860 r = 0;
861 } else {
862 ss << "no key for " << eauth;
863 r = -ENOENT;
864 }
865 } else {
866 if (f)
867 keyring.encode_formatted("auth", f.get(), rdata);
868 else
869 keyring.encode_plaintext(rdata);
870
871 ss << "exported master keyring";
872 r = 0;
873 }
874 } else if (prefix == "auth get" && !entity_name.empty()) {
875 KeyRing keyring;
876 EntityAuth entity_auth;
877 if(!mon.key_server.get_auth(entity, entity_auth)) {
878 ss << "failed to find " << entity_name << " in keyring";
879 r = -ENOENT;
880 } else {
881 keyring.add(entity, entity_auth);
882 if (f)
883 keyring.encode_formatted("auth", f.get(), rdata);
884 else
885 keyring.encode_plaintext(rdata);
886 ss << "exported keyring for " << entity_name;
887 r = 0;
888 }
889 } else if (prefix == "auth print-key" ||
890 prefix == "auth print_key" ||
891 prefix == "auth get-key") {
892 EntityAuth auth;
893 if (!mon.key_server.get_auth(entity, auth)) {
894 ss << "don't have " << entity;
895 r = -ENOENT;
896 goto done;
897 }
898 if (f) {
899 auth.key.encode_formatted("auth", f.get(), rdata);
900 } else {
901 auth.key.encode_plaintext(rdata);
902 }
903 r = 0;
904 } else if (prefix == "auth list" ||
905 prefix == "auth ls") {
906 if (f) {
907 mon.key_server.encode_formatted("auth", f.get(), rdata);
908 } else {
909 mon.key_server.encode_plaintext(rdata);
910 if (rdata.length() > 0)
911 ss << "installed auth entries:" << std::endl;
912 else
913 ss << "no installed auth entries!" << std::endl;
914 }
915 r = 0;
916 goto done;
917 } else {
918 ss << "invalid command";
919 r = -EINVAL;
920 }
921
922 done:
923 rdata.append(ds);
924 string rs;
925 getline(ss, rs, '\0');
926 mon.reply_command(op, r, rs, rdata, get_last_committed());
927 return true;
928 }
929
930 void AuthMonitor::export_keyring(KeyRing& keyring)
931 {
932 mon.key_server.export_keyring(keyring);
933 }
934
935 int AuthMonitor::import_keyring(KeyRing& keyring)
936 {
937 dout(10) << __func__ << " " << keyring.size() << " keys" << dendl;
938
939 for (map<EntityName, EntityAuth>::iterator p = keyring.get_keys().begin();
940 p != keyring.get_keys().end();
941 ++p) {
942 if (p->second.caps.empty()) {
943 dout(0) << "import: no caps supplied" << dendl;
944 return -EINVAL;
945 }
946 int err = add_entity(p->first, p->second);
947 ceph_assert(err == 0);
948 }
949 return 0;
950 }
951
952 int AuthMonitor::remove_entity(const EntityName &entity)
953 {
954 dout(10) << __func__ << " " << entity << dendl;
955 if (!mon.key_server.contains(entity))
956 return -ENOENT;
957
958 KeyServerData::Incremental auth_inc;
959 auth_inc.name = entity;
960 auth_inc.op = KeyServerData::AUTH_INC_DEL;
961 push_cephx_inc(auth_inc);
962
963 return 0;
964 }
965
966 bool AuthMonitor::entity_is_pending(EntityName& entity)
967 {
968 // are we about to have it?
969 for (auto& p : pending_auth) {
970 if (p.inc_type == AUTH_DATA) {
971 KeyServerData::Incremental inc;
972 auto q = p.auth_data.cbegin();
973 decode(inc, q);
974 if (inc.op == KeyServerData::AUTH_INC_ADD &&
975 inc.name == entity) {
976 return true;
977 }
978 }
979 }
980 return false;
981 }
982
983 int AuthMonitor::exists_and_matches_entity(
984 const auth_entity_t& entity,
985 bool has_secret,
986 stringstream& ss)
987 {
988 return exists_and_matches_entity(entity.name, entity.auth,
989 entity.auth.caps, has_secret, ss);
990 }
991
992 int AuthMonitor::exists_and_matches_entity(
993 const EntityName& name,
994 const EntityAuth& auth,
995 const map<string,bufferlist>& caps,
996 bool has_secret,
997 stringstream& ss)
998 {
999
1000 dout(20) << __func__ << " entity " << name << " auth " << auth
1001 << " caps " << caps << " has_secret " << has_secret << dendl;
1002
1003 EntityAuth existing_auth;
1004 // does entry already exist?
1005 if (mon.key_server.get_auth(name, existing_auth)) {
1006 // key match?
1007 if (has_secret) {
1008 if (existing_auth.key.get_secret().cmp(auth.key.get_secret())) {
1009 ss << "entity " << name << " exists but key does not match";
1010 return -EEXIST;
1011 }
1012 }
1013
1014 // caps match?
1015 if (caps.size() != existing_auth.caps.size()) {
1016 ss << "entity " << name << " exists but caps do not match";
1017 return -EINVAL;
1018 }
1019 for (auto& it : caps) {
1020 if (existing_auth.caps.count(it.first) == 0 ||
1021 !existing_auth.caps[it.first].contents_equal(it.second)) {
1022 ss << "entity " << name << " exists but cap "
1023 << it.first << " does not match";
1024 return -EINVAL;
1025 }
1026 }
1027
1028 // they match, no-op
1029 return 0;
1030 }
1031 return -ENOENT;
1032 }
1033
1034 int AuthMonitor::add_entity(
1035 const EntityName& name,
1036 const EntityAuth& auth)
1037 {
1038
1039 // okay, add it.
1040 KeyServerData::Incremental auth_inc;
1041 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1042 auth_inc.name = name;
1043 auth_inc.auth = auth;
1044
1045 dout(10) << " add auth entity " << auth_inc.name << dendl;
1046 dout(30) << " " << auth_inc.auth << dendl;
1047 push_cephx_inc(auth_inc);
1048 return 0;
1049 }
1050
1051 int AuthMonitor::validate_osd_destroy(
1052 int32_t id,
1053 const uuid_d& uuid,
1054 EntityName& cephx_entity,
1055 EntityName& lockbox_entity,
1056 stringstream& ss)
1057 {
1058 ceph_assert(paxos.is_plugged());
1059
1060 dout(10) << __func__ << " id " << id << " uuid " << uuid << dendl;
1061
1062 string cephx_str = "osd." + stringify(id);
1063 string lockbox_str = "client.osd-lockbox." + stringify(uuid);
1064
1065 if (!cephx_entity.from_str(cephx_str)) {
1066 dout(10) << __func__ << " invalid cephx entity '"
1067 << cephx_str << "'" << dendl;
1068 ss << "invalid cephx key entity '" << cephx_str << "'";
1069 return -EINVAL;
1070 }
1071
1072 if (!lockbox_entity.from_str(lockbox_str)) {
1073 dout(10) << __func__ << " invalid lockbox entity '"
1074 << lockbox_str << "'" << dendl;
1075 ss << "invalid lockbox key entity '" << lockbox_str << "'";
1076 return -EINVAL;
1077 }
1078
1079 if (!mon.key_server.contains(cephx_entity) &&
1080 !mon.key_server.contains(lockbox_entity)) {
1081 return -ENOENT;
1082 }
1083
1084 return 0;
1085 }
1086
1087 int AuthMonitor::do_osd_destroy(
1088 const EntityName& cephx_entity,
1089 const EntityName& lockbox_entity)
1090 {
1091 ceph_assert(paxos.is_plugged());
1092
1093 dout(10) << __func__ << " cephx " << cephx_entity
1094 << " lockbox " << lockbox_entity << dendl;
1095
1096 bool removed = false;
1097
1098 int err = remove_entity(cephx_entity);
1099 if (err == -ENOENT) {
1100 dout(10) << __func__ << " " << cephx_entity << " does not exist" << dendl;
1101 } else {
1102 removed = true;
1103 }
1104
1105 err = remove_entity(lockbox_entity);
1106 if (err == -ENOENT) {
1107 dout(10) << __func__ << " " << lockbox_entity << " does not exist" << dendl;
1108 } else {
1109 removed = true;
1110 }
1111
1112 if (!removed) {
1113 dout(10) << __func__ << " entities do not exist -- no-op." << dendl;
1114 return 0;
1115 }
1116
1117 // given we have paxos plugged, this will not result in a proposal
1118 // being triggered, but it will still be needed so that we get our
1119 // pending state encoded into the paxos' pending transaction.
1120 propose_pending();
1121 return 0;
1122 }
1123
1124 int _create_auth(
1125 EntityAuth& auth,
1126 const string& key,
1127 const map<string,bufferlist>& caps)
1128 {
1129 if (key.empty())
1130 return -EINVAL;
1131 try {
1132 auth.key.decode_base64(key);
1133 } catch (ceph::buffer::error& e) {
1134 return -EINVAL;
1135 }
1136 auth.caps = caps;
1137 return 0;
1138 }
1139
1140 int AuthMonitor::validate_osd_new(
1141 int32_t id,
1142 const uuid_d& uuid,
1143 const string& cephx_secret,
1144 const string& lockbox_secret,
1145 auth_entity_t& cephx_entity,
1146 auth_entity_t& lockbox_entity,
1147 stringstream& ss)
1148 {
1149
1150 dout(10) << __func__ << " osd." << id << " uuid " << uuid << dendl;
1151
1152 map<string,bufferlist> cephx_caps = {
1153 { "osd", _encode_cap("allow *") },
1154 { "mon", _encode_cap("allow profile osd") },
1155 { "mgr", _encode_cap("allow profile osd") }
1156 };
1157 map<string,bufferlist> lockbox_caps = {
1158 { "mon", _encode_cap("allow command \"config-key get\" "
1159 "with key=\"dm-crypt/osd/" +
1160 stringify(uuid) +
1161 "/luks\"") }
1162 };
1163
1164 bool has_lockbox = !lockbox_secret.empty();
1165
1166 string cephx_name = "osd." + stringify(id);
1167 string lockbox_name = "client.osd-lockbox." + stringify(uuid);
1168
1169 if (!cephx_entity.name.from_str(cephx_name)) {
1170 dout(10) << __func__ << " invalid cephx entity '"
1171 << cephx_name << "'" << dendl;
1172 ss << "invalid cephx key entity '" << cephx_name << "'";
1173 return -EINVAL;
1174 }
1175
1176 if (has_lockbox) {
1177 if (!lockbox_entity.name.from_str(lockbox_name)) {
1178 dout(10) << __func__ << " invalid cephx lockbox entity '"
1179 << lockbox_name << "'" << dendl;
1180 ss << "invalid cephx lockbox entity '" << lockbox_name << "'";
1181 return -EINVAL;
1182 }
1183 }
1184
1185 if (entity_is_pending(cephx_entity.name) ||
1186 (has_lockbox && entity_is_pending(lockbox_entity.name))) {
1187 // If we have pending entities for either the cephx secret or the
1188 // lockbox secret, then our safest bet is to retry the command at
1189 // a later time. These entities may be pending because an `osd new`
1190 // command has been run (which is unlikely, due to the nature of
1191 // the operation, which will force a paxos proposal), or (more likely)
1192 // because a competing client created those entities before we handled
1193 // the `osd new` command. Regardless, let's wait and see.
1194 return -EAGAIN;
1195 }
1196
1197 if (!is_valid_cephx_key(cephx_secret)) {
1198 ss << "invalid cephx secret.";
1199 return -EINVAL;
1200 }
1201
1202 if (has_lockbox && !is_valid_cephx_key(lockbox_secret)) {
1203 ss << "invalid cephx lockbox secret.";
1204 return -EINVAL;
1205 }
1206
1207 int err = _create_auth(cephx_entity.auth, cephx_secret, cephx_caps);
1208 ceph_assert(0 == err);
1209
1210 bool cephx_is_idempotent = false, lockbox_is_idempotent = false;
1211 err = exists_and_matches_entity(cephx_entity, true, ss);
1212
1213 if (err != -ENOENT) {
1214 if (err < 0) {
1215 return err;
1216 }
1217 ceph_assert(0 == err);
1218 cephx_is_idempotent = true;
1219 }
1220
1221 if (has_lockbox) {
1222 err = _create_auth(lockbox_entity.auth, lockbox_secret, lockbox_caps);
1223 ceph_assert(err == 0);
1224 err = exists_and_matches_entity(lockbox_entity, true, ss);
1225 if (err != -ENOENT) {
1226 if (err < 0) {
1227 return err;
1228 }
1229 ceph_assert(0 == err);
1230 lockbox_is_idempotent = true;
1231 }
1232 }
1233
1234 if (cephx_is_idempotent && (!has_lockbox || lockbox_is_idempotent)) {
1235 return EEXIST;
1236 }
1237
1238 return 0;
1239 }
1240
1241 int AuthMonitor::do_osd_new(
1242 const auth_entity_t& cephx_entity,
1243 const auth_entity_t& lockbox_entity,
1244 bool has_lockbox)
1245 {
1246 ceph_assert(paxos.is_plugged());
1247
1248 dout(10) << __func__ << " cephx " << cephx_entity.name
1249 << " lockbox ";
1250 if (has_lockbox) {
1251 *_dout << lockbox_entity.name;
1252 } else {
1253 *_dout << "n/a";
1254 }
1255 *_dout << dendl;
1256
1257 // we must have validated before reaching this point.
1258 // if keys exist, then this means they also match; otherwise we would
1259 // have failed before calling this function.
1260 bool cephx_exists = mon.key_server.contains(cephx_entity.name);
1261
1262 if (!cephx_exists) {
1263 int err = add_entity(cephx_entity.name, cephx_entity.auth);
1264 ceph_assert(0 == err);
1265 }
1266
1267 if (has_lockbox &&
1268 !mon.key_server.contains(lockbox_entity.name)) {
1269 int err = add_entity(lockbox_entity.name, lockbox_entity.auth);
1270 ceph_assert(0 == err);
1271 }
1272
1273 // given we have paxos plugged, this will not result in a proposal
1274 // being triggered, but it will still be needed so that we get our
1275 // pending state encoded into the paxos' pending transaction.
1276 propose_pending();
1277 return 0;
1278 }
1279
1280 bool AuthMonitor::valid_caps(
1281 const string& type,
1282 const string& caps,
1283 ostream *out)
1284 {
1285 if (type == "mon") {
1286 MonCap moncap;
1287 if (!moncap.parse(caps, out)) {
1288 return false;
1289 }
1290 return true;
1291 }
1292
1293 if (!g_conf().get_val<bool>("mon_auth_validate_all_caps")) {
1294 return true;
1295 }
1296
1297 if (type == "mgr") {
1298 MgrCap mgrcap;
1299 if (!mgrcap.parse(caps, out)) {
1300 return false;
1301 }
1302 } else if (type == "osd") {
1303 OSDCap ocap;
1304 if (!ocap.parse(caps, out)) {
1305 return false;
1306 }
1307 } else if (type == "mds") {
1308 MDSAuthCaps mdscap;
1309 if (!mdscap.parse(g_ceph_context, caps, out)) {
1310 return false;
1311 }
1312 } else {
1313 if (out) {
1314 *out << "unknown cap type '" << type << "'";
1315 }
1316 return false;
1317 }
1318 return true;
1319 }
1320
1321 bool AuthMonitor::valid_caps(const vector<string>& caps, ostream *out)
1322 {
1323 for (vector<string>::const_iterator p = caps.begin();
1324 p != caps.end(); p += 2) {
1325 if ((p+1) == caps.end()) {
1326 *out << "cap '" << *p << "' has no value";
1327 return false;
1328 }
1329 if (!valid_caps(*p, *(p+1), out)) {
1330 return false;
1331 }
1332 }
1333 return true;
1334 }
1335
1336 bool AuthMonitor::prepare_command(MonOpRequestRef op)
1337 {
1338 auto m = op->get_req<MMonCommand>();
1339 stringstream ss, ds;
1340 bufferlist rdata;
1341 string rs;
1342 int err = -EINVAL;
1343
1344 cmdmap_t cmdmap;
1345 if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
1346 // ss has reason for failure
1347 string rs = ss.str();
1348 mon.reply_command(op, -EINVAL, rs, rdata, get_last_committed());
1349 return true;
1350 }
1351
1352 string prefix;
1353 vector<string>caps_vec;
1354 string entity_name;
1355 EntityName entity;
1356
1357 cmd_getval(cmdmap, "prefix", prefix);
1358
1359 string format = cmd_getval_or<string>(cmdmap, "format", "plain");
1360 boost::scoped_ptr<Formatter> f(Formatter::create(format));
1361
1362 MonSession *session = op->get_session();
1363 if (!session) {
1364 mon.reply_command(op, -EACCES, "access denied", rdata, get_last_committed());
1365 return true;
1366 }
1367
1368 cmd_getval(cmdmap, "caps", caps_vec);
1369 // fs authorize command's can have odd number of caps arguments
1370 if ((prefix != "fs authorize") && (caps_vec.size() % 2) != 0) {
1371 ss << "bad capabilities request; odd number of arguments";
1372 err = -EINVAL;
1373 goto done;
1374 }
1375
1376 cmd_getval(cmdmap, "entity", entity_name);
1377 if (!entity_name.empty() && !entity.from_str(entity_name)) {
1378 ss << "bad entity name";
1379 err = -EINVAL;
1380 goto done;
1381 }
1382
1383 if (prefix == "auth import") {
1384 bufferlist bl = m->get_data();
1385 if (bl.length() == 0) {
1386 ss << "auth import: no data supplied";
1387 getline(ss, rs);
1388 mon.reply_command(op, -EINVAL, rs, get_last_committed());
1389 return true;
1390 }
1391 auto iter = bl.cbegin();
1392 KeyRing keyring;
1393 try {
1394 decode(keyring, iter);
1395 } catch (const ceph::buffer::error &ex) {
1396 ss << "error decoding keyring" << " " << ex.what();
1397 err = -EINVAL;
1398 goto done;
1399 }
1400 err = import_keyring(keyring);
1401 if (err < 0) {
1402 ss << "auth import: no caps supplied";
1403 getline(ss, rs);
1404 mon.reply_command(op, -EINVAL, rs, get_last_committed());
1405 return true;
1406 }
1407 ss << "imported keyring";
1408 getline(ss, rs);
1409 err = 0;
1410 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1411 get_last_committed() + 1));
1412 return true;
1413 } else if (prefix == "auth add" && !entity_name.empty()) {
1414 /* expected behavior:
1415 * - if command reproduces current state, return 0.
1416 * - if command adds brand new entity, handle it.
1417 * - if command adds new state to existing entity, return error.
1418 */
1419 KeyServerData::Incremental auth_inc;
1420 auth_inc.name = entity;
1421 bufferlist bl = m->get_data();
1422 bool has_keyring = (bl.length() > 0);
1423 map<string,bufferlist> new_caps;
1424
1425 KeyRing new_keyring;
1426 if (has_keyring) {
1427 auto iter = bl.cbegin();
1428 try {
1429 decode(new_keyring, iter);
1430 } catch (const ceph::buffer::error &ex) {
1431 ss << "error decoding keyring";
1432 err = -EINVAL;
1433 goto done;
1434 }
1435 }
1436
1437 if (!valid_caps(caps_vec, &ss)) {
1438 err = -EINVAL;
1439 goto done;
1440 }
1441
1442 // are we about to have it?
1443 if (entity_is_pending(entity)) {
1444 wait_for_finished_proposal(op,
1445 new Monitor::C_Command(mon, op, 0, rs, get_last_committed() + 1));
1446 return true;
1447 }
1448
1449 // build new caps from provided arguments (if available)
1450 for (vector<string>::iterator it = caps_vec.begin();
1451 it != caps_vec.end() && (it + 1) != caps_vec.end();
1452 it += 2) {
1453 string sys = *it;
1454 bufferlist cap;
1455 encode(*(it+1), cap);
1456 new_caps[sys] = cap;
1457 }
1458
1459 // pull info out of provided keyring
1460 EntityAuth new_inc;
1461 if (has_keyring) {
1462 if (!new_keyring.get_auth(auth_inc.name, new_inc)) {
1463 ss << "key for " << auth_inc.name
1464 << " not found in provided keyring";
1465 err = -EINVAL;
1466 goto done;
1467 }
1468 if (!new_caps.empty() && !new_inc.caps.empty()) {
1469 ss << "caps cannot be specified both in keyring and in command";
1470 err = -EINVAL;
1471 goto done;
1472 }
1473 if (new_caps.empty()) {
1474 new_caps = new_inc.caps;
1475 }
1476 }
1477
1478 err = exists_and_matches_entity(auth_inc.name, new_inc,
1479 new_caps, has_keyring, ss);
1480 // if entity/key/caps do not exist in the keyring, just fall through
1481 // and add the entity; otherwise, make sure everything matches (in
1482 // which case it's a no-op), because if not we must fail.
1483 if (err != -ENOENT) {
1484 if (err < 0) {
1485 goto done;
1486 }
1487 // no-op.
1488 ceph_assert(err == 0);
1489 goto done;
1490 }
1491 err = 0;
1492
1493 // okay, add it.
1494 if (!has_keyring) {
1495 dout(10) << "AuthMonitor::prepare_command generating random key for "
1496 << auth_inc.name << dendl;
1497 new_inc.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1498 }
1499 new_inc.caps = new_caps;
1500
1501 err = add_entity(auth_inc.name, new_inc);
1502 ceph_assert(err == 0);
1503
1504 ss << "added key for " << auth_inc.name;
1505 getline(ss, rs);
1506 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1507 get_last_committed() + 1));
1508 return true;
1509 } else if ((prefix == "auth get-or-create-key" ||
1510 prefix == "auth get-or-create") &&
1511 !entity_name.empty()) {
1512 // auth get-or-create <name> [mon osdcapa osd osdcapb ...]
1513
1514 if (!valid_caps(caps_vec, &ss)) {
1515 err = -EINVAL;
1516 goto done;
1517 }
1518
1519 // Parse the list of caps into a map
1520 std::map<std::string, bufferlist> wanted_caps;
1521 for (vector<string>::const_iterator it = caps_vec.begin();
1522 it != caps_vec.end() && (it + 1) != caps_vec.end();
1523 it += 2) {
1524 const std::string &sys = *it;
1525 bufferlist cap;
1526 encode(*(it+1), cap);
1527 wanted_caps[sys] = cap;
1528 }
1529
1530 // do we have it?
1531 EntityAuth entity_auth;
1532 if (mon.key_server.get_auth(entity, entity_auth)) {
1533 for (const auto &sys_cap : wanted_caps) {
1534 if (entity_auth.caps.count(sys_cap.first) == 0 ||
1535 !entity_auth.caps[sys_cap.first].contents_equal(sys_cap.second)) {
1536 ss << "key for " << entity << " exists but cap " << sys_cap.first
1537 << " does not match";
1538 err = -EINVAL;
1539 goto done;
1540 }
1541 }
1542
1543 if (prefix == "auth get-or-create-key") {
1544 if (f) {
1545 entity_auth.key.encode_formatted("auth", f.get(), rdata);
1546 } else {
1547 ds << entity_auth.key;
1548 }
1549 } else {
1550 KeyRing kr;
1551 kr.add(entity, entity_auth.key);
1552 if (f) {
1553 kr.set_caps(entity, entity_auth.caps);
1554 kr.encode_formatted("auth", f.get(), rdata);
1555 } else {
1556 kr.encode_plaintext(rdata);
1557 }
1558 }
1559 err = 0;
1560 goto done;
1561 }
1562
1563 // ...or are we about to?
1564 for (vector<Incremental>::iterator p = pending_auth.begin();
1565 p != pending_auth.end();
1566 ++p) {
1567 if (p->inc_type == AUTH_DATA) {
1568 KeyServerData::Incremental auth_inc;
1569 auto q = p->auth_data.cbegin();
1570 decode(auth_inc, q);
1571 if (auth_inc.op == KeyServerData::AUTH_INC_ADD &&
1572 auth_inc.name == entity) {
1573 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1574 get_last_committed() + 1));
1575 return true;
1576 }
1577 }
1578 }
1579
1580 // create it
1581 KeyServerData::Incremental auth_inc;
1582 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1583 auth_inc.name = entity;
1584 auth_inc.auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1585 auth_inc.auth.caps = wanted_caps;
1586
1587 push_cephx_inc(auth_inc);
1588
1589 if (prefix == "auth get-or-create-key") {
1590 if (f) {
1591 auth_inc.auth.key.encode_formatted("auth", f.get(), rdata);
1592 } else {
1593 ds << auth_inc.auth.key;
1594 }
1595 } else {
1596 KeyRing kr;
1597 kr.add(entity, auth_inc.auth.key);
1598 if (f) {
1599 kr.set_caps(entity, wanted_caps);
1600 kr.encode_formatted("auth", f.get(), rdata);
1601 } else {
1602 kr.encode_plaintext(rdata);
1603 }
1604 }
1605
1606 rdata.append(ds);
1607 getline(ss, rs);
1608 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs, rdata,
1609 get_last_committed() + 1));
1610 return true;
1611 } else if (prefix == "fs authorize") {
1612 string filesystem;
1613 cmd_getval(cmdmap, "filesystem", filesystem);
1614 string mon_cap_string = "allow r";
1615 string mds_cap_string, osd_cap_string;
1616 string osd_cap_wanted = "r";
1617
1618 std::shared_ptr<const Filesystem> fs;
1619 if (filesystem != "*" && filesystem != "all") {
1620 fs = mon.mdsmon()->get_fsmap().get_filesystem(filesystem);
1621 if (fs == nullptr) {
1622 ss << "filesystem " << filesystem << " does not exist.";
1623 err = -EINVAL;
1624 goto done;
1625 } else {
1626 mon_cap_string += " fsname=" + std::string(fs->mds_map.get_fs_name());
1627 }
1628 }
1629
1630 for (auto it = caps_vec.begin();
1631 it != caps_vec.end() && (it + 1) != caps_vec.end();
1632 it += 2) {
1633 const string &path = *it;
1634 const string &cap = *(it+1);
1635 bool root_squash = false;
1636 if ((it + 2) != caps_vec.end() && *(it+2) == "root_squash") {
1637 root_squash = true;
1638 ++it;
1639 }
1640
1641 if (cap != "r" && cap.compare(0, 2, "rw")) {
1642 ss << "Permission flags must start with 'r' or 'rw'.";
1643 err = -EINVAL;
1644 goto done;
1645 }
1646 if (cap.compare(0, 2, "rw") == 0)
1647 osd_cap_wanted = "rw";
1648
1649 char last='\0';
1650 for (size_t i = 2; i < cap.size(); ++i) {
1651 char c = cap.at(i);
1652 if (last >= c) {
1653 ss << "Permission flags (except 'rw') must be specified in alphabetical order.";
1654 err = -EINVAL;
1655 goto done;
1656 }
1657 switch (c) {
1658 case 'p':
1659 break;
1660 case 's':
1661 break;
1662 default:
1663 ss << "Unknown permission flag '" << c << "'.";
1664 err = -EINVAL;
1665 goto done;
1666 }
1667 }
1668
1669 mds_cap_string += mds_cap_string.empty() ? "" : ", ";
1670 mds_cap_string += "allow " + cap;
1671
1672 if (filesystem != "*" && filesystem != "all" && fs != nullptr) {
1673 mds_cap_string += " fsname=" + std::string(fs->mds_map.get_fs_name());
1674 }
1675
1676 if (path != "/") {
1677 mds_cap_string += " path=" + path;
1678 }
1679
1680 if (root_squash) {
1681 mds_cap_string += " root_squash";
1682 }
1683 }
1684
1685 osd_cap_string += osd_cap_string.empty() ? "" : ", ";
1686 osd_cap_string += "allow " + osd_cap_wanted
1687 + " tag " + pg_pool_t::APPLICATION_NAME_CEPHFS
1688 + " data=" + filesystem;
1689
1690 std::map<string, bufferlist> wanted_caps = {
1691 { "mon", _encode_cap(mon_cap_string) },
1692 { "osd", _encode_cap(osd_cap_string) },
1693 { "mds", _encode_cap(mds_cap_string) }
1694 };
1695
1696 if (!valid_caps("mon", mon_cap_string, &ss) ||
1697 !valid_caps("osd", osd_cap_string, &ss) ||
1698 !valid_caps("mds", mds_cap_string, &ss)) {
1699 err = -EINVAL;
1700 goto done;
1701 }
1702
1703 EntityAuth entity_auth;
1704 if (mon.key_server.get_auth(entity, entity_auth)) {
1705 for (const auto &sys_cap : wanted_caps) {
1706 if (entity_auth.caps.count(sys_cap.first) == 0 ||
1707 !entity_auth.caps[sys_cap.first].contents_equal(sys_cap.second)) {
1708 ss << entity << " already has fs capabilities that differ from "
1709 << "those supplied. To generate a new auth key for " << entity
1710 << ", first remove " << entity << " from configuration files, "
1711 << "execute 'ceph auth rm " << entity << "', then execute this "
1712 << "command again.";
1713 err = -EINVAL;
1714 goto done;
1715 }
1716 }
1717
1718 KeyRing kr;
1719 kr.add(entity, entity_auth.key);
1720 if (f) {
1721 kr.set_caps(entity, entity_auth.caps);
1722 kr.encode_formatted("auth", f.get(), rdata);
1723 } else {
1724 kr.encode_plaintext(rdata);
1725 }
1726 err = 0;
1727 goto done;
1728 }
1729
1730 KeyServerData::Incremental auth_inc;
1731 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1732 auth_inc.name = entity;
1733 auth_inc.auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1734 auth_inc.auth.caps = wanted_caps;
1735
1736 push_cephx_inc(auth_inc);
1737 KeyRing kr;
1738 kr.add(entity, auth_inc.auth.key);
1739 if (f) {
1740 kr.set_caps(entity, wanted_caps);
1741 kr.encode_formatted("auth", f.get(), rdata);
1742 } else {
1743 kr.encode_plaintext(rdata);
1744 }
1745
1746 rdata.append(ds);
1747 getline(ss, rs);
1748 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs, rdata,
1749 get_last_committed() + 1));
1750 return true;
1751 } else if (prefix == "auth caps" && !entity_name.empty()) {
1752 KeyServerData::Incremental auth_inc;
1753 auth_inc.name = entity;
1754 if (!mon.key_server.get_auth(auth_inc.name, auth_inc.auth)) {
1755 ss << "couldn't find entry " << auth_inc.name;
1756 err = -ENOENT;
1757 goto done;
1758 }
1759
1760 if (!valid_caps(caps_vec, &ss)) {
1761 err = -EINVAL;
1762 goto done;
1763 }
1764
1765 map<string,bufferlist> newcaps;
1766 for (vector<string>::iterator it = caps_vec.begin();
1767 it != caps_vec.end(); it += 2)
1768 encode(*(it+1), newcaps[*it]);
1769
1770 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1771 auth_inc.auth.caps = newcaps;
1772 push_cephx_inc(auth_inc);
1773
1774 ss << "updated caps for " << auth_inc.name;
1775 getline(ss, rs);
1776 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1777 get_last_committed() + 1));
1778 return true;
1779 } else if ((prefix == "auth del" || prefix == "auth rm") &&
1780 !entity_name.empty()) {
1781 KeyServerData::Incremental auth_inc;
1782 auth_inc.name = entity;
1783 if (!mon.key_server.contains(auth_inc.name)) {
1784 ss << "entity " << entity << " does not exist";
1785 err = 0;
1786 goto done;
1787 }
1788 auth_inc.op = KeyServerData::AUTH_INC_DEL;
1789 push_cephx_inc(auth_inc);
1790
1791 ss << "updated";
1792 getline(ss, rs);
1793 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1794 get_last_committed() + 1));
1795 return true;
1796 }
1797 done:
1798 rdata.append(ds);
1799 getline(ss, rs, '\0');
1800 mon.reply_command(op, err, rs, rdata, get_last_committed());
1801 return false;
1802 }
1803
1804 bool AuthMonitor::prepare_global_id(MonOpRequestRef op)
1805 {
1806 dout(10) << "AuthMonitor::prepare_global_id" << dendl;
1807 increase_max_global_id();
1808
1809 return true;
1810 }
1811
1812 bool AuthMonitor::_upgrade_format_to_dumpling()
1813 {
1814 dout(1) << __func__ << " upgrading from format 0 to 1" << dendl;
1815 ceph_assert(format_version == 0);
1816
1817 bool changed = false;
1818 map<EntityName, EntityAuth>::iterator p;
1819 for (p = mon.key_server.secrets_begin();
1820 p != mon.key_server.secrets_end();
1821 ++p) {
1822 // grab mon caps, if any
1823 string mon_caps;
1824 if (p->second.caps.count("mon") == 0)
1825 continue;
1826 try {
1827 auto it = p->second.caps["mon"].cbegin();
1828 decode(mon_caps, it);
1829 }
1830 catch (const ceph::buffer::error&) {
1831 dout(10) << __func__ << " unable to parse mon cap for "
1832 << p->first << dendl;
1833 continue;
1834 }
1835
1836 string n = p->first.to_str();
1837 string new_caps;
1838
1839 // set daemon profiles
1840 if ((p->first.is_osd() || p->first.is_mds()) &&
1841 mon_caps == "allow rwx") {
1842 new_caps = string("allow profile ") + std::string(p->first.get_type_name());
1843 }
1844
1845 // update bootstrap keys
1846 if (n == "client.bootstrap-osd") {
1847 new_caps = "allow profile bootstrap-osd";
1848 }
1849 if (n == "client.bootstrap-mds") {
1850 new_caps = "allow profile bootstrap-mds";
1851 }
1852
1853 if (new_caps.length() > 0) {
1854 dout(5) << __func__ << " updating " << p->first << " mon cap from "
1855 << mon_caps << " to " << new_caps << dendl;
1856
1857 bufferlist bl;
1858 encode(new_caps, bl);
1859
1860 KeyServerData::Incremental auth_inc;
1861 auth_inc.name = p->first;
1862 auth_inc.auth = p->second;
1863 auth_inc.auth.caps["mon"] = bl;
1864 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1865 push_cephx_inc(auth_inc);
1866 changed = true;
1867 }
1868 }
1869 return changed;
1870 }
1871
1872 bool AuthMonitor::_upgrade_format_to_luminous()
1873 {
1874 dout(1) << __func__ << " upgrading from format 1 to 2" << dendl;
1875 ceph_assert(format_version == 1);
1876
1877 bool changed = false;
1878 map<EntityName, EntityAuth>::iterator p;
1879 for (p = mon.key_server.secrets_begin();
1880 p != mon.key_server.secrets_end();
1881 ++p) {
1882 string n = p->first.to_str();
1883
1884 string newcap;
1885 if (n == "client.admin") {
1886 // admin gets it all
1887 newcap = "allow *";
1888 } else if (n.find("osd.") == 0 ||
1889 n.find("mds.") == 0 ||
1890 n.find("mon.") == 0) {
1891 // daemons follow their profile
1892 string type = n.substr(0, 3);
1893 newcap = "allow profile " + type;
1894 } else if (p->second.caps.count("mon")) {
1895 // if there are any mon caps, give them 'r' mgr caps
1896 newcap = "allow r";
1897 }
1898
1899 if (newcap.length() > 0) {
1900 dout(5) << " giving " << n << " mgr '" << newcap << "'" << dendl;
1901 bufferlist bl;
1902 encode(newcap, bl);
1903
1904 EntityAuth auth = p->second;
1905 auth.caps["mgr"] = bl;
1906
1907 add_entity(p->first, auth);
1908 changed = true;
1909 }
1910
1911 if (n.find("mgr.") == 0 &&
1912 p->second.caps.count("mon")) {
1913 // the kraken ceph-mgr@.service set the mon cap to 'allow *'.
1914 auto blp = p->second.caps["mon"].cbegin();
1915 string oldcaps;
1916 decode(oldcaps, blp);
1917 if (oldcaps == "allow *") {
1918 dout(5) << " fixing " << n << " mon cap to 'allow profile mgr'"
1919 << dendl;
1920 bufferlist bl;
1921 encode("allow profile mgr", bl);
1922
1923 EntityAuth auth = p->second;
1924 auth.caps["mon"] = bl;
1925 add_entity(p->first, p->second);
1926 changed = true;
1927 }
1928 }
1929 }
1930
1931 // add bootstrap key if it does not already exist
1932 // (might have already been get-or-create'd by
1933 // ceph-create-keys)
1934 EntityName bootstrap_mgr_name;
1935 int r = bootstrap_mgr_name.from_str("client.bootstrap-mgr");
1936 ceph_assert(r);
1937 if (!mon.key_server.contains(bootstrap_mgr_name)) {
1938
1939 EntityName name = bootstrap_mgr_name;
1940 EntityAuth auth;
1941 encode("allow profile bootstrap-mgr", auth.caps["mon"]);
1942 auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1943 add_entity(name, auth);
1944 changed = true;
1945 }
1946 return changed;
1947 }
1948
1949 bool AuthMonitor::_upgrade_format_to_mimic()
1950 {
1951 dout(1) << __func__ << " upgrading from format 2 to 3" << dendl;
1952 ceph_assert(format_version == 2);
1953
1954 list<pair<EntityName,EntityAuth> > auth_lst;
1955 _generate_bootstrap_keys(&auth_lst);
1956
1957 bool changed = false;
1958 for (auto &p : auth_lst) {
1959 if (mon.key_server.contains(p.first)) {
1960 continue;
1961 }
1962 int err = add_entity(p.first, p.second);
1963 ceph_assert(err == 0);
1964 changed = true;
1965 }
1966
1967 return changed;
1968 }
1969
1970 void AuthMonitor::upgrade_format()
1971 {
1972 constexpr unsigned int FORMAT_NONE = 0;
1973 constexpr unsigned int FORMAT_DUMPLING = 1;
1974 constexpr unsigned int FORMAT_LUMINOUS = 2;
1975 constexpr unsigned int FORMAT_MIMIC = 3;
1976
1977 // when upgrading from the current format to a new format, ensure that
1978 // the new format doesn't break the older format. I.e., if a given format N
1979 // changes or adds something, ensure that when upgrading from N-1 to N+1, we
1980 // still observe the changes for format N if those have not been superseded
1981 // by N+1.
1982
1983 unsigned int current = FORMAT_MIMIC;
1984 if (!mon.get_quorum_mon_features().contains_all(
1985 ceph::features::mon::FEATURE_LUMINOUS)) {
1986 // pre-luminous quorum
1987 current = FORMAT_DUMPLING;
1988 } else if (!mon.get_quorum_mon_features().contains_all(
1989 ceph::features::mon::FEATURE_MIMIC)) {
1990 // pre-mimic quorum
1991 current = FORMAT_LUMINOUS;
1992 }
1993 if (format_version >= current) {
1994 dout(20) << __func__ << " format " << format_version
1995 << " is current" << dendl;
1996 return;
1997 }
1998
1999 // perform a rolling upgrade of the new format, if necessary.
2000 // i.e., if we are moving from format NONE to MIMIC, we will first upgrade
2001 // to DUMPLING, then to LUMINOUS, and finally to MIMIC, in several different
2002 // proposals.
2003
2004 bool changed = false;
2005 if (format_version == FORMAT_NONE) {
2006 changed = _upgrade_format_to_dumpling();
2007
2008 } else if (format_version == FORMAT_DUMPLING) {
2009 changed = _upgrade_format_to_luminous();
2010 } else if (format_version == FORMAT_LUMINOUS) {
2011 changed = _upgrade_format_to_mimic();
2012 }
2013
2014 if (changed) {
2015 // note new format
2016 dout(10) << __func__ << " proposing update from format " << format_version
2017 << " -> " << current << dendl;
2018 format_version = current;
2019 propose_pending();
2020 }
2021 }
2022
2023 void AuthMonitor::dump_info(Formatter *f)
2024 {
2025 /*** WARNING: do not include any privileged information here! ***/
2026 f->open_object_section("auth");
2027 f->dump_unsigned("first_committed", get_first_committed());
2028 f->dump_unsigned("last_committed", get_last_committed());
2029 f->dump_unsigned("num_secrets", mon.key_server.get_num_secrets());
2030 f->close_section();
2031 }