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