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