]>
Commit | Line | Data |
---|---|---|
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" | |
22 | #include "common/config.h" | |
23 | #include "common/dout.h" | |
24 | ||
25 | #define dout_subsys ceph_subsys_auth | |
26 | #undef dout_prefix | |
27 | #define dout_prefix *_dout << "cephx client: " | |
28 | ||
29 | ||
30 | int CephxClientHandler::build_request(bufferlist& bl) const | |
31 | { | |
32 | ldout(cct, 10) << "build_request" << dendl; | |
33 | ||
34 | RWLock::RLocker l(lock); | |
35 | ||
36 | if (need & CEPH_ENTITY_TYPE_AUTH) { | |
37 | /* authenticate */ | |
38 | CephXRequestHeader header; | |
39 | header.request_type = CEPHX_GET_AUTH_SESSION_KEY; | |
40 | ::encode(header, bl); | |
41 | ||
42 | CryptoKey secret; | |
43 | const bool got = keyring->get_secret(cct->_conf->name, secret); | |
44 | if (!got) { | |
45 | ldout(cct, 20) << "no secret found for entity: " << cct->_conf->name << dendl; | |
46 | return -ENOENT; | |
47 | } | |
48 | ||
49 | // is the key OK? | |
50 | if (!secret.get_secret().length()) { | |
51 | ldout(cct, 20) << "secret for entity " << cct->_conf->name << " is invalid" << dendl; | |
52 | return -EINVAL; | |
53 | } | |
54 | ||
55 | CephXAuthenticate req; | |
56 | get_random_bytes((char *)&req.client_challenge, sizeof(req.client_challenge)); | |
57 | std::string error; | |
58 | cephx_calc_client_server_challenge(cct, secret, server_challenge, | |
59 | req.client_challenge, &req.key, error); | |
60 | if (!error.empty()) { | |
61 | ldout(cct, 20) << "cephx_calc_client_server_challenge error: " << error << dendl; | |
62 | return -EIO; | |
63 | } | |
64 | ||
65 | req.old_ticket = ticket_handler->ticket; | |
66 | ||
67 | if (req.old_ticket.blob.length()) { | |
68 | ldout(cct, 20) << "old ticket len=" << req.old_ticket.blob.length() << dendl; | |
69 | } | |
70 | ||
71 | ::encode(req, bl); | |
72 | ||
73 | ldout(cct, 10) << "get auth session key: client_challenge " | |
74 | << hex << req.client_challenge << dendl; | |
75 | return 0; | |
76 | } | |
77 | ||
78 | if (_need_tickets()) { | |
79 | /* get service tickets */ | |
80 | ldout(cct, 10) << "get service keys: want=" << want << " need=" << need << " have=" << have << dendl; | |
81 | ||
82 | CephXRequestHeader header; | |
83 | header.request_type = CEPHX_GET_PRINCIPAL_SESSION_KEY; | |
84 | ::encode(header, bl); | |
85 | ||
86 | CephXAuthorizer *authorizer = ticket_handler->build_authorizer(global_id); | |
87 | if (!authorizer) | |
88 | return -EINVAL; | |
89 | bl.claim_append(authorizer->bl); | |
90 | delete authorizer; | |
91 | ||
92 | CephXServiceTicketRequest req; | |
93 | req.keys = need; | |
94 | ::encode(req, bl); | |
95 | } | |
96 | ||
97 | return 0; | |
98 | } | |
99 | ||
100 | bool CephxClientHandler::_need_tickets() const | |
101 | { | |
102 | // do not bother (re)requesting tickets if we *only* need the MGR | |
103 | // ticket; that can happen during an upgrade and we want to avoid a | |
104 | // loop. we'll end up re-requesting it later when the secrets | |
105 | // rotating. | |
106 | return need && need != CEPH_ENTITY_TYPE_MGR; | |
107 | } | |
108 | ||
109 | int CephxClientHandler::handle_response(int ret, bufferlist::iterator& indata) | |
110 | { | |
111 | ldout(cct, 10) << "handle_response ret = " << ret << dendl; | |
112 | RWLock::WLocker l(lock); | |
113 | ||
114 | if (ret < 0) | |
115 | return ret; // hrm! | |
116 | ||
117 | if (starting) { | |
118 | CephXServerChallenge ch; | |
119 | ::decode(ch, indata); | |
120 | server_challenge = ch.server_challenge; | |
121 | ldout(cct, 10) << " got initial server challenge " | |
122 | << hex << server_challenge << dendl; | |
123 | starting = false; | |
124 | ||
125 | tickets.invalidate_ticket(CEPH_ENTITY_TYPE_AUTH); | |
126 | return -EAGAIN; | |
127 | } | |
128 | ||
129 | struct CephXResponseHeader header; | |
130 | ::decode(header, indata); | |
131 | ||
132 | switch (header.request_type) { | |
133 | case CEPHX_GET_AUTH_SESSION_KEY: | |
134 | { | |
135 | ldout(cct, 10) << " get_auth_session_key" << dendl; | |
136 | CryptoKey secret; | |
137 | const bool got = keyring->get_secret(cct->_conf->name, secret); | |
138 | if (!got) { | |
139 | ldout(cct, 0) << "key not found for " << cct->_conf->name << dendl; | |
140 | return -ENOENT; | |
141 | } | |
142 | ||
143 | if (!tickets.verify_service_ticket_reply(secret, indata)) { | |
144 | ldout(cct, 0) << "could not verify service_ticket reply" << dendl; | |
145 | return -EPERM; | |
146 | } | |
147 | ldout(cct, 10) << " want=" << want << " need=" << need << " have=" << have << dendl; | |
148 | validate_tickets(); | |
149 | if (_need_tickets()) | |
150 | ret = -EAGAIN; | |
151 | else | |
152 | ret = 0; | |
153 | } | |
154 | break; | |
155 | ||
156 | case CEPHX_GET_PRINCIPAL_SESSION_KEY: | |
157 | { | |
158 | CephXTicketHandler& ticket_handler = tickets.get_handler(CEPH_ENTITY_TYPE_AUTH); | |
159 | ldout(cct, 10) << " get_principal_session_key session_key " << ticket_handler.session_key << dendl; | |
160 | ||
161 | if (!tickets.verify_service_ticket_reply(ticket_handler.session_key, indata)) { | |
162 | ldout(cct, 0) << "could not verify service_ticket reply" << dendl; | |
163 | return -EPERM; | |
164 | } | |
165 | validate_tickets(); | |
166 | if (!_need_tickets()) { | |
167 | ret = 0; | |
168 | } | |
169 | } | |
170 | break; | |
171 | ||
172 | case CEPHX_GET_ROTATING_KEY: | |
173 | { | |
174 | ldout(cct, 10) << " get_rotating_key" << dendl; | |
175 | if (rotating_secrets) { | |
176 | RotatingSecrets secrets; | |
177 | CryptoKey secret_key; | |
178 | const bool got = keyring->get_secret(cct->_conf->name, secret_key); | |
179 | if (!got) { | |
180 | ldout(cct, 0) << "key not found for " << cct->_conf->name << dendl; | |
181 | return -ENOENT; | |
182 | } | |
183 | std::string error; | |
184 | if (decode_decrypt(cct, secrets, secret_key, indata, error)) { | |
185 | ldout(cct, 0) << "could not set rotating key: decode_decrypt failed. error:" | |
186 | << error << dendl; | |
187 | return -EINVAL; | |
188 | } else { | |
224ce89b | 189 | rotating_secrets->set_secrets(std::move(secrets)); |
7c673cae FG |
190 | } |
191 | } | |
192 | } | |
193 | break; | |
194 | ||
195 | default: | |
196 | ldout(cct, 0) << " unknown request_type " << header.request_type << dendl; | |
197 | ceph_abort(); | |
198 | } | |
199 | return ret; | |
200 | } | |
201 | ||
202 | ||
203 | ||
204 | AuthAuthorizer *CephxClientHandler::build_authorizer(uint32_t service_id) const | |
205 | { | |
206 | RWLock::RLocker l(lock); | |
207 | ldout(cct, 10) << "build_authorizer for service " << ceph_entity_type_name(service_id) << dendl; | |
208 | return tickets.build_authorizer(service_id); | |
209 | } | |
210 | ||
211 | ||
212 | bool CephxClientHandler::build_rotating_request(bufferlist& bl) const | |
213 | { | |
214 | ldout(cct, 10) << "build_rotating_request" << dendl; | |
215 | CephXRequestHeader header; | |
216 | header.request_type = CEPHX_GET_ROTATING_KEY; | |
217 | ::encode(header, bl); | |
218 | return true; | |
219 | } | |
220 | ||
221 | void CephxClientHandler::prepare_build_request() | |
222 | { | |
223 | RWLock::WLocker l(lock); | |
224 | ldout(cct, 10) << "validate_tickets: want=" << want << " need=" << need | |
225 | << " have=" << have << dendl; | |
226 | validate_tickets(); | |
227 | ldout(cct, 10) << "want=" << want << " need=" << need << " have=" << have | |
228 | << dendl; | |
229 | ||
230 | ticket_handler = &(tickets.get_handler(CEPH_ENTITY_TYPE_AUTH)); | |
231 | } | |
232 | ||
233 | void CephxClientHandler::validate_tickets() | |
234 | { | |
235 | // lock should be held for write | |
236 | tickets.validate_tickets(want, have, need); | |
237 | } | |
238 | ||
239 | bool CephxClientHandler::need_tickets() | |
240 | { | |
241 | RWLock::WLocker l(lock); | |
242 | validate_tickets(); | |
243 | ||
244 | ldout(cct, 20) << "need_tickets: want=" << want | |
245 | << " have=" << have | |
246 | << " need=" << need | |
247 | << dendl; | |
248 | ||
249 | return _need_tickets(); | |
250 | } | |
251 |