]> git.proxmox.com Git - ceph.git/blame - ceph/src/mon/AuthMonitor.cc
update sources to 12.2.7
[ceph.git] / ceph / src / mon / AuthMonitor.cc
CommitLineData
7c673cae
FG
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"
31f18b77
FG
20#include "mon/ConfigKeyService.h"
21#include "mon/OSDMonitor.h"
d2e6a577 22#include "mon/MDSMonitor.h"
7c673cae
FG
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"
31f18b77 32#include "include/stringify.h"
7c673cae
FG
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())
38static 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
44ostream& operator<<(ostream &out, const AuthMonitor &pm)
45{
46 return out << "auth";
47}
48
49bool 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
64void 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
76void 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
85void 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
121void 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
210void 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
222bool AuthMonitor::should_propose(double& delay)
223{
224 return (!pending_auth.empty());
225}
226
227void AuthMonitor::create_pending()
228{
229 pending_auth.clear();
230 dout(10) << "create_pending v " << (get_last_committed() + 1) << dendl;
231}
232
233void 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
250void 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
274version_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
283bool 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
303bool 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
320uint64_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
359bool 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 }
28e407b8
AA
420 } else if (!m->get_connection()->has_feature(CEPH_FEATURE_CEPHX_V2)) {
421 if (entity_name.get_type() == CEPH_ENTITY_TYPE_MON ||
422 entity_name.get_type() == CEPH_ENTITY_TYPE_OSD ||
423 entity_name.get_type() == CEPH_ENTITY_TYPE_MDS ||
424 entity_name.get_type() == CEPH_ENTITY_TYPE_MGR) {
425 if (g_conf->cephx_cluster_require_version >= 2 ||
426 g_conf->cephx_require_version >= 2) {
427 dout(1) << m->get_source_inst()
428 << " supports cephx but not v2 and"
429 << " 'cephx [cluster] require version >= 2';"
430 << " disallowing cephx" << dendl;
431 supported.erase(CEPH_AUTH_CEPHX);
432 }
433 } else {
434 if (g_conf->cephx_service_require_version >= 2 ||
435 g_conf->cephx_require_version >= 2) {
436 dout(1) << m->get_source_inst()
437 << " supports cephx but not v2 and"
438 << " 'cephx [service] require version >= 2';"
439 << " disallowing cephx" << dendl;
440 supported.erase(CEPH_AUTH_CEPHX);
441 }
442 }
7c673cae
FG
443 }
444
445 int type;
446 if (entity_name.get_type() == CEPH_ENTITY_TYPE_MON ||
447 entity_name.get_type() == CEPH_ENTITY_TYPE_OSD ||
448 entity_name.get_type() == CEPH_ENTITY_TYPE_MDS ||
449 entity_name.get_type() == CEPH_ENTITY_TYPE_MGR)
450 type = mon->auth_cluster_required.pick(supported);
451 else
452 type = mon->auth_service_required.pick(supported);
453
454 s->auth_handler = get_auth_service_handler(type, g_ceph_context, &mon->key_server);
455 if (!s->auth_handler) {
456 dout(1) << "client did not provide supported auth type" << dendl;
457 ret = -ENOTSUP;
458 goto reply;
459 }
460 start = true;
461 } else if (!s->auth_handler) {
462 dout(10) << "protocol specified but no s->auth_handler" << dendl;
463 ret = -EINVAL;
464 goto reply;
465 }
466
467 /* assign a new global_id? we assume this should only happen on the first
468 request. If a client tries to send it later, it'll screw up its auth
469 session */
470 if (!s->global_id) {
471 s->global_id = assign_global_id(op, paxos_writable);
472 if (!s->global_id) {
473
474 delete s->auth_handler;
475 s->auth_handler = NULL;
476
477 if (mon->is_leader() && paxos_writable) {
478 dout(10) << "increasing global id, waitlisting message" << dendl;
479 wait_for_active(op, new C_RetryMessage(this, op));
480 goto done;
481 }
482
483 if (!mon->is_leader()) {
484 dout(10) << "not the leader, requesting more ids from leader" << dendl;
485 int leader = mon->get_leader();
486 MMonGlobalID *req = new MMonGlobalID();
487 req->old_max_id = max_global_id;
488 mon->messenger->send_message(req, mon->monmap->get_inst(leader));
489 wait_for_finished_proposal(op, new C_RetryMessage(this, op));
490 return true;
491 }
492
493 assert(!paxos_writable);
494 return false;
495 }
496 }
497
498 try {
499 uint64_t auid = 0;
500 if (start) {
501 // new session
502
503 // always send the latest monmap.
504 if (m->monmap_epoch < mon->monmap->get_epoch())
505 mon->send_latest_monmap(m->get_connection().get());
506
507 proto = s->auth_handler->start_session(entity_name, indata, response_bl, caps_info);
508 ret = 0;
509 if (caps_info.allow_all)
510 s->caps.set_allow_all();
511 } else {
512 // request
513 ret = s->auth_handler->handle_request(indata, response_bl, s->global_id, caps_info, &auid);
514 }
515 if (ret == -EIO) {
516 wait_for_active(op, new C_RetryMessage(this,op));
517 goto done;
518 }
519 if (caps_info.caps.length()) {
520 bufferlist::iterator p = caps_info.caps.begin();
521 string str;
522 try {
523 ::decode(str, p);
524 } catch (const buffer::error &err) {
525 derr << "corrupt cap data for " << entity_name << " in auth db" << dendl;
526 str.clear();
527 }
528 s->caps.parse(str, NULL);
529 s->auid = auid;
530 }
531 } catch (const buffer::error &err) {
532 ret = -EINVAL;
533 dout(0) << "caught error when trying to handle auth request, probably malformed request" << dendl;
534 }
535
536reply:
537 reply = new MAuthReply(proto, &response_bl, ret, s->global_id);
538 mon->send_reply(op, reply);
539done:
540 return true;
541}
542
543bool AuthMonitor::preprocess_command(MonOpRequestRef op)
544{
545 MMonCommand *m = static_cast<MMonCommand*>(op->get_req());
546 int r = -1;
547 bufferlist rdata;
548 stringstream ss, ds;
549
550 map<string, cmd_vartype> cmdmap;
551 if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
552 // ss has reason for failure
553 string rs = ss.str();
554 mon->reply_command(op, -EINVAL, rs, rdata, get_last_committed());
555 return true;
556 }
557
558 string prefix;
559 cmd_getval(g_ceph_context, cmdmap, "prefix", prefix);
560 if (prefix == "auth add" ||
561 prefix == "auth del" ||
562 prefix == "auth rm" ||
563 prefix == "auth get-or-create" ||
564 prefix == "auth get-or-create-key" ||
d2e6a577 565 prefix == "fs authorize" ||
7c673cae
FG
566 prefix == "auth import" ||
567 prefix == "auth caps") {
568 return false;
569 }
570
571 MonSession *session = m->get_session();
572 if (!session) {
573 mon->reply_command(op, -EACCES, "access denied", rdata, get_last_committed());
574 return true;
575 }
576
577 // entity might not be supplied, but if it is, it should be valid
578 string entity_name;
579 cmd_getval(g_ceph_context, cmdmap, "entity", entity_name);
580 EntityName entity;
581 if (!entity_name.empty() && !entity.from_str(entity_name)) {
582 ss << "invalid entity_auth " << entity_name;
583 mon->reply_command(op, -EINVAL, ss.str(), get_last_committed());
584 return true;
585 }
586
587 string format;
588 cmd_getval(g_ceph_context, cmdmap, "format", format, string("plain"));
589 boost::scoped_ptr<Formatter> f(Formatter::create(format));
590
591 if (prefix == "auth export") {
592 KeyRing keyring;
593 export_keyring(keyring);
594 if (!entity_name.empty()) {
595 EntityAuth eauth;
596 if (keyring.get_auth(entity, eauth)) {
597 KeyRing kr;
598 kr.add(entity, eauth);
599 if (f)
600 kr.encode_formatted("auth", f.get(), rdata);
601 else
602 kr.encode_plaintext(rdata);
603 ss << "export " << eauth;
604 r = 0;
605 } else {
606 ss << "no key for " << eauth;
607 r = -ENOENT;
608 }
609 } else {
610 if (f)
611 keyring.encode_formatted("auth", f.get(), rdata);
612 else
613 keyring.encode_plaintext(rdata);
614
615 ss << "exported master keyring";
616 r = 0;
617 }
618 } else if (prefix == "auth get" && !entity_name.empty()) {
619 KeyRing keyring;
620 EntityAuth entity_auth;
621 if(!mon->key_server.get_auth(entity, entity_auth)) {
622 ss << "failed to find " << entity_name << " in keyring";
623 r = -ENOENT;
624 } else {
625 keyring.add(entity, entity_auth);
626 if (f)
627 keyring.encode_formatted("auth", f.get(), rdata);
628 else
629 keyring.encode_plaintext(rdata);
630 ss << "exported keyring for " << entity_name;
631 r = 0;
632 }
633 } else if (prefix == "auth print-key" ||
634 prefix == "auth print_key" ||
635 prefix == "auth get-key") {
636 EntityAuth auth;
637 if (!mon->key_server.get_auth(entity, auth)) {
638 ss << "don't have " << entity;
639 r = -ENOENT;
640 goto done;
641 }
642 if (f) {
643 auth.key.encode_formatted("auth", f.get(), rdata);
644 } else {
645 auth.key.encode_plaintext(rdata);
646 }
647 r = 0;
c07f9fc5
FG
648 } else if (prefix == "auth list" ||
649 prefix == "auth ls") {
7c673cae
FG
650 if (f) {
651 mon->key_server.encode_formatted("auth", f.get(), rdata);
652 } else {
653 mon->key_server.encode_plaintext(rdata);
654 if (rdata.length() > 0)
655 ss << "installed auth entries:" << std::endl;
656 else
657 ss << "no installed auth entries!" << std::endl;
658 }
659 r = 0;
660 goto done;
661 } else {
662 ss << "invalid command";
663 r = -EINVAL;
664 }
665
666 done:
667 rdata.append(ds);
668 string rs;
669 getline(ss, rs, '\0');
670 mon->reply_command(op, r, rs, rdata, get_last_committed());
671 return true;
672}
673
674void AuthMonitor::export_keyring(KeyRing& keyring)
675{
676 mon->key_server.export_keyring(keyring);
677}
678
679int AuthMonitor::import_keyring(KeyRing& keyring)
680{
681 for (map<EntityName, EntityAuth>::iterator p = keyring.get_keys().begin();
682 p != keyring.get_keys().end();
683 ++p) {
684 if (p->second.caps.empty()) {
685 dout(0) << "import: no caps supplied" << dendl;
686 return -EINVAL;
687 }
31f18b77
FG
688 int err = add_entity(p->first, p->second);
689 assert(err == 0);
690 }
691 return 0;
692}
693
694int AuthMonitor::remove_entity(const EntityName &entity)
695{
696 dout(10) << __func__ << " " << entity << dendl;
697 if (!mon->key_server.contains(entity))
698 return -ENOENT;
699
700 KeyServerData::Incremental auth_inc;
701 auth_inc.name = entity;
702 auth_inc.op = KeyServerData::AUTH_INC_DEL;
703 push_cephx_inc(auth_inc);
704
705 return 0;
706}
707
708bool AuthMonitor::entity_is_pending(EntityName& entity)
709{
710 // are we about to have it?
711 for (auto& p : pending_auth) {
712 if (p.inc_type == AUTH_DATA) {
713 KeyServerData::Incremental inc;
714 bufferlist::iterator q = p.auth_data.begin();
715 ::decode(inc, q);
716 if (inc.op == KeyServerData::AUTH_INC_ADD &&
717 inc.name == entity) {
718 return true;
719 }
720 }
721 }
722 return false;
723}
724
725int AuthMonitor::exists_and_matches_entity(
726 const auth_entity_t& entity,
727 bool has_secret,
728 stringstream& ss)
729{
730 return exists_and_matches_entity(entity.name, entity.auth,
731 entity.auth.caps, has_secret, ss);
732}
733
734int AuthMonitor::exists_and_matches_entity(
735 const EntityName& name,
736 const EntityAuth& auth,
737 const map<string,bufferlist>& caps,
738 bool has_secret,
739 stringstream& ss)
740{
741
742 dout(20) << __func__ << " entity " << name << " auth " << auth
743 << " caps " << caps << " has_secret " << has_secret << dendl;
744
745 EntityAuth existing_auth;
746 // does entry already exist?
747 if (mon->key_server.get_auth(name, existing_auth)) {
748 // key match?
749 if (has_secret) {
750 if (existing_auth.key.get_secret().cmp(auth.key.get_secret())) {
751 ss << "entity " << name << " exists but key does not match";
752 return -EEXIST;
753 }
754 }
755
756 // caps match?
757 if (caps.size() != existing_auth.caps.size()) {
758 ss << "entity " << name << " exists but caps do not match";
759 return -EINVAL;
760 }
761 for (auto& it : caps) {
762 if (existing_auth.caps.count(it.first) == 0 ||
763 !existing_auth.caps[it.first].contents_equal(it.second)) {
764 ss << "entity " << name << " exists but cap "
765 << it.first << " does not match";
766 return -EINVAL;
767 }
768 }
769
770 // they match, no-op
771 return 0;
772 }
773 return -ENOENT;
774}
775
776int AuthMonitor::add_entity(
777 const EntityName& name,
778 const EntityAuth& auth)
779{
780
781 // okay, add it.
782 KeyServerData::Incremental auth_inc;
783 auth_inc.op = KeyServerData::AUTH_INC_ADD;
784 auth_inc.name = name;
785 auth_inc.auth = auth;
786
787 dout(10) << " importing " << auth_inc.name << dendl;
788 dout(30) << " " << auth_inc.auth << dendl;
789 push_cephx_inc(auth_inc);
790 return 0;
791}
792
793int AuthMonitor::validate_osd_destroy(
794 int32_t id,
795 const uuid_d& uuid,
796 EntityName& cephx_entity,
797 EntityName& lockbox_entity,
798 stringstream& ss)
799{
800 assert(paxos->is_plugged());
801
802 dout(10) << __func__ << " id " << id << " uuid " << uuid << dendl;
803
804 string cephx_str = "osd." + stringify(id);
805 string lockbox_str = "client.osd-lockbox." + stringify(uuid);
806
807 if (!cephx_entity.from_str(cephx_str)) {
808 dout(10) << __func__ << " invalid cephx entity '"
809 << cephx_str << "'" << dendl;
810 ss << "invalid cephx key entity '" << cephx_str << "'";
811 return -EINVAL;
812 }
813
814 if (!lockbox_entity.from_str(lockbox_str)) {
815 dout(10) << __func__ << " invalid lockbox entity '"
816 << lockbox_str << "'" << dendl;
817 ss << "invalid lockbox key entity '" << lockbox_str << "'";
818 return -EINVAL;
819 }
820
821 if (!mon->key_server.contains(cephx_entity) &&
822 !mon->key_server.contains(lockbox_entity)) {
823 return -ENOENT;
824 }
825
826 return 0;
827}
828
829int AuthMonitor::do_osd_destroy(
830 const EntityName& cephx_entity,
831 const EntityName& lockbox_entity)
832{
833 assert(paxos->is_plugged());
834
835 dout(10) << __func__ << " cephx " << cephx_entity
836 << " lockbox " << lockbox_entity << dendl;
837
838 bool removed = false;
839
840 int err = remove_entity(cephx_entity);
841 if (err == -ENOENT) {
842 dout(10) << __func__ << " " << cephx_entity << " does not exist" << dendl;
843 } else {
844 removed = true;
845 }
846
847 err = remove_entity(lockbox_entity);
848 if (err == -ENOENT) {
849 dout(10) << __func__ << " " << lockbox_entity << " does not exist" << dendl;
850 } else {
851 removed = true;
852 }
853
854 if (!removed) {
855 dout(10) << __func__ << " entities do not exist -- no-op." << dendl;
856 return 0;
857 }
858
859 // given we have paxos plugged, this will not result in a proposal
860 // being triggered, but it will still be needed so that we get our
861 // pending state encoded into the paxos' pending transaction.
862 propose_pending();
863 return 0;
864}
865
866bufferlist _encode_cap(const string& cap)
867{
868 bufferlist bl;
869 ::encode(cap, bl);
870 return bl;
871}
872
873int _create_auth(
874 EntityAuth& auth,
875 const string& key,
876 const map<string,bufferlist>& caps)
877{
878 if (key.empty())
879 return -EINVAL;
880 try {
881 auth.key.decode_base64(key);
882 } catch (buffer::error& e) {
883 return -EINVAL;
7c673cae 884 }
31f18b77
FG
885 auth.caps = caps;
886 return 0;
887}
888
889int AuthMonitor::validate_osd_new(
890 int32_t id,
891 const uuid_d& uuid,
892 const string& cephx_secret,
893 const string& lockbox_secret,
894 auth_entity_t& cephx_entity,
895 auth_entity_t& lockbox_entity,
896 stringstream& ss)
897{
898
899 dout(10) << __func__ << " osd." << id << " uuid " << uuid << dendl;
900
901 map<string,bufferlist> cephx_caps = {
902 { "osd", _encode_cap("allow *") },
903 { "mon", _encode_cap("allow profile osd") },
904 { "mgr", _encode_cap("allow profile osd") }
905 };
906 map<string,bufferlist> lockbox_caps = {
907 { "mon", _encode_cap("allow command \"config-key get\" "
908 "with key=\"dm-crypt/osd/" +
909 stringify(uuid) +
910 "/luks\"") }
911 };
912
913 bool has_lockbox = !lockbox_secret.empty();
914
915 string cephx_name = "osd." + stringify(id);
916 string lockbox_name = "client.osd-lockbox." + stringify(uuid);
917
918 if (!cephx_entity.name.from_str(cephx_name)) {
919 dout(10) << __func__ << " invalid cephx entity '"
920 << cephx_name << "'" << dendl;
921 ss << "invalid cephx key entity '" << cephx_name << "'";
922 return -EINVAL;
923 }
924
925 if (has_lockbox) {
926 if (!lockbox_entity.name.from_str(lockbox_name)) {
927 dout(10) << __func__ << " invalid cephx lockbox entity '"
928 << lockbox_name << "'" << dendl;
929 ss << "invalid cephx lockbox entity '" << lockbox_name << "'";
930 return -EINVAL;
931 }
932 }
933
934 if (entity_is_pending(cephx_entity.name) ||
935 (has_lockbox && entity_is_pending(lockbox_entity.name))) {
936 // If we have pending entities for either the cephx secret or the
937 // lockbox secret, then our safest bet is to retry the command at
938 // a later time. These entities may be pending because an `osd new`
939 // command has been run (which is unlikely, due to the nature of
940 // the operation, which will force a paxos proposal), or (more likely)
941 // because a competing client created those entities before we handled
942 // the `osd new` command. Regardless, let's wait and see.
943 return -EAGAIN;
944 }
945
946 if (!is_valid_cephx_key(cephx_secret)) {
947 ss << "invalid cephx secret.";
948 return -EINVAL;
949 }
950
951 if (has_lockbox && !is_valid_cephx_key(lockbox_secret)) {
952 ss << "invalid cephx lockbox secret.";
953 return -EINVAL;
954 }
955
956 int err = _create_auth(cephx_entity.auth, cephx_secret, cephx_caps);
957 assert(0 == err);
958
959 bool cephx_is_idempotent = false, lockbox_is_idempotent = false;
960 err = exists_and_matches_entity(cephx_entity, true, ss);
961
962 if (err != -ENOENT) {
963 if (err < 0) {
964 return err;
965 }
966 assert(0 == err);
967 cephx_is_idempotent = true;
968 }
969
970 if (has_lockbox) {
971 err = _create_auth(lockbox_entity.auth, lockbox_secret, lockbox_caps);
972 assert(err == 0);
973 err = exists_and_matches_entity(lockbox_entity, true, ss);
974 if (err != -ENOENT) {
975 if (err < 0) {
976 return err;
977 }
978 assert(0 == err);
979 lockbox_is_idempotent = true;
980 }
981 }
982
983 if (cephx_is_idempotent && (!has_lockbox || lockbox_is_idempotent)) {
984 return EEXIST;
985 }
986
987 return 0;
988}
989
990int AuthMonitor::do_osd_new(
991 const auth_entity_t& cephx_entity,
992 const auth_entity_t& lockbox_entity,
993 bool has_lockbox)
994{
995 assert(paxos->is_plugged());
996
997 dout(10) << __func__ << " cephx " << cephx_entity.name
998 << " lockbox ";
999 if (has_lockbox) {
1000 *_dout << lockbox_entity.name;
1001 } else {
1002 *_dout << "n/a";
1003 }
1004 *_dout << dendl;
1005
1006 // we must have validated before reaching this point.
1007 // if keys exist, then this means they also match; otherwise we would
1008 // have failed before calling this function.
1009 bool cephx_exists = mon->key_server.contains(cephx_entity.name);
1010
1011 if (!cephx_exists) {
1012 int err = add_entity(cephx_entity.name, cephx_entity.auth);
1013 assert(0 == err);
1014 }
1015
1016 if (has_lockbox &&
1017 !mon->key_server.contains(lockbox_entity.name)) {
1018 int err = add_entity(lockbox_entity.name, lockbox_entity.auth);
1019 assert(0 == err);
1020 }
1021
1022 // given we have paxos plugged, this will not result in a proposal
1023 // being triggered, but it will still be needed so that we get our
1024 // pending state encoded into the paxos' pending transaction.
1025 propose_pending();
7c673cae
FG
1026 return 0;
1027}
1028
1029bool AuthMonitor::prepare_command(MonOpRequestRef op)
1030{
1031 MMonCommand *m = static_cast<MMonCommand*>(op->get_req());
1032 stringstream ss, ds;
1033 bufferlist rdata;
1034 string rs;
1035 int err = -EINVAL;
1036
1037 map<string, cmd_vartype> cmdmap;
1038 if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
1039 // ss has reason for failure
1040 string rs = ss.str();
1041 mon->reply_command(op, -EINVAL, rs, rdata, get_last_committed());
1042 return true;
1043 }
1044
1045 string prefix;
1046 vector<string>caps_vec;
1047 string entity_name;
1048 EntityName entity;
1049
1050 cmd_getval(g_ceph_context, cmdmap, "prefix", prefix);
1051
1052 string format;
1053 cmd_getval(g_ceph_context, cmdmap, "format", format, string("plain"));
1054 boost::scoped_ptr<Formatter> f(Formatter::create(format));
1055
1056 MonSession *session = m->get_session();
1057 if (!session) {
1058 mon->reply_command(op, -EACCES, "access denied", rdata, get_last_committed());
1059 return true;
1060 }
1061
1062 cmd_getval(g_ceph_context, cmdmap, "caps", caps_vec);
1063 if ((caps_vec.size() % 2) != 0) {
1064 ss << "bad capabilities request; odd number of arguments";
1065 err = -EINVAL;
1066 goto done;
1067 }
1068
1069 cmd_getval(g_ceph_context, cmdmap, "entity", entity_name);
1070 if (!entity_name.empty() && !entity.from_str(entity_name)) {
1071 ss << "bad entity name";
1072 err = -EINVAL;
1073 goto done;
1074 }
1075
1076 if (prefix == "auth import") {
1077 bufferlist bl = m->get_data();
1078 if (bl.length() == 0) {
1079 ss << "auth import: no data supplied";
1080 getline(ss, rs);
1081 mon->reply_command(op, -EINVAL, rs, get_last_committed());
1082 return true;
1083 }
1084 bufferlist::iterator iter = bl.begin();
1085 KeyRing keyring;
1086 try {
1087 ::decode(keyring, iter);
1088 } catch (const buffer::error &ex) {
1089 ss << "error decoding keyring" << " " << ex.what();
1090 err = -EINVAL;
1091 goto done;
1092 }
1093 err = import_keyring(keyring);
1094 if (err < 0) {
1095 ss << "auth import: no caps supplied";
1096 getline(ss, rs);
1097 mon->reply_command(op, -EINVAL, rs, get_last_committed());
1098 return true;
1099 }
1100 ss << "imported keyring";
1101 getline(ss, rs);
1102 err = 0;
1103 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1104 get_last_committed() + 1));
1105 return true;
1106 } else if (prefix == "auth add" && !entity_name.empty()) {
1107 /* expected behavior:
1108 * - if command reproduces current state, return 0.
1109 * - if command adds brand new entity, handle it.
1110 * - if command adds new state to existing entity, return error.
1111 */
1112 KeyServerData::Incremental auth_inc;
1113 auth_inc.name = entity;
1114 bufferlist bl = m->get_data();
1115 bool has_keyring = (bl.length() > 0);
1116 map<string,bufferlist> new_caps;
1117
1118 KeyRing new_keyring;
1119 if (has_keyring) {
1120 bufferlist::iterator iter = bl.begin();
1121 try {
1122 ::decode(new_keyring, iter);
1123 } catch (const buffer::error &ex) {
1124 ss << "error decoding keyring";
1125 err = -EINVAL;
1126 goto done;
1127 }
1128 }
1129
1130 // are we about to have it?
31f18b77
FG
1131 if (entity_is_pending(entity)) {
1132 wait_for_finished_proposal(op,
1133 new Monitor::C_Command(mon, op, 0, rs, get_last_committed() + 1));
1134 return true;
7c673cae
FG
1135 }
1136
1137 // build new caps from provided arguments (if available)
1138 for (vector<string>::iterator it = caps_vec.begin();
1139 it != caps_vec.end() && (it + 1) != caps_vec.end();
1140 it += 2) {
1141 string sys = *it;
1142 bufferlist cap;
1143 ::encode(*(it+1), cap);
1144 new_caps[sys] = cap;
1145 }
1146
1147 // pull info out of provided keyring
1148 EntityAuth new_inc;
1149 if (has_keyring) {
1150 if (!new_keyring.get_auth(auth_inc.name, new_inc)) {
1151 ss << "key for " << auth_inc.name
1152 << " not found in provided keyring";
1153 err = -EINVAL;
1154 goto done;
1155 }
1156 if (!new_caps.empty() && !new_inc.caps.empty()) {
1157 ss << "caps cannot be specified both in keyring and in command";
1158 err = -EINVAL;
1159 goto done;
1160 }
1161 if (new_caps.empty()) {
1162 new_caps = new_inc.caps;
1163 }
1164 }
1165
31f18b77
FG
1166 err = exists_and_matches_entity(auth_inc.name, new_inc,
1167 new_caps, has_keyring, ss);
1168 // if entity/key/caps do not exist in the keyring, just fall through
1169 // and add the entity; otherwise, make sure everything matches (in
1170 // which case it's a no-op), because if not we must fail.
1171 if (err != -ENOENT) {
1172 if (err < 0) {
1173 goto done;
7c673cae 1174 }
31f18b77
FG
1175 // no-op.
1176 assert(err == 0);
7c673cae
FG
1177 goto done;
1178 }
31f18b77 1179 err = 0;
7c673cae
FG
1180
1181 // okay, add it.
31f18b77 1182 if (!has_keyring) {
7c673cae 1183 dout(10) << "AuthMonitor::prepare_command generating random key for "
31f18b77
FG
1184 << auth_inc.name << dendl;
1185 new_inc.key.create(g_ceph_context, CEPH_CRYPTO_AES);
7c673cae 1186 }
31f18b77 1187 new_inc.caps = new_caps;
7c673cae 1188
31f18b77
FG
1189 err = add_entity(auth_inc.name, new_inc);
1190 assert(err == 0);
7c673cae
FG
1191
1192 ss << "added key for " << auth_inc.name;
1193 getline(ss, rs);
1194 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1195 get_last_committed() + 1));
1196 return true;
1197 } else if ((prefix == "auth get-or-create-key" ||
1198 prefix == "auth get-or-create") &&
1199 !entity_name.empty()) {
1200 // auth get-or-create <name> [mon osdcapa osd osdcapb ...]
1201
1202 if (!valid_caps(caps_vec, &ss)) {
1203 err = -EINVAL;
1204 goto done;
1205 }
1206
1207 // Parse the list of caps into a map
1208 std::map<std::string, bufferlist> wanted_caps;
1209 for (vector<string>::const_iterator it = caps_vec.begin();
1210 it != caps_vec.end() && (it + 1) != caps_vec.end();
1211 it += 2) {
1212 const std::string &sys = *it;
1213 bufferlist cap;
1214 ::encode(*(it+1), cap);
1215 wanted_caps[sys] = cap;
1216 }
1217
1218 // do we have it?
1219 EntityAuth entity_auth;
1220 if (mon->key_server.get_auth(entity, entity_auth)) {
1221 for (const auto &sys_cap : wanted_caps) {
1222 if (entity_auth.caps.count(sys_cap.first) == 0 ||
1223 !entity_auth.caps[sys_cap.first].contents_equal(sys_cap.second)) {
1224 ss << "key for " << entity << " exists but cap " << sys_cap.first
1225 << " does not match";
1226 err = -EINVAL;
1227 goto done;
1228 }
1229 }
1230
1231 if (prefix == "auth get-or-create-key") {
1232 if (f) {
1233 entity_auth.key.encode_formatted("auth", f.get(), rdata);
1234 } else {
1235 ds << entity_auth.key;
1236 }
1237 } else {
1238 KeyRing kr;
1239 kr.add(entity, entity_auth.key);
1240 if (f) {
1241 kr.set_caps(entity, entity_auth.caps);
1242 kr.encode_formatted("auth", f.get(), rdata);
1243 } else {
1244 kr.encode_plaintext(rdata);
1245 }
1246 }
1247 err = 0;
1248 goto done;
1249 }
1250
1251 // ...or are we about to?
1252 for (vector<Incremental>::iterator p = pending_auth.begin();
1253 p != pending_auth.end();
1254 ++p) {
1255 if (p->inc_type == AUTH_DATA) {
1256 KeyServerData::Incremental auth_inc;
1257 bufferlist::iterator q = p->auth_data.begin();
1258 ::decode(auth_inc, q);
1259 if (auth_inc.op == KeyServerData::AUTH_INC_ADD &&
1260 auth_inc.name == entity) {
1261 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1262 get_last_committed() + 1));
1263 return true;
1264 }
1265 }
1266 }
1267
1268 // create it
1269 KeyServerData::Incremental auth_inc;
1270 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1271 auth_inc.name = entity;
1272 auth_inc.auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1273 auth_inc.auth.caps = wanted_caps;
1274
1275 push_cephx_inc(auth_inc);
1276
1277 if (prefix == "auth get-or-create-key") {
1278 if (f) {
1279 auth_inc.auth.key.encode_formatted("auth", f.get(), rdata);
1280 } else {
1281 ds << auth_inc.auth.key;
1282 }
1283 } else {
1284 KeyRing kr;
1285 kr.add(entity, auth_inc.auth.key);
1286 if (f) {
1287 kr.set_caps(entity, wanted_caps);
1288 kr.encode_formatted("auth", f.get(), rdata);
1289 } else {
1290 kr.encode_plaintext(rdata);
1291 }
1292 }
1293
1294 rdata.append(ds);
1295 getline(ss, rs);
1296 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs, rdata,
1297 get_last_committed() + 1));
1298 return true;
d2e6a577
FG
1299 } else if (prefix == "fs authorize") {
1300 string filesystem;
1301 cmd_getval(g_ceph_context, cmdmap, "filesystem", filesystem);
1302 string mds_cap_string, osd_cap_string;
1303 string osd_cap_wanted = "r";
1304
1305 for (auto it = caps_vec.begin();
1306 it != caps_vec.end() && (it + 1) != caps_vec.end();
1307 it += 2) {
1308 const string &path = *it;
1309 const string &cap = *(it+1);
1310 if (cap != "r" && cap != "rw" && cap != "rwp") {
1311 ss << "Only 'r', 'rw', and 'rwp' permissions are allowed for filesystems.";
1312 err = -EINVAL;
1313 goto done;
1314 }
1315 if (cap.find('w') != string::npos) {
1316 osd_cap_wanted = "rw";
1317 }
1318
1319 mds_cap_string += mds_cap_string.empty() ? "" : ", ";
1320 mds_cap_string += "allow " + cap;
1321 if (path != "/") {
1322 mds_cap_string += " path=" + path;
1323 }
1324 }
1325
1326 auto fs = mon->mdsmon()->get_fsmap().get_filesystem(filesystem);
1327 if (!fs) {
1328 ss << "filesystem " << filesystem << " does not exist.";
1329 err = -EINVAL;
1330 goto done;
1331 }
1332
1333 auto data_pools = fs->mds_map.get_data_pools();
1334 for (auto p : data_pools) {
1335 const string &pool_name = mon->osdmon()->osdmap.get_pool_name(p);
1336 osd_cap_string += osd_cap_string.empty() ? "" : ", ";
1337 osd_cap_string += "allow " + osd_cap_wanted + " pool=" + pool_name;
1338 }
1339
1340 std::map<string, bufferlist> wanted_caps = {
1341 { "mon", _encode_cap("allow r") },
1342 { "osd", _encode_cap(osd_cap_string) },
1343 { "mds", _encode_cap(mds_cap_string) }
1344 };
1345
1346 EntityAuth entity_auth;
1347 if (mon->key_server.get_auth(entity, entity_auth)) {
1348 for (const auto &sys_cap : wanted_caps) {
1349 if (entity_auth.caps.count(sys_cap.first) == 0 ||
1350 !entity_auth.caps[sys_cap.first].contents_equal(sys_cap.second)) {
28e407b8
AA
1351 ss << entity << " already has fs capabilities that differ from those supplied. To generate a new auth key for "
1352 << entity << ", first remove " << entity << " from configuration files, execute 'ceph auth rm " << entity << "', then execute this command again.";
d2e6a577
FG
1353 err = -EINVAL;
1354 goto done;
1355 }
1356 }
1357
1358 KeyRing kr;
1359 kr.add(entity, entity_auth.key);
1360 if (f) {
1361 kr.set_caps(entity, entity_auth.caps);
1362 kr.encode_formatted("auth", f.get(), rdata);
1363 } else {
1364 kr.encode_plaintext(rdata);
1365 }
1366 err = 0;
1367 goto done;
1368 }
1369
1370 KeyServerData::Incremental auth_inc;
1371 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1372 auth_inc.name = entity;
1373 auth_inc.auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
1374 auth_inc.auth.caps = wanted_caps;
1375
1376 push_cephx_inc(auth_inc);
1377 KeyRing kr;
1378 kr.add(entity, auth_inc.auth.key);
1379 if (f) {
1380 kr.set_caps(entity, wanted_caps);
1381 kr.encode_formatted("auth", f.get(), rdata);
1382 } else {
1383 kr.encode_plaintext(rdata);
1384 }
1385
1386 rdata.append(ds);
1387 getline(ss, rs);
1388 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs, rdata,
1389 get_last_committed() + 1));
1390 return true;
7c673cae
FG
1391 } else if (prefix == "auth caps" && !entity_name.empty()) {
1392 KeyServerData::Incremental auth_inc;
1393 auth_inc.name = entity;
1394 if (!mon->key_server.get_auth(auth_inc.name, auth_inc.auth)) {
1395 ss << "couldn't find entry " << auth_inc.name;
1396 err = -ENOENT;
1397 goto done;
1398 }
1399
1400 if (!valid_caps(caps_vec, &ss)) {
1401 err = -EINVAL;
1402 goto done;
1403 }
1404
1405 map<string,bufferlist> newcaps;
1406 for (vector<string>::iterator it = caps_vec.begin();
1407 it != caps_vec.end(); it += 2)
1408 ::encode(*(it+1), newcaps[*it]);
1409
1410 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1411 auth_inc.auth.caps = newcaps;
1412 push_cephx_inc(auth_inc);
1413
1414 ss << "updated caps for " << auth_inc.name;
1415 getline(ss, rs);
1416 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1417 get_last_committed() + 1));
1418 return true;
1419 } else if ((prefix == "auth del" || prefix == "auth rm") &&
1420 !entity_name.empty()) {
1421 KeyServerData::Incremental auth_inc;
1422 auth_inc.name = entity;
1423 if (!mon->key_server.contains(auth_inc.name)) {
1424 ss << "entity " << entity << " does not exist";
1425 err = 0;
1426 goto done;
1427 }
1428 auth_inc.op = KeyServerData::AUTH_INC_DEL;
1429 push_cephx_inc(auth_inc);
1430
1431 ss << "updated";
1432 getline(ss, rs);
1433 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1434 get_last_committed() + 1));
1435 return true;
1436 }
7c673cae
FG
1437done:
1438 rdata.append(ds);
1439 getline(ss, rs, '\0');
1440 mon->reply_command(op, err, rs, rdata, get_last_committed());
1441 return false;
1442}
1443
1444bool AuthMonitor::prepare_global_id(MonOpRequestRef op)
1445{
1446 dout(10) << "AuthMonitor::prepare_global_id" << dendl;
1447 increase_max_global_id();
1448
1449 return true;
1450}
1451
1452void AuthMonitor::upgrade_format()
1453{
1454 unsigned int current = 2;
1455 if (!mon->get_quorum_mon_features().contains_all(
1456 ceph::features::mon::FEATURE_LUMINOUS)) {
1457 current = 1;
1458 }
1459 if (format_version >= current) {
1460 dout(20) << __func__ << " format " << format_version << " is current" << dendl;
1461 return;
1462 }
1463
1464 bool changed = false;
1465 if (format_version == 0) {
1466 dout(1) << __func__ << " upgrading from format 0 to 1" << dendl;
1467 map<EntityName, EntityAuth>::iterator p;
1468 for (p = mon->key_server.secrets_begin();
1469 p != mon->key_server.secrets_end();
1470 ++p) {
1471 // grab mon caps, if any
1472 string mon_caps;
1473 if (p->second.caps.count("mon") == 0)
1474 continue;
1475 try {
1476 bufferlist::iterator it = p->second.caps["mon"].begin();
1477 ::decode(mon_caps, it);
1478 }
1479 catch (buffer::error) {
1480 dout(10) << __func__ << " unable to parse mon cap for "
1481 << p->first << dendl;
1482 continue;
1483 }
1484
1485 string n = p->first.to_str();
1486 string new_caps;
1487
1488 // set daemon profiles
1489 if ((p->first.is_osd() || p->first.is_mds()) &&
1490 mon_caps == "allow rwx") {
1491 new_caps = string("allow profile ") + string(p->first.get_type_name());
1492 }
1493
1494 // update bootstrap keys
1495 if (n == "client.bootstrap-osd") {
1496 new_caps = "allow profile bootstrap-osd";
1497 }
1498 if (n == "client.bootstrap-mds") {
1499 new_caps = "allow profile bootstrap-mds";
1500 }
1501
1502 if (new_caps.length() > 0) {
1503 dout(5) << __func__ << " updating " << p->first << " mon cap from "
1504 << mon_caps << " to " << new_caps << dendl;
1505
1506 bufferlist bl;
1507 ::encode(new_caps, bl);
1508
1509 KeyServerData::Incremental auth_inc;
1510 auth_inc.name = p->first;
1511 auth_inc.auth = p->second;
1512 auth_inc.auth.caps["mon"] = bl;
1513 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1514 push_cephx_inc(auth_inc);
1515 changed = true;
1516 }
1517 }
1518 }
1519
1520 if (format_version == 1) {
1521 dout(1) << __func__ << " upgrading from format 1 to 2" << dendl;
1522 map<EntityName, EntityAuth>::iterator p;
1523 for (p = mon->key_server.secrets_begin();
1524 p != mon->key_server.secrets_end();
1525 ++p) {
1526 string n = p->first.to_str();
1527
1528 string newcap;
1529 if (n == "client.admin") {
1530 // admin gets it all
1531 newcap = "allow *";
1532 } else if (n.find("osd.") == 0 ||
1533 n.find("mds.") == 0 ||
1534 n.find("mon.") == 0) {
1535 // daemons follow their profile
1536 string type = n.substr(0, 3);
1537 newcap = "allow profile " + type;
1538 } else if (p->second.caps.count("mon")) {
1539 // if there are any mon caps, give them 'r' mgr caps
1540 newcap = "allow r";
1541 }
1542
1543 if (newcap.length() > 0) {
1544 dout(5) << " giving " << n << " mgr '" << newcap << "'" << dendl;
1545 bufferlist bl;
1546 ::encode(newcap, bl);
1547
1548 KeyServerData::Incremental auth_inc;
1549 auth_inc.name = p->first;
1550 auth_inc.auth = p->second;
1551 auth_inc.auth.caps["mgr"] = bl;
1552 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1553 push_cephx_inc(auth_inc);
1554 }
1555
1556 if (n.find("mgr.") == 0 &&
1557 p->second.caps.count("mon")) {
1558 // the kraken ceph-mgr@.service set the mon cap to 'allow *'.
1559 auto blp = p->second.caps["mon"].begin();
1560 string oldcaps;
1561 ::decode(oldcaps, blp);
1562 if (oldcaps == "allow *") {
1563 dout(5) << " fixing " << n << " mon cap to 'allow profile mgr'"
1564 << dendl;
1565 bufferlist bl;
1566 ::encode("allow profile mgr", bl);
1567 KeyServerData::Incremental auth_inc;
1568 auth_inc.name = p->first;
1569 auth_inc.auth = p->second;
1570 auth_inc.auth.caps["mon"] = bl;
1571 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1572 push_cephx_inc(auth_inc);
1573 }
1574 }
1575 }
1576
3efd9988
FG
1577 // add bootstrap key if it does not already exist
1578 // (might have already been get-or-create'd by
1579 // ceph-create-keys)
1580 EntityName bootstrap_mgr_name;
1581 int r = bootstrap_mgr_name.from_str("client.bootstrap-mgr");
1582 assert(r);
1583 if (!mon->key_server.contains(bootstrap_mgr_name)) {
7c673cae 1584 KeyServerData::Incremental auth_inc;
3efd9988 1585 auth_inc.name = bootstrap_mgr_name;
7c673cae
FG
1586 ::encode("allow profile bootstrap-mgr", auth_inc.auth.caps["mon"]);
1587 auth_inc.op = KeyServerData::AUTH_INC_ADD;
c07f9fc5
FG
1588 // generate key
1589 auth_inc.auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
7c673cae
FG
1590 push_cephx_inc(auth_inc);
1591 }
1592 changed = true;
1593 }
1594
1595 if (changed) {
1596 // note new format
1597 dout(10) << __func__ << " proposing update from format " << format_version
1598 << " -> " << current << dendl;
1599 format_version = current;
1600 propose_pending();
1601 }
1602}
1603
1604void AuthMonitor::dump_info(Formatter *f)
1605{
1606 /*** WARNING: do not include any privileged information here! ***/
1607 f->open_object_section("auth");
1608 f->dump_unsigned("first_committed", get_first_committed());
1609 f->dump_unsigned("last_committed", get_last_committed());
1610 f->dump_unsigned("num_secrets", mon->key_server.get_num_secrets());
1611 f->close_section();
1612}