xref: /netbsd-src/crypto/external/bsd/openssh/dist/authfd.c (revision 7d62b00eb9ad855ffcd7da46b41e23feb5476fac)
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