]>
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 "CephxServiceHandler.h" | |
17 | #include "CephxProtocol.h" | |
18 | #include "CephxKeyServer.h" | |
19 | #include <errno.h> | |
20 | #include <sstream> | |
21 | ||
22 | #include "common/config.h" | |
23 | #include "common/debug.h" | |
24 | ||
25 | #define dout_subsys ceph_subsys_auth | |
26 | #undef dout_prefix | |
27 | #define dout_prefix *_dout << "cephx server " << entity_name << ": " | |
28 | ||
29 | int CephxServiceHandler::start_session(EntityName& name, bufferlist::iterator& indata, bufferlist& result_bl, AuthCapsInfo& caps) | |
30 | { | |
31 | entity_name = name; | |
32 | ||
33 | get_random_bytes((char *)&server_challenge, sizeof(server_challenge)); | |
34 | if (!server_challenge) | |
35 | server_challenge = 1; // always non-zero. | |
36 | ldout(cct, 10) << "start_session server_challenge " << hex << server_challenge << dec << dendl; | |
37 | ||
38 | CephXServerChallenge ch; | |
39 | ch.server_challenge = server_challenge; | |
40 | ::encode(ch, result_bl); | |
41 | return CEPH_AUTH_CEPHX; | |
42 | } | |
43 | ||
44 | int CephxServiceHandler::handle_request(bufferlist::iterator& indata, bufferlist& result_bl, uint64_t& global_id, AuthCapsInfo& caps, uint64_t *auid) | |
45 | { | |
46 | int ret = 0; | |
47 | ||
48 | struct CephXRequestHeader cephx_header; | |
49 | ::decode(cephx_header, indata); | |
50 | ||
51 | ||
52 | switch (cephx_header.request_type) { | |
53 | case CEPHX_GET_AUTH_SESSION_KEY: | |
54 | { | |
55 | ldout(cct, 10) << "handle_request get_auth_session_key for " << entity_name << dendl; | |
56 | ||
57 | CephXAuthenticate req; | |
58 | ::decode(req, indata); | |
59 | ||
60 | CryptoKey secret; | |
61 | if (!key_server->get_secret(entity_name, secret)) { | |
62 | ldout(cct, 0) << "couldn't find entity name: " << entity_name << dendl; | |
63 | ret = -EPERM; | |
64 | break; | |
65 | } | |
66 | ||
67 | if (!server_challenge) { | |
68 | ret = -EPERM; | |
69 | break; | |
70 | } | |
71 | ||
72 | uint64_t expected_key; | |
73 | std::string error; | |
74 | cephx_calc_client_server_challenge(cct, secret, server_challenge, | |
75 | req.client_challenge, &expected_key, error); | |
76 | if (!error.empty()) { | |
77 | ldout(cct, 0) << " cephx_calc_client_server_challenge error: " << error << dendl; | |
78 | ret = -EPERM; | |
79 | break; | |
80 | } | |
81 | ||
82 | ldout(cct, 20) << " checking key: req.key=" << hex << req.key | |
83 | << " expected_key=" << expected_key << dec << dendl; | |
84 | if (req.key != expected_key) { | |
85 | ldout(cct, 0) << " unexpected key: req.key=" << hex << req.key | |
86 | << " expected_key=" << expected_key << dec << dendl; | |
87 | ret = -EPERM; | |
88 | break; | |
89 | } | |
90 | ||
91 | CryptoKey session_key; | |
92 | CephXSessionAuthInfo info; | |
93 | bool should_enc_ticket = false; | |
94 | ||
95 | EntityAuth eauth; | |
96 | if (! key_server->get_auth(entity_name, eauth)) { | |
97 | ret = -EPERM; | |
98 | break; | |
99 | } | |
100 | CephXServiceTicketInfo old_ticket_info; | |
101 | ||
102 | if (cephx_decode_ticket(cct, key_server, CEPH_ENTITY_TYPE_AUTH, | |
103 | req.old_ticket, old_ticket_info)) { | |
104 | global_id = old_ticket_info.ticket.global_id; | |
105 | ldout(cct, 10) << "decoded old_ticket with global_id=" << global_id << dendl; | |
106 | should_enc_ticket = true; | |
107 | } | |
108 | ||
109 | info.ticket.init_timestamps(ceph_clock_now(), cct->_conf->auth_mon_ticket_ttl); | |
110 | info.ticket.name = entity_name; | |
111 | info.ticket.global_id = global_id; | |
112 | info.ticket.auid = eauth.auid; | |
113 | info.validity += cct->_conf->auth_mon_ticket_ttl; | |
114 | ||
115 | if (auid) *auid = eauth.auid; | |
116 | ||
117 | key_server->generate_secret(session_key); | |
118 | ||
119 | info.session_key = session_key; | |
120 | info.service_id = CEPH_ENTITY_TYPE_AUTH; | |
121 | if (!key_server->get_service_secret(CEPH_ENTITY_TYPE_AUTH, info.service_secret, info.secret_id)) { | |
122 | ldout(cct, 0) << " could not get service secret for auth subsystem" << dendl; | |
123 | ret = -EIO; | |
124 | break; | |
125 | } | |
126 | ||
127 | vector<CephXSessionAuthInfo> info_vec; | |
128 | info_vec.push_back(info); | |
129 | ||
130 | build_cephx_response_header(cephx_header.request_type, 0, result_bl); | |
131 | if (!cephx_build_service_ticket_reply(cct, eauth.key, info_vec, should_enc_ticket, | |
132 | old_ticket_info.session_key, result_bl)) { | |
133 | ret = -EIO; | |
134 | } | |
135 | ||
136 | if (!key_server->get_service_caps(entity_name, CEPH_ENTITY_TYPE_MON, caps)) { | |
137 | ldout(cct, 0) << " could not get mon caps for " << entity_name << dendl; | |
138 | ret = -EACCES; | |
139 | } else { | |
140 | char *caps_str = caps.caps.c_str(); | |
141 | if (!caps_str || !caps_str[0]) { | |
142 | ldout(cct,0) << "mon caps null for " << entity_name << dendl; | |
143 | ret = -EACCES; | |
144 | } | |
145 | } | |
146 | } | |
147 | break; | |
148 | ||
149 | case CEPHX_GET_PRINCIPAL_SESSION_KEY: | |
150 | { | |
151 | ldout(cct, 10) << "handle_request get_principal_session_key" << dendl; | |
152 | ||
153 | bufferlist tmp_bl; | |
154 | CephXServiceTicketInfo auth_ticket_info; | |
155 | if (!cephx_verify_authorizer(cct, key_server, indata, auth_ticket_info, tmp_bl)) { | |
156 | ret = -EPERM; | |
157 | break; | |
158 | } | |
159 | ||
160 | CephXServiceTicketRequest ticket_req; | |
161 | ::decode(ticket_req, indata); | |
162 | ldout(cct, 10) << " ticket_req.keys = " << ticket_req.keys << dendl; | |
163 | ||
164 | ret = 0; | |
165 | vector<CephXSessionAuthInfo> info_vec; | |
166 | int found_services = 0; | |
167 | int service_err = 0; | |
168 | for (uint32_t service_id = 1; service_id <= ticket_req.keys; | |
169 | service_id <<= 1) { | |
170 | if (ticket_req.keys & service_id) { | |
171 | ldout(cct, 10) << " adding key for service " | |
172 | << ceph_entity_type_name(service_id) << dendl; | |
173 | CephXSessionAuthInfo info; | |
174 | int r = key_server->build_session_auth_info(service_id, | |
175 | auth_ticket_info, info); | |
176 | // tolerate missing MGR rotating key for the purposes of upgrades. | |
177 | if (r < 0) { | |
178 | ldout(cct, 10) << " missing key for service " | |
179 | << ceph_entity_type_name(service_id) << dendl; | |
180 | service_err = r; | |
181 | continue; | |
182 | } | |
183 | info.validity += cct->_conf->auth_service_ticket_ttl; | |
184 | info_vec.push_back(info); | |
185 | ++found_services; | |
186 | } | |
187 | } | |
188 | if (!found_services && service_err) { | |
189 | ldout(cct, 10) << __func__ << " did not find any service keys" << dendl; | |
190 | ret = service_err; | |
191 | } | |
192 | CryptoKey no_key; | |
193 | build_cephx_response_header(cephx_header.request_type, ret, result_bl); | |
194 | cephx_build_service_ticket_reply(cct, auth_ticket_info.session_key, info_vec, false, no_key, result_bl); | |
195 | } | |
196 | break; | |
197 | ||
198 | case CEPHX_GET_ROTATING_KEY: | |
199 | { | |
200 | ldout(cct, 10) << "handle_request getting rotating secret for " << entity_name << dendl; | |
201 | build_cephx_response_header(cephx_header.request_type, 0, result_bl); | |
202 | if (!key_server->get_rotating_encrypted(entity_name, result_bl)) { | |
203 | ret = -EPERM; | |
204 | break; | |
205 | } | |
206 | } | |
207 | break; | |
208 | ||
209 | default: | |
210 | ldout(cct, 10) << "handle_request unknown op " << cephx_header.request_type << dendl; | |
211 | return -EINVAL; | |
212 | } | |
213 | return ret; | |
214 | } | |
215 | ||
216 | void CephxServiceHandler::build_cephx_response_header(int request_type, int status, bufferlist& bl) | |
217 | { | |
218 | struct CephXResponseHeader header; | |
219 | header.request_type = request_type; | |
220 | header.status = status; | |
221 | ::encode(header, bl); | |
222 | } |