Line data Source code
1 : /*
2 : Authors:
3 : Pavel Březina <pbrezina@redhat.com>
4 :
5 : Copyright (C) 2011 Red Hat
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include <string.h>
22 : #include <stdint.h>
23 : #include <errno.h>
24 : #include <talloc.h>
25 : #include <tevent.h>
26 :
27 : #include "util/util.h"
28 : #include "responder/sudo/sudosrv_private.h"
29 :
30 0 : static int sudosrv_response_append_string(TALLOC_CTX *mem_ctx,
31 : const char *str,
32 : size_t str_len,
33 : uint8_t **_response_body,
34 : size_t *_response_len)
35 : {
36 0 : size_t response_len = *_response_len;
37 0 : uint8_t *response_body = *_response_body;
38 :
39 0 : response_body = talloc_realloc(mem_ctx, response_body, uint8_t,
40 : response_len + (str_len * sizeof(char)));
41 0 : if (response_body == NULL) {
42 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_realloc() failed\n");
43 0 : return ENOMEM;
44 : }
45 0 : memcpy(response_body + response_len, str, str_len);
46 0 : response_len += str_len;
47 :
48 0 : *_response_body = response_body;
49 0 : *_response_len = response_len;
50 :
51 0 : return EOK;
52 : }
53 :
54 0 : static int sudosrv_response_append_uint32(TALLOC_CTX *mem_ctx,
55 : uint32_t number,
56 : uint8_t **_response_body,
57 : size_t *_response_len)
58 : {
59 0 : size_t response_len = *_response_len;
60 0 : uint8_t *response_body = *_response_body;
61 :
62 0 : response_body = talloc_realloc(mem_ctx, response_body, uint8_t,
63 : response_len + sizeof(uint32_t));
64 0 : if (response_body == NULL) {
65 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_realloc() failed\n");
66 0 : return ENOMEM;
67 : }
68 0 : SAFEALIGN_SET_UINT32(response_body + response_len, number, &response_len);
69 :
70 0 : *_response_body = response_body;
71 0 : *_response_len = response_len;
72 :
73 0 : return EOK;
74 : }
75 :
76 0 : static int sudosrv_response_append_attr(TALLOC_CTX *mem_ctx,
77 : const char *name,
78 : unsigned int values_num,
79 : struct ldb_val *values,
80 : uint8_t **_response_body,
81 : size_t *_response_len)
82 : {
83 0 : uint8_t *response_body = *_response_body;
84 0 : size_t response_len = *_response_len;
85 0 : TALLOC_CTX *tmp_ctx = NULL;
86 0 : int i = 0;
87 0 : int ret = EOK;
88 :
89 0 : tmp_ctx = talloc_new(NULL);
90 0 : if (tmp_ctx == NULL) {
91 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
92 0 : return ENOMEM;
93 : }
94 :
95 : /* attr name */
96 0 : ret = sudosrv_response_append_string(tmp_ctx, name, strlen(name) + 1,
97 : &response_body, &response_len);
98 0 : if (ret != EOK) {
99 0 : goto done;
100 : }
101 :
102 : /* values count */
103 0 : ret = sudosrv_response_append_uint32(tmp_ctx, values_num,
104 : &response_body, &response_len);
105 0 : if (ret != EOK) {
106 0 : goto done;
107 : }
108 :
109 : /* values */
110 0 : for (i = 0; i < values_num; i++) {
111 0 : if (strlen((char*)(values[i].data)) != values[i].length) {
112 0 : DEBUG(SSSDBG_CRIT_FAILURE, "value is not a string\n");
113 0 : ret = EINVAL;
114 0 : goto done;
115 : }
116 :
117 0 : ret = sudosrv_response_append_string(tmp_ctx,
118 0 : (const char*)values[i].data,
119 0 : values[i].length + 1,
120 : &response_body, &response_len);
121 0 : if (ret != EOK) {
122 0 : goto done;
123 : }
124 : }
125 :
126 0 : *_response_body = talloc_steal(mem_ctx, response_body);
127 0 : *_response_len = response_len;
128 :
129 0 : ret = EOK;
130 :
131 : done:
132 0 : talloc_free(tmp_ctx);
133 0 : return ret;
134 : }
135 :
136 0 : static int sudosrv_response_append_rule(TALLOC_CTX *mem_ctx,
137 : int attrs_num,
138 : struct ldb_message_element *attrs,
139 : uint8_t **_response_body,
140 : size_t *_response_len)
141 : {
142 0 : uint8_t *response_body = *_response_body;
143 0 : size_t response_len = *_response_len;
144 0 : TALLOC_CTX *tmp_ctx = NULL;
145 0 : int i = 0;
146 0 : int ret = EOK;
147 :
148 0 : tmp_ctx = talloc_new(NULL);
149 0 : if (tmp_ctx == NULL) {
150 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
151 0 : return ENOMEM;
152 : }
153 :
154 : /* attrs count */
155 0 : ret = sudosrv_response_append_uint32(tmp_ctx, attrs_num,
156 : &response_body, &response_len);
157 0 : if (ret != EOK) {
158 0 : goto done;
159 : }
160 :
161 : /* attrs */
162 0 : for (i = 0; i < attrs_num; i++) {
163 0 : ret = sudosrv_response_append_attr(tmp_ctx, attrs[i].name,
164 0 : attrs[i].num_values, attrs[i].values,
165 : &response_body, &response_len);
166 0 : if (ret != EOK) {
167 0 : goto done;
168 : }
169 : }
170 :
171 0 : *_response_body = talloc_steal(mem_ctx, response_body);
172 0 : *_response_len = response_len;
173 :
174 0 : ret = EOK;
175 :
176 : done:
177 0 : talloc_free(tmp_ctx);
178 0 : return ret;
179 : }
180 :
181 : /*
182 : * Response format:
183 : * <error_code(uint32_t)><domain(char*)>\0<num_entries(uint32_t)><rule1><rule2>...
184 : * <ruleN> = <num_attrs(uint32_t)><attr1><attr2>...
185 : * <attrN> = <name(char*)>\0<num_values(uint32_t)><value1(char*)>\0<value2(char*)>\0...
186 : *
187 : * if <error_code> is not SSS_SUDO_ERROR_OK, the rest of the data is skipped.
188 : */
189 0 : errno_t sudosrv_build_response(TALLOC_CTX *mem_ctx,
190 : uint32_t error,
191 : uint32_t rules_num,
192 : struct sysdb_attrs **rules,
193 : uint8_t **_response_body,
194 : size_t *_response_len)
195 : {
196 0 : uint8_t *response_body = NULL;
197 0 : size_t response_len = 0;
198 0 : TALLOC_CTX *tmp_ctx = NULL;
199 0 : uint32_t i = 0;
200 0 : errno_t ret = EOK;
201 :
202 0 : tmp_ctx = talloc_new(NULL);
203 0 : if (tmp_ctx == NULL) {
204 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
205 0 : return ENOMEM;
206 : }
207 :
208 : /* error code */
209 0 : ret = sudosrv_response_append_uint32(tmp_ctx, error,
210 : &response_body, &response_len);
211 0 : if (ret != EOK) {
212 0 : goto fail;
213 : }
214 :
215 0 : if (error != SSS_SUDO_ERROR_OK) {
216 0 : goto done;
217 : }
218 :
219 : /* domain name - deprecated
220 : * TODO: when possible change the protocol */
221 0 : ret = sudosrv_response_append_string(tmp_ctx, "\0", 1,
222 : &response_body, &response_len);
223 0 : if (ret != EOK) {
224 0 : goto fail;
225 : }
226 :
227 : /* rules count */
228 0 : ret = sudosrv_response_append_uint32(tmp_ctx, rules_num,
229 : &response_body, &response_len);
230 0 : if (ret != EOK) {
231 0 : goto fail;
232 : }
233 :
234 : /* rules */
235 0 : for (i = 0; i < rules_num; i++) {
236 0 : ret = sudosrv_response_append_rule(tmp_ctx, rules[i]->num, rules[i]->a,
237 : &response_body, &response_len);
238 0 : if (ret != EOK) {
239 0 : goto fail;
240 : }
241 : }
242 :
243 : done:
244 0 : *_response_body = talloc_steal(mem_ctx, response_body);
245 0 : *_response_len = response_len;
246 :
247 0 : ret = EOK;
248 :
249 : fail:
250 0 : talloc_free(tmp_ctx);
251 0 : return ret;
252 : }
253 :
254 : struct sudosrv_parse_query_state {
255 : struct resp_ctx *rctx;
256 : uid_t uid;
257 : char *rawname;
258 : };
259 :
260 : static void sudosrv_parse_query_done(struct tevent_req *subreq);
261 :
262 0 : struct tevent_req *sudosrv_parse_query_send(TALLOC_CTX *mem_ctx,
263 : struct resp_ctx *rctx,
264 : uint8_t *query_body,
265 : size_t query_len)
266 : {
267 0 : struct tevent_req *req = NULL;
268 0 : struct tevent_req *subreq = NULL;
269 0 : struct sudosrv_parse_query_state *state = NULL;
270 0 : size_t offset = 0;
271 0 : size_t rawname_len = 0;
272 0 : char *rawname = NULL;
273 0 : char *domainname = NULL;
274 : errno_t ret;
275 :
276 : /* create request */
277 0 : req = tevent_req_create(mem_ctx, &state,
278 : struct sudosrv_parse_query_state);
279 0 : if (req == NULL) {
280 0 : DEBUG(SSSDBG_FATAL_FAILURE, "tevent_req_create() failed\n");
281 0 : return NULL;
282 : }
283 :
284 0 : state->rctx = rctx;
285 :
286 : /* uid */
287 :
288 0 : if (query_len < sizeof(uid_t)) {
289 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Query is too small\n");
290 0 : ret = EINVAL;
291 0 : goto done;
292 : }
293 0 : safealign_memcpy(&state->uid, query_body, sizeof(uid_t), &offset);
294 :
295 : /* username[@domain] */
296 :
297 0 : rawname = (char*)(query_body + offset);
298 0 : rawname_len = query_len - offset; /* strlen + zero */
299 :
300 0 : if (rawname[rawname_len - 1] != '\0') {
301 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Username is not zero terminated\n");
302 0 : ret = EINVAL;
303 0 : goto done;
304 : }
305 :
306 0 : if (rawname_len < 2) { /* at least one character and zero */
307 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Query does not contain username\n");
308 0 : ret = EINVAL;
309 0 : goto done;
310 : }
311 :
312 0 : if (!sss_utf8_check((uint8_t*)rawname, rawname_len - 1)) {
313 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Supplied data is not valid UTF-8 string\n");
314 0 : ret = EINVAL;
315 0 : goto done;
316 : }
317 :
318 : /* parse username */
319 :
320 0 : state->rawname = rawname;
321 0 : ret = sss_parse_name_for_domains(state, rctx->domains,
322 0 : rctx->default_domain, state->rawname,
323 : &domainname, NULL);
324 0 : if (ret == EAGAIN) {
325 0 : DEBUG(SSSDBG_TRACE_FUNC, "Domain [%s] not found, "
326 : "sending subdomain request\n", domainname);
327 :
328 0 : subreq = sss_dp_get_domains_send(state, rctx, true, domainname);
329 0 : if (subreq == NULL) {
330 0 : ret = ENOMEM;
331 : } else {
332 0 : tevent_req_set_callback(subreq, sudosrv_parse_query_done, req);
333 0 : ret = EAGAIN;
334 : }
335 0 : goto done;
336 0 : } else if (ret != EOK) {
337 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid name received [%s]\n", rawname);
338 0 : goto done;
339 : }
340 :
341 0 : ret = EOK;
342 :
343 : done:
344 0 : if (ret != EAGAIN) {
345 0 : if (ret == EOK) {
346 0 : tevent_req_done(req);
347 : } else {
348 0 : tevent_req_error(req, ret);
349 : }
350 0 : tevent_req_post(req, rctx->ev);
351 : }
352 :
353 0 : return req;
354 : }
355 :
356 0 : static void sudosrv_parse_query_done(struct tevent_req *subreq)
357 : {
358 0 : struct tevent_req *req = NULL;
359 : errno_t ret;
360 :
361 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
362 :
363 0 : ret = sss_dp_get_domains_recv(subreq);
364 0 : talloc_free(subreq);
365 0 : if (ret != EOK) {
366 0 : tevent_req_error(req, ret);
367 0 : return;
368 : }
369 :
370 0 : tevent_req_done(req);
371 : }
372 :
373 0 : errno_t sudosrv_parse_query_recv(TALLOC_CTX *mem_ctx,
374 : struct tevent_req *req,
375 : uid_t *_uid,
376 : char **_username,
377 : struct sss_domain_info **_domain)
378 : {
379 0 : struct sudosrv_parse_query_state *state = NULL;
380 0 : struct sss_domain_info *domain = NULL;
381 0 : char *username = NULL;
382 0 : char *domainname = NULL;
383 : errno_t ret;
384 :
385 0 : state = tevent_req_data(req, struct sudosrv_parse_query_state);
386 :
387 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
388 :
389 0 : if (state->rawname == NULL) {
390 0 : DEBUG(SSSDBG_CRIT_FAILURE, "No query specified?!\n");
391 0 : return EINVAL;
392 : }
393 :
394 : /* Try to parse username@domain again because if the first call
395 : * returned EAGAIN, then username is unset. If we get EAGAIN again,
396 : * we will not search for it again.
397 : */
398 0 : ret = sss_parse_name_for_domains(state, state->rctx->domains,
399 0 : state->rctx->default_domain,
400 0 : state->rawname,
401 : &domainname, &username);
402 0 : if (ret != EOK) {
403 0 : DEBUG(SSSDBG_TRACE_FUNC, "Unable to parse domain [%d]: %s\n",
404 : ret, strerror(ret));
405 0 : return ret;
406 : }
407 :
408 0 : if (username == NULL) {
409 0 : DEBUG(SSSDBG_CRIT_FAILURE, "No username specified!\n");
410 0 : return EINVAL;
411 : }
412 :
413 0 : if (domainname != NULL) {
414 : /* mem_ctx because it duplicates only subdomains not domains
415 : * so I cannot easily steal it */
416 0 : domain = responder_get_domain(state->rctx, domainname);
417 0 : if (domain == NULL) {
418 0 : DEBUG(SSSDBG_OP_FAILURE, "Corresponding domain [%s] has not been "
419 : "found\n", domainname);
420 0 : return ENOENT;
421 : }
422 : }
423 :
424 0 : *_uid = state->uid;
425 0 : *_username = talloc_steal(mem_ctx, username);
426 0 : *_domain = domain; /* do not steal on mem_ctx */
427 :
428 0 : return EOK;
429 : }
|