]> git.proxmox.com Git - ceph.git/blame - ceph/src/auth/cephx/CephxClientHandler.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / auth / cephx / CephxClientHandler.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2004-2009 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
16#include <errno.h>
17
18#include "CephxClientHandler.h"
19#include "CephxProtocol.h"
20
21#include "auth/KeyRing.h"
11fdf7f2
TL
22#include "include/random.h"
23#include "common/ceph_context.h"
7c673cae
FG
24#include "common/config.h"
25#include "common/dout.h"
26
27#define dout_subsys ceph_subsys_auth
28#undef dout_prefix
29#define dout_prefix *_dout << "cephx client: "
30
f67539c2
TL
31using std::string;
32
33using ceph::bufferlist;
34
11fdf7f2
TL
35void CephxClientHandler::reset()
36{
37 ldout(cct,10) << __func__ << dendl;
38 starting = true;
39 server_challenge = 0;
40}
7c673cae
FG
41
42int CephxClientHandler::build_request(bufferlist& bl) const
43{
44 ldout(cct, 10) << "build_request" << dendl;
45
7c673cae
FG
46 if (need & CEPH_ENTITY_TYPE_AUTH) {
47 /* authenticate */
48 CephXRequestHeader header;
49 header.request_type = CEPHX_GET_AUTH_SESSION_KEY;
11fdf7f2 50 encode(header, bl);
7c673cae
FG
51
52 CryptoKey secret;
53 const bool got = keyring->get_secret(cct->_conf->name, secret);
54 if (!got) {
55 ldout(cct, 20) << "no secret found for entity: " << cct->_conf->name << dendl;
56 return -ENOENT;
57 }
58
59 // is the key OK?
60 if (!secret.get_secret().length()) {
61 ldout(cct, 20) << "secret for entity " << cct->_conf->name << " is invalid" << dendl;
62 return -EINVAL;
63 }
64
65 CephXAuthenticate req;
11fdf7f2 66 req.client_challenge = ceph::util::generate_random_number<uint64_t>();
7c673cae
FG
67 std::string error;
68 cephx_calc_client_server_challenge(cct, secret, server_challenge,
69 req.client_challenge, &req.key, error);
70 if (!error.empty()) {
71 ldout(cct, 20) << "cephx_calc_client_server_challenge error: " << error << dendl;
72 return -EIO;
73 }
74
75 req.old_ticket = ticket_handler->ticket;
76
11fdf7f2
TL
77 // for nautilus+ servers: request other keys at the same time
78 req.other_keys = need;
79
7c673cae
FG
80 if (req.old_ticket.blob.length()) {
81 ldout(cct, 20) << "old ticket len=" << req.old_ticket.blob.length() << dendl;
82 }
83
11fdf7f2 84 encode(req, bl);
7c673cae
FG
85
86 ldout(cct, 10) << "get auth session key: client_challenge "
11fdf7f2 87 << std::hex << req.client_challenge << std::dec << dendl;
7c673cae
FG
88 return 0;
89 }
90
91 if (_need_tickets()) {
92 /* get service tickets */
93 ldout(cct, 10) << "get service keys: want=" << want << " need=" << need << " have=" << have << dendl;
94
95 CephXRequestHeader header;
96 header.request_type = CEPHX_GET_PRINCIPAL_SESSION_KEY;
11fdf7f2 97 encode(header, bl);
7c673cae
FG
98
99 CephXAuthorizer *authorizer = ticket_handler->build_authorizer(global_id);
100 if (!authorizer)
101 return -EINVAL;
102 bl.claim_append(authorizer->bl);
103 delete authorizer;
104
105 CephXServiceTicketRequest req;
106 req.keys = need;
11fdf7f2 107 encode(req, bl);
7c673cae
FG
108 }
109
110 return 0;
111}
112
113bool CephxClientHandler::_need_tickets() const
114{
115 // do not bother (re)requesting tickets if we *only* need the MGR
116 // ticket; that can happen during an upgrade and we want to avoid a
117 // loop. we'll end up re-requesting it later when the secrets
118 // rotating.
119 return need && need != CEPH_ENTITY_TYPE_MGR;
120}
121
11fdf7f2
TL
122int CephxClientHandler::handle_response(
123 int ret,
124 bufferlist::const_iterator& indata,
125 CryptoKey *session_key,
126 std::string *connection_secret)
7c673cae 127{
11fdf7f2 128 ldout(cct, 10) << this << " handle_response ret = " << ret << dendl;
7c673cae
FG
129
130 if (ret < 0)
131 return ret; // hrm!
132
133 if (starting) {
134 CephXServerChallenge ch;
eafe8130
TL
135 try {
136 decode(ch, indata);
f67539c2 137 } catch (ceph::buffer::error& e) {
eafe8130
TL
138 ldout(cct, 1) << __func__ << " failed to decode CephXServerChallenge: "
139 << e.what() << dendl;
140 return -EPERM;
141 }
7c673cae
FG
142 server_challenge = ch.server_challenge;
143 ldout(cct, 10) << " got initial server challenge "
11fdf7f2 144 << std::hex << server_challenge << std::dec << dendl;
7c673cae
FG
145 starting = false;
146
147 tickets.invalidate_ticket(CEPH_ENTITY_TYPE_AUTH);
148 return -EAGAIN;
149 }
150
151 struct CephXResponseHeader header;
eafe8130
TL
152 try {
153 decode(header, indata);
f67539c2 154 } catch (ceph::buffer::error& e) {
eafe8130
TL
155 ldout(cct, 1) << __func__ << " failed to decode CephXResponseHeader: "
156 << e.what() << dendl;
157 return -EPERM;
158 }
7c673cae
FG
159
160 switch (header.request_type) {
161 case CEPHX_GET_AUTH_SESSION_KEY:
162 {
163 ldout(cct, 10) << " get_auth_session_key" << dendl;
164 CryptoKey secret;
165 const bool got = keyring->get_secret(cct->_conf->name, secret);
166 if (!got) {
167 ldout(cct, 0) << "key not found for " << cct->_conf->name << dendl;
168 return -ENOENT;
169 }
170
171 if (!tickets.verify_service_ticket_reply(secret, indata)) {
172 ldout(cct, 0) << "could not verify service_ticket reply" << dendl;
9f95a23c 173 return -EACCES;
7c673cae
FG
174 }
175 ldout(cct, 10) << " want=" << want << " need=" << need << " have=" << have << dendl;
11fdf7f2
TL
176 if (!indata.end()) {
177 bufferlist cbl, extra_tickets;
f67539c2 178 using ceph::decode;
eafe8130
TL
179 try {
180 decode(cbl, indata);
181 decode(extra_tickets, indata);
f67539c2 182 } catch (ceph::buffer::error& e) {
eafe8130
TL
183 ldout(cct, 1) << __func__ << " failed to decode tickets: "
184 << e.what() << dendl;
185 return -EPERM;
186 }
11fdf7f2
TL
187 ldout(cct, 10) << " got connection bl " << cbl.length()
188 << " and extra tickets " << extra_tickets.length()
189 << dendl;
20effc67
TL
190 // for msgr1, both session_key and connection_secret are NULL
191 // so we skip extra_tickets and incur an additional round-trip
192 // to get service tickets via CEPHX_GET_PRINCIPAL_SESSION_KEY
193 // as if talking to a pre-nautilus mon
194 // this wasn't intended but turns out to be needed because in
195 // msgr1 case MonClient doesn't explicitly wait for the monmap
196 // (which is shared together with CEPHX_GET_AUTH_SESSION_KEY
197 // reply)
198 // instead, it waits for CEPHX_GET_PRINCIPAL_SESSION_KEY reply
199 // which comes after the monmap and hence the monmap is always
200 // handled by the time authentication is considered finished
201 // if we start to always process extra_tickets here, MonClient
202 // would have no reason to send CEPHX_GET_PRINCIPAL_SESSION_KEY
203 // and RadosClient::connect() or similar could return with no
204 // actual monmap but just an initial bootstrap stub, leading
205 // to mon commands going out with zero fsid and other issues
11fdf7f2
TL
206 if (session_key && connection_secret) {
207 CephXTicketHandler& ticket_handler =
208 tickets.get_handler(CEPH_ENTITY_TYPE_AUTH);
209 if (session_key) {
210 *session_key = ticket_handler.session_key;
211 }
212 if (cbl.length() && connection_secret) {
213 auto p = cbl.cbegin();
214 string err;
215 if (decode_decrypt(cct, *connection_secret, *session_key, p,
216 err)) {
217 lderr(cct) << __func__ << " failed to decrypt connection_secret"
218 << dendl;
219 } else {
220 ldout(cct, 10) << " got connection_secret "
221 << connection_secret->size() << " bytes" << dendl;
222 }
223 }
224 if (extra_tickets.length()) {
225 auto p = extra_tickets.cbegin();
226 if (!tickets.verify_service_ticket_reply(
227 *session_key, p)) {
228 lderr(cct) << "could not verify extra service_tickets" << dendl;
229 } else {
230 ldout(cct, 10) << " got extra service_tickets" << dendl;
231 }
232 }
233 }
234 }
7c673cae
FG
235 validate_tickets();
236 if (_need_tickets())
237 ret = -EAGAIN;
238 else
239 ret = 0;
11fdf7f2 240 }
7c673cae
FG
241 break;
242
243 case CEPHX_GET_PRINCIPAL_SESSION_KEY:
244 {
245 CephXTicketHandler& ticket_handler = tickets.get_handler(CEPH_ENTITY_TYPE_AUTH);
246 ldout(cct, 10) << " get_principal_session_key session_key " << ticket_handler.session_key << dendl;
247
248 if (!tickets.verify_service_ticket_reply(ticket_handler.session_key, indata)) {
249 ldout(cct, 0) << "could not verify service_ticket reply" << dendl;
9f95a23c 250 return -EACCES;
7c673cae
FG
251 }
252 validate_tickets();
253 if (!_need_tickets()) {
254 ret = 0;
255 }
256 }
257 break;
258
259 case CEPHX_GET_ROTATING_KEY:
260 {
261 ldout(cct, 10) << " get_rotating_key" << dendl;
262 if (rotating_secrets) {
263 RotatingSecrets secrets;
264 CryptoKey secret_key;
265 const bool got = keyring->get_secret(cct->_conf->name, secret_key);
266 if (!got) {
267 ldout(cct, 0) << "key not found for " << cct->_conf->name << dendl;
268 return -ENOENT;
269 }
270 std::string error;
271 if (decode_decrypt(cct, secrets, secret_key, indata, error)) {
272 ldout(cct, 0) << "could not set rotating key: decode_decrypt failed. error:"
273 << error << dendl;
274 return -EINVAL;
275 } else {
224ce89b 276 rotating_secrets->set_secrets(std::move(secrets));
7c673cae
FG
277 }
278 }
279 }
280 break;
281
282 default:
283 ldout(cct, 0) << " unknown request_type " << header.request_type << dendl;
284 ceph_abort();
285 }
286 return ret;
287}
288
289
7c673cae
FG
290AuthAuthorizer *CephxClientHandler::build_authorizer(uint32_t service_id) const
291{
7c673cae
FG
292 ldout(cct, 10) << "build_authorizer for service " << ceph_entity_type_name(service_id) << dendl;
293 return tickets.build_authorizer(service_id);
294}
295
296
297bool CephxClientHandler::build_rotating_request(bufferlist& bl) const
298{
299 ldout(cct, 10) << "build_rotating_request" << dendl;
300 CephXRequestHeader header;
301 header.request_type = CEPHX_GET_ROTATING_KEY;
11fdf7f2 302 encode(header, bl);
7c673cae
FG
303 return true;
304}
305
306void CephxClientHandler::prepare_build_request()
307{
7c673cae
FG
308 ldout(cct, 10) << "validate_tickets: want=" << want << " need=" << need
309 << " have=" << have << dendl;
310 validate_tickets();
311 ldout(cct, 10) << "want=" << want << " need=" << need << " have=" << have
312 << dendl;
313
314 ticket_handler = &(tickets.get_handler(CEPH_ENTITY_TYPE_AUTH));
315}
316
317void CephxClientHandler::validate_tickets()
318{
319 // lock should be held for write
320 tickets.validate_tickets(want, have, need);
321}
322
323bool CephxClientHandler::need_tickets()
324{
7c673cae
FG
325 validate_tickets();
326
327 ldout(cct, 20) << "need_tickets: want=" << want
328 << " have=" << have
329 << " need=" << need
330 << dendl;
331
332 return _need_tickets();
333}