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