Home | History | Annotate | Download | only in openssh
      1 /* $OpenBSD: authfd.c,v 1.98 2015/07/03 03:43:18 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 	*sigp = NULL;
    442 	*lenp = 0;
    443 
    444 	if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE)
    445 		return SSH_ERR_INVALID_ARGUMENT;
    446 	if (compat & SSH_BUG_SIGBLOB)
    447 		flags |= SSH_AGENT_OLD_SIGNATURE;
    448 	if ((msg = sshbuf_new()) == NULL)
    449 		return SSH_ERR_ALLOC_FAIL;
    450 	if ((r = sshkey_to_blob(key, &blob, &blen)) != 0)
    451 		goto out;
    452 	if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 ||
    453 	    (r = sshbuf_put_string(msg, blob, blen)) != 0 ||
    454 	    (r = sshbuf_put_string(msg, data, datalen)) != 0 ||
    455 	    (r = sshbuf_put_u32(msg, flags)) != 0)
    456 		goto out;
    457 	if ((r = ssh_request_reply(sock, msg, msg) != 0))
    458 		goto out;
    459 	if ((r = sshbuf_get_u8(msg, &type)) != 0)
    460 		goto out;
    461 	if (agent_failed(type)) {
    462 		r = SSH_ERR_AGENT_FAILURE;
    463 		goto out;
    464 	} else if (type != SSH2_AGENT_SIGN_RESPONSE) {
    465 		r = SSH_ERR_INVALID_FORMAT;
    466 		goto out;
    467 	}
    468 	if ((r = sshbuf_get_string(msg, sigp, &len)) != 0)
    469 		goto out;
    470 	*lenp = len;
    471 	r = 0;
    472  out:
    473 	if (blob != NULL) {
    474 		explicit_bzero(blob, blen);
    475 		free(blob);
    476 	}
    477 	sshbuf_free(msg);
    478 	return r;
    479 }
    480 
    481 /* Encode key for a message to the agent. */
    482 
    483 #ifdef WITH_SSH1
    484 static int
    485 ssh_encode_identity_rsa1(struct sshbuf *b, RSA *key, const char *comment)
    486 {
    487 	int r;
    488 
    489 	/* To keep within the protocol: p < q for ssh. in SSL p > q */
    490 	if ((r = sshbuf_put_u32(b, BN_num_bits(key->n))) != 0 ||
    491 	    (r = sshbuf_put_bignum1(b, key->n)) != 0 ||
    492 	    (r = sshbuf_put_bignum1(b, key->e)) != 0 ||
    493 	    (r = sshbuf_put_bignum1(b, key->d)) != 0 ||
    494 	    (r = sshbuf_put_bignum1(b, key->iqmp)) != 0 ||
    495 	    (r = sshbuf_put_bignum1(b, key->q)) != 0 ||
    496 	    (r = sshbuf_put_bignum1(b, key->p)) != 0 ||
    497 	    (r = sshbuf_put_cstring(b, comment)) != 0)
    498 		return r;
    499 	return 0;
    500 }
    501 #endif
    502 
    503 static int
    504 ssh_encode_identity_ssh2(struct sshbuf *b, struct sshkey *key,
    505     const char *comment)
    506 {
    507 	int r;
    508 
    509 	if ((r = sshkey_private_serialize(key, b)) != 0 ||
    510 	    (r = sshbuf_put_cstring(b, comment)) != 0)
    511 		return r;
    512 	return 0;
    513 }
    514 
    515 static int
    516 encode_constraints(struct sshbuf *m, u_int life, u_int confirm)
    517 {
    518 	int r;
    519 
    520 	if (life != 0) {
    521 		if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_LIFETIME)) != 0 ||
    522 		    (r = sshbuf_put_u32(m, life)) != 0)
    523 			goto out;
    524 	}
    525 	if (confirm != 0) {
    526 		if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_CONFIRM)) != 0)
    527 			goto out;
    528 	}
    529 	r = 0;
    530  out:
    531 	return r;
    532 }
    533 
    534 /*
    535  * Adds an identity to the authentication server.
    536  * This call is intended only for use by ssh-add(1) and like applications.
    537  */
    538 int
    539 ssh_add_identity_constrained(int sock, struct sshkey *key, const char *comment,
    540     u_int life, u_int confirm)
    541 {
    542 	struct sshbuf *msg;
    543 	int r, constrained = (life || confirm);
    544 	u_char type;
    545 
    546 	if ((msg = sshbuf_new()) == NULL)
    547 		return SSH_ERR_ALLOC_FAIL;
    548 
    549 	switch (key->type) {
    550 #ifdef WITH_SSH1
    551 	case KEY_RSA1:
    552 		type = constrained ?
    553 		    SSH_AGENTC_ADD_RSA_ID_CONSTRAINED :
    554 		    SSH_AGENTC_ADD_RSA_IDENTITY;
    555 		if ((r = sshbuf_put_u8(msg, type)) != 0 ||
    556 		    (r = ssh_encode_identity_rsa1(msg, key->rsa, comment)) != 0)
    557 			goto out;
    558 		break;
    559 #endif
    560 #ifdef WITH_OPENSSL
    561 	case KEY_RSA:
    562 	case KEY_RSA_CERT:
    563 	case KEY_DSA:
    564 	case KEY_DSA_CERT:
    565 	case KEY_ECDSA:
    566 	case KEY_ECDSA_CERT:
    567 #endif
    568 	case KEY_ED25519:
    569 	case KEY_ED25519_CERT:
    570 		type = constrained ?
    571 		    SSH2_AGENTC_ADD_ID_CONSTRAINED :
    572 		    SSH2_AGENTC_ADD_IDENTITY;
    573 		if ((r = sshbuf_put_u8(msg, type)) != 0 ||
    574 		    (r = ssh_encode_identity_ssh2(msg, key, comment)) != 0)
    575 			goto out;
    576 		break;
    577 	default:
    578 		r = SSH_ERR_INVALID_ARGUMENT;
    579 		goto out;
    580 	}
    581 	if (constrained &&
    582 	    (r = encode_constraints(msg, life, confirm)) != 0)
    583 		goto out;
    584 	if ((r = ssh_request_reply(sock, msg, msg)) != 0)
    585 		goto out;
    586 	if ((r = sshbuf_get_u8(msg, &type)) != 0)
    587 		goto out;
    588 	r = decode_reply(type);
    589  out:
    590 	sshbuf_free(msg);
    591 	return r;
    592 }
    593 
    594 /*
    595  * Removes an identity from the authentication server.
    596  * This call is intended only for use by ssh-add(1) and like applications.
    597  */
    598 int
    599 ssh_remove_identity(int sock, struct sshkey *key)
    600 {
    601 	struct sshbuf *msg;
    602 	int r;
    603 	u_char type, *blob = NULL;
    604 	size_t blen;
    605 
    606 	if ((msg = sshbuf_new()) == NULL)
    607 		return SSH_ERR_ALLOC_FAIL;
    608 
    609 #ifdef WITH_SSH1
    610 	if (key->type == KEY_RSA1) {
    611 		if ((r = sshbuf_put_u8(msg,
    612 		    SSH_AGENTC_REMOVE_RSA_IDENTITY)) != 0 ||
    613 		    (r = sshbuf_put_u32(msg, BN_num_bits(key->rsa->n))) != 0 ||
    614 		    (r = sshbuf_put_bignum1(msg, key->rsa->e)) != 0 ||
    615 		    (r = sshbuf_put_bignum1(msg, key->rsa->n)) != 0)
    616 			goto out;
    617 	} else
    618 #endif
    619 	if (key->type != KEY_UNSPEC) {
    620 		if ((r = sshkey_to_blob(key, &blob, &blen)) != 0)
    621 			goto out;
    622 		if ((r = sshbuf_put_u8(msg,
    623 		    SSH2_AGENTC_REMOVE_IDENTITY)) != 0 ||
    624 		    (r = sshbuf_put_string(msg, blob, blen)) != 0)
    625 			goto out;
    626 	} else {
    627 		r = SSH_ERR_INVALID_ARGUMENT;
    628 		goto out;
    629 	}
    630 	if ((r = ssh_request_reply(sock, msg, msg)) != 0)
    631 		goto out;
    632 	if ((r = sshbuf_get_u8(msg, &type)) != 0)
    633 		goto out;
    634 	r = decode_reply(type);
    635  out:
    636 	if (blob != NULL) {
    637 		explicit_bzero(blob, blen);
    638 		free(blob);
    639 	}
    640 	sshbuf_free(msg);
    641 	return r;
    642 }
    643 
    644 /*
    645  * Add/remove an token-based identity from the authentication server.
    646  * This call is intended only for use by ssh-add(1) and like applications.
    647  */
    648 int
    649 ssh_update_card(int sock, int add, const char *reader_id, const char *pin,
    650     u_int life, u_int confirm)
    651 {
    652 	struct sshbuf *msg;
    653 	int r, constrained = (life || confirm);
    654 	u_char type;
    655 
    656 	if (add) {
    657 		type = constrained ?
    658 		    SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED :
    659 		    SSH_AGENTC_ADD_SMARTCARD_KEY;
    660 	} else
    661 		type = SSH_AGENTC_REMOVE_SMARTCARD_KEY;
    662 
    663 	if ((msg = sshbuf_new()) == NULL)
    664 		return SSH_ERR_ALLOC_FAIL;
    665 	if ((r = sshbuf_put_u8(msg, type)) != 0 ||
    666 	    (r = sshbuf_put_cstring(msg, reader_id)) != 0 ||
    667 	    (r = sshbuf_put_cstring(msg, pin)) != 0)
    668 		goto out;
    669 	if (constrained &&
    670 	    (r = encode_constraints(msg, life, confirm)) != 0)
    671 		goto out;
    672 	if ((r = ssh_request_reply(sock, msg, msg)) != 0)
    673 		goto out;
    674 	if ((r = sshbuf_get_u8(msg, &type)) != 0)
    675 		goto out;
    676 	r = decode_reply(type);
    677  out:
    678 	sshbuf_free(msg);
    679 	return r;
    680 }
    681 
    682 /*
    683  * Removes all identities from the agent.
    684  * This call is intended only for use by ssh-add(1) and like applications.
    685  */
    686 int
    687 ssh_remove_all_identities(int sock, int version)
    688 {
    689 	struct sshbuf *msg;
    690 	u_char type = (version == 1) ?
    691 	    SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES :
    692 	    SSH2_AGENTC_REMOVE_ALL_IDENTITIES;
    693 	int r;
    694 
    695 	if ((msg = sshbuf_new()) == NULL)
    696 		return SSH_ERR_ALLOC_FAIL;
    697 	if ((r = sshbuf_put_u8(msg, type)) != 0)
    698 		goto out;
    699 	if ((r = ssh_request_reply(sock, msg, msg)) != 0)
    700 		goto out;
    701 	if ((r = sshbuf_get_u8(msg, &type)) != 0)
    702 		goto out;
    703 	r = decode_reply(type);
    704  out:
    705 	sshbuf_free(msg);
    706 	return r;
    707 }
    708