Line data Source code
1 : /*
2 : Authors:
3 : Jan Cholasta <jcholast@redhat.com>
4 :
5 : Copyright (C) 2012 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 "config.h"
22 :
23 : #include <talloc.h>
24 : #include <string.h>
25 : #include <netdb.h>
26 :
27 : #include "util/util.h"
28 : #include "util/crypto/sss_crypto.h"
29 : #include "util/sss_ssh.h"
30 : #include "util/cert.h"
31 : #include "db/sysdb.h"
32 : #include "db/sysdb_ssh.h"
33 : #include "providers/data_provider.h"
34 : #include "responder/common/responder.h"
35 : #include "responder/common/responder_packet.h"
36 : #include "responder/ssh/sshsrv_private.h"
37 :
38 : static errno_t
39 : ssh_cmd_parse_request(struct ssh_cmd_ctx *cmd_ctx);
40 :
41 : static errno_t
42 : ssh_user_pubkeys_search(struct ssh_cmd_ctx *cmd_ctx);
43 : static errno_t
44 : ssh_cmd_get_user_pubkeys_done(struct ssh_cmd_ctx *cmd_ctx,
45 : errno_t ret);
46 :
47 : int
48 0 : sss_ssh_cmd_get_user_pubkeys(struct cli_ctx *cctx)
49 : {
50 : errno_t ret;
51 : struct ssh_cmd_ctx *cmd_ctx;
52 :
53 0 : cmd_ctx = talloc_zero(cctx, struct ssh_cmd_ctx);
54 0 : if (!cmd_ctx) {
55 0 : return ENOMEM;
56 : }
57 0 : cmd_ctx->cctx = cctx;
58 0 : cmd_ctx->is_user = true;
59 :
60 0 : ret = ssh_cmd_parse_request(cmd_ctx);
61 0 : if (ret != EOK) {
62 0 : goto done;
63 : }
64 :
65 0 : DEBUG(SSSDBG_TRACE_FUNC,
66 : "Requesting SSH user public keys for [%s] from [%s]\n",
67 : cmd_ctx->name, cmd_ctx->domname ? cmd_ctx->domname : "<ALL>");
68 :
69 0 : if (strcmp(cmd_ctx->name, "root") == 0) {
70 0 : ret = ENOENT;
71 0 : goto done;
72 : }
73 :
74 0 : if (cmd_ctx->domname) {
75 0 : cmd_ctx->domain = responder_get_domain(cctx->rctx, cmd_ctx->domname);
76 0 : if (!cmd_ctx->domain) {
77 0 : ret = ENOENT;
78 0 : goto done;
79 : }
80 : } else {
81 0 : cmd_ctx->domain = cctx->rctx->domains;
82 0 : cmd_ctx->check_next = true;
83 : }
84 :
85 0 : ret = ssh_user_pubkeys_search(cmd_ctx);
86 :
87 : done:
88 0 : return ssh_cmd_get_user_pubkeys_done(cmd_ctx, ret);
89 : }
90 :
91 : static errno_t
92 : ssh_host_pubkeys_search(struct ssh_cmd_ctx *cmd_ctx);
93 : static errno_t
94 : ssh_cmd_get_host_pubkeys_done(struct ssh_cmd_ctx *cmd_ctx,
95 : errno_t ret);
96 :
97 : static int
98 0 : sss_ssh_cmd_get_host_pubkeys(struct cli_ctx *cctx)
99 : {
100 : errno_t ret;
101 : struct ssh_cmd_ctx *cmd_ctx;
102 :
103 0 : cmd_ctx = talloc_zero(cctx, struct ssh_cmd_ctx);
104 0 : if (!cmd_ctx) {
105 0 : return ENOMEM;
106 : }
107 0 : cmd_ctx->cctx = cctx;
108 0 : cmd_ctx->is_user = false;
109 :
110 0 : ret = ssh_cmd_parse_request(cmd_ctx);
111 0 : if (ret != EOK) {
112 0 : goto done;
113 : }
114 :
115 0 : DEBUG(SSSDBG_TRACE_FUNC,
116 : "Requesting SSH host public keys for [%s][%s] from [%s]\n",
117 : cmd_ctx->name, cmd_ctx->alias ? cmd_ctx->alias : "",
118 : cmd_ctx->domname ? cmd_ctx->domname : "<ALL>");
119 :
120 0 : if (cmd_ctx->domname) {
121 0 : cmd_ctx->domain = responder_get_domain(cctx->rctx, cmd_ctx->domname);
122 0 : if (!cmd_ctx->domain) {
123 0 : ret = ENOENT;
124 0 : goto done;
125 : }
126 : } else {
127 0 : cmd_ctx->domain = cctx->rctx->domains;
128 0 : cmd_ctx->check_next = true;
129 : }
130 :
131 0 : ret = ssh_host_pubkeys_search(cmd_ctx);
132 :
133 : done:
134 0 : return ssh_cmd_get_host_pubkeys_done(cmd_ctx, ret);
135 : }
136 :
137 : static void
138 0 : ssh_dp_send_req_done(struct tevent_req *req)
139 : {
140 0 : struct dp_callback_ctx *cb_ctx =
141 0 : tevent_req_callback_data(req, struct dp_callback_ctx);
142 :
143 : errno_t ret;
144 : dbus_uint16_t err_maj;
145 : dbus_uint32_t err_min;
146 : char *err_msg;
147 :
148 0 : ret = sss_dp_get_ssh_host_recv(cb_ctx->mem_ctx, req,
149 : &err_maj, &err_min,
150 : &err_msg);
151 0 : talloc_zfree(req);
152 0 : if (ret != EOK) {
153 0 : DEBUG(SSSDBG_CRIT_FAILURE,
154 : "Fatal error, killing connection!\n");
155 0 : talloc_free(cb_ctx->cctx);
156 0 : return;
157 : }
158 :
159 0 : cb_ctx->callback(err_maj, err_min, err_msg, cb_ctx->ptr);
160 : }
161 :
162 : static errno_t
163 : ssh_user_pubkeys_search_next(struct ssh_cmd_ctx *cmd_ctx);
164 : static void
165 : ssh_user_pubkeys_search_dp_callback(uint16_t err_maj,
166 : uint32_t err_min,
167 : const char *err_msg,
168 : void *ptr);
169 :
170 : static errno_t
171 0 : ssh_user_pubkeys_search(struct ssh_cmd_ctx *cmd_ctx)
172 : {
173 : struct tevent_req *req;
174 : struct dp_callback_ctx *cb_ctx;
175 :
176 : /* if it is a domainless search, skip domains that require fully
177 : * qualified names instead */
178 0 : while (cmd_ctx->domain && cmd_ctx->check_next && cmd_ctx->domain->fqnames) {
179 0 : cmd_ctx->domain = get_next_domain(cmd_ctx->domain, false);
180 : }
181 :
182 0 : if (!cmd_ctx->domain) {
183 0 : DEBUG(SSSDBG_OP_FAILURE,
184 : "No matching domain found for [%s], fail!\n", cmd_ctx->name);
185 0 : return ENOENT;
186 : }
187 :
188 : /* refresh the user's cache entry */
189 0 : if (NEED_CHECK_PROVIDER(cmd_ctx->domain->provider)) {
190 0 : req = sss_dp_get_account_send(cmd_ctx, cmd_ctx->cctx->rctx,
191 : cmd_ctx->domain, false, SSS_DP_USER,
192 0 : cmd_ctx->name, 0, NULL);
193 0 : if (!req) {
194 0 : DEBUG(SSSDBG_CRIT_FAILURE,
195 : "Out of memory sending data provider request\n");
196 0 : return ENOMEM;
197 : }
198 :
199 0 : cb_ctx = talloc_zero(cmd_ctx, struct dp_callback_ctx);
200 0 : if (!cb_ctx) {
201 0 : talloc_zfree(req);
202 0 : return ENOMEM;
203 : }
204 :
205 0 : cb_ctx->callback = ssh_user_pubkeys_search_dp_callback;
206 0 : cb_ctx->ptr = cmd_ctx;
207 0 : cb_ctx->cctx = cmd_ctx->cctx;
208 0 : cb_ctx->mem_ctx = cmd_ctx;
209 :
210 0 : tevent_req_set_callback(req, ssh_dp_send_req_done, cb_ctx);
211 :
212 : /* tell caller we are in an async call */
213 0 : return EAGAIN;
214 : }
215 :
216 0 : return ssh_user_pubkeys_search_next(cmd_ctx);
217 : }
218 :
219 : static errno_t
220 0 : ssh_user_pubkeys_search_next(struct ssh_cmd_ctx *cmd_ctx)
221 : {
222 : errno_t ret;
223 0 : const char *attrs[] = { SYSDB_NAME, SYSDB_SSH_PUBKEY, SYSDB_USER_CERT,
224 : NULL };
225 : struct ldb_result *res;
226 :
227 0 : DEBUG(SSSDBG_TRACE_FUNC,
228 : "Requesting SSH user public keys for [%s@%s]\n",
229 : cmd_ctx->name, cmd_ctx->domain->name);
230 :
231 0 : if (cmd_ctx->domain->sysdb == NULL) {
232 0 : DEBUG(SSSDBG_FATAL_FAILURE,
233 : "Fatal: Sysdb CTX not found for this domain!\n");
234 0 : return EFAULT;
235 : }
236 :
237 0 : ret = sysdb_get_user_attr_with_views(cmd_ctx, cmd_ctx->domain,
238 0 : cmd_ctx->name, attrs, &res);
239 0 : if (ret != EOK) {
240 0 : DEBUG(SSSDBG_CRIT_FAILURE,
241 : "Failed to make request to our cache!\n");
242 0 : return EIO;
243 : }
244 :
245 0 : if (res->count > 1) {
246 0 : DEBUG(SSSDBG_FATAL_FAILURE,
247 : "User search by name (%s) returned > 1 results!\n",
248 : cmd_ctx->name);
249 0 : return EINVAL;
250 : }
251 :
252 0 : if (!res->count) {
253 : /* if a multidomain search, try with next */
254 0 : if (cmd_ctx->check_next) {
255 0 : cmd_ctx->domain = get_next_domain(cmd_ctx->domain, false);
256 0 : return ssh_user_pubkeys_search(cmd_ctx);
257 : }
258 :
259 0 : DEBUG(SSSDBG_OP_FAILURE,
260 : "No attributes for user [%s] found.\n", cmd_ctx->name);
261 :
262 0 : return ENOENT;
263 : }
264 :
265 0 : cmd_ctx->result = res->msgs[0];
266 :
267 : /* one result found */
268 0 : return EOK;
269 : }
270 :
271 : static void
272 0 : ssh_user_pubkeys_search_dp_callback(uint16_t err_maj,
273 : uint32_t err_min,
274 : const char *err_msg,
275 : void *ptr)
276 : {
277 0 : struct ssh_cmd_ctx *cmd_ctx = talloc_get_type(ptr, struct ssh_cmd_ctx);
278 : errno_t ret;
279 :
280 0 : if (err_maj) {
281 0 : DEBUG(SSSDBG_OP_FAILURE,
282 : "Unable to get information from Data Provider\n"
283 : "Error: %u, %u, %s\n",
284 : (unsigned int)err_maj, (unsigned int)err_min, err_msg);
285 : }
286 :
287 0 : ret = ssh_user_pubkeys_search_next(cmd_ctx);
288 0 : ssh_cmd_get_user_pubkeys_done(cmd_ctx, ret);
289 0 : }
290 :
291 : static errno_t
292 : ssh_host_pubkeys_search_next(struct ssh_cmd_ctx *cmd_ctx);
293 : static void
294 : ssh_host_pubkeys_search_dp_callback(uint16_t err_maj,
295 : uint32_t err_min,
296 : const char *err_msg,
297 : void *ptr);
298 :
299 : static errno_t
300 0 : ssh_host_pubkeys_search(struct ssh_cmd_ctx *cmd_ctx)
301 : {
302 : struct tevent_req *req;
303 : struct dp_callback_ctx *cb_ctx;
304 :
305 0 : if (!cmd_ctx->domain) {
306 0 : DEBUG(SSSDBG_OP_FAILURE,
307 : "No matching domain found for [%s], fail!\n", cmd_ctx->name);
308 0 : return ENOENT;
309 : }
310 :
311 : /* refresh the host's cache entry */
312 0 : if (NEED_CHECK_PROVIDER(cmd_ctx->domain->provider)) {
313 0 : req = sss_dp_get_ssh_host_send(cmd_ctx, cmd_ctx->cctx->rctx,
314 : cmd_ctx->domain, false,
315 0 : cmd_ctx->name, cmd_ctx->alias);
316 0 : if (!req) {
317 0 : DEBUG(SSSDBG_CRIT_FAILURE,
318 : "Out of memory sending data provider request\n");
319 0 : return ENOMEM;
320 : }
321 :
322 0 : cb_ctx = talloc_zero(cmd_ctx, struct dp_callback_ctx);
323 0 : if (!cb_ctx) {
324 0 : talloc_zfree(req);
325 0 : return ENOMEM;
326 : }
327 :
328 0 : cb_ctx->callback = ssh_host_pubkeys_search_dp_callback;
329 0 : cb_ctx->ptr = cmd_ctx;
330 0 : cb_ctx->cctx = cmd_ctx->cctx;
331 0 : cb_ctx->mem_ctx = cmd_ctx;
332 :
333 0 : tevent_req_set_callback(req, ssh_dp_send_req_done, cb_ctx);
334 :
335 : /* tell caller we are in an async call */
336 0 : return EAGAIN;
337 : }
338 :
339 0 : return ssh_host_pubkeys_search_next(cmd_ctx);
340 : }
341 :
342 : static errno_t
343 0 : ssh_host_pubkeys_search_next(struct ssh_cmd_ctx *cmd_ctx)
344 : {
345 : errno_t ret;
346 : struct sysdb_ctx *sysdb;
347 0 : const char *attrs[] = { SYSDB_NAME, SYSDB_SSH_PUBKEY, NULL };
348 :
349 0 : DEBUG(SSSDBG_TRACE_FUNC,
350 : "Requesting SSH host public keys for [%s@%s]\n",
351 : cmd_ctx->name, cmd_ctx->domain->name);
352 :
353 0 : sysdb = cmd_ctx->domain->sysdb;
354 0 : if (sysdb == NULL) {
355 0 : DEBUG(SSSDBG_FATAL_FAILURE,
356 : "Fatal: Sysdb CTX not found for this domain!\n");
357 0 : return EFAULT;
358 : }
359 :
360 0 : ret = sysdb_get_ssh_host(cmd_ctx, cmd_ctx->domain,
361 0 : cmd_ctx->name, attrs, &cmd_ctx->result);
362 0 : if (ret != EOK && ret != ENOENT) {
363 0 : DEBUG(SSSDBG_CRIT_FAILURE,
364 : "Failed to make request to our cache!\n");
365 0 : return EIO;
366 : }
367 :
368 0 : if (ret == ENOENT) {
369 : /* if a multidomain search, try with next */
370 0 : if (cmd_ctx->check_next) {
371 0 : cmd_ctx->domain = get_next_domain(cmd_ctx->domain, false);
372 0 : return ssh_host_pubkeys_search(cmd_ctx);
373 : }
374 :
375 0 : DEBUG(SSSDBG_OP_FAILURE,
376 : "No attributes for host [%s] found.\n", cmd_ctx->name);
377 :
378 0 : return ENOENT;
379 : }
380 :
381 0 : return EOK;
382 : }
383 :
384 : static void
385 0 : ssh_host_pubkeys_search_dp_callback(uint16_t err_maj,
386 : uint32_t err_min,
387 : const char *err_msg,
388 : void *ptr)
389 : {
390 0 : struct ssh_cmd_ctx *cmd_ctx = talloc_get_type(ptr, struct ssh_cmd_ctx);
391 : errno_t ret;
392 :
393 0 : if (err_maj) {
394 0 : DEBUG(SSSDBG_OP_FAILURE,
395 : "Unable to get information from Data Provider\n"
396 : "Error: %u, %u, %s\n",
397 : (unsigned int)err_maj, (unsigned int)err_min, err_msg);
398 : }
399 :
400 0 : ret = ssh_host_pubkeys_search_next(cmd_ctx);
401 0 : ssh_cmd_get_host_pubkeys_done(cmd_ctx, ret);
402 0 : }
403 :
404 : static char *
405 0 : ssh_host_pubkeys_format_known_host_plain(TALLOC_CTX *mem_ctx,
406 : struct sss_ssh_ent *ent)
407 : {
408 : TALLOC_CTX *tmp_ctx;
409 : errno_t ret;
410 : char *name, *pubkey;
411 0 : char *result = NULL;
412 : size_t i;
413 :
414 0 : tmp_ctx = talloc_new(NULL);
415 0 : if (!tmp_ctx) {
416 0 : return NULL;
417 : }
418 :
419 0 : name = talloc_strdup(tmp_ctx, ent->name);
420 0 : if (!name) {
421 0 : goto done;
422 : }
423 :
424 0 : for (i = 0; i < ent->num_aliases; i++) {
425 0 : name = talloc_asprintf_append(name, ",%s", ent->aliases[i]);
426 0 : if (!name) {
427 0 : goto done;
428 : }
429 : }
430 :
431 0 : result = talloc_strdup(tmp_ctx, "");
432 0 : if (!result) {
433 0 : goto done;
434 : }
435 :
436 0 : for (i = 0; i < ent->num_pubkeys; i++) {
437 0 : ret = sss_ssh_format_pubkey(tmp_ctx, &ent->pubkeys[i], &pubkey);
438 0 : if (ret != EOK) {
439 0 : result = NULL;
440 0 : goto done;
441 : }
442 :
443 0 : result = talloc_asprintf_append(result, "%s %s\n", name, pubkey);
444 0 : if (!result) {
445 0 : goto done;
446 : }
447 :
448 0 : talloc_free(pubkey);
449 : }
450 :
451 0 : talloc_steal(mem_ctx, result);
452 :
453 : done:
454 0 : talloc_free(tmp_ctx);
455 :
456 0 : return result;
457 : }
458 :
459 : static char *
460 0 : ssh_host_pubkeys_format_known_host_hashed(TALLOC_CTX *mem_ctx,
461 : struct sss_ssh_ent *ent)
462 : {
463 : TALLOC_CTX *tmp_ctx;
464 : errno_t ret;
465 : char *name, *pubkey, *saltstr, *hashstr, *result;
466 : unsigned char salt[SSS_SHA1_LENGTH], hash[SSS_SHA1_LENGTH];
467 : size_t i, j, k;
468 :
469 0 : tmp_ctx = talloc_new(NULL);
470 0 : if (!tmp_ctx) {
471 0 : return NULL;
472 : }
473 :
474 0 : result = talloc_strdup(tmp_ctx, "");
475 0 : if (!result) {
476 0 : goto done;
477 : }
478 :
479 0 : for (i = 0; i < ent->num_pubkeys; i++) {
480 0 : ret = sss_ssh_format_pubkey(tmp_ctx, &ent->pubkeys[i], &pubkey);
481 0 : if (ret != EOK) {
482 0 : result = NULL;
483 0 : goto done;
484 : }
485 :
486 0 : for (j = 0; j <= ent->num_aliases; j++) {
487 0 : name = (j == 0 ? ent->name : ent->aliases[j-1]);
488 :
489 0 : for (k = 0; k < SSS_SHA1_LENGTH; k++) {
490 0 : salt[k] = rand();
491 : }
492 :
493 0 : ret = sss_hmac_sha1(salt, SSS_SHA1_LENGTH,
494 : (unsigned char *)name, strlen(name),
495 : hash);
496 0 : if (ret != EOK) {
497 0 : DEBUG(SSSDBG_OP_FAILURE,
498 : "sss_hmac_sha1() failed (%d): %s\n",
499 : ret, strerror(ret));
500 0 : result = NULL;
501 0 : goto done;
502 : }
503 :
504 0 : saltstr = sss_base64_encode(tmp_ctx, salt, SSS_SHA1_LENGTH);
505 0 : if (!saltstr) {
506 0 : result = NULL;
507 0 : goto done;
508 : }
509 :
510 0 : hashstr = sss_base64_encode(tmp_ctx, hash, SSS_SHA1_LENGTH);
511 0 : if (!hashstr) {
512 0 : result = NULL;
513 0 : goto done;
514 : }
515 :
516 0 : result = talloc_asprintf_append(result, "|1|%s|%s %s\n",
517 : saltstr, hashstr, pubkey);
518 0 : if (!result) {
519 0 : goto done;
520 : }
521 :
522 0 : talloc_free(saltstr);
523 0 : talloc_free(hashstr);
524 : }
525 :
526 0 : talloc_free(pubkey);
527 : }
528 :
529 0 : talloc_steal(mem_ctx, result);
530 :
531 : done:
532 0 : talloc_free(tmp_ctx);
533 :
534 0 : return result;
535 : }
536 :
537 : static errno_t
538 0 : ssh_host_pubkeys_update_known_hosts(struct ssh_cmd_ctx *cmd_ctx)
539 : {
540 : TALLOC_CTX *tmp_ctx;
541 : errno_t ret;
542 0 : const char *attrs[] = {
543 : SYSDB_NAME,
544 : SYSDB_NAME_ALIAS,
545 : SYSDB_SSH_PUBKEY,
546 : NULL
547 : };
548 0 : struct cli_ctx *cctx = cmd_ctx->cctx;
549 0 : struct sss_domain_info *dom = cctx->rctx->domains;
550 0 : struct ssh_ctx *ssh_ctx = (struct ssh_ctx *)cctx->rctx->pvt_ctx;
551 : struct sysdb_ctx *sysdb;
552 0 : time_t now = time(NULL);
553 : struct ldb_message **hosts;
554 : size_t num_hosts, i;
555 : struct sss_ssh_ent *ent;
556 0 : int fd = -1;
557 0 : char *filename = NULL;
558 : char *entstr;
559 : ssize_t wret;
560 :
561 0 : tmp_ctx = talloc_new(NULL);
562 0 : if (!tmp_ctx) {
563 0 : return ENOMEM;
564 : }
565 :
566 0 : if (cmd_ctx->domain) {
567 0 : ret = sysdb_update_ssh_known_host_expire(cmd_ctx->domain,
568 0 : cmd_ctx->name, now,
569 : ssh_ctx->known_hosts_timeout);
570 0 : if (ret != EOK && ret != ENOENT) {
571 0 : goto done;
572 : }
573 : }
574 :
575 : /* write known_hosts file */
576 0 : filename = talloc_strdup(tmp_ctx, SSS_SSH_KNOWN_HOSTS_TEMP_TMPL);
577 0 : if (!filename) {
578 0 : ret = ENOMEM;
579 0 : goto done;
580 : }
581 :
582 0 : fd = sss_unique_file_ex(tmp_ctx, filename, 0133, &ret);
583 0 : if (fd == -1) {
584 0 : filename = NULL;
585 0 : goto done;
586 : }
587 :
588 0 : for (; dom; dom = get_next_domain(dom, false)) {
589 0 : sysdb = dom->sysdb;
590 0 : if (sysdb == NULL) {
591 0 : DEBUG(SSSDBG_FATAL_FAILURE,
592 : "Fatal: Sysdb CTX not found for this domain!\n");
593 0 : ret = EFAULT;
594 0 : goto done;
595 : }
596 :
597 0 : ret = sysdb_get_ssh_known_hosts(tmp_ctx, dom, now, attrs,
598 : &hosts, &num_hosts);
599 0 : if (ret != EOK) {
600 0 : if (ret != ENOENT) {
601 0 : DEBUG(SSSDBG_OP_FAILURE,
602 : "Host search failed for domain [%s]\n", dom->name);
603 : }
604 0 : continue;
605 : }
606 :
607 0 : for (i = 0; i < num_hosts; i++) {
608 0 : ret = sss_ssh_make_ent(tmp_ctx, hosts[i], &ent);
609 0 : if (ret != EOK) {
610 0 : DEBUG(SSSDBG_OP_FAILURE,
611 : "Failed to get SSH host public keys\n");
612 0 : continue;
613 : }
614 :
615 0 : if (ssh_ctx->hash_known_hosts) {
616 0 : entstr = ssh_host_pubkeys_format_known_host_hashed(ent, ent);
617 : } else {
618 0 : entstr = ssh_host_pubkeys_format_known_host_plain(ent, ent);
619 : }
620 0 : if (!entstr) {
621 0 : DEBUG(SSSDBG_OP_FAILURE,
622 : "Failed to format known_hosts data for [%s]\n",
623 : ent->name);
624 0 : continue;
625 : }
626 :
627 0 : wret = sss_atomic_write_s(fd, entstr, strlen(entstr));
628 0 : if (wret == -1) {
629 0 : ret = errno;
630 0 : goto done;
631 : }
632 :
633 0 : talloc_free(ent);
634 : }
635 :
636 0 : talloc_free(hosts);
637 : }
638 :
639 0 : ret = fchmod(fd, 0644);
640 0 : if (ret == -1) {
641 0 : ret = errno;
642 0 : goto done;
643 : }
644 :
645 0 : ret = rename(filename, SSS_SSH_KNOWN_HOSTS_PATH);
646 0 : if (ret == -1) {
647 0 : ret = errno;
648 0 : goto done;
649 : }
650 :
651 0 : ret = EOK;
652 :
653 : done:
654 0 : if (fd != -1) {
655 0 : close(fd);
656 : }
657 0 : talloc_free(tmp_ctx);
658 :
659 0 : return ret;
660 : }
661 :
662 : static errno_t
663 0 : ssh_cmd_parse_request(struct ssh_cmd_ctx *cmd_ctx)
664 : {
665 0 : struct cli_ctx *cctx = cmd_ctx->cctx;
666 0 : struct ssh_ctx *ssh_ctx = talloc_get_type(cctx->rctx->pvt_ctx,
667 : struct ssh_ctx);
668 : errno_t ret;
669 : uint8_t *body;
670 : size_t body_len;
671 0 : size_t c = 0;
672 : uint32_t flags;
673 : uint32_t name_len;
674 : char *name;
675 : uint32_t alias_len;
676 0 : char *alias = NULL;
677 : uint32_t domain_len;
678 0 : char *domain = NULL;
679 :
680 0 : sss_packet_get_body(cctx->creq->in, &body, &body_len);
681 :
682 0 : SAFEALIGN_COPY_UINT32_CHECK(&flags, body+c, body_len, &c);
683 0 : if (flags & ~(uint32_t)SSS_SSH_REQ_MASK) {
684 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid flags received [0x%x]\n", flags);
685 0 : return EINVAL;
686 : }
687 :
688 0 : SAFEALIGN_COPY_UINT32_CHECK(&name_len, body+c, body_len, &c);
689 0 : if (name_len == 0 || name_len > body_len - c) {
690 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid name length\n");
691 0 : return EINVAL;
692 : }
693 :
694 0 : name = (char *)(body+c);
695 0 : if (!sss_utf8_check((const uint8_t *)name, name_len-1) ||
696 0 : name[name_len-1] != 0) {
697 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Name is not valid UTF-8 string\n");
698 0 : return EINVAL;
699 : }
700 0 : c += name_len;
701 :
702 0 : if (flags & SSS_SSH_REQ_ALIAS) {
703 0 : SAFEALIGN_COPY_UINT32_CHECK(&alias_len, body+c, body_len, &c);
704 0 : if (alias_len == 0 || alias_len > body_len - c) {
705 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid alias length\n");
706 0 : return EINVAL;
707 : }
708 :
709 0 : alias = (char *)(body+c);
710 0 : if (!sss_utf8_check((const uint8_t *)alias, alias_len-1) ||
711 0 : alias[alias_len-1] != 0) {
712 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Alias is not valid UTF-8 string\n");
713 0 : return EINVAL;
714 : }
715 0 : c += alias_len;
716 : }
717 :
718 0 : if (flags & SSS_SSH_REQ_DOMAIN) {
719 0 : SAFEALIGN_COPY_UINT32_CHECK(&domain_len, body+c, body_len, &c);
720 0 : if (domain_len > 0) {
721 0 : if (domain_len > body_len - c) {
722 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid domain length\n");
723 0 : return EINVAL;
724 : }
725 :
726 0 : domain = (char *)(body+c);
727 0 : if (!sss_utf8_check((const uint8_t *)domain, domain_len-1) ||
728 0 : domain[domain_len-1] != 0) {
729 0 : DEBUG(SSSDBG_CRIT_FAILURE,
730 : "Domain is not valid UTF-8 string\n");
731 0 : return EINVAL;
732 : }
733 0 : c += domain_len;
734 : }
735 :
736 0 : DEBUG(SSSDBG_TRACE_FUNC,
737 : "Requested domain [%s]\n", domain ? domain : "<ALL>");
738 : } else {
739 0 : DEBUG(SSSDBG_TRACE_FUNC, "Splitting domain from name [%s]\n", name);
740 :
741 0 : ret = sss_parse_name(cmd_ctx, ssh_ctx->snctx, name,
742 : &cmd_ctx->domname, &cmd_ctx->name);
743 0 : if (ret != EOK) {
744 0 : DEBUG(SSSDBG_OP_FAILURE, "Invalid name received [%s]\n", name);
745 0 : return ENOENT;
746 : }
747 :
748 0 : name = cmd_ctx->name;
749 : }
750 :
751 0 : if (cmd_ctx->is_user && cmd_ctx->domname == NULL) {
752 0 : DEBUG(SSSDBG_TRACE_FUNC,
753 : "Parsing name [%s][%s]\n", name, domain ? domain : "<ALL>");
754 :
755 0 : ret = sss_parse_name_for_domains(cmd_ctx, cctx->rctx->domains,
756 : domain, name,
757 : &cmd_ctx->domname,
758 : &cmd_ctx->name);
759 0 : if (ret != EOK) {
760 0 : DEBUG(SSSDBG_OP_FAILURE,
761 : "Invalid name received [%s]\n", name);
762 0 : return ENOENT;
763 : }
764 : } else {
765 0 : if (cmd_ctx->name == NULL) {
766 0 : cmd_ctx->name = talloc_strdup(cmd_ctx, name);
767 0 : if (!cmd_ctx->name) return ENOMEM;
768 : }
769 :
770 0 : if (cmd_ctx->domname == NULL && domain != NULL) {
771 0 : cmd_ctx->domname = talloc_strdup(cmd_ctx, domain);
772 0 : if (!cmd_ctx->domname) return ENOMEM;
773 : }
774 : }
775 :
776 0 : if (alias != NULL && strcmp(cmd_ctx->name, alias) != 0) {
777 0 : cmd_ctx->alias = talloc_strdup(cmd_ctx, alias);
778 0 : if (!cmd_ctx->alias) return ENOMEM;
779 : }
780 :
781 0 : return EOK;
782 : }
783 :
784 0 : static errno_t decode_and_add_base64_data(struct ssh_cmd_ctx *cmd_ctx,
785 : struct ldb_message_element *el,
786 : bool cert_data,
787 : struct ssh_ctx *ssh_ctx,
788 : size_t fqname_len,
789 : const char *fqname,
790 : size_t *c)
791 : {
792 0 : struct cli_ctx *cctx = cmd_ctx->cctx;
793 : uint8_t *key;
794 : size_t key_len;
795 : uint8_t *body;
796 : size_t body_len;
797 : int ret;
798 : size_t d;
799 : TALLOC_CTX *tmp_ctx;
800 :
801 0 : if (el == NULL) {
802 0 : DEBUG(SSSDBG_TRACE_ALL, "Mssing element, nothing to do.\n");
803 0 : return EOK;
804 : }
805 :
806 0 : tmp_ctx = talloc_new(NULL);
807 0 : if (tmp_ctx == NULL) {
808 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
809 0 : return ENOMEM;
810 : }
811 :
812 0 : for (d = 0; d < el->num_values; d++) {
813 0 : if (cert_data) {
814 0 : ret = cert_to_ssh_key(tmp_ctx, ssh_ctx->ca_db,
815 0 : el->values[d].data, el->values[d].length,
816 : &key, &key_len);
817 0 : if (ret != EOK) {
818 0 : DEBUG(SSSDBG_OP_FAILURE, "cert_to_ssh_key failed.\n");
819 0 : return ret;
820 : }
821 : } else {
822 0 : key = sss_base64_decode(tmp_ctx, (const char *) el->values[d].data,
823 : &key_len);
824 0 : if (key == NULL) {
825 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n");
826 0 : ret = ENOMEM;
827 0 : goto done;
828 : }
829 : }
830 :
831 0 : ret = sss_packet_grow(cctx->creq->out,
832 0 : 3*sizeof(uint32_t) + key_len + fqname_len);
833 0 : if (ret != EOK) {
834 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
835 0 : goto done;
836 : }
837 0 : sss_packet_get_body(cctx->creq->out, &body, &body_len);
838 :
839 0 : SAFEALIGN_SET_UINT32(body+(*c), 0, c);
840 0 : SAFEALIGN_SET_UINT32(body+(*c), fqname_len, c);
841 0 : safealign_memcpy(body+(*c), fqname, fqname_len, c);
842 0 : SAFEALIGN_SET_UINT32(body+(*c), key_len, c);
843 0 : safealign_memcpy(body+(*c), key, key_len, c);
844 :
845 : }
846 :
847 0 : ret = EOK;
848 :
849 : done:
850 0 : talloc_free(tmp_ctx);
851 :
852 0 : return ret;
853 : }
854 :
855 : static errno_t
856 0 : ssh_cmd_build_reply(struct ssh_cmd_ctx *cmd_ctx)
857 : {
858 0 : struct cli_ctx *cctx = cmd_ctx->cctx;
859 : errno_t ret;
860 : uint8_t *body;
861 : size_t body_len;
862 0 : size_t c = 0;
863 0 : struct ldb_message_element *el = NULL;
864 0 : struct ldb_message_element *el_override = NULL;
865 0 : struct ldb_message_element *el_orig = NULL;
866 0 : struct ldb_message_element *el_user_cert = NULL;
867 0 : uint32_t count = 0;
868 : const char *name;
869 : char *fqname;
870 : uint32_t fqname_len;
871 0 : struct ssh_ctx *ssh_ctx = talloc_get_type(cctx->rctx->pvt_ctx,
872 : struct ssh_ctx);
873 :
874 0 : ret = sss_packet_new(cctx->creq, 0,
875 0 : sss_packet_get_cmd(cctx->creq->in),
876 0 : &cctx->creq->out);
877 0 : if (ret != EOK) {
878 0 : return ret;
879 : }
880 :
881 0 : el = ldb_msg_find_element(cmd_ctx->result, SYSDB_SSH_PUBKEY);
882 0 : if (el) {
883 0 : count = el->num_values;
884 : }
885 :
886 0 : el_orig = ldb_msg_find_element(cmd_ctx->result,
887 : ORIGINALAD_PREFIX SYSDB_SSH_PUBKEY);
888 0 : if (el_orig) {
889 0 : count = el_orig->num_values;
890 : }
891 :
892 0 : if (DOM_HAS_VIEWS(cmd_ctx->domain)) {
893 0 : el_override = ldb_msg_find_element(cmd_ctx->result,
894 : OVERRIDE_PREFIX SYSDB_SSH_PUBKEY);
895 0 : if (el_override) {
896 0 : count += el_override->num_values;
897 : }
898 : }
899 :
900 0 : el_user_cert = ldb_msg_find_element(cmd_ctx->result, SYSDB_USER_CERT);
901 0 : if (el_user_cert) {
902 : /* TODO check if cert is valid */
903 0 : count += el_user_cert->num_values;
904 : }
905 :
906 0 : ret = sss_packet_grow(cctx->creq->out, 2*sizeof(uint32_t));
907 0 : if (ret != EOK) {
908 0 : return ret;
909 : }
910 0 : sss_packet_get_body(cctx->creq->out, &body, &body_len);
911 :
912 0 : SAFEALIGN_SET_UINT32(body+c, count, &c);
913 0 : SAFEALIGN_SET_UINT32(body+c, 0, &c);
914 :
915 0 : if (count == 0) {
916 0 : return EOK;
917 : }
918 :
919 0 : name = ldb_msg_find_attr_as_string(cmd_ctx->result, SYSDB_NAME, NULL);
920 0 : if (!name) {
921 0 : DEBUG(SSSDBG_OP_FAILURE,
922 : "Got unnamed result for [%s@%s]\n",
923 : cmd_ctx->name, cmd_ctx->domain->name);
924 0 : return ENOENT;
925 : }
926 :
927 0 : fqname = talloc_asprintf(cmd_ctx, "%s@%s",
928 0 : name, cmd_ctx->domain->name);
929 0 : if (!fqname) {
930 0 : return ENOMEM;
931 : }
932 :
933 0 : fqname_len = strlen(fqname)+1;
934 :
935 0 : ret = decode_and_add_base64_data(cmd_ctx, el, false, ssh_ctx,
936 : fqname_len, fqname, &c);
937 0 : if (ret != EOK) {
938 0 : DEBUG(SSSDBG_OP_FAILURE, "decode_and_add_base64_data failed.\n");
939 0 : return ret;
940 : }
941 :
942 0 : ret = decode_and_add_base64_data(cmd_ctx, el_orig, false, ssh_ctx,
943 : fqname_len, fqname, &c);
944 0 : if (ret != EOK) {
945 0 : DEBUG(SSSDBG_OP_FAILURE, "decode_and_add_base64_data failed.\n");
946 0 : return ret;
947 : }
948 :
949 0 : ret = decode_and_add_base64_data(cmd_ctx, el_override, false, ssh_ctx,
950 : fqname_len, fqname, &c);
951 0 : if (ret != EOK) {
952 0 : DEBUG(SSSDBG_OP_FAILURE, "decode_and_add_base64_data failed.\n");
953 0 : return ret;
954 : }
955 :
956 0 : ret = decode_and_add_base64_data(cmd_ctx, el_user_cert, true, ssh_ctx,
957 : fqname_len, fqname, &c);
958 0 : if (ret != EOK) {
959 0 : DEBUG(SSSDBG_OP_FAILURE, "decode_and_add_base64_data failed.\n");
960 0 : return ret;
961 : }
962 :
963 0 : return EOK;
964 : }
965 :
966 : static errno_t
967 0 : ssh_cmd_send_error(struct ssh_cmd_ctx *cmd_ctx,
968 : errno_t error)
969 : {
970 0 : struct cli_ctx *cctx = cmd_ctx->cctx;
971 : errno_t ret;
972 :
973 0 : ret = sss_cmd_send_error(cctx, error);
974 0 : if (ret != EOK) {
975 0 : return ret;
976 : }
977 :
978 0 : sss_cmd_done(cctx, cmd_ctx);
979 :
980 0 : return EOK;
981 : }
982 :
983 : static errno_t
984 0 : ssh_cmd_send_reply(struct ssh_cmd_ctx *cmd_ctx)
985 : {
986 0 : struct cli_ctx *cctx = cmd_ctx->cctx;
987 : errno_t ret;
988 :
989 : /* create response packet */
990 0 : ret = ssh_cmd_build_reply(cmd_ctx);
991 0 : if (ret != EOK) {
992 0 : return ret;
993 : }
994 :
995 0 : sss_packet_set_error(cctx->creq->out, EOK);
996 0 : sss_cmd_done(cctx, cmd_ctx);
997 :
998 0 : return EOK;
999 : }
1000 :
1001 : static errno_t
1002 0 : ssh_cmd_done(struct ssh_cmd_ctx *cmd_ctx,
1003 : errno_t ret)
1004 : {
1005 0 : switch (ret) {
1006 : case EOK:
1007 0 : ret = ssh_cmd_send_reply(cmd_ctx);
1008 0 : break;
1009 :
1010 : case EAGAIN:
1011 0 : return EOK;
1012 :
1013 : case EFAULT:
1014 0 : break;
1015 :
1016 : default:
1017 0 : ret = ssh_cmd_send_error(cmd_ctx, ret);
1018 0 : break;
1019 : }
1020 :
1021 0 : if (ret != EOK) {
1022 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Fatal error, killing connection!\n");
1023 0 : talloc_free(cmd_ctx->cctx);
1024 0 : return EFAULT;
1025 : }
1026 :
1027 0 : return EOK;
1028 : }
1029 :
1030 : static errno_t
1031 0 : ssh_cmd_get_user_pubkeys_done(struct ssh_cmd_ctx *cmd_ctx,
1032 : errno_t ret)
1033 : {
1034 0 : return ssh_cmd_done(cmd_ctx, ret);
1035 : }
1036 :
1037 : static errno_t
1038 0 : ssh_cmd_get_host_pubkeys_done(struct ssh_cmd_ctx *cmd_ctx,
1039 : errno_t ret)
1040 : {
1041 0 : if (ret == EOK || ret == ENOENT) {
1042 0 : ssh_host_pubkeys_update_known_hosts(cmd_ctx);
1043 : }
1044 :
1045 0 : return ssh_cmd_done(cmd_ctx, ret);
1046 : }
1047 :
1048 0 : struct cli_protocol_version *register_cli_protocol_version(void)
1049 : {
1050 : static struct cli_protocol_version ssh_cli_protocol_version[] = {
1051 : {0, NULL, NULL}
1052 : };
1053 :
1054 0 : return ssh_cli_protocol_version;
1055 : }
1056 :
1057 0 : struct sss_cmd_table *get_ssh_cmds(void) {
1058 : static struct sss_cmd_table ssh_cmds[] = {
1059 : {SSS_GET_VERSION, sss_cmd_get_version},
1060 : {SSS_SSH_GET_USER_PUBKEYS, sss_ssh_cmd_get_user_pubkeys},
1061 : {SSS_SSH_GET_HOST_PUBKEYS, sss_ssh_cmd_get_host_pubkeys},
1062 : {SSS_CLI_NULL, NULL}
1063 : };
1064 :
1065 0 : return ssh_cmds;
1066 : }
|