1/*	$NetBSD: authfd.c,v 1.27 2023/12/20 17:15:20 christos Exp $	*/
2/* $OpenBSD: authfd.c,v 1.134 2023/12/18 14:46:56 djm 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.27 2023/12/20 17:15:20 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 "log.h"
61#include "atomicio.h"
62#include "misc.h"
63#include "ssherr.h"
64
65#define MAX_AGENT_IDENTITIES	2048		/* Max keys in agent reply */
66#define MAX_AGENT_REPLY_LEN	(256 * 1024)	/* Max bytes in agent reply */
67
68/* macro to check for "agent failure" message */
69#define agent_failed(x) \
70    ((x == SSH_AGENT_FAILURE) || \
71    (x == SSH_COM_AGENT2_FAILURE) || \
72    (x == SSH2_AGENT_FAILURE))
73
74/* Convert success/failure response from agent to a err.h status */
75static int
76decode_reply(u_char type)
77{
78	if (agent_failed(type))
79		return SSH_ERR_AGENT_FAILURE;
80	else if (type == SSH_AGENT_SUCCESS)
81		return 0;
82	else
83		return SSH_ERR_INVALID_FORMAT;
84}
85
86/*
87 * Opens an authentication socket at the provided path and stores the file
88 * descriptor in fdp. Returns 0 on success and an error on failure.
89 */
90int
91ssh_get_authentication_socket_path(const char *authsocket, int *fdp)
92{
93	int sock, oerrno;
94	struct sockaddr_un sunaddr;
95
96	debug3_f("path '%s'", authsocket);
97	memset(&sunaddr, 0, sizeof(sunaddr));
98	sunaddr.sun_family = AF_UNIX;
99	strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path));
100
101	if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
102		return SSH_ERR_SYSTEM_ERROR;
103
104	/* close on exec */
105	if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1 ||
106	    connect(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) {
107		oerrno = errno;
108		close(sock);
109		errno = oerrno;
110		return SSH_ERR_SYSTEM_ERROR;
111	}
112	if (fdp != NULL)
113		*fdp = sock;
114	else
115		close(sock);
116	return 0;
117}
118
119/*
120 * Opens the default authentication socket and stores the file descriptor in
121 * fdp. Returns 0 on success and an error on failure.
122 */
123int
124ssh_get_authentication_socket(int *fdp)
125{
126	const char *authsocket;
127
128	if (fdp != NULL)
129		*fdp = -1;
130
131	authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME);
132	if (authsocket == NULL || *authsocket == '\0')
133		return SSH_ERR_AGENT_NOT_PRESENT;
134
135	return ssh_get_authentication_socket_path(authsocket, fdp);
136}
137
138/* Communicate with agent: send request and read reply */
139static int
140ssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply)
141{
142	int r;
143	size_t l, len;
144	char buf[1024];
145
146	/* Get the length of the message, and format it in the buffer. */
147	len = sshbuf_len(request);
148	POKE_U32(buf, len);
149
150	/* Send the length and then the packet to the agent. */
151	if (atomicio(vwrite, sock, buf, 4) != 4 ||
152	    atomicio(vwrite, sock, sshbuf_mutable_ptr(request),
153	    sshbuf_len(request)) != sshbuf_len(request))
154		return SSH_ERR_AGENT_COMMUNICATION;
155	/*
156	 * Wait for response from the agent.  First read the length of the
157	 * response packet.
158	 */
159	if (atomicio(read, sock, buf, 4) != 4)
160	    return SSH_ERR_AGENT_COMMUNICATION;
161
162	/* Extract the length, and check it for sanity. */
163	len = PEEK_U32(buf);
164	if (len > MAX_AGENT_REPLY_LEN)
165		return SSH_ERR_INVALID_FORMAT;
166
167	/* Read the rest of the response in to the buffer. */
168	sshbuf_reset(reply);
169	while (len > 0) {
170		l = len;
171		if (l > sizeof(buf))
172			l = sizeof(buf);
173		if (atomicio(read, sock, buf, l) != l)
174			return SSH_ERR_AGENT_COMMUNICATION;
175		if ((r = sshbuf_put(reply, buf, l)) != 0)
176			return r;
177		len -= l;
178	}
179	return 0;
180}
181
182/* Communicate with agent: sent request, read and decode status reply */
183static int
184ssh_request_reply_decode(int sock, struct sshbuf *request)
185{
186	struct sshbuf *reply;
187	int r;
188	u_char type;
189
190	if ((reply = sshbuf_new()) == NULL)
191		return SSH_ERR_ALLOC_FAIL;
192	if ((r = ssh_request_reply(sock, request, reply)) != 0 ||
193	    (r = sshbuf_get_u8(reply, &type)) != 0 ||
194	    (r = decode_reply(type)) != 0)
195		goto out;
196	/* success */
197	r = 0;
198 out:
199	sshbuf_free(reply);
200	return r;
201}
202
203/*
204 * Closes the agent socket if it should be closed (depends on how it was
205 * obtained).  The argument must have been returned by
206 * ssh_get_authentication_socket().
207 */
208void
209ssh_close_authentication_socket(int sock)
210{
211	if (getenv(SSH_AUTHSOCKET_ENV_NAME))
212		close(sock);
213}
214
215/* Lock/unlock agent */
216int
217ssh_lock_agent(int sock, int lock, const char *password)
218{
219	int r;
220	u_char type = lock ? SSH_AGENTC_LOCK : SSH_AGENTC_UNLOCK;
221	struct sshbuf *msg;
222
223	if ((msg = sshbuf_new()) == NULL)
224		return SSH_ERR_ALLOC_FAIL;
225	if ((r = sshbuf_put_u8(msg, type)) != 0 ||
226	    (r = sshbuf_put_cstring(msg, password)) != 0 ||
227	    (r = ssh_request_reply_decode(sock, msg)) != 0)
228		goto out;
229	/* success */
230	r = 0;
231 out:
232	sshbuf_free(msg);
233	return r;
234}
235
236
237static int
238deserialise_identity2(struct sshbuf *ids, struct sshkey **keyp, char **commentp)
239{
240	int r;
241	char *comment = NULL;
242	const u_char *blob;
243	size_t blen;
244
245	if ((r = sshbuf_get_string_direct(ids, &blob, &blen)) != 0 ||
246	    (r = sshbuf_get_cstring(ids, &comment, NULL)) != 0)
247		goto out;
248	if ((r = sshkey_from_blob(blob, blen, keyp)) != 0)
249		goto out;
250	if (commentp != NULL) {
251		*commentp = comment;
252		comment = NULL;
253	}
254	r = 0;
255 out:
256	free(comment);
257	return r;
258}
259
260/*
261 * Fetch list of identities held by the agent.
262 */
263int
264ssh_fetch_identitylist(int sock, struct ssh_identitylist **idlp)
265{
266	u_char type;
267	u_int32_t num, i;
268	struct sshbuf *msg;
269	struct ssh_identitylist *idl = NULL;
270	int r;
271
272	/*
273	 * Send a message to the agent requesting for a list of the
274	 * identities it can represent.
275	 */
276	if ((msg = sshbuf_new()) == NULL)
277		return SSH_ERR_ALLOC_FAIL;
278	if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_REQUEST_IDENTITIES)) != 0)
279		goto out;
280
281	if ((r = ssh_request_reply(sock, msg, msg)) != 0)
282		goto out;
283
284	/* Get message type, and verify that we got a proper answer. */
285	if ((r = sshbuf_get_u8(msg, &type)) != 0)
286		goto out;
287	if (agent_failed(type)) {
288		r = SSH_ERR_AGENT_FAILURE;
289		goto out;
290	} else if (type != SSH2_AGENT_IDENTITIES_ANSWER) {
291		r = SSH_ERR_INVALID_FORMAT;
292		goto out;
293	}
294
295	/* Get the number of entries in the response and check it for sanity. */
296	if ((r = sshbuf_get_u32(msg, &num)) != 0)
297		goto out;
298	if (num > MAX_AGENT_IDENTITIES) {
299		r = SSH_ERR_INVALID_FORMAT;
300		goto out;
301	}
302	if (num == 0) {
303		r = SSH_ERR_AGENT_NO_IDENTITIES;
304		goto out;
305	}
306
307	/* Deserialise the response into a list of keys/comments */
308	if ((idl = calloc(1, sizeof(*idl))) == NULL ||
309	    (idl->keys = calloc(num, sizeof(*idl->keys))) == NULL ||
310	    (idl->comments = calloc(num, sizeof(*idl->comments))) == NULL) {
311		r = SSH_ERR_ALLOC_FAIL;
312		goto out;
313	}
314	for (i = 0; i < num;) {
315		if ((r = deserialise_identity2(msg, &(idl->keys[i]),
316		    &(idl->comments[i]))) != 0) {
317			if (r == SSH_ERR_KEY_TYPE_UNKNOWN) {
318				/* Gracefully skip unknown key types */
319				num--;
320				continue;
321			} else
322				goto out;
323		}
324		i++;
325	}
326	idl->nkeys = num;
327	*idlp = idl;
328	idl = NULL;
329	r = 0;
330 out:
331	sshbuf_free(msg);
332	if (idl != NULL)
333		ssh_free_identitylist(idl);
334	return r;
335}
336
337void
338ssh_free_identitylist(struct ssh_identitylist *idl)
339{
340	size_t i;
341
342	if (idl == NULL)
343		return;
344	for (i = 0; i < idl->nkeys; i++) {
345		if (idl->keys != NULL)
346			sshkey_free(idl->keys[i]);
347		if (idl->comments != NULL)
348			free(idl->comments[i]);
349	}
350	free(idl->keys);
351	free(idl->comments);
352	free(idl);
353}
354
355/*
356 * Check if the ssh agent has a given key.
357 * Returns 0 if found, or a negative SSH_ERR_* error code on failure.
358 */
359int
360ssh_agent_has_key(int sock, const struct sshkey *key)
361{
362	int r, ret = SSH_ERR_KEY_NOT_FOUND;
363	size_t i;
364	struct ssh_identitylist *idlist = NULL;
365
366	if ((r = ssh_fetch_identitylist(sock, &idlist)) != 0) {
367		return r;
368	}
369
370	for (i = 0; i < idlist->nkeys; i++) {
371		if (sshkey_equal_public(idlist->keys[i], key)) {
372			ret = 0;
373			break;
374		}
375	}
376
377	ssh_free_identitylist(idlist);
378	return ret;
379}
380
381/*
382 * Sends a challenge (typically from a server via ssh(1)) to the agent,
383 * and waits for a response from the agent.
384 * Returns true (non-zero) if the agent gave the correct answer, zero
385 * otherwise.
386 */
387
388
389/* encode signature algorithm in flag bits, so we can keep the msg format */
390static u_int
391agent_encode_alg(const struct sshkey *key, const char *alg)
392{
393	if (alg != NULL && sshkey_type_plain(key->type) == KEY_RSA) {
394		if (strcmp(alg, "rsa-sha2-256") == 0 ||
395		    strcmp(alg, "rsa-sha2-256-cert-v01@openssh.com") == 0)
396			return SSH_AGENT_RSA_SHA2_256;
397		if (strcmp(alg, "rsa-sha2-512") == 0 ||
398		    strcmp(alg, "rsa-sha2-512-cert-v01@openssh.com") == 0)
399			return SSH_AGENT_RSA_SHA2_512;
400	}
401	return 0;
402}
403
404/* ask agent to sign data, returns err.h code on error, 0 on success */
405int
406ssh_agent_sign(int sock, const struct sshkey *key,
407    u_char **sigp, size_t *lenp,
408    const u_char *data, size_t datalen, const char *alg, u_int compat)
409{
410	struct sshbuf *msg;
411	u_char *sig = NULL, type = 0;
412	size_t len = 0;
413	u_int flags = 0;
414	int r = SSH_ERR_INTERNAL_ERROR;
415
416	*sigp = NULL;
417	*lenp = 0;
418
419	if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE)
420		return SSH_ERR_INVALID_ARGUMENT;
421	if ((msg = sshbuf_new()) == NULL)
422		return SSH_ERR_ALLOC_FAIL;
423	flags |= agent_encode_alg(key, alg);
424	if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 ||
425	    (r = sshkey_puts(key, msg)) != 0 ||
426	    (r = sshbuf_put_string(msg, data, datalen)) != 0 ||
427	    (r = sshbuf_put_u32(msg, flags)) != 0)
428		goto out;
429	if ((r = ssh_request_reply(sock, msg, msg)) != 0)
430		goto out;
431	if ((r = sshbuf_get_u8(msg, &type)) != 0)
432		goto out;
433	if (agent_failed(type)) {
434		r = SSH_ERR_AGENT_FAILURE;
435		goto out;
436	} else if (type != SSH2_AGENT_SIGN_RESPONSE) {
437		r = SSH_ERR_INVALID_FORMAT;
438		goto out;
439	}
440	if ((r = sshbuf_get_string(msg, &sig, &len)) != 0)
441		goto out;
442	/* Check what we actually got back from the agent. */
443	if ((r = sshkey_check_sigtype(sig, len, alg)) != 0)
444		goto out;
445	/* success */
446	*sigp = sig;
447	*lenp = len;
448	sig = NULL;
449	len = 0;
450	r = 0;
451 out:
452	freezero(sig, len);
453	sshbuf_free(msg);
454	return r;
455}
456
457/* Encode key for a message to the agent. */
458
459static int
460encode_dest_constraint_hop(struct sshbuf *m,
461    const struct dest_constraint_hop *dch)
462{
463	struct sshbuf *b;
464	u_int i;
465	int r;
466
467	if ((b = sshbuf_new()) == NULL)
468		return SSH_ERR_ALLOC_FAIL;
469	if ((r = sshbuf_put_cstring(b, dch->user)) != 0 ||
470	    (r = sshbuf_put_cstring(b, dch->hostname)) != 0 ||
471	    (r = sshbuf_put_string(b, NULL, 0)) != 0) /* reserved */
472		goto out;
473	for (i = 0; i < dch->nkeys; i++) {
474		if ((r = sshkey_puts(dch->keys[i], b)) != 0 ||
475		    (r = sshbuf_put_u8(b, dch->key_is_ca[i] != 0)) != 0)
476			goto out;
477	}
478	if ((r = sshbuf_put_stringb(m, b)) != 0)
479		goto out;
480	/* success */
481	r = 0;
482 out:
483	sshbuf_free(b);
484	return r;
485}
486
487static int
488encode_dest_constraint(struct sshbuf *m, const struct dest_constraint *dc)
489{
490	struct sshbuf *b;
491	int r;
492
493	if ((b = sshbuf_new()) == NULL)
494		return SSH_ERR_ALLOC_FAIL;
495	if ((r = encode_dest_constraint_hop(b, &dc->from)) != 0 ||
496	    (r = encode_dest_constraint_hop(b, &dc->to)) != 0 ||
497	    (r = sshbuf_put_string(b, NULL, 0)) != 0) /* reserved */
498		goto out;
499	if ((r = sshbuf_put_stringb(m, b)) != 0)
500		goto out;
501	/* success */
502	r = 0;
503 out:
504	sshbuf_free(b);
505	return r;
506}
507
508static int
509encode_constraints(struct sshbuf *m, u_int life, u_int confirm,
510    u_int maxsign, const char *provider,
511    struct dest_constraint **dest_constraints, size_t ndest_constraints,
512    int cert_only, struct sshkey **certs, size_t ncerts)
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		sshbuf_free(b);
557		b = NULL;
558	}
559	if (ncerts != 0) {
560		if ((b = sshbuf_new()) == NULL) {
561			r = SSH_ERR_ALLOC_FAIL;
562			goto out;
563		}
564		for (i = 0; i < ncerts; i++) {
565			if ((r = sshkey_puts(certs[i], b)) != 0)
566				goto out;
567		}
568		if ((r = sshbuf_put_u8(m,
569		    SSH_AGENT_CONSTRAIN_EXTENSION)) != 0 ||
570		    (r = sshbuf_put_cstring(m,
571		    "associated-certs-v00@openssh.com")) != 0 ||
572		    (r = sshbuf_put_u8(m, cert_only != 0)) != 0 ||
573		    (r = sshbuf_put_stringb(m, b)) != 0)
574			goto out;
575		sshbuf_free(b);
576		b = NULL;
577	}
578	r = 0;
579 out:
580	sshbuf_free(b);
581	return r;
582}
583
584/*
585 * Adds an identity to the authentication server.
586 * This call is intended only for use by ssh-add(1) and like applications.
587 */
588int
589ssh_add_identity_constrained(int sock, struct sshkey *key,
590    const char *comment, u_int life, u_int confirm, u_int maxsign,
591    const char *provider, struct dest_constraint **dest_constraints,
592    size_t ndest_constraints)
593{
594	struct sshbuf *msg;
595	int r, constrained = (life || confirm || maxsign ||
596	    provider || dest_constraints);
597	u_char type;
598
599	if ((msg = sshbuf_new()) == NULL)
600		return SSH_ERR_ALLOC_FAIL;
601
602	switch (key->type) {
603#ifdef WITH_OPENSSL
604	case KEY_RSA:
605	case KEY_RSA_CERT:
606	case KEY_DSA:
607	case KEY_DSA_CERT:
608	case KEY_ECDSA:
609	case KEY_ECDSA_CERT:
610	case KEY_ECDSA_SK:
611	case KEY_ECDSA_SK_CERT:
612#endif
613	case KEY_ED25519:
614	case KEY_ED25519_CERT:
615	case KEY_ED25519_SK:
616	case KEY_ED25519_SK_CERT:
617	case KEY_XMSS:
618	case KEY_XMSS_CERT:
619		type = constrained ?
620		    SSH2_AGENTC_ADD_ID_CONSTRAINED :
621		    SSH2_AGENTC_ADD_IDENTITY;
622		if ((r = sshbuf_put_u8(msg, type)) != 0 ||
623		    (r = sshkey_private_serialize_maxsign(key, msg, maxsign,
624		    0)) != 0 ||
625		    (r = sshbuf_put_cstring(msg, comment)) != 0)
626			goto out;
627		break;
628	default:
629		r = SSH_ERR_INVALID_ARGUMENT;
630		goto out;
631	}
632	if (constrained &&
633	    (r = encode_constraints(msg, life, confirm, maxsign,
634	    provider, dest_constraints, ndest_constraints, 0, NULL, 0)) != 0)
635		goto out;
636	if ((r = ssh_request_reply_decode(sock, msg)) != 0)
637		goto out;
638	/* success */
639	r = 0;
640 out:
641	sshbuf_free(msg);
642	return r;
643}
644
645/*
646 * Removes an identity from the authentication server.
647 * This call is intended only for use by ssh-add(1) and like applications.
648 */
649int
650ssh_remove_identity(int sock, const struct sshkey *key)
651{
652	struct sshbuf *msg;
653	int r;
654	u_char *blob = NULL;
655	size_t blen;
656
657	if ((msg = sshbuf_new()) == NULL)
658		return SSH_ERR_ALLOC_FAIL;
659
660	if (key->type != KEY_UNSPEC) {
661		if ((r = sshkey_to_blob(key, &blob, &blen)) != 0)
662			goto out;
663		if ((r = sshbuf_put_u8(msg,
664		    SSH2_AGENTC_REMOVE_IDENTITY)) != 0 ||
665		    (r = sshbuf_put_string(msg, blob, blen)) != 0)
666			goto out;
667	} else {
668		r = SSH_ERR_INVALID_ARGUMENT;
669		goto out;
670	}
671	if ((r = ssh_request_reply_decode(sock, msg)) != 0)
672		goto out;
673	/* success */
674	r = 0;
675 out:
676	if (blob != NULL)
677		freezero(blob, blen);
678	sshbuf_free(msg);
679	return r;
680}
681
682/*
683 * Add/remove an token-based identity from the authentication server.
684 * This call is intended only for use by ssh-add(1) and like applications.
685 */
686int
687ssh_update_card(int sock, int add, const char *reader_id, const char *pin,
688    u_int life, u_int confirm,
689    struct dest_constraint **dest_constraints, size_t ndest_constraints,
690    int cert_only, struct sshkey **certs, size_t ncerts)
691{
692	struct sshbuf *msg;
693	int r, constrained = (life || confirm || dest_constraints || certs);
694	u_char type;
695
696	if (add) {
697		type = constrained ?
698		    SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED :
699		    SSH_AGENTC_ADD_SMARTCARD_KEY;
700	} else
701		type = SSH_AGENTC_REMOVE_SMARTCARD_KEY;
702
703	if ((msg = sshbuf_new()) == NULL)
704		return SSH_ERR_ALLOC_FAIL;
705	if ((r = sshbuf_put_u8(msg, type)) != 0 ||
706	    (r = sshbuf_put_cstring(msg, reader_id)) != 0 ||
707	    (r = sshbuf_put_cstring(msg, pin)) != 0)
708		goto out;
709	if (constrained &&
710	    (r = encode_constraints(msg, life, confirm, 0, NULL,
711	    dest_constraints, ndest_constraints,
712	    cert_only, certs, ncerts)) != 0)
713		goto out;
714	if ((r = ssh_request_reply_decode(sock, msg)) != 0)
715		goto out;
716	/* success */
717	r = 0;
718 out:
719	sshbuf_free(msg);
720	return r;
721}
722
723/*
724 * Removes all identities from the agent.
725 * This call is intended only for use by ssh-add(1) and like applications.
726 *
727 * This supports the SSH protocol 1 message to because, when clearing all
728 * keys from an agent, we generally want to clear both protocol v1 and v2
729 * keys.
730 */
731int
732ssh_remove_all_identities(int sock, int version)
733{
734	struct sshbuf *msg;
735	u_char type = (version == 1) ?
736	    SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES :
737	    SSH2_AGENTC_REMOVE_ALL_IDENTITIES;
738	int r;
739
740	if ((msg = sshbuf_new()) == NULL)
741		return SSH_ERR_ALLOC_FAIL;
742	if ((r = sshbuf_put_u8(msg, type)) != 0)
743		goto out;
744	if ((r = ssh_request_reply_decode(sock, msg)) != 0)
745		goto out;
746	/* success */
747	r = 0;
748 out:
749	sshbuf_free(msg);
750	return r;
751}
752
753/* Binds a session ID to a hostkey via the initial KEX signature. */
754int
755ssh_agent_bind_hostkey(int sock, const struct sshkey *key,
756    const struct sshbuf *session_id, const struct sshbuf *signature,
757    int forwarding)
758{
759	struct sshbuf *msg;
760	int r;
761
762	if (key == NULL || session_id == NULL || signature == NULL)
763		return SSH_ERR_INVALID_ARGUMENT;
764	if ((msg = sshbuf_new()) == NULL)
765		return SSH_ERR_ALLOC_FAIL;
766	if ((r = sshbuf_put_u8(msg, SSH_AGENTC_EXTENSION)) != 0 ||
767	    (r = sshbuf_put_cstring(msg, "session-bind@openssh.com")) != 0 ||
768	    (r = sshkey_puts(key, msg)) != 0 ||
769	    (r = sshbuf_put_stringb(msg, session_id)) != 0 ||
770	    (r = sshbuf_put_stringb(msg, signature)) != 0 ||
771	    (r = sshbuf_put_u8(msg, forwarding ? 1 : 0)) != 0)
772		goto out;
773	if ((r = ssh_request_reply_decode(sock, msg)) != 0)
774		goto out;
775	/* success */
776	r = 0;
777 out:
778	sshbuf_free(msg);
779	return r;
780}
781