]> git.proxmox.com Git - ceph.git/blob - ceph/src/mon/AuthMonitor.cc
update sources to v12.1.3
[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
24 #include "messages/MMonCommand.h"
25 #include "messages/MAuth.h"
26 #include "messages/MAuthReply.h"
27 #include "messages/MMonGlobalID.h"
28 #include "msg/Messenger.h"
29
30 #include "auth/AuthServiceHandler.h"
31 #include "auth/KeyRing.h"
32 #include "include/stringify.h"
33 #include "include/assert.h"
34
35 #define dout_subsys ceph_subsys_mon
36 #undef dout_prefix
37 #define dout_prefix _prefix(_dout, mon, get_last_committed())
38 static ostream& _prefix(std::ostream *_dout, Monitor *mon, version_t v) {
39 return *_dout << "mon." << mon->name << "@" << mon->rank
40 << "(" << mon->get_state_name()
41 << ").auth v" << v << " ";
42 }
43
44 ostream& operator<<(ostream &out, const AuthMonitor &pm)
45 {
46 return out << "auth";
47 }
48
49 bool AuthMonitor::check_rotate()
50 {
51 KeyServerData::Incremental rot_inc;
52 rot_inc.op = KeyServerData::AUTH_INC_SET_ROTATING;
53 if (!mon->key_server.updated_rotating(rot_inc.rotating_bl, last_rotating_ver))
54 return false;
55 dout(10) << __func__ << " updated rotating" << dendl;
56 push_cephx_inc(rot_inc);
57 return true;
58 }
59
60 /*
61 Tick function to update the map based on performance every N seconds
62 */
63
64 void AuthMonitor::tick()
65 {
66 if (!is_active()) return;
67
68 dout(10) << *this << dendl;
69
70 if (!mon->is_leader()) return;
71
72 if (check_rotate())
73 propose_pending();
74 }
75
76 void AuthMonitor::on_active()
77 {
78 dout(10) << "AuthMonitor::on_active()" << dendl;
79
80 if (!mon->is_leader())
81 return;
82 mon->key_server.start_server();
83 }
84
85 void AuthMonitor::create_initial()
86 {
87 dout(10) << "create_initial -- creating initial map" << dendl;
88
89 // initialize rotating keys
90 last_rotating_ver = 0;
91 check_rotate();
92 assert(pending_auth.size() == 1);
93
94 if (mon->is_keyring_required()) {
95 KeyRing keyring;
96 bufferlist bl;
97 int ret = mon->store->get("mkfs", "keyring", bl);
98 // fail hard only if there's an error we're not expecting to see
99 assert((ret == 0) || (ret == -ENOENT));
100
101 // try importing only if there's a key
102 if (ret == 0) {
103 KeyRing keyring;
104 bufferlist::iterator p = bl.begin();
105
106 ::decode(keyring, p);
107 import_keyring(keyring);
108 }
109 }
110
111 max_global_id = MIN_GLOBAL_ID;
112
113 Incremental inc;
114 inc.inc_type = GLOBAL_ID;
115 inc.max_global_id = max_global_id;
116 pending_auth.push_back(inc);
117
118 format_version = 2;
119 }
120
121 void AuthMonitor::update_from_paxos(bool *need_bootstrap)
122 {
123 dout(10) << __func__ << dendl;
124 version_t version = get_last_committed();
125 version_t keys_ver = mon->key_server.get_ver();
126 if (version == keys_ver)
127 return;
128 assert(version > keys_ver);
129
130 version_t latest_full = get_version_latest_full();
131
132 dout(10) << __func__ << " version " << version << " keys ver " << keys_ver
133 << " latest " << latest_full << dendl;
134
135 if ((latest_full > 0) && (latest_full > keys_ver)) {
136 bufferlist latest_bl;
137 int err = get_version_full(latest_full, latest_bl);
138 assert(err == 0);
139 assert(latest_bl.length() != 0);
140 dout(7) << __func__ << " loading summary e " << latest_full << dendl;
141 dout(7) << __func__ << " latest length " << latest_bl.length() << dendl;
142 bufferlist::iterator p = latest_bl.begin();
143 __u8 struct_v;
144 ::decode(struct_v, p);
145 ::decode(max_global_id, p);
146 ::decode(mon->key_server, p);
147 mon->key_server.set_ver(latest_full);
148 keys_ver = latest_full;
149 }
150
151 dout(10) << __func__ << " key server version " << mon->key_server.get_ver() << dendl;
152
153 // walk through incrementals
154 while (version > keys_ver) {
155 bufferlist bl;
156 int ret = get_version(keys_ver+1, bl);
157 assert(ret == 0);
158 assert(bl.length());
159
160 // reset if we are moving to initial state. we will normally have
161 // keys in here temporarily for bootstrapping that we need to
162 // clear out.
163 if (keys_ver == 0)
164 mon->key_server.clear_secrets();
165
166 dout(20) << __func__ << " walking through version " << (keys_ver+1)
167 << " len " << bl.length() << dendl;
168
169 bufferlist::iterator p = bl.begin();
170 __u8 v;
171 ::decode(v, p);
172 while (!p.end()) {
173 Incremental inc;
174 ::decode(inc, p);
175 switch (inc.inc_type) {
176 case GLOBAL_ID:
177 max_global_id = inc.max_global_id;
178 break;
179
180 case AUTH_DATA:
181 {
182 KeyServerData::Incremental auth_inc;
183 bufferlist::iterator iter = inc.auth_data.begin();
184 ::decode(auth_inc, iter);
185 mon->key_server.apply_data_incremental(auth_inc);
186 break;
187 }
188 }
189 }
190
191 keys_ver++;
192 mon->key_server.set_ver(keys_ver);
193
194 if (keys_ver == 1 && mon->is_keyring_required()) {
195 auto t(std::make_shared<MonitorDBStore::Transaction>());
196 t->erase("mkfs", "keyring");
197 mon->store->apply_transaction(t);
198 }
199 }
200
201 if (last_allocated_id == 0)
202 last_allocated_id = max_global_id;
203
204 dout(10) << "update_from_paxos() last_allocated_id=" << last_allocated_id
205 << " max_global_id=" << max_global_id
206 << " format_version " << format_version
207 << dendl;
208 }
209
210 void AuthMonitor::increase_max_global_id()
211 {
212 assert(mon->is_leader());
213
214 max_global_id += g_conf->mon_globalid_prealloc;
215 dout(10) << "increasing max_global_id to " << max_global_id << dendl;
216 Incremental inc;
217 inc.inc_type = GLOBAL_ID;
218 inc.max_global_id = max_global_id;
219 pending_auth.push_back(inc);
220 }
221
222 bool AuthMonitor::should_propose(double& delay)
223 {
224 return (!pending_auth.empty());
225 }
226
227 void AuthMonitor::create_pending()
228 {
229 pending_auth.clear();
230 dout(10) << "create_pending v " << (get_last_committed() + 1) << dendl;
231 }
232
233 void AuthMonitor::encode_pending(MonitorDBStore::TransactionRef t)
234 {
235 dout(10) << __func__ << " v " << (get_last_committed() + 1) << dendl;
236
237 bufferlist bl;
238
239 __u8 v = 1;
240 ::encode(v, bl);
241 vector<Incremental>::iterator p;
242 for (p = pending_auth.begin(); p != pending_auth.end(); ++p)
243 p->encode(bl, mon->get_quorum_con_features());
244
245 version_t version = get_last_committed() + 1;
246 put_version(t, version, bl);
247 put_last_committed(t, version);
248 }
249
250 void AuthMonitor::encode_full(MonitorDBStore::TransactionRef t)
251 {
252 version_t version = mon->key_server.get_ver();
253 // do not stash full version 0 as it will never be removed nor read
254 if (version == 0)
255 return;
256
257 dout(10) << __func__ << " auth v " << version << dendl;
258 assert(get_last_committed() == version);
259
260 bufferlist full_bl;
261 Mutex::Locker l(mon->key_server.get_lock());
262 dout(20) << __func__ << " key server has "
263 << (mon->key_server.has_secrets() ? "" : "no ")
264 << "secrets!" << dendl;
265 __u8 v = 1;
266 ::encode(v, full_bl);
267 ::encode(max_global_id, full_bl);
268 ::encode(mon->key_server, full_bl);
269
270 put_version_full(t, version, full_bl);
271 put_version_latest_full(t, version);
272 }
273
274 version_t AuthMonitor::get_trim_to()
275 {
276 unsigned max = g_conf->paxos_max_join_drift * 2;
277 version_t version = get_last_committed();
278 if (mon->is_leader() && (version > max))
279 return version - max;
280 return 0;
281 }
282
283 bool AuthMonitor::preprocess_query(MonOpRequestRef op)
284 {
285 PaxosServiceMessage *m = static_cast<PaxosServiceMessage*>(op->get_req());
286 dout(10) << "preprocess_query " << *m << " from " << m->get_orig_source_inst() << dendl;
287 switch (m->get_type()) {
288 case MSG_MON_COMMAND:
289 return preprocess_command(op);
290
291 case CEPH_MSG_AUTH:
292 return prep_auth(op, false);
293
294 case MSG_MON_GLOBAL_ID:
295 return false;
296
297 default:
298 ceph_abort();
299 return true;
300 }
301 }
302
303 bool AuthMonitor::prepare_update(MonOpRequestRef op)
304 {
305 PaxosServiceMessage *m = static_cast<PaxosServiceMessage*>(op->get_req());
306 dout(10) << "prepare_update " << *m << " from " << m->get_orig_source_inst() << dendl;
307 switch (m->get_type()) {
308 case MSG_MON_COMMAND:
309 return prepare_command(op);
310 case MSG_MON_GLOBAL_ID:
311 return prepare_global_id(op);
312 case CEPH_MSG_AUTH:
313 return prep_auth(op, true);
314 default:
315 ceph_abort();
316 return false;
317 }
318 }
319
320 uint64_t AuthMonitor::assign_global_id(MonOpRequestRef op, bool should_increase_max)
321 {
322 MAuth *m = static_cast<MAuth*>(op->get_req());
323 int total_mon = mon->monmap->size();
324 dout(10) << "AuthMonitor::assign_global_id m=" << *m << " mon=" << mon->rank << "/" << total_mon
325 << " last_allocated=" << last_allocated_id << " max_global_id=" << max_global_id << dendl;
326
327 uint64_t next_global_id = last_allocated_id + 1;
328 int remainder = next_global_id % total_mon;
329 if (remainder)
330 remainder = total_mon - remainder;
331 next_global_id += remainder + mon->rank;
332 dout(10) << "next_global_id should be " << next_global_id << dendl;
333
334 // if we can't bump the max, bail out now on an out-of-bounds gid
335 if (next_global_id > max_global_id &&
336 (!mon->is_leader() || !should_increase_max)) {
337 return 0;
338 }
339
340 // can we return a gid?
341 bool return_next = (next_global_id <= max_global_id);
342
343 // bump the max?
344 while (mon->is_leader() &&
345 (max_global_id < g_conf->mon_globalid_prealloc ||
346 next_global_id >= max_global_id - g_conf->mon_globalid_prealloc / 2)) {
347 increase_max_global_id();
348 }
349
350 if (return_next) {
351 last_allocated_id = next_global_id;
352 return next_global_id;
353 } else {
354 return 0;
355 }
356 }
357
358
359 bool AuthMonitor::prep_auth(MonOpRequestRef op, bool paxos_writable)
360 {
361 MAuth *m = static_cast<MAuth*>(op->get_req());
362 dout(10) << "prep_auth() blob_size=" << m->get_auth_payload().length() << dendl;
363
364 MonSession *s = op->get_session();
365 if (!s) {
366 dout(10) << "no session, dropping" << dendl;
367 return true;
368 }
369
370 int ret = 0;
371 AuthCapsInfo caps_info;
372 MAuthReply *reply;
373 bufferlist response_bl;
374 bufferlist::iterator indata = m->auth_payload.begin();
375 __u32 proto = m->protocol;
376 bool start = false;
377 EntityName entity_name;
378
379 // set up handler?
380 if (m->protocol == 0 && !s->auth_handler) {
381 set<__u32> supported;
382
383 try {
384 __u8 struct_v = 1;
385 ::decode(struct_v, indata);
386 ::decode(supported, indata);
387 ::decode(entity_name, indata);
388 ::decode(s->global_id, indata);
389 } catch (const buffer::error &e) {
390 dout(10) << "failed to decode initial auth message" << dendl;
391 ret = -EINVAL;
392 goto reply;
393 }
394
395 // do we require cephx signatures?
396
397 if (!m->get_connection()->has_feature(CEPH_FEATURE_MSG_AUTH)) {
398 if (entity_name.get_type() == CEPH_ENTITY_TYPE_MON ||
399 entity_name.get_type() == CEPH_ENTITY_TYPE_OSD ||
400 entity_name.get_type() == CEPH_ENTITY_TYPE_MDS ||
401 entity_name.get_type() == CEPH_ENTITY_TYPE_MGR) {
402 if (g_conf->cephx_cluster_require_signatures ||
403 g_conf->cephx_require_signatures) {
404 dout(1) << m->get_source_inst()
405 << " supports cephx but not signatures and"
406 << " 'cephx [cluster] require signatures = true';"
407 << " disallowing cephx" << dendl;
408 supported.erase(CEPH_AUTH_CEPHX);
409 }
410 } else {
411 if (g_conf->cephx_service_require_signatures ||
412 g_conf->cephx_require_signatures) {
413 dout(1) << m->get_source_inst()
414 << " supports cephx but not signatures and"
415 << " 'cephx [service] require signatures = true';"
416 << " disallowing cephx" << dendl;
417 supported.erase(CEPH_AUTH_CEPHX);
418 }
419 }
420 }
421
422 int type;
423 if (entity_name.get_type() == CEPH_ENTITY_TYPE_MON ||
424 entity_name.get_type() == CEPH_ENTITY_TYPE_OSD ||
425 entity_name.get_type() == CEPH_ENTITY_TYPE_MDS ||
426 entity_name.get_type() == CEPH_ENTITY_TYPE_MGR)
427 type = mon->auth_cluster_required.pick(supported);
428 else
429 type = mon->auth_service_required.pick(supported);
430
431 s->auth_handler = get_auth_service_handler(type, g_ceph_context, &mon->key_server);
432 if (!s->auth_handler) {
433 dout(1) << "client did not provide supported auth type" << dendl;
434 ret = -ENOTSUP;
435 goto reply;
436 }
437 start = true;
438 } else if (!s->auth_handler) {
439 dout(10) << "protocol specified but no s->auth_handler" << dendl;
440 ret = -EINVAL;
441 goto reply;
442 }
443
444 /* assign a new global_id? we assume this should only happen on the first
445 request. If a client tries to send it later, it'll screw up its auth
446 session */
447 if (!s->global_id) {
448 s->global_id = assign_global_id(op, paxos_writable);
449 if (!s->global_id) {
450
451 delete s->auth_handler;
452 s->auth_handler = NULL;
453
454 if (mon->is_leader() && paxos_writable) {
455 dout(10) << "increasing global id, waitlisting message" << dendl;
456 wait_for_active(op, new C_RetryMessage(this, op));
457 goto done;
458 }
459
460 if (!mon->is_leader()) {
461 dout(10) << "not the leader, requesting more ids from leader" << dendl;
462 int leader = mon->get_leader();
463 MMonGlobalID *req = new MMonGlobalID();
464 req->old_max_id = max_global_id;
465 mon->messenger->send_message(req, mon->monmap->get_inst(leader));
466 wait_for_finished_proposal(op, new C_RetryMessage(this, op));
467 return true;
468 }
469
470 assert(!paxos_writable);
471 return false;
472 }
473 }
474
475 try {
476 uint64_t auid = 0;
477 if (start) {
478 // new session
479
480 // always send the latest monmap.
481 if (m->monmap_epoch < mon->monmap->get_epoch())
482 mon->send_latest_monmap(m->get_connection().get());
483
484 proto = s->auth_handler->start_session(entity_name, indata, response_bl, caps_info);
485 ret = 0;
486 if (caps_info.allow_all)
487 s->caps.set_allow_all();
488 } else {
489 // request
490 ret = s->auth_handler->handle_request(indata, response_bl, s->global_id, caps_info, &auid);
491 }
492 if (ret == -EIO) {
493 wait_for_active(op, new C_RetryMessage(this,op));
494 goto done;
495 }
496 if (caps_info.caps.length()) {
497 bufferlist::iterator p = caps_info.caps.begin();
498 string str;
499 try {
500 ::decode(str, p);
501 } catch (const buffer::error &err) {
502 derr << "corrupt cap data for " << entity_name << " in auth db" << dendl;
503 str.clear();
504 }
505 s->caps.parse(str, NULL);
506 s->auid = auid;
507 }
508 } catch (const buffer::error &err) {
509 ret = -EINVAL;
510 dout(0) << "caught error when trying to handle auth request, probably malformed request" << dendl;
511 }
512
513 reply:
514 reply = new MAuthReply(proto, &response_bl, ret, s->global_id);
515 mon->send_reply(op, reply);
516 done:
517 return true;
518 }
519
520 bool AuthMonitor::preprocess_command(MonOpRequestRef op)
521 {
522 MMonCommand *m = static_cast<MMonCommand*>(op->get_req());
523 int r = -1;
524 bufferlist rdata;
525 stringstream ss, ds;
526
527 map<string, cmd_vartype> cmdmap;
528 if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
529 // ss has reason for failure
530 string rs = ss.str();
531 mon->reply_command(op, -EINVAL, rs, rdata, get_last_committed());
532 return true;
533 }
534
535 string prefix;
536 cmd_getval(g_ceph_context, cmdmap, "prefix", prefix);
537 if (prefix == "auth add" ||
538 prefix == "auth del" ||
539 prefix == "auth rm" ||
540 prefix == "auth get-or-create" ||
541 prefix == "auth get-or-create-key" ||
542 prefix == "fs authorize" ||
543 prefix == "auth import" ||
544 prefix == "auth caps") {
545 return false;
546 }
547
548 MonSession *session = m->get_session();
549 if (!session) {
550 mon->reply_command(op, -EACCES, "access denied", rdata, get_last_committed());
551 return true;
552 }
553
554 // entity might not be supplied, but if it is, it should be valid
555 string entity_name;
556 cmd_getval(g_ceph_context, cmdmap, "entity", entity_name);
557 EntityName entity;
558 if (!entity_name.empty() && !entity.from_str(entity_name)) {
559 ss << "invalid entity_auth " << entity_name;
560 mon->reply_command(op, -EINVAL, ss.str(), get_last_committed());
561 return true;
562 }
563
564 string format;
565 cmd_getval(g_ceph_context, cmdmap, "format", format, string("plain"));
566 boost::scoped_ptr<Formatter> f(Formatter::create(format));
567
568 if (prefix == "auth export") {
569 KeyRing keyring;
570 export_keyring(keyring);
571 if (!entity_name.empty()) {
572 EntityAuth eauth;
573 if (keyring.get_auth(entity, eauth)) {
574 KeyRing kr;
575 kr.add(entity, eauth);
576 if (f)
577 kr.encode_formatted("auth", f.get(), rdata);
578 else
579 kr.encode_plaintext(rdata);
580 ss << "export " << eauth;
581 r = 0;
582 } else {
583 ss << "no key for " << eauth;
584 r = -ENOENT;
585 }
586 } else {
587 if (f)
588 keyring.encode_formatted("auth", f.get(), rdata);
589 else
590 keyring.encode_plaintext(rdata);
591
592 ss << "exported master keyring";
593 r = 0;
594 }
595 } else if (prefix == "auth get" && !entity_name.empty()) {
596 KeyRing keyring;
597 EntityAuth entity_auth;
598 if(!mon->key_server.get_auth(entity, entity_auth)) {
599 ss << "failed to find " << entity_name << " in keyring";
600 r = -ENOENT;
601 } else {
602 keyring.add(entity, entity_auth);
603 if (f)
604 keyring.encode_formatted("auth", f.get(), rdata);
605 else
606 keyring.encode_plaintext(rdata);
607 ss << "exported keyring for " << entity_name;
608 r = 0;
609 }
610 } else if (prefix == "auth print-key" ||
611 prefix == "auth print_key" ||
612 prefix == "auth get-key") {
613 EntityAuth auth;
614 if (!mon->key_server.get_auth(entity, auth)) {
615 ss << "don't have " << entity;
616 r = -ENOENT;
617 goto done;
618 }
619 if (f) {
620 auth.key.encode_formatted("auth", f.get(), rdata);
621 } else {
622 auth.key.encode_plaintext(rdata);
623 }
624 r = 0;
625 } else if (prefix == "auth list" ||
626 prefix == "auth ls") {
627 if (f) {
628 mon->key_server.encode_formatted("auth", f.get(), rdata);
629 } else {
630 mon->key_server.encode_plaintext(rdata);
631 if (rdata.length() > 0)
632 ss << "installed auth entries:" << std::endl;
633 else
634 ss << "no installed auth entries!" << std::endl;
635 }
636 r = 0;
637 goto done;
638 } else {
639 ss << "invalid command";
640 r = -EINVAL;
641 }
642
643 done:
644 rdata.append(ds);
645 string rs;
646 getline(ss, rs, '\0');
647 mon->reply_command(op, r, rs, rdata, get_last_committed());
648 return true;
649 }
650
651 void AuthMonitor::export_keyring(KeyRing& keyring)
652 {
653 mon->key_server.export_keyring(keyring);
654 }
655
656 int AuthMonitor::import_keyring(KeyRing& keyring)
657 {
658 for (map<EntityName, EntityAuth>::iterator p = keyring.get_keys().begin();
659 p != keyring.get_keys().end();
660 ++p) {
661 if (p->second.caps.empty()) {
662 dout(0) << "import: no caps supplied" << dendl;
663 return -EINVAL;
664 }
665 int err = add_entity(p->first, p->second);
666 assert(err == 0);
667 }
668 return 0;
669 }
670
671 int AuthMonitor::remove_entity(const EntityName &entity)
672 {
673 dout(10) << __func__ << " " << entity << dendl;
674 if (!mon->key_server.contains(entity))
675 return -ENOENT;
676
677 KeyServerData::Incremental auth_inc;
678 auth_inc.name = entity;
679 auth_inc.op = KeyServerData::AUTH_INC_DEL;
680 push_cephx_inc(auth_inc);
681
682 return 0;
683 }
684
685 bool AuthMonitor::entity_is_pending(EntityName& entity)
686 {
687 // are we about to have it?
688 for (auto& p : pending_auth) {
689 if (p.inc_type == AUTH_DATA) {
690 KeyServerData::Incremental inc;
691 bufferlist::iterator q = p.auth_data.begin();
692 ::decode(inc, q);
693 if (inc.op == KeyServerData::AUTH_INC_ADD &&
694 inc.name == entity) {
695 return true;
696 }
697 }
698 }
699 return false;
700 }
701
702 int AuthMonitor::exists_and_matches_entity(
703 const auth_entity_t& entity,
704 bool has_secret,
705 stringstream& ss)
706 {
707 return exists_and_matches_entity(entity.name, entity.auth,
708 entity.auth.caps, has_secret, ss);
709 }
710
711 int AuthMonitor::exists_and_matches_entity(
712 const EntityName& name,
713 const EntityAuth& auth,
714 const map<string,bufferlist>& caps,
715 bool has_secret,
716 stringstream& ss)
717 {
718
719 dout(20) << __func__ << " entity " << name << " auth " << auth
720 << " caps " << caps << " has_secret " << has_secret << dendl;
721
722 EntityAuth existing_auth;
723 // does entry already exist?
724 if (mon->key_server.get_auth(name, existing_auth)) {
725 // key match?
726 if (has_secret) {
727 if (existing_auth.key.get_secret().cmp(auth.key.get_secret())) {
728 ss << "entity " << name << " exists but key does not match";
729 return -EEXIST;
730 }
731 }
732
733 // caps match?
734 if (caps.size() != existing_auth.caps.size()) {
735 ss << "entity " << name << " exists but caps do not match";
736 return -EINVAL;
737 }
738 for (auto& it : caps) {
739 if (existing_auth.caps.count(it.first) == 0 ||
740 !existing_auth.caps[it.first].contents_equal(it.second)) {
741 ss << "entity " << name << " exists but cap "
742 << it.first << " does not match";
743 return -EINVAL;
744 }
745 }
746
747 // they match, no-op
748 return 0;
749 }
750 return -ENOENT;
751 }
752
753 int AuthMonitor::add_entity(
754 const EntityName& name,
755 const EntityAuth& auth)
756 {
757
758 // okay, add it.
759 KeyServerData::Incremental auth_inc;
760 auth_inc.op = KeyServerData::AUTH_INC_ADD;
761 auth_inc.name = name;
762 auth_inc.auth = auth;
763
764 dout(10) << " importing " << auth_inc.name << dendl;
765 dout(30) << " " << auth_inc.auth << dendl;
766 push_cephx_inc(auth_inc);
767 return 0;
768 }
769
770 int AuthMonitor::validate_osd_destroy(
771 int32_t id,
772 const uuid_d& uuid,
773 EntityName& cephx_entity,
774 EntityName& lockbox_entity,
775 stringstream& ss)
776 {
777 assert(paxos->is_plugged());
778
779 dout(10) << __func__ << " id " << id << " uuid " << uuid << dendl;
780
781 string cephx_str = "osd." + stringify(id);
782 string lockbox_str = "client.osd-lockbox." + stringify(uuid);
783
784 if (!cephx_entity.from_str(cephx_str)) {
785 dout(10) << __func__ << " invalid cephx entity '"
786 << cephx_str << "'" << dendl;
787 ss << "invalid cephx key entity '" << cephx_str << "'";
788 return -EINVAL;
789 }
790
791 if (!lockbox_entity.from_str(lockbox_str)) {
792 dout(10) << __func__ << " invalid lockbox entity '"
793 << lockbox_str << "'" << dendl;
794 ss << "invalid lockbox key entity '" << lockbox_str << "'";
795 return -EINVAL;
796 }
797
798 if (!mon->key_server.contains(cephx_entity) &&
799 !mon->key_server.contains(lockbox_entity)) {
800 return -ENOENT;
801 }
802
803 return 0;
804 }
805
806 int AuthMonitor::do_osd_destroy(
807 const EntityName& cephx_entity,
808 const EntityName& lockbox_entity)
809 {
810 assert(paxos->is_plugged());
811
812 dout(10) << __func__ << " cephx " << cephx_entity
813 << " lockbox " << lockbox_entity << dendl;
814
815 bool removed = false;
816
817 int err = remove_entity(cephx_entity);
818 if (err == -ENOENT) {
819 dout(10) << __func__ << " " << cephx_entity << " does not exist" << dendl;
820 } else {
821 removed = true;
822 }
823
824 err = remove_entity(lockbox_entity);
825 if (err == -ENOENT) {
826 dout(10) << __func__ << " " << lockbox_entity << " does not exist" << dendl;
827 } else {
828 removed = true;
829 }
830
831 if (!removed) {
832 dout(10) << __func__ << " entities do not exist -- no-op." << dendl;
833 return 0;
834 }
835
836 // given we have paxos plugged, this will not result in a proposal
837 // being triggered, but it will still be needed so that we get our
838 // pending state encoded into the paxos' pending transaction.
839 propose_pending();
840 return 0;
841 }
842
843 bufferlist _encode_cap(const string& cap)
844 {
845 bufferlist bl;
846 ::encode(cap, bl);
847 return bl;
848 }
849
850 int _create_auth(
851 EntityAuth& auth,
852 const string& key,
853 const map<string,bufferlist>& caps)
854 {
855 if (key.empty())
856 return -EINVAL;
857 try {
858 auth.key.decode_base64(key);
859 } catch (buffer::error& e) {
860 return -EINVAL;
861 }
862 auth.caps = caps;
863 return 0;
864 }
865
866 int AuthMonitor::validate_osd_new(
867 int32_t id,
868 const uuid_d& uuid,
869 const string& cephx_secret,
870 const string& lockbox_secret,
871 auth_entity_t& cephx_entity,
872 auth_entity_t& lockbox_entity,
873 stringstream& ss)
874 {
875
876 dout(10) << __func__ << " osd." << id << " uuid " << uuid << dendl;
877
878 map<string,bufferlist> cephx_caps = {
879 { "osd", _encode_cap("allow *") },
880 { "mon", _encode_cap("allow profile osd") },
881 { "mgr", _encode_cap("allow profile osd") }
882 };
883 map<string,bufferlist> lockbox_caps = {
884 { "mon", _encode_cap("allow command \"config-key get\" "
885 "with key=\"dm-crypt/osd/" +
886 stringify(uuid) +
887 "/luks\"") }
888 };
889
890 bool has_lockbox = !lockbox_secret.empty();
891
892 string cephx_name = "osd." + stringify(id);
893 string lockbox_name = "client.osd-lockbox." + stringify(uuid);
894
895 if (!cephx_entity.name.from_str(cephx_name)) {
896 dout(10) << __func__ << " invalid cephx entity '"
897 << cephx_name << "'" << dendl;
898 ss << "invalid cephx key entity '" << cephx_name << "'";
899 return -EINVAL;
900 }
901
902 if (has_lockbox) {
903 if (!lockbox_entity.name.from_str(lockbox_name)) {
904 dout(10) << __func__ << " invalid cephx lockbox entity '"
905 << lockbox_name << "'" << dendl;
906 ss << "invalid cephx lockbox entity '" << lockbox_name << "'";
907 return -EINVAL;
908 }
909 }
910
911 if (entity_is_pending(cephx_entity.name) ||
912 (has_lockbox && entity_is_pending(lockbox_entity.name))) {
913 // If we have pending entities for either the cephx secret or the
914 // lockbox secret, then our safest bet is to retry the command at
915 // a later time. These entities may be pending because an `osd new`
916 // command has been run (which is unlikely, due to the nature of
917 // the operation, which will force a paxos proposal), or (more likely)
918 // because a competing client created those entities before we handled
919 // the `osd new` command. Regardless, let's wait and see.
920 return -EAGAIN;
921 }
922
923 if (!is_valid_cephx_key(cephx_secret)) {
924 ss << "invalid cephx secret.";
925 return -EINVAL;
926 }
927
928 if (has_lockbox && !is_valid_cephx_key(lockbox_secret)) {
929 ss << "invalid cephx lockbox secret.";
930 return -EINVAL;
931 }
932
933 int err = _create_auth(cephx_entity.auth, cephx_secret, cephx_caps);
934 assert(0 == err);
935
936 bool cephx_is_idempotent = false, lockbox_is_idempotent = false;
937 err = exists_and_matches_entity(cephx_entity, true, ss);
938
939 if (err != -ENOENT) {
940 if (err < 0) {
941 return err;
942 }
943 assert(0 == err);
944 cephx_is_idempotent = true;
945 }
946
947 if (has_lockbox) {
948 err = _create_auth(lockbox_entity.auth, lockbox_secret, lockbox_caps);
949 assert(err == 0);
950 err = exists_and_matches_entity(lockbox_entity, true, ss);
951 if (err != -ENOENT) {
952 if (err < 0) {
953 return err;
954 }
955 assert(0 == err);
956 lockbox_is_idempotent = true;
957 }
958 }
959
960 if (cephx_is_idempotent && (!has_lockbox || lockbox_is_idempotent)) {
961 return EEXIST;
962 }
963
964 return 0;
965 }
966
967 int AuthMonitor::do_osd_new(
968 const auth_entity_t& cephx_entity,
969 const auth_entity_t& lockbox_entity,
970 bool has_lockbox)
971 {
972 assert(paxos->is_plugged());
973
974 dout(10) << __func__ << " cephx " << cephx_entity.name
975 << " lockbox ";
976 if (has_lockbox) {
977 *_dout << lockbox_entity.name;
978 } else {
979 *_dout << "n/a";
980 }
981 *_dout << dendl;
982
983 // we must have validated before reaching this point.
984 // if keys exist, then this means they also match; otherwise we would
985 // have failed before calling this function.
986 bool cephx_exists = mon->key_server.contains(cephx_entity.name);
987
988 if (!cephx_exists) {
989 int err = add_entity(cephx_entity.name, cephx_entity.auth);
990 assert(0 == err);
991 }
992
993 if (has_lockbox &&
994 !mon->key_server.contains(lockbox_entity.name)) {
995 int err = add_entity(lockbox_entity.name, lockbox_entity.auth);
996 assert(0 == err);
997 }
998
999 // given we have paxos plugged, this will not result in a proposal
1000 // being triggered, but it will still be needed so that we get our
1001 // pending state encoded into the paxos' pending transaction.
1002 propose_pending();
1003 return 0;
1004 }
1005
1006 bool AuthMonitor::prepare_command(MonOpRequestRef op)
1007 {
1008 MMonCommand *m = static_cast<MMonCommand*>(op->get_req());
1009 stringstream ss, ds;
1010 bufferlist rdata;
1011 string rs;
1012 int err = -EINVAL;
1013
1014 map<string, cmd_vartype> cmdmap;
1015 if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
1016 // ss has reason for failure
1017 string rs = ss.str();
1018 mon->reply_command(op, -EINVAL, rs, rdata, get_last_committed());
1019 return true;
1020 }
1021
1022 string prefix;
1023 vector<string>caps_vec;
1024 string entity_name;
1025 EntityName entity;
1026
1027 cmd_getval(g_ceph_context, cmdmap, "prefix", prefix);
1028
1029 string format;
1030 cmd_getval(g_ceph_context, cmdmap, "format", format, string("plain"));
1031 boost::scoped_ptr<Formatter> f(Formatter::create(format));
1032
1033 MonSession *session = m->get_session();
1034 if (!session) {
1035 mon->reply_command(op, -EACCES, "access denied", rdata, get_last_committed());
1036 return true;
1037 }
1038
1039 cmd_getval(g_ceph_context, cmdmap, "caps", caps_vec);
1040 if ((caps_vec.size() % 2) != 0) {
1041 ss << "bad capabilities request; odd number of arguments";
1042 err = -EINVAL;
1043 goto done;
1044 }
1045
1046 cmd_getval(g_ceph_context, cmdmap, "entity", entity_name);
1047 if (!entity_name.empty() && !entity.from_str(entity_name)) {
1048 ss << "bad entity name";
1049 err = -EINVAL;
1050 goto done;
1051 }
1052
1053 if (prefix == "auth import") {
1054 bufferlist bl = m->get_data();
1055 if (bl.length() == 0) {
1056 ss << "auth import: no data supplied";
1057 getline(ss, rs);
1058 mon->reply_command(op, -EINVAL, rs, get_last_committed());
1059 return true;
1060 }
1061 bufferlist::iterator iter = bl.begin();
1062 KeyRing keyring;
1063 try {
1064 ::decode(keyring, iter);
1065 } catch (const buffer::error &ex) {
1066 ss << "error decoding keyring" << " " << ex.what();
1067 err = -EINVAL;
1068 goto done;
1069 }
1070 err = import_keyring(keyring);
1071 if (err < 0) {
1072 ss << "auth import: no caps supplied";
1073 getline(ss, rs);
1074 mon->reply_command(op, -EINVAL, rs, get_last_committed());
1075 return true;
1076 }
1077 ss << "imported keyring";
1078 getline(ss, rs);
1079 err = 0;
1080 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1081 get_last_committed() + 1));
1082 return true;
1083 } else if (prefix == "auth add" && !entity_name.empty()) {
1084 /* expected behavior:
1085 * - if command reproduces current state, return 0.
1086 * - if command adds brand new entity, handle it.
1087 * - if command adds new state to existing entity, return error.
1088 */
1089 KeyServerData::Incremental auth_inc;
1090 auth_inc.name = entity;
1091 bufferlist bl = m->get_data();
1092 bool has_keyring = (bl.length() > 0);
1093 map<string,bufferlist> new_caps;
1094
1095 KeyRing new_keyring;
1096 if (has_keyring) {
1097 bufferlist::iterator iter = bl.begin();
1098 try {
1099 ::decode(new_keyring, iter);
1100 } catch (const buffer::error &ex) {
1101 ss << "error decoding keyring";
1102 err = -EINVAL;
1103 goto done;
1104 }
1105 }
1106
1107 // are we about to have it?
1108 if (entity_is_pending(entity)) {
1109 wait_for_finished_proposal(op,
1110 new Monitor::C_Command(mon, op, 0, rs, get_last_committed() + 1));
1111 return true;
1112 }
1113
1114 // build new caps from provided arguments (if available)
1115 for (vector<string>::iterator it = caps_vec.begin();
1116 it != caps_vec.end() && (it + 1) != caps_vec.end();
1117 it += 2) {
1118 string sys = *it;
1119 bufferlist cap;
1120 ::encode(*(it+1), cap);
1121 new_caps[sys] = cap;
1122 }
1123
1124 // pull info out of provided keyring
1125 EntityAuth new_inc;
1126 if (has_keyring) {
1127 if (!new_keyring.get_auth(auth_inc.name, new_inc)) {
1128 ss << "key for " << auth_inc.name
1129 << " not found in provided keyring";
1130 err = -EINVAL;
1131 goto done;
1132 }
1133 if (!new_caps.empty() && !new_inc.caps.empty()) {
1134 ss << "caps cannot be specified both in keyring and in command";
1135 err = -EINVAL;
1136 goto done;
1137 }
1138 if (new_caps.empty()) {
1139 new_caps = new_inc.caps;
1140 }
1141 }
1142
1143 err = exists_and_matches_entity(auth_inc.name, new_inc,
1144 new_caps, has_keyring, ss);
1145 // if entity/key/caps do not exist in the keyring, just fall through
1146 // and add the entity; otherwise, make sure everything matches (in
1147 // which case it's a no-op), because if not we must fail.
1148 if (err != -ENOENT) {
1149 if (err < 0) {
1150 goto done;
1151 }
1152 // no-op.
1153 assert(err == 0);
1154 goto done;
1155 }
1156 err = 0;
1157
1158 // okay, add it.
1159 if (!has_keyring) {
1160 dout(10) << "AuthMonitor::prepare_command generating random key for "
1161 << auth_inc.name << dendl;
1162 new_inc.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1163 }
1164 new_inc.caps = new_caps;
1165
1166 err = add_entity(auth_inc.name, new_inc);
1167 assert(err == 0);
1168
1169 ss << "added key for " << auth_inc.name;
1170 getline(ss, rs);
1171 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1172 get_last_committed() + 1));
1173 return true;
1174 } else if ((prefix == "auth get-or-create-key" ||
1175 prefix == "auth get-or-create") &&
1176 !entity_name.empty()) {
1177 // auth get-or-create <name> [mon osdcapa osd osdcapb ...]
1178
1179 if (!valid_caps(caps_vec, &ss)) {
1180 err = -EINVAL;
1181 goto done;
1182 }
1183
1184 // Parse the list of caps into a map
1185 std::map<std::string, bufferlist> wanted_caps;
1186 for (vector<string>::const_iterator it = caps_vec.begin();
1187 it != caps_vec.end() && (it + 1) != caps_vec.end();
1188 it += 2) {
1189 const std::string &sys = *it;
1190 bufferlist cap;
1191 ::encode(*(it+1), cap);
1192 wanted_caps[sys] = cap;
1193 }
1194
1195 // do we have it?
1196 EntityAuth entity_auth;
1197 if (mon->key_server.get_auth(entity, entity_auth)) {
1198 for (const auto &sys_cap : wanted_caps) {
1199 if (entity_auth.caps.count(sys_cap.first) == 0 ||
1200 !entity_auth.caps[sys_cap.first].contents_equal(sys_cap.second)) {
1201 ss << "key for " << entity << " exists but cap " << sys_cap.first
1202 << " does not match";
1203 err = -EINVAL;
1204 goto done;
1205 }
1206 }
1207
1208 if (prefix == "auth get-or-create-key") {
1209 if (f) {
1210 entity_auth.key.encode_formatted("auth", f.get(), rdata);
1211 } else {
1212 ds << entity_auth.key;
1213 }
1214 } else {
1215 KeyRing kr;
1216 kr.add(entity, entity_auth.key);
1217 if (f) {
1218 kr.set_caps(entity, entity_auth.caps);
1219 kr.encode_formatted("auth", f.get(), rdata);
1220 } else {
1221 kr.encode_plaintext(rdata);
1222 }
1223 }
1224 err = 0;
1225 goto done;
1226 }
1227
1228 // ...or are we about to?
1229 for (vector<Incremental>::iterator p = pending_auth.begin();
1230 p != pending_auth.end();
1231 ++p) {
1232 if (p->inc_type == AUTH_DATA) {
1233 KeyServerData::Incremental auth_inc;
1234 bufferlist::iterator q = p->auth_data.begin();
1235 ::decode(auth_inc, q);
1236 if (auth_inc.op == KeyServerData::AUTH_INC_ADD &&
1237 auth_inc.name == entity) {
1238 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1239 get_last_committed() + 1));
1240 return true;
1241 }
1242 }
1243 }
1244
1245 // create it
1246 KeyServerData::Incremental auth_inc;
1247 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1248 auth_inc.name = entity;
1249 auth_inc.auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1250 auth_inc.auth.caps = wanted_caps;
1251
1252 push_cephx_inc(auth_inc);
1253
1254 if (prefix == "auth get-or-create-key") {
1255 if (f) {
1256 auth_inc.auth.key.encode_formatted("auth", f.get(), rdata);
1257 } else {
1258 ds << auth_inc.auth.key;
1259 }
1260 } else {
1261 KeyRing kr;
1262 kr.add(entity, auth_inc.auth.key);
1263 if (f) {
1264 kr.set_caps(entity, wanted_caps);
1265 kr.encode_formatted("auth", f.get(), rdata);
1266 } else {
1267 kr.encode_plaintext(rdata);
1268 }
1269 }
1270
1271 rdata.append(ds);
1272 getline(ss, rs);
1273 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs, rdata,
1274 get_last_committed() + 1));
1275 return true;
1276 } else if (prefix == "fs authorize") {
1277 string filesystem;
1278 cmd_getval(g_ceph_context, cmdmap, "filesystem", filesystem);
1279 string mds_cap_string, osd_cap_string;
1280 string osd_cap_wanted = "r";
1281
1282 for (auto it = caps_vec.begin();
1283 it != caps_vec.end() && (it + 1) != caps_vec.end();
1284 it += 2) {
1285 const string &path = *it;
1286 const string &cap = *(it+1);
1287 if (cap != "r" && cap != "rw" && cap != "rwp") {
1288 ss << "Only 'r', 'rw', and 'rwp' permissions are allowed for filesystems.";
1289 err = -EINVAL;
1290 goto done;
1291 }
1292 if (cap.find('w') != string::npos) {
1293 osd_cap_wanted = "rw";
1294 }
1295
1296 mds_cap_string += mds_cap_string.empty() ? "" : ", ";
1297 mds_cap_string += "allow " + cap;
1298 if (path != "/") {
1299 mds_cap_string += " path=" + path;
1300 }
1301 }
1302
1303 auto fs = mon->mdsmon()->get_fsmap().get_filesystem(filesystem);
1304 if (!fs) {
1305 ss << "filesystem " << filesystem << " does not exist.";
1306 err = -EINVAL;
1307 goto done;
1308 }
1309
1310 auto data_pools = fs->mds_map.get_data_pools();
1311 for (auto p : data_pools) {
1312 const string &pool_name = mon->osdmon()->osdmap.get_pool_name(p);
1313 osd_cap_string += osd_cap_string.empty() ? "" : ", ";
1314 osd_cap_string += "allow " + osd_cap_wanted + " pool=" + pool_name;
1315 }
1316
1317 std::map<string, bufferlist> wanted_caps = {
1318 { "mon", _encode_cap("allow r") },
1319 { "osd", _encode_cap(osd_cap_string) },
1320 { "mds", _encode_cap(mds_cap_string) }
1321 };
1322
1323 EntityAuth entity_auth;
1324 if (mon->key_server.get_auth(entity, entity_auth)) {
1325 for (const auto &sys_cap : wanted_caps) {
1326 if (entity_auth.caps.count(sys_cap.first) == 0 ||
1327 !entity_auth.caps[sys_cap.first].contents_equal(sys_cap.second)) {
1328 ss << "key for " << entity << " exists but cap " << sys_cap.first
1329 << " does not match";
1330 err = -EINVAL;
1331 goto done;
1332 }
1333 }
1334
1335 KeyRing kr;
1336 kr.add(entity, entity_auth.key);
1337 if (f) {
1338 kr.set_caps(entity, entity_auth.caps);
1339 kr.encode_formatted("auth", f.get(), rdata);
1340 } else {
1341 kr.encode_plaintext(rdata);
1342 }
1343 err = 0;
1344 goto done;
1345 }
1346
1347 KeyServerData::Incremental auth_inc;
1348 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1349 auth_inc.name = entity;
1350 auth_inc.auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1351 auth_inc.auth.caps = wanted_caps;
1352
1353 push_cephx_inc(auth_inc);
1354 KeyRing kr;
1355 kr.add(entity, auth_inc.auth.key);
1356 if (f) {
1357 kr.set_caps(entity, wanted_caps);
1358 kr.encode_formatted("auth", f.get(), rdata);
1359 } else {
1360 kr.encode_plaintext(rdata);
1361 }
1362
1363 rdata.append(ds);
1364 getline(ss, rs);
1365 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs, rdata,
1366 get_last_committed() + 1));
1367 return true;
1368 } else if (prefix == "auth caps" && !entity_name.empty()) {
1369 KeyServerData::Incremental auth_inc;
1370 auth_inc.name = entity;
1371 if (!mon->key_server.get_auth(auth_inc.name, auth_inc.auth)) {
1372 ss << "couldn't find entry " << auth_inc.name;
1373 err = -ENOENT;
1374 goto done;
1375 }
1376
1377 if (!valid_caps(caps_vec, &ss)) {
1378 err = -EINVAL;
1379 goto done;
1380 }
1381
1382 map<string,bufferlist> newcaps;
1383 for (vector<string>::iterator it = caps_vec.begin();
1384 it != caps_vec.end(); it += 2)
1385 ::encode(*(it+1), newcaps[*it]);
1386
1387 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1388 auth_inc.auth.caps = newcaps;
1389 push_cephx_inc(auth_inc);
1390
1391 ss << "updated caps for " << auth_inc.name;
1392 getline(ss, rs);
1393 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1394 get_last_committed() + 1));
1395 return true;
1396 } else if ((prefix == "auth del" || prefix == "auth rm") &&
1397 !entity_name.empty()) {
1398 KeyServerData::Incremental auth_inc;
1399 auth_inc.name = entity;
1400 if (!mon->key_server.contains(auth_inc.name)) {
1401 ss << "entity " << entity << " does not exist";
1402 err = 0;
1403 goto done;
1404 }
1405 auth_inc.op = KeyServerData::AUTH_INC_DEL;
1406 push_cephx_inc(auth_inc);
1407
1408 ss << "updated";
1409 getline(ss, rs);
1410 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1411 get_last_committed() + 1));
1412 return true;
1413 }
1414 done:
1415 rdata.append(ds);
1416 getline(ss, rs, '\0');
1417 mon->reply_command(op, err, rs, rdata, get_last_committed());
1418 return false;
1419 }
1420
1421 bool AuthMonitor::prepare_global_id(MonOpRequestRef op)
1422 {
1423 dout(10) << "AuthMonitor::prepare_global_id" << dendl;
1424 increase_max_global_id();
1425
1426 return true;
1427 }
1428
1429 void AuthMonitor::upgrade_format()
1430 {
1431 unsigned int current = 2;
1432 if (!mon->get_quorum_mon_features().contains_all(
1433 ceph::features::mon::FEATURE_LUMINOUS)) {
1434 current = 1;
1435 }
1436 if (format_version >= current) {
1437 dout(20) << __func__ << " format " << format_version << " is current" << dendl;
1438 return;
1439 }
1440
1441 bool changed = false;
1442 if (format_version == 0) {
1443 dout(1) << __func__ << " upgrading from format 0 to 1" << dendl;
1444 map<EntityName, EntityAuth>::iterator p;
1445 for (p = mon->key_server.secrets_begin();
1446 p != mon->key_server.secrets_end();
1447 ++p) {
1448 // grab mon caps, if any
1449 string mon_caps;
1450 if (p->second.caps.count("mon") == 0)
1451 continue;
1452 try {
1453 bufferlist::iterator it = p->second.caps["mon"].begin();
1454 ::decode(mon_caps, it);
1455 }
1456 catch (buffer::error) {
1457 dout(10) << __func__ << " unable to parse mon cap for "
1458 << p->first << dendl;
1459 continue;
1460 }
1461
1462 string n = p->first.to_str();
1463 string new_caps;
1464
1465 // set daemon profiles
1466 if ((p->first.is_osd() || p->first.is_mds()) &&
1467 mon_caps == "allow rwx") {
1468 new_caps = string("allow profile ") + string(p->first.get_type_name());
1469 }
1470
1471 // update bootstrap keys
1472 if (n == "client.bootstrap-osd") {
1473 new_caps = "allow profile bootstrap-osd";
1474 }
1475 if (n == "client.bootstrap-mds") {
1476 new_caps = "allow profile bootstrap-mds";
1477 }
1478
1479 if (new_caps.length() > 0) {
1480 dout(5) << __func__ << " updating " << p->first << " mon cap from "
1481 << mon_caps << " to " << new_caps << dendl;
1482
1483 bufferlist bl;
1484 ::encode(new_caps, bl);
1485
1486 KeyServerData::Incremental auth_inc;
1487 auth_inc.name = p->first;
1488 auth_inc.auth = p->second;
1489 auth_inc.auth.caps["mon"] = bl;
1490 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1491 push_cephx_inc(auth_inc);
1492 changed = true;
1493 }
1494 }
1495 }
1496
1497 if (format_version == 1) {
1498 dout(1) << __func__ << " upgrading from format 1 to 2" << dendl;
1499 map<EntityName, EntityAuth>::iterator p;
1500 for (p = mon->key_server.secrets_begin();
1501 p != mon->key_server.secrets_end();
1502 ++p) {
1503 string n = p->first.to_str();
1504
1505 string newcap;
1506 if (n == "client.admin") {
1507 // admin gets it all
1508 newcap = "allow *";
1509 } else if (n.find("osd.") == 0 ||
1510 n.find("mds.") == 0 ||
1511 n.find("mon.") == 0) {
1512 // daemons follow their profile
1513 string type = n.substr(0, 3);
1514 newcap = "allow profile " + type;
1515 } else if (p->second.caps.count("mon")) {
1516 // if there are any mon caps, give them 'r' mgr caps
1517 newcap = "allow r";
1518 }
1519
1520 if (newcap.length() > 0) {
1521 dout(5) << " giving " << n << " mgr '" << newcap << "'" << dendl;
1522 bufferlist bl;
1523 ::encode(newcap, bl);
1524
1525 KeyServerData::Incremental auth_inc;
1526 auth_inc.name = p->first;
1527 auth_inc.auth = p->second;
1528 auth_inc.auth.caps["mgr"] = bl;
1529 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1530 push_cephx_inc(auth_inc);
1531 }
1532
1533 if (n.find("mgr.") == 0 &&
1534 p->second.caps.count("mon")) {
1535 // the kraken ceph-mgr@.service set the mon cap to 'allow *'.
1536 auto blp = p->second.caps["mon"].begin();
1537 string oldcaps;
1538 ::decode(oldcaps, blp);
1539 if (oldcaps == "allow *") {
1540 dout(5) << " fixing " << n << " mon cap to 'allow profile mgr'"
1541 << dendl;
1542 bufferlist bl;
1543 ::encode("allow profile mgr", bl);
1544 KeyServerData::Incremental auth_inc;
1545 auth_inc.name = p->first;
1546 auth_inc.auth = p->second;
1547 auth_inc.auth.caps["mon"] = bl;
1548 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1549 push_cephx_inc(auth_inc);
1550 }
1551 }
1552 }
1553
1554 // add bootstrap key
1555 {
1556 KeyServerData::Incremental auth_inc;
1557 bool r = auth_inc.name.from_str("client.bootstrap-mgr");
1558 assert(r);
1559 ::encode("allow profile bootstrap-mgr", auth_inc.auth.caps["mon"]);
1560 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1561 // generate key
1562 auth_inc.auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1563 push_cephx_inc(auth_inc);
1564 }
1565 changed = true;
1566 }
1567
1568 if (changed) {
1569 // note new format
1570 dout(10) << __func__ << " proposing update from format " << format_version
1571 << " -> " << current << dendl;
1572 format_version = current;
1573 propose_pending();
1574 }
1575 }
1576
1577 void AuthMonitor::dump_info(Formatter *f)
1578 {
1579 /*** WARNING: do not include any privileged information here! ***/
1580 f->open_object_section("auth");
1581 f->dump_unsigned("first_committed", get_first_committed());
1582 f->dump_unsigned("last_committed", get_last_committed());
1583 f->dump_unsigned("num_secrets", mon->key_server.get_num_secrets());
1584 f->close_section();
1585 }