1296781Sdes/* $OpenBSD: authfd.c,v 1.100 2015/12/04 16:41:28 markus Exp $ */ 257429Smarkm/* 357429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi> 457429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 557429Smarkm * All rights reserved 665674Skris * Functions for connecting the local authentication agent. 760576Skris * 865674Skris * As far as I am concerned, the code I have written for this software 965674Skris * can be used freely for any purpose. Any derived versions of this 1065674Skris * software must be clearly marked as such, and if the derived work is 1165674Skris * incompatible with the protocol description in the RFC file, it must be 1265674Skris * called by a name other than "ssh" or "Secure Shell". 1360576Skris * 1465674Skris * SSH2 implementation, 1565674Skris * Copyright (c) 2000 Markus Friedl. All rights reserved. 1660576Skris * 1765674Skris * Redistribution and use in source and binary forms, with or without 1865674Skris * modification, are permitted provided that the following conditions 1965674Skris * are met: 2065674Skris * 1. Redistributions of source code must retain the above copyright 2165674Skris * notice, this list of conditions and the following disclaimer. 2265674Skris * 2. Redistributions in binary form must reproduce the above copyright 2365674Skris * notice, this list of conditions and the following disclaimer in the 2465674Skris * documentation and/or other materials provided with the distribution. 2565674Skris * 2665674Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2765674Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2865674Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2965674Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 3065674Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 3165674Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3265674Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3365674Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3465674Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3565674Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3657429Smarkm */ 3757429Smarkm 3857429Smarkm#include "includes.h" 3957429Smarkm 40162856Sdes#include <sys/types.h> 41162856Sdes#include <sys/un.h> 42162856Sdes#include <sys/socket.h> 43162856Sdes 44162856Sdes#include <fcntl.h> 45162856Sdes#include <stdlib.h> 46162856Sdes#include <signal.h> 47162856Sdes#include <stdarg.h> 48162856Sdes#include <string.h> 49162856Sdes#include <unistd.h> 50295367Sdes#include <errno.h> 51162856Sdes 52162856Sdes#include "xmalloc.h" 5357429Smarkm#include "ssh.h" 5457429Smarkm#include "rsa.h" 55295367Sdes#include "sshbuf.h" 56295367Sdes#include "sshkey.h" 5765674Skris#include "authfd.h" 5876262Sgreen#include "cipher.h" 5969591Sgreen#include "compat.h" 6076262Sgreen#include "log.h" 6176262Sgreen#include "atomicio.h" 62162856Sdes#include "misc.h" 63295367Sdes#include "ssherr.h" 6457429Smarkm 65295367Sdes#define MAX_AGENT_IDENTITIES 2048 /* Max keys in agent reply */ 66295367Sdes#define MAX_AGENT_REPLY_LEN (256 * 1024) /* Max bytes in agent reply */ 67106130Sdes 6869591Sgreen/* macro to check for "agent failure" message */ 6969591Sgreen#define agent_failed(x) \ 70295367Sdes ((x == SSH_AGENT_FAILURE) || \ 71295367Sdes (x == SSH_COM_AGENT2_FAILURE) || \ 7298684Sdes (x == SSH2_AGENT_FAILURE)) 7369591Sgreen 74295367Sdes/* Convert success/failure response from agent to a err.h status */ 75295367Sdesstatic int 76295367Sdesdecode_reply(u_char type) 77106130Sdes{ 78295367Sdes if (agent_failed(type)) 79295367Sdes return SSH_ERR_AGENT_FAILURE; 80295367Sdes else if (type == SSH_AGENT_SUCCESS) 81106130Sdes return 0; 82295367Sdes else 83295367Sdes return SSH_ERR_INVALID_FORMAT; 84106130Sdes} 85106130Sdes 8657429Smarkm/* Returns the number of the authentication fd, or -1 if there is none. */ 8757429Smarkmint 88295367Sdesssh_get_authentication_socket(int *fdp) 8957429Smarkm{ 9057429Smarkm const char *authsocket; 91295367Sdes int sock, oerrno; 9257429Smarkm struct sockaddr_un sunaddr; 9357429Smarkm 94295367Sdes if (fdp != NULL) 95295367Sdes *fdp = -1; 96295367Sdes 9757429Smarkm authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME); 9857429Smarkm if (!authsocket) 99295367Sdes return SSH_ERR_AGENT_NOT_PRESENT; 10057429Smarkm 101264377Sdes memset(&sunaddr, 0, sizeof(sunaddr)); 10257429Smarkm sunaddr.sun_family = AF_UNIX; 10357429Smarkm strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path)); 10457429Smarkm 105295367Sdes if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) 106295367Sdes return SSH_ERR_SYSTEM_ERROR; 10757429Smarkm 10857429Smarkm /* close on exec */ 109295367Sdes if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1 || 110295367Sdes connect(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) { 111295367Sdes oerrno = errno; 11257429Smarkm close(sock); 113295367Sdes errno = oerrno; 114295367Sdes return SSH_ERR_SYSTEM_ERROR; 11557429Smarkm } 116295367Sdes if (fdp != NULL) 117295367Sdes *fdp = sock; 118295367Sdes else 11957429Smarkm close(sock); 120295367Sdes return 0; 12157429Smarkm} 12257429Smarkm 123295367Sdes/* Communicate with agent: send request and read reply */ 12492559Sdesstatic int 125295367Sdesssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply) 12665674Skris{ 127295367Sdes int r; 128295367Sdes size_t l, len; 12965674Skris char buf[1024]; 13065674Skris 13165674Skris /* Get the length of the message, and format it in the buffer. */ 132295367Sdes len = sshbuf_len(request); 133162856Sdes put_u32(buf, len); 13465674Skris 13565674Skris /* Send the length and then the packet to the agent. */ 136295367Sdes if (atomicio(vwrite, sock, buf, 4) != 4 || 137295367Sdes atomicio(vwrite, sock, (u_char *)sshbuf_ptr(request), 138295367Sdes sshbuf_len(request)) != sshbuf_len(request)) 139295367Sdes return SSH_ERR_AGENT_COMMUNICATION; 14065674Skris /* 14165674Skris * Wait for response from the agent. First read the length of the 14265674Skris * response packet. 14365674Skris */ 144295367Sdes if (atomicio(read, sock, buf, 4) != 4) 145295367Sdes return SSH_ERR_AGENT_COMMUNICATION; 14665674Skris 14765674Skris /* Extract the length, and check it for sanity. */ 148162856Sdes len = get_u32(buf); 149295367Sdes if (len > MAX_AGENT_REPLY_LEN) 150295367Sdes return SSH_ERR_INVALID_FORMAT; 15165674Skris 15265674Skris /* Read the rest of the response in to the buffer. */ 153295367Sdes sshbuf_reset(reply); 15465674Skris while (len > 0) { 15565674Skris l = len; 15665674Skris if (l > sizeof(buf)) 15765674Skris l = sizeof(buf); 158295367Sdes if (atomicio(read, sock, buf, l) != l) 159295367Sdes return SSH_ERR_AGENT_COMMUNICATION; 160295367Sdes if ((r = sshbuf_put(reply, buf, l)) != 0) 161295367Sdes return r; 16265674Skris len -= l; 16365674Skris } 164295367Sdes return 0; 16565674Skris} 16665674Skris 16757429Smarkm/* 16857429Smarkm * Closes the agent socket if it should be closed (depends on how it was 16957429Smarkm * obtained). The argument must have been returned by 17057429Smarkm * ssh_get_authentication_socket(). 17157429Smarkm */ 17260576Skrisvoid 17357429Smarkmssh_close_authentication_socket(int sock) 17457429Smarkm{ 17557429Smarkm if (getenv(SSH_AUTHSOCKET_ENV_NAME)) 17657429Smarkm close(sock); 17757429Smarkm} 17857429Smarkm 179295367Sdes/* Lock/unlock agent */ 180295367Sdesint 181295367Sdesssh_lock_agent(int sock, int lock, const char *password) 18257429Smarkm{ 183295367Sdes int r; 184295367Sdes u_char type = lock ? SSH_AGENTC_LOCK : SSH_AGENTC_UNLOCK; 185295367Sdes struct sshbuf *msg; 18657429Smarkm 187295367Sdes if ((msg = sshbuf_new()) == NULL) 188295367Sdes return SSH_ERR_ALLOC_FAIL; 189295367Sdes if ((r = sshbuf_put_u8(msg, type)) != 0 || 190295367Sdes (r = sshbuf_put_cstring(msg, password)) != 0) 191295367Sdes goto out; 192295367Sdes if ((r = ssh_request_reply(sock, msg, msg)) != 0) 193295367Sdes goto out; 194295367Sdes if ((r = sshbuf_get_u8(msg, &type)) != 0) 195295367Sdes goto out; 196295367Sdes r = decode_reply(type); 197295367Sdes out: 198295367Sdes sshbuf_free(msg); 199295367Sdes return r; 20057429Smarkm} 20157429Smarkm 202295367Sdes#ifdef WITH_SSH1 203295367Sdesstatic int 204295367Sdesdeserialise_identity1(struct sshbuf *ids, struct sshkey **keyp, char **commentp) 205295367Sdes{ 206295367Sdes struct sshkey *key; 207295367Sdes int r, keybits; 208295367Sdes u_int32_t bits; 209295367Sdes char *comment = NULL; 21057429Smarkm 211295367Sdes if ((key = sshkey_new(KEY_RSA1)) == NULL) 212295367Sdes return SSH_ERR_ALLOC_FAIL; 213295367Sdes if ((r = sshbuf_get_u32(ids, &bits)) != 0 || 214295367Sdes (r = sshbuf_get_bignum1(ids, key->rsa->e)) != 0 || 215295367Sdes (r = sshbuf_get_bignum1(ids, key->rsa->n)) != 0 || 216295367Sdes (r = sshbuf_get_cstring(ids, &comment, NULL)) != 0) 217295367Sdes goto out; 218295367Sdes keybits = BN_num_bits(key->rsa->n); 219295367Sdes /* XXX previously we just warned here. I think we should be strict */ 220295367Sdes if (keybits < 0 || bits != (u_int)keybits) { 221295367Sdes r = SSH_ERR_KEY_BITS_MISMATCH; 222295367Sdes goto out; 223295367Sdes } 224295367Sdes if (keyp != NULL) { 225295367Sdes *keyp = key; 226295367Sdes key = NULL; 227295367Sdes } 228295367Sdes if (commentp != NULL) { 229295367Sdes *commentp = comment; 230295367Sdes comment = NULL; 231295367Sdes } 232295367Sdes r = 0; 233295367Sdes out: 234295367Sdes sshkey_free(key); 235295367Sdes free(comment); 236295367Sdes return r; 23757429Smarkm} 238295367Sdes#endif 23957429Smarkm 240295367Sdesstatic int 241295367Sdesdeserialise_identity2(struct sshbuf *ids, struct sshkey **keyp, char **commentp) 24298684Sdes{ 243295367Sdes int r; 244295367Sdes char *comment = NULL; 245295367Sdes const u_char *blob; 246295367Sdes size_t blen; 24798684Sdes 248295367Sdes if ((r = sshbuf_get_string_direct(ids, &blob, &blen)) != 0 || 249295367Sdes (r = sshbuf_get_cstring(ids, &comment, NULL)) != 0) 250295367Sdes goto out; 251295367Sdes if ((r = sshkey_from_blob(blob, blen, keyp)) != 0) 252295367Sdes goto out; 253295367Sdes if (commentp != NULL) { 254295367Sdes *commentp = comment; 255295367Sdes comment = NULL; 25698684Sdes } 257295367Sdes r = 0; 258295367Sdes out: 259295367Sdes free(comment); 260295367Sdes return r; 26198684Sdes} 26298684Sdes 26357429Smarkm/* 264295367Sdes * Fetch list of identities held by the agent. 26557429Smarkm */ 26676262Sgreenint 267295367Sdesssh_fetch_identitylist(int sock, int version, struct ssh_identitylist **idlp) 26857429Smarkm{ 269295367Sdes u_char type, code1 = 0, code2 = 0; 270295367Sdes u_int32_t num, i; 271295367Sdes struct sshbuf *msg; 272295367Sdes struct ssh_identitylist *idl = NULL; 273295367Sdes int r; 27457429Smarkm 275295367Sdes /* Determine request and expected response types */ 27692559Sdes switch (version) { 27765674Skris case 1: 27865674Skris code1 = SSH_AGENTC_REQUEST_RSA_IDENTITIES; 27965674Skris code2 = SSH_AGENT_RSA_IDENTITIES_ANSWER; 28065674Skris break; 28165674Skris case 2: 28265674Skris code1 = SSH2_AGENTC_REQUEST_IDENTITIES; 28365674Skris code2 = SSH2_AGENT_IDENTITIES_ANSWER; 28465674Skris break; 28565674Skris default: 286295367Sdes return SSH_ERR_INVALID_ARGUMENT; 28765674Skris } 28865674Skris 28957429Smarkm /* 29057429Smarkm * Send a message to the agent requesting for a list of the 29157429Smarkm * identities it can represent. 29257429Smarkm */ 293295367Sdes if ((msg = sshbuf_new()) == NULL) 294295367Sdes return SSH_ERR_ALLOC_FAIL; 295295367Sdes if ((r = sshbuf_put_u8(msg, code1)) != 0) 296295367Sdes goto out; 29757429Smarkm 298295367Sdes if ((r = ssh_request_reply(sock, msg, msg)) != 0) 299295367Sdes goto out; 30057429Smarkm 30157429Smarkm /* Get message type, and verify that we got a proper answer. */ 302295367Sdes if ((r = sshbuf_get_u8(msg, &type)) != 0) 303295367Sdes goto out; 30469591Sgreen if (agent_failed(type)) { 305295367Sdes r = SSH_ERR_AGENT_FAILURE; 306295367Sdes goto out; 30765674Skris } else if (type != code2) { 308295367Sdes r = SSH_ERR_INVALID_FORMAT; 309295367Sdes goto out; 31065674Skris } 31157429Smarkm 31257429Smarkm /* Get the number of entries in the response and check it for sanity. */ 313295367Sdes if ((r = sshbuf_get_u32(msg, &num)) != 0) 314295367Sdes goto out; 315295367Sdes if (num > MAX_AGENT_IDENTITIES) { 316295367Sdes r = SSH_ERR_INVALID_FORMAT; 317295367Sdes goto out; 318295367Sdes } 319295367Sdes if (num == 0) { 320295367Sdes r = SSH_ERR_AGENT_NO_IDENTITIES; 321295367Sdes goto out; 322295367Sdes } 32357429Smarkm 324295367Sdes /* Deserialise the response into a list of keys/comments */ 325295367Sdes if ((idl = calloc(1, sizeof(*idl))) == NULL || 326295367Sdes (idl->keys = calloc(num, sizeof(*idl->keys))) == NULL || 327295367Sdes (idl->comments = calloc(num, sizeof(*idl->comments))) == NULL) { 328295367Sdes r = SSH_ERR_ALLOC_FAIL; 329295367Sdes goto out; 330295367Sdes } 331295367Sdes for (i = 0; i < num;) { 332295367Sdes switch (version) { 333295367Sdes case 1: 334295367Sdes#ifdef WITH_SSH1 335295367Sdes if ((r = deserialise_identity1(msg, 336295367Sdes &(idl->keys[i]), &(idl->comments[i]))) != 0) 337295367Sdes goto out; 338295367Sdes#endif 339295367Sdes break; 340295367Sdes case 2: 341295367Sdes if ((r = deserialise_identity2(msg, 342295367Sdes &(idl->keys[i]), &(idl->comments[i]))) != 0) { 343295367Sdes if (r == SSH_ERR_KEY_TYPE_UNKNOWN) { 344295367Sdes /* Gracefully skip unknown key types */ 345295367Sdes num--; 346295367Sdes continue; 347295367Sdes } else 348295367Sdes goto out; 349295367Sdes } 350295367Sdes break; 351295367Sdes } 352295367Sdes i++; 353295367Sdes } 354295367Sdes idl->nkeys = num; 355295367Sdes *idlp = idl; 356295367Sdes idl = NULL; 357295367Sdes r = 0; 358295367Sdes out: 359295367Sdes sshbuf_free(msg); 360295367Sdes if (idl != NULL) 361295367Sdes ssh_free_identitylist(idl); 362295367Sdes return r; 36357429Smarkm} 36457429Smarkm 365295367Sdesvoid 366295367Sdesssh_free_identitylist(struct ssh_identitylist *idl) 36776262Sgreen{ 368295367Sdes size_t i; 36976262Sgreen 370295367Sdes if (idl == NULL) 371295367Sdes return; 372295367Sdes for (i = 0; i < idl->nkeys; i++) { 373295367Sdes if (idl->keys != NULL) 374295367Sdes sshkey_free(idl->keys[i]); 375295367Sdes if (idl->comments != NULL) 376295367Sdes free(idl->comments[i]); 37765674Skris } 378295367Sdes free(idl); 37957429Smarkm} 38057429Smarkm 38157429Smarkm/* 382295367Sdes * Sends a challenge (typically from a server via ssh(1)) to the agent, 383295367Sdes * and waits for a response from the agent. 384295367Sdes * Returns true (non-zero) if the agent gave the correct answer, zero 385295367Sdes * otherwise. 38657429Smarkm */ 38757429Smarkm 388295367Sdes#ifdef WITH_SSH1 38957429Smarkmint 390295367Sdesssh_decrypt_challenge(int sock, struct sshkey* key, BIGNUM *challenge, 391295367Sdes u_char session_id[16], u_char response[16]) 39257429Smarkm{ 393295367Sdes struct sshbuf *msg; 394295367Sdes int r; 395295367Sdes u_char type; 39657429Smarkm 39776262Sgreen if (key->type != KEY_RSA1) 398295367Sdes return SSH_ERR_INVALID_ARGUMENT; 399295367Sdes if ((msg = sshbuf_new()) == NULL) 400295367Sdes return SSH_ERR_ALLOC_FAIL; 401295367Sdes if ((r = sshbuf_put_u8(msg, SSH_AGENTC_RSA_CHALLENGE)) != 0 || 402295367Sdes (r = sshbuf_put_u32(msg, BN_num_bits(key->rsa->n))) != 0 || 403295367Sdes (r = sshbuf_put_bignum1(msg, key->rsa->e)) != 0 || 404295367Sdes (r = sshbuf_put_bignum1(msg, key->rsa->n)) != 0 || 405295367Sdes (r = sshbuf_put_bignum1(msg, challenge)) != 0 || 406295367Sdes (r = sshbuf_put(msg, session_id, 16)) != 0 || 407295367Sdes (r = sshbuf_put_u32(msg, 1)) != 0) /* Response type for proto 1.1 */ 408295367Sdes goto out; 409295367Sdes if ((r = ssh_request_reply(sock, msg, msg)) != 0) 410295367Sdes goto out; 411295367Sdes if ((r = sshbuf_get_u8(msg, &type)) != 0) 412295367Sdes goto out; 41369591Sgreen if (agent_failed(type)) { 414295367Sdes r = SSH_ERR_AGENT_FAILURE; 415295367Sdes goto out; 41665674Skris } else if (type != SSH_AGENT_RSA_RESPONSE) { 417295367Sdes r = SSH_ERR_INVALID_FORMAT; 418295367Sdes goto out; 41957429Smarkm } 420295367Sdes if ((r = sshbuf_get(msg, response, 16)) != 0) 421295367Sdes goto out; 422295367Sdes r = 0; 423295367Sdes out: 424295367Sdes sshbuf_free(msg); 425295367Sdes return r; 42665674Skris} 427295367Sdes#endif 42857429Smarkm 429296781Sdes/* encode signature algoritm in flag bits, so we can keep the msg format */ 430296781Sdesstatic u_int 431296781Sdesagent_encode_alg(struct sshkey *key, const char *alg) 432296781Sdes{ 433296781Sdes if (alg != NULL && key->type == KEY_RSA) { 434296781Sdes if (strcmp(alg, "rsa-sha2-256") == 0) 435296781Sdes return SSH_AGENT_RSA_SHA2_256; 436296781Sdes else if (strcmp(alg, "rsa-sha2-512") == 0) 437296781Sdes return SSH_AGENT_RSA_SHA2_512; 438296781Sdes } 439296781Sdes return 0; 440296781Sdes} 441296781Sdes 442295367Sdes/* ask agent to sign data, returns err.h code on error, 0 on success */ 44365674Skrisint 444295367Sdesssh_agent_sign(int sock, struct sshkey *key, 445295367Sdes u_char **sigp, size_t *lenp, 446296781Sdes const u_char *data, size_t datalen, const char *alg, u_int compat) 44765674Skris{ 448295367Sdes struct sshbuf *msg; 449295367Sdes u_char *blob = NULL, type; 450295367Sdes size_t blen = 0, len = 0; 451295367Sdes u_int flags = 0; 452295367Sdes int r = SSH_ERR_INTERNAL_ERROR; 45357429Smarkm 454295367Sdes *sigp = NULL; 455295367Sdes *lenp = 0; 45657429Smarkm 457295367Sdes if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE) 458295367Sdes return SSH_ERR_INVALID_ARGUMENT; 459295367Sdes if (compat & SSH_BUG_SIGBLOB) 460295367Sdes flags |= SSH_AGENT_OLD_SIGNATURE; 461295367Sdes if ((msg = sshbuf_new()) == NULL) 462295367Sdes return SSH_ERR_ALLOC_FAIL; 463295367Sdes if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) 464295367Sdes goto out; 465296781Sdes flags |= agent_encode_alg(key, alg); 466295367Sdes if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || 467295367Sdes (r = sshbuf_put_string(msg, blob, blen)) != 0 || 468295367Sdes (r = sshbuf_put_string(msg, data, datalen)) != 0 || 469295367Sdes (r = sshbuf_put_u32(msg, flags)) != 0) 470295367Sdes goto out; 471296781Sdes if ((r = ssh_request_reply(sock, msg, msg)) != 0) 472295367Sdes goto out; 473295367Sdes if ((r = sshbuf_get_u8(msg, &type)) != 0) 474295367Sdes goto out; 47569591Sgreen if (agent_failed(type)) { 476295367Sdes r = SSH_ERR_AGENT_FAILURE; 477295367Sdes goto out; 47865674Skris } else if (type != SSH2_AGENT_SIGN_RESPONSE) { 479295367Sdes r = SSH_ERR_INVALID_FORMAT; 480295367Sdes goto out; 48165674Skris } 482295367Sdes if ((r = sshbuf_get_string(msg, sigp, &len)) != 0) 483295367Sdes goto out; 484295367Sdes *lenp = len; 485295367Sdes r = 0; 486295367Sdes out: 487295367Sdes if (blob != NULL) { 488295367Sdes explicit_bzero(blob, blen); 489295367Sdes free(blob); 490295367Sdes } 491295367Sdes sshbuf_free(msg); 492295367Sdes return r; 49365674Skris} 49457429Smarkm 49565674Skris/* Encode key for a message to the agent. */ 49657429Smarkm 497295367Sdes#ifdef WITH_SSH1 498295367Sdesstatic int 499295367Sdesssh_encode_identity_rsa1(struct sshbuf *b, RSA *key, const char *comment) 50065674Skris{ 501295367Sdes int r; 502295367Sdes 50365674Skris /* To keep within the protocol: p < q for ssh. in SSL p > q */ 504295367Sdes if ((r = sshbuf_put_u32(b, BN_num_bits(key->n))) != 0 || 505295367Sdes (r = sshbuf_put_bignum1(b, key->n)) != 0 || 506295367Sdes (r = sshbuf_put_bignum1(b, key->e)) != 0 || 507295367Sdes (r = sshbuf_put_bignum1(b, key->d)) != 0 || 508295367Sdes (r = sshbuf_put_bignum1(b, key->iqmp)) != 0 || 509295367Sdes (r = sshbuf_put_bignum1(b, key->q)) != 0 || 510295367Sdes (r = sshbuf_put_bignum1(b, key->p)) != 0 || 511295367Sdes (r = sshbuf_put_cstring(b, comment)) != 0) 512295367Sdes return r; 513295367Sdes return 0; 51465674Skris} 515295367Sdes#endif 51657429Smarkm 517295367Sdesstatic int 518295367Sdesssh_encode_identity_ssh2(struct sshbuf *b, struct sshkey *key, 519295367Sdes const char *comment) 52065674Skris{ 521295367Sdes int r; 522295367Sdes 523295367Sdes if ((r = sshkey_private_serialize(key, b)) != 0 || 524295367Sdes (r = sshbuf_put_cstring(b, comment)) != 0) 525295367Sdes return r; 526295367Sdes return 0; 52757429Smarkm} 52857429Smarkm 529295367Sdesstatic int 530295367Sdesencode_constraints(struct sshbuf *m, u_int life, u_int confirm) 531295367Sdes{ 532295367Sdes int r; 533295367Sdes 534295367Sdes if (life != 0) { 535295367Sdes if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_LIFETIME)) != 0 || 536295367Sdes (r = sshbuf_put_u32(m, life)) != 0) 537295367Sdes goto out; 538295367Sdes } 539295367Sdes if (confirm != 0) { 540295367Sdes if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_CONFIRM)) != 0) 541295367Sdes goto out; 542295367Sdes } 543295367Sdes r = 0; 544295367Sdes out: 545295367Sdes return r; 546295367Sdes} 547295367Sdes 54857429Smarkm/* 549295367Sdes * Adds an identity to the authentication server. 550295367Sdes * This call is intended only for use by ssh-add(1) and like applications. 55157429Smarkm */ 55260576Skrisint 553295367Sdesssh_add_identity_constrained(int sock, struct sshkey *key, const char *comment, 554295367Sdes u_int life, u_int confirm) 55557429Smarkm{ 556295367Sdes struct sshbuf *msg; 557295367Sdes int r, constrained = (life || confirm); 558295367Sdes u_char type; 55957429Smarkm 560295367Sdes if ((msg = sshbuf_new()) == NULL) 561295367Sdes return SSH_ERR_ALLOC_FAIL; 56257429Smarkm 56365674Skris switch (key->type) { 564295367Sdes#ifdef WITH_SSH1 56576262Sgreen case KEY_RSA1: 56698684Sdes type = constrained ? 56798684Sdes SSH_AGENTC_ADD_RSA_ID_CONSTRAINED : 56898684Sdes SSH_AGENTC_ADD_RSA_IDENTITY; 569295367Sdes if ((r = sshbuf_put_u8(msg, type)) != 0 || 570295367Sdes (r = ssh_encode_identity_rsa1(msg, key->rsa, comment)) != 0) 571295367Sdes goto out; 57276262Sgreen break; 573295367Sdes#endif 574295367Sdes#ifdef WITH_OPENSSL 57565674Skris case KEY_RSA: 576204917Sdes case KEY_RSA_CERT: 57765674Skris case KEY_DSA: 578204917Sdes case KEY_DSA_CERT: 579221420Sdes case KEY_ECDSA: 580221420Sdes case KEY_ECDSA_CERT: 581295367Sdes#endif 582262566Sdes case KEY_ED25519: 583262566Sdes case KEY_ED25519_CERT: 58498684Sdes type = constrained ? 58598684Sdes SSH2_AGENTC_ADD_ID_CONSTRAINED : 58698684Sdes SSH2_AGENTC_ADD_IDENTITY; 587295367Sdes if ((r = sshbuf_put_u8(msg, type)) != 0 || 588295367Sdes (r = ssh_encode_identity_ssh2(msg, key, comment)) != 0) 589295367Sdes goto out; 59065674Skris break; 59165674Skris default: 592295367Sdes r = SSH_ERR_INVALID_ARGUMENT; 593295367Sdes goto out; 59457429Smarkm } 595295367Sdes if (constrained && 596295367Sdes (r = encode_constraints(msg, life, confirm)) != 0) 597295367Sdes goto out; 598295367Sdes if ((r = ssh_request_reply(sock, msg, msg)) != 0) 599295367Sdes goto out; 600295367Sdes if ((r = sshbuf_get_u8(msg, &type)) != 0) 601295367Sdes goto out; 602295367Sdes r = decode_reply(type); 603295367Sdes out: 604295367Sdes sshbuf_free(msg); 605295367Sdes return r; 60657429Smarkm} 60757429Smarkm 60857429Smarkm/* 609295367Sdes * Removes an identity from the authentication server. 610295367Sdes * This call is intended only for use by ssh-add(1) and like applications. 61157429Smarkm */ 61260576Skrisint 613295367Sdesssh_remove_identity(int sock, struct sshkey *key) 61457429Smarkm{ 615295367Sdes struct sshbuf *msg; 616295367Sdes int r; 617295367Sdes u_char type, *blob = NULL; 618295367Sdes size_t blen; 61957429Smarkm 620295367Sdes if ((msg = sshbuf_new()) == NULL) 621295367Sdes return SSH_ERR_ALLOC_FAIL; 62257429Smarkm 623295367Sdes#ifdef WITH_SSH1 62476262Sgreen if (key->type == KEY_RSA1) { 625295367Sdes if ((r = sshbuf_put_u8(msg, 626295367Sdes SSH_AGENTC_REMOVE_RSA_IDENTITY)) != 0 || 627295367Sdes (r = sshbuf_put_u32(msg, BN_num_bits(key->rsa->n))) != 0 || 628295367Sdes (r = sshbuf_put_bignum1(msg, key->rsa->e)) != 0 || 629295367Sdes (r = sshbuf_put_bignum1(msg, key->rsa->n)) != 0) 630295367Sdes goto out; 631295367Sdes } else 632295367Sdes#endif 633295367Sdes if (key->type != KEY_UNSPEC) { 634295367Sdes if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) 635295367Sdes goto out; 636295367Sdes if ((r = sshbuf_put_u8(msg, 637295367Sdes SSH2_AGENTC_REMOVE_IDENTITY)) != 0 || 638295367Sdes (r = sshbuf_put_string(msg, blob, blen)) != 0) 639295367Sdes goto out; 64065674Skris } else { 641295367Sdes r = SSH_ERR_INVALID_ARGUMENT; 642295367Sdes goto out; 64357429Smarkm } 644295367Sdes if ((r = ssh_request_reply(sock, msg, msg)) != 0) 645295367Sdes goto out; 646295367Sdes if ((r = sshbuf_get_u8(msg, &type)) != 0) 647295367Sdes goto out; 648295367Sdes r = decode_reply(type); 649295367Sdes out: 650295367Sdes if (blob != NULL) { 651295367Sdes explicit_bzero(blob, blen); 652295367Sdes free(blob); 65357429Smarkm } 654295367Sdes sshbuf_free(msg); 655295367Sdes return r; 65657429Smarkm} 65757429Smarkm 658295367Sdes/* 659295367Sdes * Add/remove an token-based identity from the authentication server. 660295367Sdes * This call is intended only for use by ssh-add(1) and like applications. 661295367Sdes */ 66292559Sdesint 663295367Sdesssh_update_card(int sock, int add, const char *reader_id, const char *pin, 664295367Sdes u_int life, u_int confirm) 66592559Sdes{ 666295367Sdes struct sshbuf *msg; 667295367Sdes int r, constrained = (life || confirm); 668295367Sdes u_char type; 66992559Sdes 670124211Sdes if (add) { 671124211Sdes type = constrained ? 672124211Sdes SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED : 673124211Sdes SSH_AGENTC_ADD_SMARTCARD_KEY; 674124211Sdes } else 675124211Sdes type = SSH_AGENTC_REMOVE_SMARTCARD_KEY; 676124211Sdes 677295367Sdes if ((msg = sshbuf_new()) == NULL) 678295367Sdes return SSH_ERR_ALLOC_FAIL; 679295367Sdes if ((r = sshbuf_put_u8(msg, type)) != 0 || 680295367Sdes (r = sshbuf_put_cstring(msg, reader_id)) != 0 || 681295367Sdes (r = sshbuf_put_cstring(msg, pin)) != 0) 682295367Sdes goto out; 683295367Sdes if (constrained && 684295367Sdes (r = encode_constraints(msg, life, confirm)) != 0) 685295367Sdes goto out; 686295367Sdes if ((r = ssh_request_reply(sock, msg, msg)) != 0) 687295367Sdes goto out; 688295367Sdes if ((r = sshbuf_get_u8(msg, &type)) != 0) 689295367Sdes goto out; 690295367Sdes r = decode_reply(type); 691295367Sdes out: 692295367Sdes sshbuf_free(msg); 693295367Sdes return r; 69492559Sdes} 69592559Sdes 69657429Smarkm/* 697295367Sdes * Removes all identities from the agent. 698295367Sdes * This call is intended only for use by ssh-add(1) and like applications. 69957429Smarkm */ 70060576Skrisint 701295367Sdesssh_remove_all_identities(int sock, int version) 70257429Smarkm{ 703295367Sdes struct sshbuf *msg; 704295367Sdes u_char type = (version == 1) ? 705295367Sdes SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES : 706295367Sdes SSH2_AGENTC_REMOVE_ALL_IDENTITIES; 707295367Sdes int r; 70857429Smarkm 709295367Sdes if ((msg = sshbuf_new()) == NULL) 710295367Sdes return SSH_ERR_ALLOC_FAIL; 711295367Sdes if ((r = sshbuf_put_u8(msg, type)) != 0) 712295367Sdes goto out; 713295367Sdes if ((r = ssh_request_reply(sock, msg, msg)) != 0) 714295367Sdes goto out; 715295367Sdes if ((r = sshbuf_get_u8(msg, &type)) != 0) 716295367Sdes goto out; 717295367Sdes r = decode_reply(type); 718295367Sdes out: 719295367Sdes sshbuf_free(msg); 720295367Sdes return r; 72165674Skris} 722