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