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