1 /* $NetBSD: authfd.c,v 1.25 2022/10/05 22:39:36 christos Exp $ */ 2 /* $OpenBSD: authfd.c,v 1.130 2022/04/27 11:08:55 dtucker Exp $ */ 3 4 /* 5 * Author: Tatu Ylonen <ylo@cs.hut.fi> 6 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 7 * All rights reserved 8 * Functions for connecting the local authentication agent. 9 * 10 * As far as I am concerned, the code I have written for this software 11 * can be used freely for any purpose. Any derived versions of this 12 * software must be clearly marked as such, and if the derived work is 13 * incompatible with the protocol description in the RFC file, it must be 14 * called by a name other than "ssh" or "Secure Shell". 15 * 16 * SSH2 implementation, 17 * Copyright (c) 2000 Markus Friedl. All rights reserved. 18 * 19 * Redistribution and use in source and binary forms, with or without 20 * modification, are permitted provided that the following conditions 21 * are met: 22 * 1. Redistributions of source code must retain the above copyright 23 * notice, this list of conditions and the following disclaimer. 24 * 2. Redistributions in binary form must reproduce the above copyright 25 * notice, this list of conditions and the following disclaimer in the 26 * documentation and/or other materials provided with the distribution. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 29 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 30 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 31 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 32 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 33 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 34 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 35 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 36 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 37 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 #include "includes.h" 41 __RCSID("$NetBSD: authfd.c,v 1.25 2022/10/05 22:39:36 christos Exp $"); 42 #include <sys/types.h> 43 #include <sys/un.h> 44 #include <sys/socket.h> 45 46 #include <fcntl.h> 47 #include <stdlib.h> 48 #include <signal.h> 49 #include <string.h> 50 #include <stdarg.h> 51 #include <unistd.h> 52 #include <errno.h> 53 54 #include "xmalloc.h" 55 #include "ssh.h" 56 #include "sshbuf.h" 57 #include "sshkey.h" 58 #include "authfd.h" 59 #include "cipher.h" 60 #include "compat.h" 61 #include "log.h" 62 #include "atomicio.h" 63 #include "misc.h" 64 #include "ssherr.h" 65 66 #define MAX_AGENT_IDENTITIES 2048 /* Max keys in agent reply */ 67 #define MAX_AGENT_REPLY_LEN (256 * 1024) /* Max bytes in agent reply */ 68 69 /* macro to check for "agent failure" message */ 70 #define agent_failed(x) \ 71 ((x == SSH_AGENT_FAILURE) || \ 72 (x == SSH_COM_AGENT2_FAILURE) || \ 73 (x == SSH2_AGENT_FAILURE)) 74 75 /* Convert success/failure response from agent to a err.h status */ 76 static int 77 decode_reply(u_char type) 78 { 79 if (agent_failed(type)) 80 return SSH_ERR_AGENT_FAILURE; 81 else if (type == SSH_AGENT_SUCCESS) 82 return 0; 83 else 84 return SSH_ERR_INVALID_FORMAT; 85 } 86 87 /* 88 * Opens an authentication socket at the provided path and stores the file 89 * descriptor in fdp. Returns 0 on success and an error on failure. 90 */ 91 int 92 ssh_get_authentication_socket_path(const char *authsocket, int *fdp) 93 { 94 int sock, oerrno; 95 struct sockaddr_un sunaddr; 96 97 debug3_f("path '%s'", authsocket); 98 memset(&sunaddr, 0, sizeof(sunaddr)); 99 sunaddr.sun_family = AF_UNIX; 100 strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path)); 101 102 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 103 return SSH_ERR_SYSTEM_ERROR; 104 105 /* close on exec */ 106 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1 || 107 connect(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) { 108 oerrno = errno; 109 close(sock); 110 errno = oerrno; 111 return SSH_ERR_SYSTEM_ERROR; 112 } 113 if (fdp != NULL) 114 *fdp = sock; 115 else 116 close(sock); 117 return 0; 118 } 119 120 /* 121 * Opens the default authentication socket and stores the file descriptor in 122 * fdp. Returns 0 on success and an error on failure. 123 */ 124 int 125 ssh_get_authentication_socket(int *fdp) 126 { 127 const char *authsocket; 128 129 if (fdp != NULL) 130 *fdp = -1; 131 132 authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME); 133 if (authsocket == NULL || *authsocket == '\0') 134 return SSH_ERR_AGENT_NOT_PRESENT; 135 136 return ssh_get_authentication_socket_path(authsocket, fdp); 137 } 138 139 /* Communicate with agent: send request and read reply */ 140 static int 141 ssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply) 142 { 143 int r; 144 size_t l, len; 145 char buf[1024]; 146 147 /* Get the length of the message, and format it in the buffer. */ 148 len = sshbuf_len(request); 149 POKE_U32(buf, len); 150 151 /* Send the length and then the packet to the agent. */ 152 if (atomicio(vwrite, sock, buf, 4) != 4 || 153 atomicio(vwrite, sock, sshbuf_mutable_ptr(request), 154 sshbuf_len(request)) != sshbuf_len(request)) 155 return SSH_ERR_AGENT_COMMUNICATION; 156 /* 157 * Wait for response from the agent. First read the length of the 158 * response packet. 159 */ 160 if (atomicio(read, sock, buf, 4) != 4) 161 return SSH_ERR_AGENT_COMMUNICATION; 162 163 /* Extract the length, and check it for sanity. */ 164 len = PEEK_U32(buf); 165 if (len > MAX_AGENT_REPLY_LEN) 166 return SSH_ERR_INVALID_FORMAT; 167 168 /* Read the rest of the response in to the buffer. */ 169 sshbuf_reset(reply); 170 while (len > 0) { 171 l = len; 172 if (l > sizeof(buf)) 173 l = sizeof(buf); 174 if (atomicio(read, sock, buf, l) != l) 175 return SSH_ERR_AGENT_COMMUNICATION; 176 if ((r = sshbuf_put(reply, buf, l)) != 0) 177 return r; 178 len -= l; 179 } 180 return 0; 181 } 182 183 /* Communicate with agent: sent request, read and decode status reply */ 184 static int 185 ssh_request_reply_decode(int sock, struct sshbuf *request) 186 { 187 struct sshbuf *reply; 188 int r; 189 u_char type; 190 191 if ((reply = sshbuf_new()) == NULL) 192 return SSH_ERR_ALLOC_FAIL; 193 if ((r = ssh_request_reply(sock, request, reply)) != 0 || 194 (r = sshbuf_get_u8(reply, &type)) != 0 || 195 (r = decode_reply(type)) != 0) 196 goto out; 197 /* success */ 198 r = 0; 199 out: 200 sshbuf_free(reply); 201 return r; 202 } 203 204 /* 205 * Closes the agent socket if it should be closed (depends on how it was 206 * obtained). The argument must have been returned by 207 * ssh_get_authentication_socket(). 208 */ 209 void 210 ssh_close_authentication_socket(int sock) 211 { 212 if (getenv(SSH_AUTHSOCKET_ENV_NAME)) 213 close(sock); 214 } 215 216 /* Lock/unlock agent */ 217 int 218 ssh_lock_agent(int sock, int lock, const char *password) 219 { 220 int r; 221 u_char type = lock ? SSH_AGENTC_LOCK : SSH_AGENTC_UNLOCK; 222 struct sshbuf *msg; 223 224 if ((msg = sshbuf_new()) == NULL) 225 return SSH_ERR_ALLOC_FAIL; 226 if ((r = sshbuf_put_u8(msg, type)) != 0 || 227 (r = sshbuf_put_cstring(msg, password)) != 0 || 228 (r = ssh_request_reply_decode(sock, msg)) != 0) 229 goto out; 230 /* success */ 231 r = 0; 232 out: 233 sshbuf_free(msg); 234 return r; 235 } 236 237 238 static int 239 deserialise_identity2(struct sshbuf *ids, struct sshkey **keyp, char **commentp) 240 { 241 int r; 242 char *comment = NULL; 243 const u_char *blob; 244 size_t blen; 245 246 if ((r = sshbuf_get_string_direct(ids, &blob, &blen)) != 0 || 247 (r = sshbuf_get_cstring(ids, &comment, NULL)) != 0) 248 goto out; 249 if ((r = sshkey_from_blob(blob, blen, keyp)) != 0) 250 goto out; 251 if (commentp != NULL) { 252 *commentp = comment; 253 comment = NULL; 254 } 255 r = 0; 256 out: 257 free(comment); 258 return r; 259 } 260 261 /* 262 * Fetch list of identities held by the agent. 263 */ 264 int 265 ssh_fetch_identitylist(int sock, struct ssh_identitylist **idlp) 266 { 267 u_char type; 268 u_int32_t num, i; 269 struct sshbuf *msg; 270 struct ssh_identitylist *idl = NULL; 271 int r; 272 273 /* 274 * Send a message to the agent requesting for a list of the 275 * identities it can represent. 276 */ 277 if ((msg = sshbuf_new()) == NULL) 278 return SSH_ERR_ALLOC_FAIL; 279 if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_REQUEST_IDENTITIES)) != 0) 280 goto out; 281 282 if ((r = ssh_request_reply(sock, msg, msg)) != 0) 283 goto out; 284 285 /* Get message type, and verify that we got a proper answer. */ 286 if ((r = sshbuf_get_u8(msg, &type)) != 0) 287 goto out; 288 if (agent_failed(type)) { 289 r = SSH_ERR_AGENT_FAILURE; 290 goto out; 291 } else if (type != SSH2_AGENT_IDENTITIES_ANSWER) { 292 r = SSH_ERR_INVALID_FORMAT; 293 goto out; 294 } 295 296 /* Get the number of entries in the response and check it for sanity. */ 297 if ((r = sshbuf_get_u32(msg, &num)) != 0) 298 goto out; 299 if (num > MAX_AGENT_IDENTITIES) { 300 r = SSH_ERR_INVALID_FORMAT; 301 goto out; 302 } 303 if (num == 0) { 304 r = SSH_ERR_AGENT_NO_IDENTITIES; 305 goto out; 306 } 307 308 /* Deserialise the response into a list of keys/comments */ 309 if ((idl = calloc(1, sizeof(*idl))) == NULL || 310 (idl->keys = calloc(num, sizeof(*idl->keys))) == NULL || 311 (idl->comments = calloc(num, sizeof(*idl->comments))) == NULL) { 312 r = SSH_ERR_ALLOC_FAIL; 313 goto out; 314 } 315 for (i = 0; i < num;) { 316 if ((r = deserialise_identity2(msg, &(idl->keys[i]), 317 &(idl->comments[i]))) != 0) { 318 if (r == SSH_ERR_KEY_TYPE_UNKNOWN) { 319 /* Gracefully skip unknown key types */ 320 num--; 321 continue; 322 } else 323 goto out; 324 } 325 i++; 326 } 327 idl->nkeys = num; 328 *idlp = idl; 329 idl = NULL; 330 r = 0; 331 out: 332 sshbuf_free(msg); 333 if (idl != NULL) 334 ssh_free_identitylist(idl); 335 return r; 336 } 337 338 void 339 ssh_free_identitylist(struct ssh_identitylist *idl) 340 { 341 size_t i; 342 343 if (idl == NULL) 344 return; 345 for (i = 0; i < idl->nkeys; i++) { 346 if (idl->keys != NULL) 347 sshkey_free(idl->keys[i]); 348 if (idl->comments != NULL) 349 free(idl->comments[i]); 350 } 351 free(idl->keys); 352 free(idl->comments); 353 free(idl); 354 } 355 356 /* 357 * Check if the ssh agent has a given key. 358 * Returns 0 if found, or a negative SSH_ERR_* error code on failure. 359 */ 360 int 361 ssh_agent_has_key(int sock, const struct sshkey *key) 362 { 363 int r, ret = SSH_ERR_KEY_NOT_FOUND; 364 size_t i; 365 struct ssh_identitylist *idlist = NULL; 366 367 if ((r = ssh_fetch_identitylist(sock, &idlist)) != 0) { 368 return r; 369 } 370 371 for (i = 0; i < idlist->nkeys; i++) { 372 if (sshkey_equal_public(idlist->keys[i], key)) { 373 ret = 0; 374 break; 375 } 376 } 377 378 ssh_free_identitylist(idlist); 379 return ret; 380 } 381 382 /* 383 * Sends a challenge (typically from a server via ssh(1)) to the agent, 384 * and waits for a response from the agent. 385 * Returns true (non-zero) if the agent gave the correct answer, zero 386 * otherwise. 387 */ 388 389 390 /* encode signature algorithm in flag bits, so we can keep the msg format */ 391 static u_int 392 agent_encode_alg(const struct sshkey *key, const char *alg) 393 { 394 if (alg != NULL && sshkey_type_plain(key->type) == KEY_RSA) { 395 if (strcmp(alg, "rsa-sha2-256") == 0 || 396 strcmp(alg, "rsa-sha2-256-cert-v01@openssh.com") == 0) 397 return SSH_AGENT_RSA_SHA2_256; 398 if (strcmp(alg, "rsa-sha2-512") == 0 || 399 strcmp(alg, "rsa-sha2-512-cert-v01@openssh.com") == 0) 400 return SSH_AGENT_RSA_SHA2_512; 401 } 402 return 0; 403 } 404 405 /* ask agent to sign data, returns err.h code on error, 0 on success */ 406 int 407 ssh_agent_sign(int sock, const struct sshkey *key, 408 u_char **sigp, size_t *lenp, 409 const u_char *data, size_t datalen, const char *alg, u_int compat) 410 { 411 struct sshbuf *msg; 412 u_char *sig = NULL, type = 0; 413 size_t len = 0; 414 u_int flags = 0; 415 int r = SSH_ERR_INTERNAL_ERROR; 416 417 *sigp = NULL; 418 *lenp = 0; 419 420 if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE) 421 return SSH_ERR_INVALID_ARGUMENT; 422 if ((msg = sshbuf_new()) == NULL) 423 return SSH_ERR_ALLOC_FAIL; 424 flags |= agent_encode_alg(key, alg); 425 if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || 426 (r = sshkey_puts(key, msg)) != 0 || 427 (r = sshbuf_put_string(msg, data, datalen)) != 0 || 428 (r = sshbuf_put_u32(msg, flags)) != 0) 429 goto out; 430 if ((r = ssh_request_reply(sock, msg, msg)) != 0) 431 goto out; 432 if ((r = sshbuf_get_u8(msg, &type)) != 0) 433 goto out; 434 if (agent_failed(type)) { 435 r = SSH_ERR_AGENT_FAILURE; 436 goto out; 437 } else if (type != SSH2_AGENT_SIGN_RESPONSE) { 438 r = SSH_ERR_INVALID_FORMAT; 439 goto out; 440 } 441 if ((r = sshbuf_get_string(msg, &sig, &len)) != 0) 442 goto out; 443 /* Check what we actually got back from the agent. */ 444 if ((r = sshkey_check_sigtype(sig, len, alg)) != 0) 445 goto out; 446 /* success */ 447 *sigp = sig; 448 *lenp = len; 449 sig = NULL; 450 len = 0; 451 r = 0; 452 out: 453 freezero(sig, len); 454 sshbuf_free(msg); 455 return r; 456 } 457 458 /* Encode key for a message to the agent. */ 459 460 static int 461 encode_dest_constraint_hop(struct sshbuf *m, 462 const struct dest_constraint_hop *dch) 463 { 464 struct sshbuf *b; 465 u_int i; 466 int r; 467 468 if ((b = sshbuf_new()) == NULL) 469 return SSH_ERR_ALLOC_FAIL; 470 if ((r = sshbuf_put_cstring(b, dch->user)) != 0 || 471 (r = sshbuf_put_cstring(b, dch->hostname)) != 0 || 472 (r = sshbuf_put_string(b, NULL, 0)) != 0) /* reserved */ 473 goto out; 474 for (i = 0; i < dch->nkeys; i++) { 475 if ((r = sshkey_puts(dch->keys[i], b)) != 0 || 476 (r = sshbuf_put_u8(b, dch->key_is_ca[i] != 0)) != 0) 477 goto out; 478 } 479 if ((r = sshbuf_put_stringb(m, b)) != 0) 480 goto out; 481 /* success */ 482 r = 0; 483 out: 484 sshbuf_free(b); 485 return r; 486 } 487 488 static int 489 encode_dest_constraint(struct sshbuf *m, const struct dest_constraint *dc) 490 { 491 struct sshbuf *b; 492 int r; 493 494 if ((b = sshbuf_new()) == NULL) 495 return SSH_ERR_ALLOC_FAIL; 496 if ((r = encode_dest_constraint_hop(b, &dc->from) != 0) || 497 (r = encode_dest_constraint_hop(b, &dc->to) != 0) || 498 (r = sshbuf_put_string(b, NULL, 0)) != 0) /* reserved */ 499 goto out; 500 if ((r = sshbuf_put_stringb(m, b)) != 0) 501 goto out; 502 /* success */ 503 r = 0; 504 out: 505 sshbuf_free(b); 506 return r; 507 } 508 509 static int 510 encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign, 511 const char *provider, struct dest_constraint **dest_constraints, 512 size_t ndest_constraints) 513 { 514 int r; 515 struct sshbuf *b = NULL; 516 size_t i; 517 518 if (life != 0) { 519 if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_LIFETIME)) != 0 || 520 (r = sshbuf_put_u32(m, life)) != 0) 521 goto out; 522 } 523 if (confirm != 0) { 524 if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_CONFIRM)) != 0) 525 goto out; 526 } 527 if (maxsign != 0) { 528 if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_MAXSIGN)) != 0 || 529 (r = sshbuf_put_u32(m, maxsign)) != 0) 530 goto out; 531 } 532 if (provider != NULL) { 533 if ((r = sshbuf_put_u8(m, 534 SSH_AGENT_CONSTRAIN_EXTENSION)) != 0 || 535 (r = sshbuf_put_cstring(m, 536 "sk-provider@openssh.com")) != 0 || 537 (r = sshbuf_put_cstring(m, provider)) != 0) 538 goto out; 539 } 540 if (dest_constraints != NULL && ndest_constraints > 0) { 541 if ((b = sshbuf_new()) == NULL) { 542 r = SSH_ERR_ALLOC_FAIL; 543 goto out; 544 } 545 for (i = 0; i < ndest_constraints; i++) { 546 if ((r = encode_dest_constraint(b, 547 dest_constraints[i])) != 0) 548 goto out; 549 } 550 if ((r = sshbuf_put_u8(m, 551 SSH_AGENT_CONSTRAIN_EXTENSION)) != 0 || 552 (r = sshbuf_put_cstring(m, 553 "restrict-destination-v00@openssh.com")) != 0 || 554 (r = sshbuf_put_stringb(m, b)) != 0) 555 goto out; 556 } 557 r = 0; 558 out: 559 sshbuf_free(b); 560 return r; 561 } 562 563 /* 564 * Adds an identity to the authentication server. 565 * This call is intended only for use by ssh-add(1) and like applications. 566 */ 567 int 568 ssh_add_identity_constrained(int sock, struct sshkey *key, 569 const char *comment, u_int life, u_int confirm, u_int maxsign, 570 const char *provider, struct dest_constraint **dest_constraints, 571 size_t ndest_constraints) 572 { 573 struct sshbuf *msg; 574 int r, constrained = (life || confirm || maxsign || 575 provider || dest_constraints); 576 u_char type; 577 578 if ((msg = sshbuf_new()) == NULL) 579 return SSH_ERR_ALLOC_FAIL; 580 581 switch (key->type) { 582 #ifdef WITH_OPENSSL 583 case KEY_RSA: 584 case KEY_RSA_CERT: 585 case KEY_DSA: 586 case KEY_DSA_CERT: 587 case KEY_ECDSA: 588 case KEY_ECDSA_CERT: 589 case KEY_ECDSA_SK: 590 case KEY_ECDSA_SK_CERT: 591 #endif 592 case KEY_ED25519: 593 case KEY_ED25519_CERT: 594 case KEY_ED25519_SK: 595 case KEY_ED25519_SK_CERT: 596 case KEY_XMSS: 597 case KEY_XMSS_CERT: 598 type = constrained ? 599 SSH2_AGENTC_ADD_ID_CONSTRAINED : 600 SSH2_AGENTC_ADD_IDENTITY; 601 if ((r = sshbuf_put_u8(msg, type)) != 0 || 602 (r = sshkey_private_serialize_maxsign(key, msg, maxsign, 603 0)) != 0 || 604 (r = sshbuf_put_cstring(msg, comment)) != 0) 605 goto out; 606 break; 607 default: 608 r = SSH_ERR_INVALID_ARGUMENT; 609 goto out; 610 } 611 if (constrained && 612 (r = encode_constraints(msg, life, confirm, maxsign, 613 provider, dest_constraints, ndest_constraints)) != 0) 614 goto out; 615 if ((r = ssh_request_reply_decode(sock, msg)) != 0) 616 goto out; 617 /* success */ 618 r = 0; 619 out: 620 sshbuf_free(msg); 621 return r; 622 } 623 624 /* 625 * Removes an identity from the authentication server. 626 * This call is intended only for use by ssh-add(1) and like applications. 627 */ 628 int 629 ssh_remove_identity(int sock, const struct sshkey *key) 630 { 631 struct sshbuf *msg; 632 int r; 633 u_char *blob = NULL; 634 size_t blen; 635 636 if ((msg = sshbuf_new()) == NULL) 637 return SSH_ERR_ALLOC_FAIL; 638 639 if (key->type != KEY_UNSPEC) { 640 if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) 641 goto out; 642 if ((r = sshbuf_put_u8(msg, 643 SSH2_AGENTC_REMOVE_IDENTITY)) != 0 || 644 (r = sshbuf_put_string(msg, blob, blen)) != 0) 645 goto out; 646 } else { 647 r = SSH_ERR_INVALID_ARGUMENT; 648 goto out; 649 } 650 if ((r = ssh_request_reply_decode(sock, msg)) != 0) 651 goto out; 652 /* success */ 653 r = 0; 654 out: 655 if (blob != NULL) 656 freezero(blob, blen); 657 sshbuf_free(msg); 658 return r; 659 } 660 661 /* 662 * Add/remove an token-based identity from the authentication server. 663 * This call is intended only for use by ssh-add(1) and like applications. 664 */ 665 int 666 ssh_update_card(int sock, int add, const char *reader_id, const char *pin, 667 u_int life, u_int confirm, 668 struct dest_constraint **dest_constraints, size_t ndest_constraints) 669 { 670 struct sshbuf *msg; 671 int r, constrained = (life || confirm); 672 u_char type; 673 674 if (add) { 675 type = constrained ? 676 SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED : 677 SSH_AGENTC_ADD_SMARTCARD_KEY; 678 } else 679 type = SSH_AGENTC_REMOVE_SMARTCARD_KEY; 680 681 if ((msg = sshbuf_new()) == NULL) 682 return SSH_ERR_ALLOC_FAIL; 683 if ((r = sshbuf_put_u8(msg, type)) != 0 || 684 (r = sshbuf_put_cstring(msg, reader_id)) != 0 || 685 (r = sshbuf_put_cstring(msg, pin)) != 0) 686 goto out; 687 if (constrained && 688 (r = encode_constraints(msg, life, confirm, 0, NULL, 689 dest_constraints, ndest_constraints)) != 0) 690 goto out; 691 if ((r = ssh_request_reply_decode(sock, msg)) != 0) 692 goto out; 693 /* success */ 694 r = 0; 695 out: 696 sshbuf_free(msg); 697 return r; 698 } 699 700 /* 701 * Removes all identities from the agent. 702 * This call is intended only for use by ssh-add(1) and like applications. 703 * 704 * This supports the SSH protocol 1 message to because, when clearing all 705 * keys from an agent, we generally want to clear both protocol v1 and v2 706 * keys. 707 */ 708 int 709 ssh_remove_all_identities(int sock, int version) 710 { 711 struct sshbuf *msg; 712 u_char type = (version == 1) ? 713 SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES : 714 SSH2_AGENTC_REMOVE_ALL_IDENTITIES; 715 int r; 716 717 if ((msg = sshbuf_new()) == NULL) 718 return SSH_ERR_ALLOC_FAIL; 719 if ((r = sshbuf_put_u8(msg, type)) != 0) 720 goto out; 721 if ((r = ssh_request_reply_decode(sock, msg)) != 0) 722 goto out; 723 /* success */ 724 r = 0; 725 out: 726 sshbuf_free(msg); 727 return r; 728 } 729 730 /* Binds a session ID to a hostkey via the initial KEX signature. */ 731 int 732 ssh_agent_bind_hostkey(int sock, const struct sshkey *key, 733 const struct sshbuf *session_id, const struct sshbuf *signature, 734 int forwarding) 735 { 736 struct sshbuf *msg; 737 int r; 738 739 if (key == NULL || session_id == NULL || signature == NULL) 740 return SSH_ERR_INVALID_ARGUMENT; 741 if ((msg = sshbuf_new()) == NULL) 742 return SSH_ERR_ALLOC_FAIL; 743 if ((r = sshbuf_put_u8(msg, SSH_AGENTC_EXTENSION)) != 0 || 744 (r = sshbuf_put_cstring(msg, "session-bind@openssh.com")) != 0 || 745 (r = sshkey_puts(key, msg)) != 0 || 746 (r = sshbuf_put_stringb(msg, session_id)) != 0 || 747 (r = sshbuf_put_stringb(msg, signature)) != 0 || 748 (r = sshbuf_put_u8(msg, forwarding ? 1 : 0)) != 0) 749 goto out; 750 if ((r = ssh_request_reply_decode(sock, msg)) != 0) 751 goto out; 752 /* success */ 753 r = 0; 754 out: 755 sshbuf_free(msg); 756 return r; 757 } 758