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