]>
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 | #ifndef CEPH_CEPHXPROTOCOL_H | |
16 | #define CEPH_CEPHXPROTOCOL_H | |
17 | ||
18 | /* | |
19 | Ceph X protocol | |
20 | ||
21 | First, the principal has to authenticate with the authenticator. A | |
22 | shared-secret mechanism is being used, and the negotitaion goes like this: | |
23 | ||
24 | A = Authenticator | |
25 | P = Principle | |
26 | S = Service | |
27 | ||
28 | 1. Obtaining principal/auth session key | |
29 | ||
30 | (Authenticate Request) | |
31 | p->a : principal, principal_addr. authenticate me! | |
32 | ||
33 | ...authenticator does lookup in database... | |
34 | ||
35 | a->p : A= {principal/auth session key, validity}^principal_secret (*) | |
36 | B= {principal ticket, validity, principal/auth session key}^authsecret | |
37 | ||
38 | ||
39 | [principal/auth session key, validity] = service ticket | |
40 | [principal ticket, validity, principal/auth session key] = service ticket info | |
41 | ||
42 | (*) annotation: ^ signifies 'encrypted by' | |
43 | ||
44 | At this point, if is genuine, the principal should have the principal/auth | |
45 | session key at hand. The next step would be to request an authorization to | |
46 | use some other service: | |
47 | ||
48 | 2. Obtaining principal/service session key | |
49 | ||
50 | p->a : B, {principal_addr, timestamp}^principal/auth session key. authorize | |
51 | me! | |
52 | a->p : E= {service ticket}^svcsecret | |
53 | F= {principal/service session key, validity}^principal/auth session key | |
54 | ||
55 | principal_addr, timestamp = authenticator | |
56 | ||
57 | service ticket = principal name, client network address, validity, principal/service session key | |
58 | ||
59 | Note that steps 1 and 2 are pretty much the same thing; contacting the | |
60 | authenticator and requesting for a key. | |
61 | ||
62 | Following this the principal should have a principal/service session key that | |
63 | could be used later on for creating a session: | |
64 | ||
65 | 3. Opening a session to a service | |
66 | ||
67 | p->s : E + {principal_addr, timestamp}^principal/service session key | |
68 | s->p : {timestamp+1}^principal/service/session key | |
69 | ||
70 | timestamp+1 = reply authenticator | |
71 | ||
72 | Now, the principal is fully authenticated with the service. So, logically we | |
73 | have 2 main actions here. The first one would be to obtain a session key to | |
74 | the service (steps 1 and 2), and the second one would be to authenticate with | |
75 | the service, using that ticket. | |
76 | */ | |
77 | ||
78 | /* authenticate requests */ | |
79 | #define CEPHX_GET_AUTH_SESSION_KEY 0x0100 | |
80 | #define CEPHX_GET_PRINCIPAL_SESSION_KEY 0x0200 | |
81 | #define CEPHX_GET_ROTATING_KEY 0x0400 | |
82 | ||
83 | #define CEPHX_REQUEST_TYPE_MASK 0x0F00 | |
84 | #define CEPHX_CRYPT_ERR 1 | |
85 | ||
86 | #include "auth/Auth.h" | |
87 | #include <errno.h> | |
88 | #include <sstream> | |
89 | ||
90 | class CephContext; | |
91 | ||
92 | /* | |
93 | * Authentication | |
94 | */ | |
95 | ||
96 | // initial server -> client challenge | |
97 | struct CephXServerChallenge { | |
98 | uint64_t server_challenge; | |
99 | ||
100 | void encode(bufferlist& bl) const { | |
101 | __u8 struct_v = 1; | |
102 | ::encode(struct_v, bl); | |
103 | ::encode(server_challenge, bl); | |
104 | } | |
105 | void decode(bufferlist::iterator& bl) { | |
106 | __u8 struct_v; | |
107 | ::decode(struct_v, bl); | |
108 | ::decode(server_challenge, bl); | |
109 | } | |
110 | }; | |
111 | WRITE_CLASS_ENCODER(CephXServerChallenge) | |
112 | ||
113 | ||
114 | // request/reply headers, for subsequent exchanges. | |
115 | ||
116 | struct CephXRequestHeader { | |
117 | __u16 request_type; | |
118 | ||
119 | void encode(bufferlist& bl) const { | |
120 | ::encode(request_type, bl); | |
121 | } | |
122 | void decode(bufferlist::iterator& bl) { | |
123 | ::decode(request_type, bl); | |
124 | } | |
125 | }; | |
126 | WRITE_CLASS_ENCODER(CephXRequestHeader) | |
127 | ||
128 | struct CephXResponseHeader { | |
129 | uint16_t request_type; | |
130 | int32_t status; | |
131 | ||
132 | void encode(bufferlist& bl) const { | |
133 | ::encode(request_type, bl); | |
134 | ::encode(status, bl); | |
135 | } | |
136 | void decode(bufferlist::iterator& bl) { | |
137 | ::decode(request_type, bl); | |
138 | ::decode(status, bl); | |
139 | } | |
140 | }; | |
141 | WRITE_CLASS_ENCODER(CephXResponseHeader) | |
142 | ||
143 | struct CephXTicketBlob { | |
144 | uint64_t secret_id; | |
145 | bufferlist blob; | |
146 | ||
147 | CephXTicketBlob() : secret_id(0) {} | |
148 | ||
149 | void encode(bufferlist& bl) const { | |
150 | __u8 struct_v = 1; | |
151 | ::encode(struct_v, bl); | |
152 | ::encode(secret_id, bl); | |
153 | ::encode(blob, bl); | |
154 | } | |
155 | ||
156 | void decode(bufferlist::iterator& bl) { | |
157 | __u8 struct_v; | |
158 | ::decode(struct_v, bl); | |
159 | ::decode(secret_id, bl); | |
160 | ::decode(blob, bl); | |
161 | } | |
162 | }; | |
163 | WRITE_CLASS_ENCODER(CephXTicketBlob) | |
164 | ||
165 | // client -> server response to challenge | |
166 | struct CephXAuthenticate { | |
167 | uint64_t client_challenge; | |
168 | uint64_t key; | |
169 | CephXTicketBlob old_ticket; | |
170 | ||
171 | void encode(bufferlist& bl) const { | |
172 | __u8 struct_v = 1; | |
173 | ::encode(struct_v, bl); | |
174 | ::encode(client_challenge, bl); | |
175 | ::encode(key, bl); | |
176 | ::encode(old_ticket, bl); | |
177 | } | |
178 | void decode(bufferlist::iterator& bl) { | |
179 | __u8 struct_v; | |
180 | ::decode(struct_v, bl); | |
181 | ::decode(client_challenge, bl); | |
182 | ::decode(key, bl); | |
183 | ::decode(old_ticket, bl); | |
184 | } | |
185 | }; | |
186 | WRITE_CLASS_ENCODER(CephXAuthenticate) | |
187 | ||
188 | struct CephXChallengeBlob { | |
189 | uint64_t server_challenge, client_challenge; | |
190 | ||
191 | void encode(bufferlist& bl) const { | |
192 | ::encode(server_challenge, bl); | |
193 | ::encode(client_challenge, bl); | |
194 | } | |
195 | void decode(bufferlist::iterator& bl) { | |
196 | ::decode(server_challenge, bl); | |
197 | ::decode(client_challenge, bl); | |
198 | } | |
199 | }; | |
200 | WRITE_CLASS_ENCODER(CephXChallengeBlob) | |
201 | ||
202 | void cephx_calc_client_server_challenge(CephContext *cct, | |
203 | CryptoKey& secret, uint64_t server_challenge, uint64_t client_challenge, | |
204 | uint64_t *key, std::string &error); | |
205 | ||
206 | ||
207 | /* | |
208 | * getting service tickets | |
209 | */ | |
210 | struct CephXSessionAuthInfo { | |
211 | uint32_t service_id; | |
212 | uint64_t secret_id; | |
213 | AuthTicket ticket; | |
214 | CryptoKey session_key; | |
215 | CryptoKey service_secret; | |
216 | utime_t validity; | |
217 | }; | |
218 | ||
219 | ||
220 | extern bool cephx_build_service_ticket_blob(CephContext *cct, | |
221 | CephXSessionAuthInfo& ticket_info, CephXTicketBlob& blob); | |
222 | ||
223 | extern void cephx_build_service_ticket_request(CephContext *cct, | |
224 | uint32_t keys, | |
225 | bufferlist& request); | |
226 | ||
227 | extern bool cephx_build_service_ticket_reply(CephContext *cct, | |
228 | CryptoKey& principal_secret, | |
229 | vector<CephXSessionAuthInfo> ticket_info, | |
230 | bool should_encrypt_ticket, | |
231 | CryptoKey& ticket_enc_key, | |
232 | bufferlist& reply); | |
233 | ||
234 | struct CephXServiceTicketRequest { | |
235 | uint32_t keys; | |
236 | ||
237 | void encode(bufferlist& bl) const { | |
238 | __u8 struct_v = 1; | |
239 | ::encode(struct_v, bl); | |
240 | ::encode(keys, bl); | |
241 | } | |
242 | void decode(bufferlist::iterator& bl) { | |
243 | __u8 struct_v; | |
244 | ::decode(struct_v, bl); | |
245 | ::decode(keys, bl); | |
246 | } | |
247 | }; | |
248 | WRITE_CLASS_ENCODER(CephXServiceTicketRequest) | |
249 | ||
250 | ||
251 | /* | |
252 | * Authorize | |
253 | */ | |
254 | ||
255 | struct CephXAuthorizeReply { | |
256 | uint64_t nonce_plus_one; | |
257 | void encode(bufferlist& bl) const { | |
258 | __u8 struct_v = 1; | |
259 | ::encode(struct_v, bl); | |
260 | ::encode(nonce_plus_one, bl); | |
261 | } | |
262 | void decode(bufferlist::iterator& bl) { | |
263 | __u8 struct_v; | |
264 | ::decode(struct_v, bl); | |
265 | ::decode(nonce_plus_one, bl); | |
266 | } | |
267 | }; | |
268 | WRITE_CLASS_ENCODER(CephXAuthorizeReply) | |
269 | ||
270 | ||
271 | struct CephXAuthorizer : public AuthAuthorizer { | |
272 | private: | |
273 | CephContext *cct; | |
274 | public: | |
275 | uint64_t nonce; | |
28e407b8 | 276 | bufferlist base_bl; |
7c673cae FG |
277 | |
278 | explicit CephXAuthorizer(CephContext *cct_) | |
279 | : AuthAuthorizer(CEPH_AUTH_CEPHX), cct(cct_), nonce(0) {} | |
280 | ||
281 | bool build_authorizer(); | |
282 | bool verify_reply(bufferlist::iterator& reply) override; | |
28e407b8 | 283 | bool add_challenge(CephContext *cct, bufferlist& challenge) override; |
7c673cae FG |
284 | }; |
285 | ||
286 | ||
287 | ||
288 | /* | |
289 | * TicketHandler | |
290 | */ | |
291 | struct CephXTicketHandler { | |
292 | uint32_t service_id; | |
293 | CryptoKey session_key; | |
294 | CephXTicketBlob ticket; // opaque to us | |
295 | utime_t renew_after, expires; | |
296 | bool have_key_flag; | |
297 | ||
298 | CephXTicketHandler(CephContext *cct_, uint32_t service_id_) | |
299 | : service_id(service_id_), have_key_flag(false), cct(cct_) { } | |
300 | ||
301 | // to build our ServiceTicket | |
302 | bool verify_service_ticket_reply(CryptoKey& principal_secret, | |
303 | bufferlist::iterator& indata); | |
304 | // to access the service | |
305 | CephXAuthorizer *build_authorizer(uint64_t global_id) const; | |
306 | ||
307 | bool have_key(); | |
308 | bool need_key() const; | |
309 | ||
310 | void invalidate_ticket() { | |
311 | have_key_flag = 0; | |
312 | } | |
313 | private: | |
314 | CephContext *cct; | |
315 | }; | |
316 | ||
317 | struct CephXTicketManager { | |
318 | typedef map<uint32_t, CephXTicketHandler> tickets_map_t; | |
319 | tickets_map_t tickets_map; | |
320 | uint64_t global_id; | |
321 | ||
322 | explicit CephXTicketManager(CephContext *cct_) : global_id(0), cct(cct_) {} | |
323 | ||
324 | bool verify_service_ticket_reply(CryptoKey& principal_secret, | |
325 | bufferlist::iterator& indata); | |
326 | ||
327 | CephXTicketHandler& get_handler(uint32_t type) { | |
328 | tickets_map_t::iterator i = tickets_map.find(type); | |
329 | if (i != tickets_map.end()) | |
330 | return i->second; | |
331 | CephXTicketHandler newTicketHandler(cct, type); | |
332 | std::pair < tickets_map_t::iterator, bool > res = | |
333 | tickets_map.insert(std::make_pair(type, newTicketHandler)); | |
334 | assert(res.second); | |
335 | return res.first->second; | |
336 | } | |
337 | CephXAuthorizer *build_authorizer(uint32_t service_id) const; | |
338 | bool have_key(uint32_t service_id); | |
339 | bool need_key(uint32_t service_id) const; | |
340 | void set_have_need_key(uint32_t service_id, uint32_t& have, uint32_t& need); | |
341 | void validate_tickets(uint32_t mask, uint32_t& have, uint32_t& need); | |
342 | void invalidate_ticket(uint32_t service_id); | |
343 | ||
344 | private: | |
345 | CephContext *cct; | |
346 | }; | |
347 | ||
348 | ||
349 | /* A */ | |
350 | struct CephXServiceTicket { | |
351 | CryptoKey session_key; | |
352 | utime_t validity; | |
353 | ||
354 | void encode(bufferlist& bl) const { | |
355 | __u8 struct_v = 1; | |
356 | ::encode(struct_v, bl); | |
357 | ::encode(session_key, bl); | |
358 | ::encode(validity, bl); | |
359 | } | |
360 | void decode(bufferlist::iterator& bl) { | |
361 | __u8 struct_v; | |
362 | ::decode(struct_v, bl); | |
363 | ::decode(session_key, bl); | |
364 | ::decode(validity, bl); | |
365 | } | |
366 | }; | |
367 | WRITE_CLASS_ENCODER(CephXServiceTicket) | |
368 | ||
369 | /* B */ | |
370 | struct CephXServiceTicketInfo { | |
371 | AuthTicket ticket; | |
372 | CryptoKey session_key; | |
373 | ||
374 | void encode(bufferlist& bl) const { | |
375 | __u8 struct_v = 1; | |
376 | ::encode(struct_v, bl); | |
377 | ::encode(ticket, bl); | |
378 | ::encode(session_key, bl); | |
379 | } | |
380 | void decode(bufferlist::iterator& bl) { | |
381 | __u8 struct_v; | |
382 | ::decode(struct_v, bl); | |
383 | ::decode(ticket, bl); | |
384 | ::decode(session_key, bl); | |
385 | } | |
386 | }; | |
387 | WRITE_CLASS_ENCODER(CephXServiceTicketInfo) | |
388 | ||
28e407b8 AA |
389 | struct CephXAuthorizeChallenge : public AuthAuthorizerChallenge { |
390 | uint64_t server_challenge; | |
391 | void encode(bufferlist& bl) const { | |
392 | __u8 struct_v = 1; | |
393 | ::encode(struct_v, bl); | |
394 | ::encode(server_challenge, bl); | |
395 | } | |
396 | void decode(bufferlist::iterator& bl) { | |
397 | __u8 struct_v; | |
398 | ::decode(struct_v, bl); | |
399 | ::decode(server_challenge, bl); | |
400 | } | |
401 | }; | |
402 | WRITE_CLASS_ENCODER(CephXAuthorizeChallenge) | |
403 | ||
7c673cae FG |
404 | struct CephXAuthorize { |
405 | uint64_t nonce; | |
28e407b8 AA |
406 | bool have_challenge = false; |
407 | uint64_t server_challenge_plus_one = 0; | |
7c673cae | 408 | void encode(bufferlist& bl) const { |
28e407b8 | 409 | __u8 struct_v = 2; |
7c673cae FG |
410 | ::encode(struct_v, bl); |
411 | ::encode(nonce, bl); | |
28e407b8 AA |
412 | ::encode(have_challenge, bl); |
413 | ::encode(server_challenge_plus_one, bl); | |
7c673cae FG |
414 | } |
415 | void decode(bufferlist::iterator& bl) { | |
416 | __u8 struct_v; | |
417 | ::decode(struct_v, bl); | |
418 | ::decode(nonce, bl); | |
28e407b8 AA |
419 | if (struct_v >= 2) { |
420 | ::decode(have_challenge, bl); | |
421 | ::decode(server_challenge_plus_one, bl); | |
422 | } | |
423 | ||
7c673cae FG |
424 | } |
425 | }; | |
426 | WRITE_CLASS_ENCODER(CephXAuthorize) | |
427 | ||
428 | /* | |
429 | * Decode an extract ticket | |
430 | */ | |
431 | bool cephx_decode_ticket(CephContext *cct, KeyStore *keys, | |
432 | uint32_t service_id, CephXTicketBlob& ticket_blob, | |
433 | CephXServiceTicketInfo& ticket_info); | |
434 | ||
435 | /* | |
436 | * Verify authorizer and generate reply authorizer | |
437 | */ | |
28e407b8 AA |
438 | extern bool cephx_verify_authorizer( |
439 | CephContext *cct, KeyStore *keys, | |
440 | bufferlist::iterator& indata, | |
441 | CephXServiceTicketInfo& ticket_info, | |
442 | std::unique_ptr<AuthAuthorizerChallenge> *challenge, | |
443 | bufferlist& reply_bl); | |
7c673cae FG |
444 | |
445 | ||
446 | ||
447 | ||
448 | ||
449 | ||
450 | /* | |
451 | * encode+encrypt macros | |
452 | */ | |
453 | static constexpr uint64_t AUTH_ENC_MAGIC = 0xff009cad8826aa55ull; | |
454 | ||
455 | template <typename T> | |
456 | void decode_decrypt_enc_bl(CephContext *cct, T& t, CryptoKey key, bufferlist& bl_enc, | |
457 | std::string &error) | |
458 | { | |
459 | uint64_t magic; | |
460 | bufferlist bl; | |
461 | ||
462 | if (key.decrypt(cct, bl_enc, bl, &error) < 0) | |
463 | return; | |
464 | ||
465 | bufferlist::iterator iter2 = bl.begin(); | |
466 | __u8 struct_v; | |
467 | ::decode(struct_v, iter2); | |
468 | ::decode(magic, iter2); | |
469 | if (magic != AUTH_ENC_MAGIC) { | |
470 | ostringstream oss; | |
471 | oss << "bad magic in decode_decrypt, " << magic << " != " << AUTH_ENC_MAGIC; | |
472 | error = oss.str(); | |
473 | return; | |
474 | } | |
475 | ||
476 | ::decode(t, iter2); | |
477 | } | |
478 | ||
479 | template <typename T> | |
480 | void encode_encrypt_enc_bl(CephContext *cct, const T& t, const CryptoKey& key, | |
481 | bufferlist& out, std::string &error) | |
482 | { | |
483 | bufferlist bl; | |
484 | __u8 struct_v = 1; | |
485 | ::encode(struct_v, bl); | |
486 | uint64_t magic = AUTH_ENC_MAGIC; | |
487 | ::encode(magic, bl); | |
488 | ::encode(t, bl); | |
489 | ||
490 | key.encrypt(cct, bl, out, &error); | |
491 | } | |
492 | ||
493 | template <typename T> | |
494 | int decode_decrypt(CephContext *cct, T& t, const CryptoKey& key, | |
495 | bufferlist::iterator& iter, std::string &error) | |
496 | { | |
497 | bufferlist bl_enc; | |
498 | try { | |
499 | ::decode(bl_enc, iter); | |
500 | decode_decrypt_enc_bl(cct, t, key, bl_enc, error); | |
501 | } | |
502 | catch (buffer::error &e) { | |
503 | error = "error decoding block for decryption"; | |
504 | } | |
505 | if (!error.empty()) | |
506 | return CEPHX_CRYPT_ERR; | |
507 | return 0; | |
508 | } | |
509 | ||
510 | template <typename T> | |
511 | int encode_encrypt(CephContext *cct, const T& t, const CryptoKey& key, | |
512 | bufferlist& out, std::string &error) | |
513 | { | |
514 | bufferlist bl_enc; | |
515 | encode_encrypt_enc_bl(cct, t, key, bl_enc, error); | |
516 | if (!error.empty()){ | |
517 | return CEPHX_CRYPT_ERR; | |
518 | } | |
519 | ::encode(bl_enc, out); | |
520 | return 0; | |
521 | } | |
522 | ||
523 | ||
524 | #endif |