]> git.proxmox.com Git - ceph.git/blob - ceph/src/mon/AuthMonitor.cc
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / mon / AuthMonitor.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15 #include <sstream>
16
17 #include "mon/AuthMonitor.h"
18 #include "mon/Monitor.h"
19 #include "mon/MonitorDBStore.h"
20
21 #include "messages/MMonCommand.h"
22 #include "messages/MAuth.h"
23 #include "messages/MAuthReply.h"
24 #include "messages/MMonGlobalID.h"
25 #include "msg/Messenger.h"
26
27 #include "auth/AuthServiceHandler.h"
28 #include "auth/KeyRing.h"
29 #include "include/assert.h"
30
31 #define dout_subsys ceph_subsys_mon
32 #undef dout_prefix
33 #define dout_prefix _prefix(_dout, mon, get_last_committed())
34 static ostream& _prefix(std::ostream *_dout, Monitor *mon, version_t v) {
35 return *_dout << "mon." << mon->name << "@" << mon->rank
36 << "(" << mon->get_state_name()
37 << ").auth v" << v << " ";
38 }
39
40 ostream& operator<<(ostream &out, const AuthMonitor &pm)
41 {
42 return out << "auth";
43 }
44
45 bool AuthMonitor::check_rotate()
46 {
47 KeyServerData::Incremental rot_inc;
48 rot_inc.op = KeyServerData::AUTH_INC_SET_ROTATING;
49 if (!mon->key_server.updated_rotating(rot_inc.rotating_bl, last_rotating_ver))
50 return false;
51 dout(10) << __func__ << " updated rotating" << dendl;
52 push_cephx_inc(rot_inc);
53 return true;
54 }
55
56 /*
57 Tick function to update the map based on performance every N seconds
58 */
59
60 void AuthMonitor::tick()
61 {
62 if (!is_active()) return;
63
64 dout(10) << *this << dendl;
65
66 if (!mon->is_leader()) return;
67
68 if (check_rotate())
69 propose_pending();
70 }
71
72 void AuthMonitor::on_active()
73 {
74 dout(10) << "AuthMonitor::on_active()" << dendl;
75
76 if (!mon->is_leader())
77 return;
78 mon->key_server.start_server();
79 }
80
81 void AuthMonitor::create_initial()
82 {
83 dout(10) << "create_initial -- creating initial map" << dendl;
84
85 // initialize rotating keys
86 last_rotating_ver = 0;
87 check_rotate();
88 assert(pending_auth.size() == 1);
89
90 if (mon->is_keyring_required()) {
91 KeyRing keyring;
92 bufferlist bl;
93 int ret = mon->store->get("mkfs", "keyring", bl);
94 // fail hard only if there's an error we're not expecting to see
95 assert((ret == 0) || (ret == -ENOENT));
96
97 // try importing only if there's a key
98 if (ret == 0) {
99 KeyRing keyring;
100 bufferlist::iterator p = bl.begin();
101
102 ::decode(keyring, p);
103 import_keyring(keyring);
104 }
105 }
106
107 max_global_id = MIN_GLOBAL_ID;
108
109 Incremental inc;
110 inc.inc_type = GLOBAL_ID;
111 inc.max_global_id = max_global_id;
112 pending_auth.push_back(inc);
113
114 format_version = 2;
115 }
116
117 void AuthMonitor::update_from_paxos(bool *need_bootstrap)
118 {
119 dout(10) << __func__ << dendl;
120 version_t version = get_last_committed();
121 version_t keys_ver = mon->key_server.get_ver();
122 if (version == keys_ver)
123 return;
124 assert(version > keys_ver);
125
126 version_t latest_full = get_version_latest_full();
127
128 dout(10) << __func__ << " version " << version << " keys ver " << keys_ver
129 << " latest " << latest_full << dendl;
130
131 if ((latest_full > 0) && (latest_full > keys_ver)) {
132 bufferlist latest_bl;
133 int err = get_version_full(latest_full, latest_bl);
134 assert(err == 0);
135 assert(latest_bl.length() != 0);
136 dout(7) << __func__ << " loading summary e " << latest_full << dendl;
137 dout(7) << __func__ << " latest length " << latest_bl.length() << dendl;
138 bufferlist::iterator p = latest_bl.begin();
139 __u8 struct_v;
140 ::decode(struct_v, p);
141 ::decode(max_global_id, p);
142 ::decode(mon->key_server, p);
143 mon->key_server.set_ver(latest_full);
144 keys_ver = latest_full;
145 }
146
147 dout(10) << __func__ << " key server version " << mon->key_server.get_ver() << dendl;
148
149 // walk through incrementals
150 while (version > keys_ver) {
151 bufferlist bl;
152 int ret = get_version(keys_ver+1, bl);
153 assert(ret == 0);
154 assert(bl.length());
155
156 // reset if we are moving to initial state. we will normally have
157 // keys in here temporarily for bootstrapping that we need to
158 // clear out.
159 if (keys_ver == 0)
160 mon->key_server.clear_secrets();
161
162 dout(20) << __func__ << " walking through version " << (keys_ver+1)
163 << " len " << bl.length() << dendl;
164
165 bufferlist::iterator p = bl.begin();
166 __u8 v;
167 ::decode(v, p);
168 while (!p.end()) {
169 Incremental inc;
170 ::decode(inc, p);
171 switch (inc.inc_type) {
172 case GLOBAL_ID:
173 max_global_id = inc.max_global_id;
174 break;
175
176 case AUTH_DATA:
177 {
178 KeyServerData::Incremental auth_inc;
179 bufferlist::iterator iter = inc.auth_data.begin();
180 ::decode(auth_inc, iter);
181 mon->key_server.apply_data_incremental(auth_inc);
182 break;
183 }
184 }
185 }
186
187 keys_ver++;
188 mon->key_server.set_ver(keys_ver);
189
190 if (keys_ver == 1 && mon->is_keyring_required()) {
191 auto t(std::make_shared<MonitorDBStore::Transaction>());
192 t->erase("mkfs", "keyring");
193 mon->store->apply_transaction(t);
194 }
195 }
196
197 if (last_allocated_id == 0)
198 last_allocated_id = max_global_id;
199
200 dout(10) << "update_from_paxos() last_allocated_id=" << last_allocated_id
201 << " max_global_id=" << max_global_id
202 << " format_version " << format_version
203 << dendl;
204 }
205
206 void AuthMonitor::increase_max_global_id()
207 {
208 assert(mon->is_leader());
209
210 max_global_id += g_conf->mon_globalid_prealloc;
211 dout(10) << "increasing max_global_id to " << max_global_id << dendl;
212 Incremental inc;
213 inc.inc_type = GLOBAL_ID;
214 inc.max_global_id = max_global_id;
215 pending_auth.push_back(inc);
216 }
217
218 bool AuthMonitor::should_propose(double& delay)
219 {
220 return (!pending_auth.empty());
221 }
222
223 void AuthMonitor::create_pending()
224 {
225 pending_auth.clear();
226 dout(10) << "create_pending v " << (get_last_committed() + 1) << dendl;
227 }
228
229 void AuthMonitor::encode_pending(MonitorDBStore::TransactionRef t)
230 {
231 dout(10) << __func__ << " v " << (get_last_committed() + 1) << dendl;
232
233 bufferlist bl;
234
235 __u8 v = 1;
236 ::encode(v, bl);
237 vector<Incremental>::iterator p;
238 for (p = pending_auth.begin(); p != pending_auth.end(); ++p)
239 p->encode(bl, mon->get_quorum_con_features());
240
241 version_t version = get_last_committed() + 1;
242 put_version(t, version, bl);
243 put_last_committed(t, version);
244 }
245
246 void AuthMonitor::encode_full(MonitorDBStore::TransactionRef t)
247 {
248 version_t version = mon->key_server.get_ver();
249 // do not stash full version 0 as it will never be removed nor read
250 if (version == 0)
251 return;
252
253 dout(10) << __func__ << " auth v " << version << dendl;
254 assert(get_last_committed() == version);
255
256 bufferlist full_bl;
257 Mutex::Locker l(mon->key_server.get_lock());
258 dout(20) << __func__ << " key server has "
259 << (mon->key_server.has_secrets() ? "" : "no ")
260 << "secrets!" << dendl;
261 __u8 v = 1;
262 ::encode(v, full_bl);
263 ::encode(max_global_id, full_bl);
264 ::encode(mon->key_server, full_bl);
265
266 put_version_full(t, version, full_bl);
267 put_version_latest_full(t, version);
268 }
269
270 version_t AuthMonitor::get_trim_to()
271 {
272 unsigned max = g_conf->paxos_max_join_drift * 2;
273 version_t version = get_last_committed();
274 if (mon->is_leader() && (version > max))
275 return version - max;
276 return 0;
277 }
278
279 bool AuthMonitor::preprocess_query(MonOpRequestRef op)
280 {
281 PaxosServiceMessage *m = static_cast<PaxosServiceMessage*>(op->get_req());
282 dout(10) << "preprocess_query " << *m << " from " << m->get_orig_source_inst() << dendl;
283 switch (m->get_type()) {
284 case MSG_MON_COMMAND:
285 return preprocess_command(op);
286
287 case CEPH_MSG_AUTH:
288 return prep_auth(op, false);
289
290 case MSG_MON_GLOBAL_ID:
291 return false;
292
293 default:
294 ceph_abort();
295 return true;
296 }
297 }
298
299 bool AuthMonitor::prepare_update(MonOpRequestRef op)
300 {
301 PaxosServiceMessage *m = static_cast<PaxosServiceMessage*>(op->get_req());
302 dout(10) << "prepare_update " << *m << " from " << m->get_orig_source_inst() << dendl;
303 switch (m->get_type()) {
304 case MSG_MON_COMMAND:
305 return prepare_command(op);
306 case MSG_MON_GLOBAL_ID:
307 return prepare_global_id(op);
308 case CEPH_MSG_AUTH:
309 return prep_auth(op, true);
310 default:
311 ceph_abort();
312 return false;
313 }
314 }
315
316 uint64_t AuthMonitor::assign_global_id(MonOpRequestRef op, bool should_increase_max)
317 {
318 MAuth *m = static_cast<MAuth*>(op->get_req());
319 int total_mon = mon->monmap->size();
320 dout(10) << "AuthMonitor::assign_global_id m=" << *m << " mon=" << mon->rank << "/" << total_mon
321 << " last_allocated=" << last_allocated_id << " max_global_id=" << max_global_id << dendl;
322
323 uint64_t next_global_id = last_allocated_id + 1;
324 int remainder = next_global_id % total_mon;
325 if (remainder)
326 remainder = total_mon - remainder;
327 next_global_id += remainder + mon->rank;
328 dout(10) << "next_global_id should be " << next_global_id << dendl;
329
330 // if we can't bump the max, bail out now on an out-of-bounds gid
331 if (next_global_id > max_global_id &&
332 (!mon->is_leader() || !should_increase_max)) {
333 return 0;
334 }
335
336 // can we return a gid?
337 bool return_next = (next_global_id <= max_global_id);
338
339 // bump the max?
340 while (mon->is_leader() &&
341 (max_global_id < g_conf->mon_globalid_prealloc ||
342 next_global_id >= max_global_id - g_conf->mon_globalid_prealloc / 2)) {
343 increase_max_global_id();
344 }
345
346 if (return_next) {
347 last_allocated_id = next_global_id;
348 return next_global_id;
349 } else {
350 return 0;
351 }
352 }
353
354
355 bool AuthMonitor::prep_auth(MonOpRequestRef op, bool paxos_writable)
356 {
357 MAuth *m = static_cast<MAuth*>(op->get_req());
358 dout(10) << "prep_auth() blob_size=" << m->get_auth_payload().length() << dendl;
359
360 MonSession *s = op->get_session();
361 if (!s) {
362 dout(10) << "no session, dropping" << dendl;
363 return true;
364 }
365
366 int ret = 0;
367 AuthCapsInfo caps_info;
368 MAuthReply *reply;
369 bufferlist response_bl;
370 bufferlist::iterator indata = m->auth_payload.begin();
371 __u32 proto = m->protocol;
372 bool start = false;
373 EntityName entity_name;
374
375 // set up handler?
376 if (m->protocol == 0 && !s->auth_handler) {
377 set<__u32> supported;
378
379 try {
380 __u8 struct_v = 1;
381 ::decode(struct_v, indata);
382 ::decode(supported, indata);
383 ::decode(entity_name, indata);
384 ::decode(s->global_id, indata);
385 } catch (const buffer::error &e) {
386 dout(10) << "failed to decode initial auth message" << dendl;
387 ret = -EINVAL;
388 goto reply;
389 }
390
391 // do we require cephx signatures?
392
393 if (!m->get_connection()->has_feature(CEPH_FEATURE_MSG_AUTH)) {
394 if (entity_name.get_type() == CEPH_ENTITY_TYPE_MON ||
395 entity_name.get_type() == CEPH_ENTITY_TYPE_OSD ||
396 entity_name.get_type() == CEPH_ENTITY_TYPE_MDS ||
397 entity_name.get_type() == CEPH_ENTITY_TYPE_MGR) {
398 if (g_conf->cephx_cluster_require_signatures ||
399 g_conf->cephx_require_signatures) {
400 dout(1) << m->get_source_inst()
401 << " supports cephx but not signatures and"
402 << " 'cephx [cluster] require signatures = true';"
403 << " disallowing cephx" << dendl;
404 supported.erase(CEPH_AUTH_CEPHX);
405 }
406 } else {
407 if (g_conf->cephx_service_require_signatures ||
408 g_conf->cephx_require_signatures) {
409 dout(1) << m->get_source_inst()
410 << " supports cephx but not signatures and"
411 << " 'cephx [service] require signatures = true';"
412 << " disallowing cephx" << dendl;
413 supported.erase(CEPH_AUTH_CEPHX);
414 }
415 }
416 }
417
418 int type;
419 if (entity_name.get_type() == CEPH_ENTITY_TYPE_MON ||
420 entity_name.get_type() == CEPH_ENTITY_TYPE_OSD ||
421 entity_name.get_type() == CEPH_ENTITY_TYPE_MDS ||
422 entity_name.get_type() == CEPH_ENTITY_TYPE_MGR)
423 type = mon->auth_cluster_required.pick(supported);
424 else
425 type = mon->auth_service_required.pick(supported);
426
427 s->auth_handler = get_auth_service_handler(type, g_ceph_context, &mon->key_server);
428 if (!s->auth_handler) {
429 dout(1) << "client did not provide supported auth type" << dendl;
430 ret = -ENOTSUP;
431 goto reply;
432 }
433 start = true;
434 } else if (!s->auth_handler) {
435 dout(10) << "protocol specified but no s->auth_handler" << dendl;
436 ret = -EINVAL;
437 goto reply;
438 }
439
440 /* assign a new global_id? we assume this should only happen on the first
441 request. If a client tries to send it later, it'll screw up its auth
442 session */
443 if (!s->global_id) {
444 s->global_id = assign_global_id(op, paxos_writable);
445 if (!s->global_id) {
446
447 delete s->auth_handler;
448 s->auth_handler = NULL;
449
450 if (mon->is_leader() && paxos_writable) {
451 dout(10) << "increasing global id, waitlisting message" << dendl;
452 wait_for_active(op, new C_RetryMessage(this, op));
453 goto done;
454 }
455
456 if (!mon->is_leader()) {
457 dout(10) << "not the leader, requesting more ids from leader" << dendl;
458 int leader = mon->get_leader();
459 MMonGlobalID *req = new MMonGlobalID();
460 req->old_max_id = max_global_id;
461 mon->messenger->send_message(req, mon->monmap->get_inst(leader));
462 wait_for_finished_proposal(op, new C_RetryMessage(this, op));
463 return true;
464 }
465
466 assert(!paxos_writable);
467 return false;
468 }
469 }
470
471 try {
472 uint64_t auid = 0;
473 if (start) {
474 // new session
475
476 // always send the latest monmap.
477 if (m->monmap_epoch < mon->monmap->get_epoch())
478 mon->send_latest_monmap(m->get_connection().get());
479
480 proto = s->auth_handler->start_session(entity_name, indata, response_bl, caps_info);
481 ret = 0;
482 if (caps_info.allow_all)
483 s->caps.set_allow_all();
484 } else {
485 // request
486 ret = s->auth_handler->handle_request(indata, response_bl, s->global_id, caps_info, &auid);
487 }
488 if (ret == -EIO) {
489 wait_for_active(op, new C_RetryMessage(this,op));
490 goto done;
491 }
492 if (caps_info.caps.length()) {
493 bufferlist::iterator p = caps_info.caps.begin();
494 string str;
495 try {
496 ::decode(str, p);
497 } catch (const buffer::error &err) {
498 derr << "corrupt cap data for " << entity_name << " in auth db" << dendl;
499 str.clear();
500 }
501 s->caps.parse(str, NULL);
502 s->auid = auid;
503 }
504 } catch (const buffer::error &err) {
505 ret = -EINVAL;
506 dout(0) << "caught error when trying to handle auth request, probably malformed request" << dendl;
507 }
508
509 reply:
510 reply = new MAuthReply(proto, &response_bl, ret, s->global_id);
511 mon->send_reply(op, reply);
512 done:
513 return true;
514 }
515
516 bool AuthMonitor::preprocess_command(MonOpRequestRef op)
517 {
518 MMonCommand *m = static_cast<MMonCommand*>(op->get_req());
519 int r = -1;
520 bufferlist rdata;
521 stringstream ss, ds;
522
523 map<string, cmd_vartype> cmdmap;
524 if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
525 // ss has reason for failure
526 string rs = ss.str();
527 mon->reply_command(op, -EINVAL, rs, rdata, get_last_committed());
528 return true;
529 }
530
531 string prefix;
532 cmd_getval(g_ceph_context, cmdmap, "prefix", prefix);
533 if (prefix == "auth add" ||
534 prefix == "auth del" ||
535 prefix == "auth rm" ||
536 prefix == "auth get-or-create" ||
537 prefix == "auth get-or-create-key" ||
538 prefix == "auth import" ||
539 prefix == "auth caps") {
540 return false;
541 }
542
543 MonSession *session = m->get_session();
544 if (!session) {
545 mon->reply_command(op, -EACCES, "access denied", rdata, get_last_committed());
546 return true;
547 }
548
549 // entity might not be supplied, but if it is, it should be valid
550 string entity_name;
551 cmd_getval(g_ceph_context, cmdmap, "entity", entity_name);
552 EntityName entity;
553 if (!entity_name.empty() && !entity.from_str(entity_name)) {
554 ss << "invalid entity_auth " << entity_name;
555 mon->reply_command(op, -EINVAL, ss.str(), get_last_committed());
556 return true;
557 }
558
559 string format;
560 cmd_getval(g_ceph_context, cmdmap, "format", format, string("plain"));
561 boost::scoped_ptr<Formatter> f(Formatter::create(format));
562
563 if (prefix == "auth export") {
564 KeyRing keyring;
565 export_keyring(keyring);
566 if (!entity_name.empty()) {
567 EntityAuth eauth;
568 if (keyring.get_auth(entity, eauth)) {
569 KeyRing kr;
570 kr.add(entity, eauth);
571 if (f)
572 kr.encode_formatted("auth", f.get(), rdata);
573 else
574 kr.encode_plaintext(rdata);
575 ss << "export " << eauth;
576 r = 0;
577 } else {
578 ss << "no key for " << eauth;
579 r = -ENOENT;
580 }
581 } else {
582 if (f)
583 keyring.encode_formatted("auth", f.get(), rdata);
584 else
585 keyring.encode_plaintext(rdata);
586
587 ss << "exported master keyring";
588 r = 0;
589 }
590 } else if (prefix == "auth get" && !entity_name.empty()) {
591 KeyRing keyring;
592 EntityAuth entity_auth;
593 if(!mon->key_server.get_auth(entity, entity_auth)) {
594 ss << "failed to find " << entity_name << " in keyring";
595 r = -ENOENT;
596 } else {
597 keyring.add(entity, entity_auth);
598 if (f)
599 keyring.encode_formatted("auth", f.get(), rdata);
600 else
601 keyring.encode_plaintext(rdata);
602 ss << "exported keyring for " << entity_name;
603 r = 0;
604 }
605 } else if (prefix == "auth print-key" ||
606 prefix == "auth print_key" ||
607 prefix == "auth get-key") {
608 EntityAuth auth;
609 if (!mon->key_server.get_auth(entity, auth)) {
610 ss << "don't have " << entity;
611 r = -ENOENT;
612 goto done;
613 }
614 if (f) {
615 auth.key.encode_formatted("auth", f.get(), rdata);
616 } else {
617 auth.key.encode_plaintext(rdata);
618 }
619 r = 0;
620 } else if (prefix == "auth list") {
621 if (f) {
622 mon->key_server.encode_formatted("auth", f.get(), rdata);
623 } else {
624 mon->key_server.encode_plaintext(rdata);
625 if (rdata.length() > 0)
626 ss << "installed auth entries:" << std::endl;
627 else
628 ss << "no installed auth entries!" << std::endl;
629 }
630 r = 0;
631 goto done;
632 } else {
633 ss << "invalid command";
634 r = -EINVAL;
635 }
636
637 done:
638 rdata.append(ds);
639 string rs;
640 getline(ss, rs, '\0');
641 mon->reply_command(op, r, rs, rdata, get_last_committed());
642 return true;
643 }
644
645 void AuthMonitor::export_keyring(KeyRing& keyring)
646 {
647 mon->key_server.export_keyring(keyring);
648 }
649
650 int AuthMonitor::import_keyring(KeyRing& keyring)
651 {
652 for (map<EntityName, EntityAuth>::iterator p = keyring.get_keys().begin();
653 p != keyring.get_keys().end();
654 ++p) {
655 if (p->second.caps.empty()) {
656 dout(0) << "import: no caps supplied" << dendl;
657 return -EINVAL;
658 }
659 KeyServerData::Incremental auth_inc;
660 auth_inc.name = p->first;
661 auth_inc.auth = p->second;
662 auth_inc.op = KeyServerData::AUTH_INC_ADD;
663 dout(10) << " importing " << auth_inc.name << dendl;
664 dout(30) << " " << auth_inc.auth << dendl;
665 push_cephx_inc(auth_inc);
666 }
667 return 0;
668 }
669
670 bool AuthMonitor::prepare_command(MonOpRequestRef op)
671 {
672 MMonCommand *m = static_cast<MMonCommand*>(op->get_req());
673 stringstream ss, ds;
674 bufferlist rdata;
675 string rs;
676 int err = -EINVAL;
677
678 map<string, cmd_vartype> cmdmap;
679 if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
680 // ss has reason for failure
681 string rs = ss.str();
682 mon->reply_command(op, -EINVAL, rs, rdata, get_last_committed());
683 return true;
684 }
685
686 string prefix;
687 vector<string>caps_vec;
688 string entity_name;
689 EntityName entity;
690
691 cmd_getval(g_ceph_context, cmdmap, "prefix", prefix);
692
693 string format;
694 cmd_getval(g_ceph_context, cmdmap, "format", format, string("plain"));
695 boost::scoped_ptr<Formatter> f(Formatter::create(format));
696
697 MonSession *session = m->get_session();
698 if (!session) {
699 mon->reply_command(op, -EACCES, "access denied", rdata, get_last_committed());
700 return true;
701 }
702
703 cmd_getval(g_ceph_context, cmdmap, "caps", caps_vec);
704 if ((caps_vec.size() % 2) != 0) {
705 ss << "bad capabilities request; odd number of arguments";
706 err = -EINVAL;
707 goto done;
708 }
709
710 cmd_getval(g_ceph_context, cmdmap, "entity", entity_name);
711 if (!entity_name.empty() && !entity.from_str(entity_name)) {
712 ss << "bad entity name";
713 err = -EINVAL;
714 goto done;
715 }
716
717 if (prefix == "auth import") {
718 bufferlist bl = m->get_data();
719 if (bl.length() == 0) {
720 ss << "auth import: no data supplied";
721 getline(ss, rs);
722 mon->reply_command(op, -EINVAL, rs, get_last_committed());
723 return true;
724 }
725 bufferlist::iterator iter = bl.begin();
726 KeyRing keyring;
727 try {
728 ::decode(keyring, iter);
729 } catch (const buffer::error &ex) {
730 ss << "error decoding keyring" << " " << ex.what();
731 err = -EINVAL;
732 goto done;
733 }
734 err = import_keyring(keyring);
735 if (err < 0) {
736 ss << "auth import: no caps supplied";
737 getline(ss, rs);
738 mon->reply_command(op, -EINVAL, rs, get_last_committed());
739 return true;
740 }
741 ss << "imported keyring";
742 getline(ss, rs);
743 err = 0;
744 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
745 get_last_committed() + 1));
746 return true;
747 } else if (prefix == "auth add" && !entity_name.empty()) {
748 /* expected behavior:
749 * - if command reproduces current state, return 0.
750 * - if command adds brand new entity, handle it.
751 * - if command adds new state to existing entity, return error.
752 */
753 KeyServerData::Incremental auth_inc;
754 auth_inc.name = entity;
755 bufferlist bl = m->get_data();
756 bool has_keyring = (bl.length() > 0);
757 map<string,bufferlist> new_caps;
758
759 KeyRing new_keyring;
760 if (has_keyring) {
761 bufferlist::iterator iter = bl.begin();
762 try {
763 ::decode(new_keyring, iter);
764 } catch (const buffer::error &ex) {
765 ss << "error decoding keyring";
766 err = -EINVAL;
767 goto done;
768 }
769 }
770
771 // are we about to have it?
772 for (vector<Incremental>::iterator p = pending_auth.begin();
773 p != pending_auth.end();
774 ++p) {
775 if (p->inc_type == AUTH_DATA) {
776 KeyServerData::Incremental inc;
777 bufferlist::iterator q = p->auth_data.begin();
778 ::decode(inc, q);
779 if (inc.op == KeyServerData::AUTH_INC_ADD &&
780 inc.name == entity) {
781 wait_for_finished_proposal(op,
782 new Monitor::C_Command(mon, op, 0, rs, get_last_committed() + 1));
783 return true;
784 }
785 }
786 }
787
788 // build new caps from provided arguments (if available)
789 for (vector<string>::iterator it = caps_vec.begin();
790 it != caps_vec.end() && (it + 1) != caps_vec.end();
791 it += 2) {
792 string sys = *it;
793 bufferlist cap;
794 ::encode(*(it+1), cap);
795 new_caps[sys] = cap;
796 }
797
798 // pull info out of provided keyring
799 EntityAuth new_inc;
800 if (has_keyring) {
801 if (!new_keyring.get_auth(auth_inc.name, new_inc)) {
802 ss << "key for " << auth_inc.name
803 << " not found in provided keyring";
804 err = -EINVAL;
805 goto done;
806 }
807 if (!new_caps.empty() && !new_inc.caps.empty()) {
808 ss << "caps cannot be specified both in keyring and in command";
809 err = -EINVAL;
810 goto done;
811 }
812 if (new_caps.empty()) {
813 new_caps = new_inc.caps;
814 }
815 }
816
817 // does entry already exist?
818 if (mon->key_server.get_auth(auth_inc.name, auth_inc.auth)) {
819 // key match?
820 if (has_keyring) {
821 if (auth_inc.auth.key.get_secret().cmp(new_inc.key.get_secret())) {
822 ss << "entity " << auth_inc.name << " exists but key does not match";
823 err = -EINVAL;
824 goto done;
825 }
826 }
827
828 // caps match?
829 if (new_caps.size() != auth_inc.auth.caps.size()) {
830 ss << "entity " << auth_inc.name << " exists but caps do not match";
831 err = -EINVAL;
832 goto done;
833 }
834 for (map<string,bufferlist>::iterator it = new_caps.begin();
835 it != new_caps.end(); ++it) {
836 if (auth_inc.auth.caps.count(it->first) == 0 ||
837 !auth_inc.auth.caps[it->first].contents_equal(it->second)) {
838 ss << "entity " << auth_inc.name << " exists but cap "
839 << it->first << " does not match";
840 err = -EINVAL;
841 goto done;
842 }
843 }
844
845 // they match, no-op
846 err = 0;
847 goto done;
848 }
849
850 // okay, add it.
851 auth_inc.op = KeyServerData::AUTH_INC_ADD;
852 auth_inc.auth.caps = new_caps;
853 if (has_keyring) {
854 auth_inc.auth.key = new_inc.key;
855 } else {
856 dout(10) << "AuthMonitor::prepare_command generating random key for "
857 << auth_inc.name << dendl;
858 auth_inc.auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
859 }
860
861 dout(10) << " importing " << auth_inc.name << dendl;
862 dout(30) << " " << auth_inc.auth << dendl;
863 push_cephx_inc(auth_inc);
864
865 ss << "added key for " << auth_inc.name;
866 getline(ss, rs);
867 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
868 get_last_committed() + 1));
869 return true;
870 } else if ((prefix == "auth get-or-create-key" ||
871 prefix == "auth get-or-create") &&
872 !entity_name.empty()) {
873 // auth get-or-create <name> [mon osdcapa osd osdcapb ...]
874
875 if (!valid_caps(caps_vec, &ss)) {
876 err = -EINVAL;
877 goto done;
878 }
879
880 // Parse the list of caps into a map
881 std::map<std::string, bufferlist> wanted_caps;
882 for (vector<string>::const_iterator it = caps_vec.begin();
883 it != caps_vec.end() && (it + 1) != caps_vec.end();
884 it += 2) {
885 const std::string &sys = *it;
886 bufferlist cap;
887 ::encode(*(it+1), cap);
888 wanted_caps[sys] = cap;
889 }
890
891 // do we have it?
892 EntityAuth entity_auth;
893 if (mon->key_server.get_auth(entity, entity_auth)) {
894 for (const auto &sys_cap : wanted_caps) {
895 if (entity_auth.caps.count(sys_cap.first) == 0 ||
896 !entity_auth.caps[sys_cap.first].contents_equal(sys_cap.second)) {
897 ss << "key for " << entity << " exists but cap " << sys_cap.first
898 << " does not match";
899 err = -EINVAL;
900 goto done;
901 }
902 }
903
904 if (prefix == "auth get-or-create-key") {
905 if (f) {
906 entity_auth.key.encode_formatted("auth", f.get(), rdata);
907 } else {
908 ds << entity_auth.key;
909 }
910 } else {
911 KeyRing kr;
912 kr.add(entity, entity_auth.key);
913 if (f) {
914 kr.set_caps(entity, entity_auth.caps);
915 kr.encode_formatted("auth", f.get(), rdata);
916 } else {
917 kr.encode_plaintext(rdata);
918 }
919 }
920 err = 0;
921 goto done;
922 }
923
924 // ...or are we about to?
925 for (vector<Incremental>::iterator p = pending_auth.begin();
926 p != pending_auth.end();
927 ++p) {
928 if (p->inc_type == AUTH_DATA) {
929 KeyServerData::Incremental auth_inc;
930 bufferlist::iterator q = p->auth_data.begin();
931 ::decode(auth_inc, q);
932 if (auth_inc.op == KeyServerData::AUTH_INC_ADD &&
933 auth_inc.name == entity) {
934 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
935 get_last_committed() + 1));
936 return true;
937 }
938 }
939 }
940
941 // create it
942 KeyServerData::Incremental auth_inc;
943 auth_inc.op = KeyServerData::AUTH_INC_ADD;
944 auth_inc.name = entity;
945 auth_inc.auth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
946 auth_inc.auth.caps = wanted_caps;
947
948 push_cephx_inc(auth_inc);
949
950 if (prefix == "auth get-or-create-key") {
951 if (f) {
952 auth_inc.auth.key.encode_formatted("auth", f.get(), rdata);
953 } else {
954 ds << auth_inc.auth.key;
955 }
956 } else {
957 KeyRing kr;
958 kr.add(entity, auth_inc.auth.key);
959 if (f) {
960 kr.set_caps(entity, wanted_caps);
961 kr.encode_formatted("auth", f.get(), rdata);
962 } else {
963 kr.encode_plaintext(rdata);
964 }
965 }
966
967 rdata.append(ds);
968 getline(ss, rs);
969 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs, rdata,
970 get_last_committed() + 1));
971 return true;
972 } else if (prefix == "auth caps" && !entity_name.empty()) {
973 KeyServerData::Incremental auth_inc;
974 auth_inc.name = entity;
975 if (!mon->key_server.get_auth(auth_inc.name, auth_inc.auth)) {
976 ss << "couldn't find entry " << auth_inc.name;
977 err = -ENOENT;
978 goto done;
979 }
980
981 if (!valid_caps(caps_vec, &ss)) {
982 err = -EINVAL;
983 goto done;
984 }
985
986 map<string,bufferlist> newcaps;
987 for (vector<string>::iterator it = caps_vec.begin();
988 it != caps_vec.end(); it += 2)
989 ::encode(*(it+1), newcaps[*it]);
990
991 auth_inc.op = KeyServerData::AUTH_INC_ADD;
992 auth_inc.auth.caps = newcaps;
993 push_cephx_inc(auth_inc);
994
995 ss << "updated caps for " << auth_inc.name;
996 getline(ss, rs);
997 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
998 get_last_committed() + 1));
999 return true;
1000 } else if ((prefix == "auth del" || prefix == "auth rm") &&
1001 !entity_name.empty()) {
1002 KeyServerData::Incremental auth_inc;
1003 auth_inc.name = entity;
1004 if (!mon->key_server.contains(auth_inc.name)) {
1005 ss << "entity " << entity << " does not exist";
1006 err = 0;
1007 goto done;
1008 }
1009 auth_inc.op = KeyServerData::AUTH_INC_DEL;
1010 push_cephx_inc(auth_inc);
1011
1012 ss << "updated";
1013 getline(ss, rs);
1014 wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
1015 get_last_committed() + 1));
1016 return true;
1017 }
1018
1019 done:
1020 rdata.append(ds);
1021 getline(ss, rs, '\0');
1022 mon->reply_command(op, err, rs, rdata, get_last_committed());
1023 return false;
1024 }
1025
1026 bool AuthMonitor::prepare_global_id(MonOpRequestRef op)
1027 {
1028 dout(10) << "AuthMonitor::prepare_global_id" << dendl;
1029 increase_max_global_id();
1030
1031 return true;
1032 }
1033
1034 void AuthMonitor::upgrade_format()
1035 {
1036 unsigned int current = 2;
1037 if (!mon->get_quorum_mon_features().contains_all(
1038 ceph::features::mon::FEATURE_LUMINOUS)) {
1039 current = 1;
1040 }
1041 if (format_version >= current) {
1042 dout(20) << __func__ << " format " << format_version << " is current" << dendl;
1043 return;
1044 }
1045
1046 bool changed = false;
1047 if (format_version == 0) {
1048 dout(1) << __func__ << " upgrading from format 0 to 1" << dendl;
1049 map<EntityName, EntityAuth>::iterator p;
1050 for (p = mon->key_server.secrets_begin();
1051 p != mon->key_server.secrets_end();
1052 ++p) {
1053 // grab mon caps, if any
1054 string mon_caps;
1055 if (p->second.caps.count("mon") == 0)
1056 continue;
1057 try {
1058 bufferlist::iterator it = p->second.caps["mon"].begin();
1059 ::decode(mon_caps, it);
1060 }
1061 catch (buffer::error) {
1062 dout(10) << __func__ << " unable to parse mon cap for "
1063 << p->first << dendl;
1064 continue;
1065 }
1066
1067 string n = p->first.to_str();
1068 string new_caps;
1069
1070 // set daemon profiles
1071 if ((p->first.is_osd() || p->first.is_mds()) &&
1072 mon_caps == "allow rwx") {
1073 new_caps = string("allow profile ") + string(p->first.get_type_name());
1074 }
1075
1076 // update bootstrap keys
1077 if (n == "client.bootstrap-osd") {
1078 new_caps = "allow profile bootstrap-osd";
1079 }
1080 if (n == "client.bootstrap-mds") {
1081 new_caps = "allow profile bootstrap-mds";
1082 }
1083
1084 if (new_caps.length() > 0) {
1085 dout(5) << __func__ << " updating " << p->first << " mon cap from "
1086 << mon_caps << " to " << new_caps << dendl;
1087
1088 bufferlist bl;
1089 ::encode(new_caps, bl);
1090
1091 KeyServerData::Incremental auth_inc;
1092 auth_inc.name = p->first;
1093 auth_inc.auth = p->second;
1094 auth_inc.auth.caps["mon"] = bl;
1095 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1096 push_cephx_inc(auth_inc);
1097 changed = true;
1098 }
1099 }
1100 }
1101
1102 if (format_version == 1) {
1103 dout(1) << __func__ << " upgrading from format 1 to 2" << dendl;
1104 map<EntityName, EntityAuth>::iterator p;
1105 for (p = mon->key_server.secrets_begin();
1106 p != mon->key_server.secrets_end();
1107 ++p) {
1108 string n = p->first.to_str();
1109
1110 string newcap;
1111 if (n == "client.admin") {
1112 // admin gets it all
1113 newcap = "allow *";
1114 } else if (n.find("osd.") == 0 ||
1115 n.find("mds.") == 0 ||
1116 n.find("mon.") == 0) {
1117 // daemons follow their profile
1118 string type = n.substr(0, 3);
1119 newcap = "allow profile " + type;
1120 } else if (p->second.caps.count("mon")) {
1121 // if there are any mon caps, give them 'r' mgr caps
1122 newcap = "allow r";
1123 }
1124
1125 if (newcap.length() > 0) {
1126 dout(5) << " giving " << n << " mgr '" << newcap << "'" << dendl;
1127 bufferlist bl;
1128 ::encode(newcap, bl);
1129
1130 KeyServerData::Incremental auth_inc;
1131 auth_inc.name = p->first;
1132 auth_inc.auth = p->second;
1133 auth_inc.auth.caps["mgr"] = bl;
1134 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1135 push_cephx_inc(auth_inc);
1136 }
1137
1138 if (n.find("mgr.") == 0 &&
1139 p->second.caps.count("mon")) {
1140 // the kraken ceph-mgr@.service set the mon cap to 'allow *'.
1141 auto blp = p->second.caps["mon"].begin();
1142 string oldcaps;
1143 ::decode(oldcaps, blp);
1144 if (oldcaps == "allow *") {
1145 dout(5) << " fixing " << n << " mon cap to 'allow profile mgr'"
1146 << dendl;
1147 bufferlist bl;
1148 ::encode("allow profile mgr", bl);
1149 KeyServerData::Incremental auth_inc;
1150 auth_inc.name = p->first;
1151 auth_inc.auth = p->second;
1152 auth_inc.auth.caps["mon"] = bl;
1153 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1154 push_cephx_inc(auth_inc);
1155 }
1156 }
1157 }
1158
1159 // add bootstrap key
1160 {
1161 KeyServerData::Incremental auth_inc;
1162 bool r = auth_inc.name.from_str("client.bootstrap-mgr");
1163 assert(r);
1164 ::encode("allow profile bootstrap-mgr", auth_inc.auth.caps["mon"]);
1165 auth_inc.op = KeyServerData::AUTH_INC_ADD;
1166 push_cephx_inc(auth_inc);
1167 }
1168 changed = true;
1169 }
1170
1171 if (changed) {
1172 // note new format
1173 dout(10) << __func__ << " proposing update from format " << format_version
1174 << " -> " << current << dendl;
1175 format_version = current;
1176 propose_pending();
1177 }
1178 }
1179
1180 void AuthMonitor::dump_info(Formatter *f)
1181 {
1182 /*** WARNING: do not include any privileged information here! ***/
1183 f->open_object_section("auth");
1184 f->dump_unsigned("first_committed", get_first_committed());
1185 f->dump_unsigned("last_committed", get_last_committed());
1186 f->dump_unsigned("num_secrets", mon->key_server.get_num_secrets());
1187 f->close_section();
1188 }