1323124Sdes/* $OpenBSD: ssh-agent.c,v 1.213 2016/05/02 08:49:03 djm 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 657429Smarkm * The authentication agent program. 765674Skris * 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". 1365674Skris * 1492559Sdes * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 1565674Skris * 1665674Skris * Redistribution and use in source and binary forms, with or without 1765674Skris * modification, are permitted provided that the following conditions 1865674Skris * are met: 1965674Skris * 1. Redistributions of source code must retain the above copyright 2065674Skris * notice, this list of conditions and the following disclaimer. 2165674Skris * 2. Redistributions in binary form must reproduce the above copyright 2265674Skris * notice, this list of conditions and the following disclaimer in the 2365674Skris * documentation and/or other materials provided with the distribution. 2465674Skris * 2565674Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2665674Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2765674Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2865674Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2965674Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 3065674Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3165674Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3265674Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3365674Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3465674Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3557429Smarkm */ 3657429Smarkm 3757429Smarkm#include "includes.h" 38162856Sdes__RCSID("$FreeBSD: stable/10/crypto/openssh/ssh-agent.c 323124 2017-09-01 22:52:18Z des $"); 39162856Sdes 40295367Sdes#include <sys/param.h> /* MIN MAX */ 41162856Sdes#include <sys/types.h> 42162856Sdes#include <sys/param.h> 43162856Sdes#include <sys/resource.h> 44162856Sdes#include <sys/stat.h> 45162856Sdes#include <sys/socket.h> 46162856Sdes#ifdef HAVE_SYS_TIME_H 47162856Sdes# include <sys/time.h> 48162856Sdes#endif 49162856Sdes#ifdef HAVE_SYS_UN_H 50162856Sdes# include <sys/un.h> 51162856Sdes#endif 52106130Sdes#include "openbsd-compat/sys-queue.h" 5357429Smarkm 54295367Sdes#ifdef WITH_OPENSSL 5576262Sgreen#include <openssl/evp.h> 56181111Sdes#include "openbsd-compat/openssl-compat.h" 57295367Sdes#endif 5876262Sgreen 59162856Sdes#include <errno.h> 60162856Sdes#include <fcntl.h> 61295367Sdes#include <limits.h> 62162856Sdes#ifdef HAVE_PATHS_H 63162856Sdes# include <paths.h> 64162856Sdes#endif 65162856Sdes#include <signal.h> 66162856Sdes#include <stdarg.h> 67162856Sdes#include <stdio.h> 68162856Sdes#include <stdlib.h> 69162856Sdes#include <time.h> 70162856Sdes#include <string.h> 71162856Sdes#include <unistd.h> 72295367Sdes#ifdef HAVE_UTIL_H 73295367Sdes# include <util.h> 74295367Sdes#endif 75162856Sdes 76162856Sdes#include "xmalloc.h" 7757429Smarkm#include "ssh.h" 7857429Smarkm#include "rsa.h" 79295367Sdes#include "sshbuf.h" 80295367Sdes#include "sshkey.h" 8165674Skris#include "authfd.h" 8269591Sgreen#include "compat.h" 8376262Sgreen#include "log.h" 84113911Sdes#include "misc.h" 85264377Sdes#include "digest.h" 86295367Sdes#include "ssherr.h" 8757429Smarkm 88204917Sdes#ifdef ENABLE_PKCS11 89204917Sdes#include "ssh-pkcs11.h" 9092559Sdes#endif 9192559Sdes 9292559Sdestypedef enum { 9392559Sdes AUTH_UNUSED, 9492559Sdes AUTH_SOCKET, 9592559Sdes AUTH_CONNECTION 9692559Sdes} sock_type; 9792559Sdes 9857429Smarkmtypedef struct { 9957429Smarkm int fd; 10092559Sdes sock_type type; 101295367Sdes struct sshbuf *input; 102295367Sdes struct sshbuf *output; 103295367Sdes struct sshbuf *request; 10457429Smarkm} SocketEntry; 10557429Smarkm 10676262Sgreenu_int sockets_alloc = 0; 10757429SmarkmSocketEntry *sockets = NULL; 10857429Smarkm 10992559Sdestypedef struct identity { 11092559Sdes TAILQ_ENTRY(identity) next; 111295367Sdes struct sshkey *key; 11257429Smarkm char *comment; 113204917Sdes char *provider; 114255767Sdes time_t death; 115113911Sdes u_int confirm; 11657429Smarkm} Identity; 11757429Smarkm 11865674Skristypedef struct { 11965674Skris int nentries; 12092559Sdes TAILQ_HEAD(idqueue, identity) idlist; 12165674Skris} Idtab; 12257429Smarkm 12365674Skris/* private key table, one per protocol version */ 12465674SkrisIdtab idtable[3]; 12565674Skris 12657429Smarkmint max_fd = 0; 12757429Smarkm 12857429Smarkm/* pid of shell == parent of agent */ 12960576Skrispid_t parent_pid = -1; 130255767Sdestime_t parent_alive_interval = 0; 13157429Smarkm 132295367Sdes/* pid of process for which cleanup_socket is applicable */ 133295367Sdespid_t cleanup_pid = 0; 134295367Sdes 13557429Smarkm/* pathname and directory for AUTH_SOCKET */ 136295367Sdeschar socket_name[PATH_MAX]; 137295367Sdeschar socket_dir[PATH_MAX]; 13857429Smarkm 13998684Sdes/* locking */ 140295367Sdes#define LOCK_SIZE 32 141295367Sdes#define LOCK_SALT_SIZE 16 142295367Sdes#define LOCK_ROUNDS 1 14398684Sdesint locked = 0; 144323124Sdesu_char lock_pwhash[LOCK_SIZE]; 145323124Sdesu_char lock_salt[LOCK_SALT_SIZE]; 14698684Sdes 14757429Smarkmextern char *__progname; 14857429Smarkm 149255767Sdes/* Default lifetime in seconds (0 == forever) */ 150255767Sdesstatic long lifetime = 0; 151113911Sdes 152295367Sdesstatic int fingerprint_hash = SSH_FP_HASH_DEFAULT; 153295367Sdes 154226103Sdes/* 155226103Sdes * Client connection count; incremented in new_socket() and decremented in 156226103Sdes * close_socket(). When it reaches 0, ssh-agent will exit. Since it is 157226103Sdes * normally initialized to 1, it will never reach 0. However, if the -x 158226103Sdes * option is specified, it is initialized to 0 in main(); in that case, 159226103Sdes * ssh-agent will exit as soon as it has had at least one client but no 160226103Sdes * longer has any. 161226103Sdes */ 162226103Sdesstatic int xcount = 1; 163226103Sdes 16492559Sdesstatic void 165106130Sdesclose_socket(SocketEntry *e) 166106130Sdes{ 167226103Sdes int last = 0; 168226103Sdes 169226103Sdes if (e->type == AUTH_CONNECTION) { 170226103Sdes debug("xcount %d -> %d", xcount, xcount - 1); 171226103Sdes if (--xcount == 0) 172226103Sdes last = 1; 173226103Sdes } 174106130Sdes close(e->fd); 175106130Sdes e->fd = -1; 176106130Sdes e->type = AUTH_UNUSED; 177295367Sdes sshbuf_free(e->input); 178295367Sdes sshbuf_free(e->output); 179295367Sdes sshbuf_free(e->request); 180226103Sdes if (last) 181226103Sdes cleanup_exit(0); 182106130Sdes} 183106130Sdes 184106130Sdesstatic void 18565674Skrisidtab_init(void) 18657429Smarkm{ 18765674Skris int i; 18899063Sdes 18992559Sdes for (i = 0; i <=2; i++) { 19092559Sdes TAILQ_INIT(&idtable[i].idlist); 19165674Skris idtable[i].nentries = 0; 19265674Skris } 19365674Skris} 19465674Skris 19565674Skris/* return private key table for requested protocol version */ 19692559Sdesstatic Idtab * 19765674Skrisidtab_lookup(int version) 19865674Skris{ 19965674Skris if (version < 1 || version > 2) 20065674Skris fatal("internal error, bad protocol version %d", version); 20165674Skris return &idtable[version]; 20265674Skris} 20365674Skris 20498684Sdesstatic void 20598684Sdesfree_identity(Identity *id) 20698684Sdes{ 207295367Sdes sshkey_free(id->key); 208255767Sdes free(id->provider); 209255767Sdes free(id->comment); 210255767Sdes free(id); 21198684Sdes} 21298684Sdes 21365674Skris/* return matching private key for given public key */ 21492559Sdesstatic Identity * 215295367Sdeslookup_identity(struct sshkey *key, int version) 21665674Skris{ 21792559Sdes Identity *id; 21892559Sdes 21965674Skris Idtab *tab = idtab_lookup(version); 22092559Sdes TAILQ_FOREACH(id, &tab->idlist, next) { 221295367Sdes if (sshkey_equal(key, id->key)) 22292559Sdes return (id); 22365674Skris } 22492559Sdes return (NULL); 22565674Skris} 22665674Skris 227113911Sdes/* Check confirmation of keysign request */ 228113911Sdesstatic int 229113911Sdesconfirm_key(Identity *id) 230113911Sdes{ 231147005Sdes char *p; 232113911Sdes int ret = -1; 233113911Sdes 234295367Sdes p = sshkey_fingerprint(id->key, fingerprint_hash, SSH_FP_DEFAULT); 235295367Sdes if (p != NULL && 236295367Sdes ask_permission("Allow use of key %s?\nKey fingerprint %s.", 237147005Sdes id->comment, p)) 238147005Sdes ret = 0; 239255767Sdes free(p); 240147005Sdes 241113911Sdes return (ret); 242113911Sdes} 243113911Sdes 244295367Sdesstatic void 245295367Sdessend_status(SocketEntry *e, int success) 246295367Sdes{ 247295367Sdes int r; 248295367Sdes 249295367Sdes if ((r = sshbuf_put_u32(e->output, 1)) != 0 || 250295367Sdes (r = sshbuf_put_u8(e->output, success ? 251295367Sdes SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE)) != 0) 252295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 253295367Sdes} 254295367Sdes 25565674Skris/* send list of supported public keys to 'client' */ 25692559Sdesstatic void 25765674Skrisprocess_request_identities(SocketEntry *e, int version) 25865674Skris{ 25965674Skris Idtab *tab = idtab_lookup(version); 26099063Sdes Identity *id; 261295367Sdes struct sshbuf *msg; 262295367Sdes int r; 26357429Smarkm 264295367Sdes if ((msg = sshbuf_new()) == NULL) 265295367Sdes fatal("%s: sshbuf_new failed", __func__); 266295367Sdes if ((r = sshbuf_put_u8(msg, (version == 1) ? 267295367Sdes SSH_AGENT_RSA_IDENTITIES_ANSWER : 268295367Sdes SSH2_AGENT_IDENTITIES_ANSWER)) != 0 || 269295367Sdes (r = sshbuf_put_u32(msg, tab->nentries)) != 0) 270295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 27192559Sdes TAILQ_FOREACH(id, &tab->idlist, next) { 27276262Sgreen if (id->key->type == KEY_RSA1) { 273295367Sdes#ifdef WITH_SSH1 274295367Sdes if ((r = sshbuf_put_u32(msg, 275295367Sdes BN_num_bits(id->key->rsa->n))) != 0 || 276295367Sdes (r = sshbuf_put_bignum1(msg, 277295367Sdes id->key->rsa->e)) != 0 || 278295367Sdes (r = sshbuf_put_bignum1(msg, 279295367Sdes id->key->rsa->n)) != 0) 280295367Sdes fatal("%s: buffer error: %s", 281295367Sdes __func__, ssh_err(r)); 282295367Sdes#endif 28365674Skris } else { 28476262Sgreen u_char *blob; 285295367Sdes size_t blen; 286295367Sdes 287295367Sdes if ((r = sshkey_to_blob(id->key, &blob, &blen)) != 0) { 288295367Sdes error("%s: sshkey_to_blob: %s", __func__, 289295367Sdes ssh_err(r)); 290295367Sdes continue; 291295367Sdes } 292295367Sdes if ((r = sshbuf_put_string(msg, blob, blen)) != 0) 293295367Sdes fatal("%s: buffer error: %s", 294295367Sdes __func__, ssh_err(r)); 295255767Sdes free(blob); 29665674Skris } 297295367Sdes if ((r = sshbuf_put_cstring(msg, id->comment)) != 0) 298295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 29957429Smarkm } 300295367Sdes if ((r = sshbuf_put_stringb(e->output, msg)) != 0) 301295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 302295367Sdes sshbuf_free(msg); 30357429Smarkm} 30457429Smarkm 305295367Sdes#ifdef WITH_SSH1 30665674Skris/* ssh1 only */ 30792559Sdesstatic void 30865674Skrisprocess_authentication_challenge1(SocketEntry *e) 30957429Smarkm{ 31099063Sdes u_char buf[32], mdbuf[16], session_id[16]; 31199063Sdes u_int response_type; 31299063Sdes BIGNUM *challenge; 31392559Sdes Identity *id; 314295367Sdes int r, len; 315295367Sdes struct sshbuf *msg; 316264377Sdes struct ssh_digest_ctx *md; 317295367Sdes struct sshkey *key; 31857429Smarkm 319295367Sdes if ((msg = sshbuf_new()) == NULL) 320295367Sdes fatal("%s: sshbuf_new failed", __func__); 321295367Sdes if ((key = sshkey_new(KEY_RSA1)) == NULL) 322295367Sdes fatal("%s: sshkey_new failed", __func__); 32392559Sdes if ((challenge = BN_new()) == NULL) 324295367Sdes fatal("%s: BN_new failed", __func__); 32565674Skris 326295367Sdes if ((r = sshbuf_get_u32(e->request, NULL)) != 0 || /* ignored */ 327295367Sdes (r = sshbuf_get_bignum1(e->request, key->rsa->e)) != 0 || 328295367Sdes (r = sshbuf_get_bignum1(e->request, key->rsa->n)) != 0 || 329295367Sdes (r = sshbuf_get_bignum1(e->request, challenge))) 330295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 33157429Smarkm 33265674Skris /* Only protocol 1.1 is supported */ 333295367Sdes if (sshbuf_len(e->request) == 0) 33465674Skris goto failure; 335295367Sdes if ((r = sshbuf_get(e->request, session_id, sizeof(session_id))) != 0 || 336295367Sdes (r = sshbuf_get_u32(e->request, &response_type)) != 0) 337295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 33865674Skris if (response_type != 1) 33965674Skris goto failure; 34057429Smarkm 34192559Sdes id = lookup_identity(key, 1); 342113911Sdes if (id != NULL && (!id->confirm || confirm_key(id) == 0)) { 343295367Sdes struct sshkey *private = id->key; 34465674Skris /* Decrypt the challenge using the private key. */ 345295367Sdes if ((r = rsa_private_decrypt(challenge, challenge, 346295367Sdes private->rsa) != 0)) { 347295367Sdes fatal("%s: rsa_public_encrypt: %s", __func__, 348295367Sdes ssh_err(r)); 349295367Sdes goto failure; /* XXX ? */ 350295367Sdes } 35157429Smarkm 352295367Sdes /* The response is MD5 of decrypted challenge plus session id */ 35365674Skris len = BN_num_bytes(challenge); 35465674Skris if (len <= 0 || len > 32) { 355295367Sdes logit("%s: bad challenge length %d", __func__, len); 35665674Skris goto failure; 35765674Skris } 35865674Skris memset(buf, 0, 32); 35965674Skris BN_bn2bin(challenge, buf + 32 - len); 360264377Sdes if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL || 361264377Sdes ssh_digest_update(md, buf, 32) < 0 || 362264377Sdes ssh_digest_update(md, session_id, 16) < 0 || 363264377Sdes ssh_digest_final(md, mdbuf, sizeof(mdbuf)) < 0) 364264377Sdes fatal("%s: md5 failed", __func__); 365264377Sdes ssh_digest_free(md); 36657429Smarkm 36765674Skris /* Send the response. */ 368295367Sdes if ((r = sshbuf_put_u8(msg, SSH_AGENT_RSA_RESPONSE)) != 0 || 369295367Sdes (r = sshbuf_put(msg, mdbuf, sizeof(mdbuf))) != 0) 370295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 37165674Skris goto send; 37265674Skris } 37357429Smarkm 374295367Sdes failure: 37565674Skris /* Unknown identity or protocol error. Send failure. */ 376295367Sdes if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0) 377295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 378295367Sdes send: 379295367Sdes if ((r = sshbuf_put_stringb(e->output, msg)) != 0) 380295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 381295367Sdes sshkey_free(key); 38265674Skris BN_clear_free(challenge); 383295367Sdes sshbuf_free(msg); 38465674Skris} 385295367Sdes#endif 38665674Skris 387296781Sdesstatic char * 388296781Sdesagent_decode_alg(struct sshkey *key, u_int flags) 389296781Sdes{ 390296781Sdes if (key->type == KEY_RSA) { 391296781Sdes if (flags & SSH_AGENT_RSA_SHA2_256) 392296781Sdes return "rsa-sha2-256"; 393296781Sdes else if (flags & SSH_AGENT_RSA_SHA2_512) 394296781Sdes return "rsa-sha2-512"; 395296781Sdes } 396296781Sdes return NULL; 397296781Sdes} 398296781Sdes 39965674Skris/* ssh2 only */ 40092559Sdesstatic void 40165674Skrisprocess_sign_request2(SocketEntry *e) 40265674Skris{ 40376262Sgreen u_char *blob, *data, *signature = NULL; 404295367Sdes size_t blen, dlen, slen = 0; 405295367Sdes u_int compat = 0, flags; 406295367Sdes int r, ok = -1; 407295367Sdes struct sshbuf *msg; 408295367Sdes struct sshkey *key; 409295367Sdes struct identity *id; 41065674Skris 411295367Sdes if ((msg = sshbuf_new()) == NULL) 412295367Sdes fatal("%s: sshbuf_new failed", __func__); 413295367Sdes if ((r = sshbuf_get_string(e->request, &blob, &blen)) != 0 || 414295367Sdes (r = sshbuf_get_string(e->request, &data, &dlen)) != 0 || 415295367Sdes (r = sshbuf_get_u32(e->request, &flags)) != 0) 416295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 41769591Sgreen if (flags & SSH_AGENT_OLD_SIGNATURE) 418295367Sdes compat = SSH_BUG_SIGBLOB; 419295367Sdes if ((r = sshkey_from_blob(blob, blen, &key)) != 0) { 420296781Sdes error("%s: cannot parse key blob: %s", __func__, ssh_err(r)); 421295367Sdes goto send; 42265674Skris } 423295367Sdes if ((id = lookup_identity(key, 2)) == NULL) { 424295367Sdes verbose("%s: %s key not found", __func__, sshkey_type(key)); 425295367Sdes goto send; 426295367Sdes } 427295367Sdes if (id->confirm && confirm_key(id) != 0) { 428295367Sdes verbose("%s: user refused key", __func__); 429295367Sdes goto send; 430295367Sdes } 431295367Sdes if ((r = sshkey_sign(id->key, &signature, &slen, 432296781Sdes data, dlen, agent_decode_alg(key, flags), compat)) != 0) { 433296781Sdes error("%s: sshkey_sign: %s", __func__, ssh_err(r)); 434295367Sdes goto send; 435295367Sdes } 436295367Sdes /* Success */ 437295367Sdes ok = 0; 438295367Sdes send: 439295367Sdes sshkey_free(key); 44065674Skris if (ok == 0) { 441295367Sdes if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE)) != 0 || 442295367Sdes (r = sshbuf_put_string(msg, signature, slen)) != 0) 443295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 444295367Sdes } else if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0) 445295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 446295367Sdes 447295367Sdes if ((r = sshbuf_put_stringb(e->output, msg)) != 0) 448295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 449295367Sdes 450295367Sdes sshbuf_free(msg); 451255767Sdes free(data); 452255767Sdes free(blob); 453255767Sdes free(signature); 45457429Smarkm} 45557429Smarkm 45665674Skris/* shared */ 45792559Sdesstatic void 45865674Skrisprocess_remove_identity(SocketEntry *e, int version) 45957429Smarkm{ 460295367Sdes size_t blen; 461295367Sdes int r, success = 0; 462295367Sdes struct sshkey *key = NULL; 46376262Sgreen u_char *blob; 464295367Sdes#ifdef WITH_SSH1 465295367Sdes u_int bits; 466295367Sdes#endif /* WITH_SSH1 */ 46757429Smarkm 46892559Sdes switch (version) { 469295367Sdes#ifdef WITH_SSH1 47065674Skris case 1: 471295367Sdes if ((key = sshkey_new(KEY_RSA1)) == NULL) { 472295367Sdes error("%s: sshkey_new failed", __func__); 473295367Sdes return; 474295367Sdes } 475295367Sdes if ((r = sshbuf_get_u32(e->request, &bits)) != 0 || 476295367Sdes (r = sshbuf_get_bignum1(e->request, key->rsa->e)) != 0 || 477295367Sdes (r = sshbuf_get_bignum1(e->request, key->rsa->n)) != 0) 478295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 47957429Smarkm 480295367Sdes if (bits != sshkey_size(key)) 481295367Sdes logit("Warning: identity keysize mismatch: " 482295367Sdes "actual %u, announced %u", 483295367Sdes sshkey_size(key), bits); 48465674Skris break; 485295367Sdes#endif /* WITH_SSH1 */ 48665674Skris case 2: 487295367Sdes if ((r = sshbuf_get_string(e->request, &blob, &blen)) != 0) 488295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 489295367Sdes if ((r = sshkey_from_blob(blob, blen, &key)) != 0) 490295367Sdes error("%s: sshkey_from_blob failed: %s", 491295367Sdes __func__, ssh_err(r)); 492255767Sdes free(blob); 49365674Skris break; 49465674Skris } 49565674Skris if (key != NULL) { 49692559Sdes Identity *id = lookup_identity(key, version); 49792559Sdes if (id != NULL) { 49857429Smarkm /* 49957429Smarkm * We have this key. Free the old key. Since we 500157019Sdes * don't want to leave empty slots in the middle of 50176262Sgreen * the array, we actually free the key there and move 50276262Sgreen * all the entries between the empty slot and the end 50376262Sgreen * of the array. 50457429Smarkm */ 50565674Skris Idtab *tab = idtab_lookup(version); 50676262Sgreen if (tab->nentries < 1) 50776262Sgreen fatal("process_remove_identity: " 50876262Sgreen "internal error: tab->nentries %d", 50976262Sgreen tab->nentries); 51092559Sdes TAILQ_REMOVE(&tab->idlist, id, next); 51192559Sdes free_identity(id); 51265674Skris tab->nentries--; 51365674Skris success = 1; 51457429Smarkm } 515295367Sdes sshkey_free(key); 51665674Skris } 517295367Sdes send_status(e, success); 51857429Smarkm} 51957429Smarkm 52092559Sdesstatic void 52165674Skrisprocess_remove_all_identities(SocketEntry *e, int version) 52257429Smarkm{ 52365674Skris Idtab *tab = idtab_lookup(version); 52492559Sdes Identity *id; 52557429Smarkm 52657429Smarkm /* Loop over all identities and clear the keys. */ 52792559Sdes for (id = TAILQ_FIRST(&tab->idlist); id; 52892559Sdes id = TAILQ_FIRST(&tab->idlist)) { 52992559Sdes TAILQ_REMOVE(&tab->idlist, id, next); 53092559Sdes free_identity(id); 53157429Smarkm } 53257429Smarkm 53357429Smarkm /* Mark that there are no identities. */ 53465674Skris tab->nentries = 0; 53557429Smarkm 53657429Smarkm /* Send success. */ 537295367Sdes send_status(e, 1); 53857429Smarkm} 53957429Smarkm 540181111Sdes/* removes expired keys and returns number of seconds until the next expiry */ 541255767Sdesstatic time_t 54298684Sdesreaper(void) 54398684Sdes{ 544255767Sdes time_t deadline = 0, now = monotime(); 54598684Sdes Identity *id, *nxt; 54698684Sdes int version; 54799063Sdes Idtab *tab; 54898684Sdes 54998684Sdes for (version = 1; version < 3; version++) { 55098684Sdes tab = idtab_lookup(version); 55198684Sdes for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) { 55298684Sdes nxt = TAILQ_NEXT(id, next); 553181111Sdes if (id->death == 0) 554181111Sdes continue; 555181111Sdes if (now >= id->death) { 556181111Sdes debug("expiring key '%s'", id->comment); 55798684Sdes TAILQ_REMOVE(&tab->idlist, id, next); 55898684Sdes free_identity(id); 55998684Sdes tab->nentries--; 560181111Sdes } else 561181111Sdes deadline = (deadline == 0) ? id->death : 562181111Sdes MIN(deadline, id->death); 56398684Sdes } 56498684Sdes } 565181111Sdes if (deadline == 0 || deadline <= now) 566181111Sdes return 0; 567181111Sdes else 568181111Sdes return (deadline - now); 56998684Sdes} 57098684Sdes 571295367Sdes/* 572295367Sdes * XXX this and the corresponding serialisation function probably belongs 573295367Sdes * in key.c 574295367Sdes */ 575295367Sdes#ifdef WITH_SSH1 576295367Sdesstatic int 577295367Sdesagent_decode_rsa1(struct sshbuf *m, struct sshkey **kp) 578295367Sdes{ 579295367Sdes struct sshkey *k = NULL; 580295367Sdes int r = SSH_ERR_INTERNAL_ERROR; 581295367Sdes 582295367Sdes *kp = NULL; 583295367Sdes if ((k = sshkey_new_private(KEY_RSA1)) == NULL) 584295367Sdes return SSH_ERR_ALLOC_FAIL; 585295367Sdes 586295367Sdes if ((r = sshbuf_get_u32(m, NULL)) != 0 || /* ignored */ 587295367Sdes (r = sshbuf_get_bignum1(m, k->rsa->n)) != 0 || 588295367Sdes (r = sshbuf_get_bignum1(m, k->rsa->e)) != 0 || 589295367Sdes (r = sshbuf_get_bignum1(m, k->rsa->d)) != 0 || 590295367Sdes (r = sshbuf_get_bignum1(m, k->rsa->iqmp)) != 0 || 591295367Sdes /* SSH1 and SSL have p and q swapped */ 592295367Sdes (r = sshbuf_get_bignum1(m, k->rsa->q)) != 0 || /* p */ 593295367Sdes (r = sshbuf_get_bignum1(m, k->rsa->p)) != 0) /* q */ 594295367Sdes goto out; 595295367Sdes 596295367Sdes /* Generate additional parameters */ 597295367Sdes if ((r = rsa_generate_additional_parameters(k->rsa)) != 0) 598295367Sdes goto out; 599295367Sdes /* enable blinding */ 600295367Sdes if (RSA_blinding_on(k->rsa, NULL) != 1) { 601295367Sdes r = SSH_ERR_LIBCRYPTO_ERROR; 602295367Sdes goto out; 603295367Sdes } 604295367Sdes 605295367Sdes r = 0; /* success */ 606295367Sdes out: 607295367Sdes if (r == 0) 608295367Sdes *kp = k; 609295367Sdes else 610295367Sdes sshkey_free(k); 611295367Sdes return r; 612295367Sdes} 613295367Sdes#endif /* WITH_SSH1 */ 614295367Sdes 61598684Sdesstatic void 61665674Skrisprocess_add_identity(SocketEntry *e, int version) 61757429Smarkm{ 61899063Sdes Idtab *tab = idtab_lookup(version); 619181111Sdes Identity *id; 620295367Sdes int success = 0, confirm = 0; 621295367Sdes u_int seconds; 622295367Sdes char *comment = NULL; 623255767Sdes time_t death = 0; 624295367Sdes struct sshkey *k = NULL; 625295367Sdes u_char ctype; 626295367Sdes int r = SSH_ERR_INTERNAL_ERROR; 62757429Smarkm 62865674Skris switch (version) { 629295367Sdes#ifdef WITH_SSH1 63065674Skris case 1: 631295367Sdes r = agent_decode_rsa1(e->request, &k); 632113911Sdes break; 633295367Sdes#endif /* WITH_SSH1 */ 634262566Sdes case 2: 635295367Sdes r = sshkey_private_deserialize(e->request, &k); 636262566Sdes break; 637113911Sdes } 638295367Sdes if (r != 0 || k == NULL || 639295367Sdes (r = sshbuf_get_cstring(e->request, &comment, NULL)) != 0) { 640295367Sdes error("%s: decode private key: %s", __func__, ssh_err(r)); 641295367Sdes goto err; 64265674Skris } 643295367Sdes 644295367Sdes while (sshbuf_len(e->request)) { 645295367Sdes if ((r = sshbuf_get_u8(e->request, &ctype)) != 0) { 646295367Sdes error("%s: buffer error: %s", __func__, ssh_err(r)); 647295367Sdes goto err; 648295367Sdes } 649295367Sdes switch (ctype) { 65098684Sdes case SSH_AGENT_CONSTRAIN_LIFETIME: 651295367Sdes if ((r = sshbuf_get_u32(e->request, &seconds)) != 0) { 652295367Sdes error("%s: bad lifetime constraint: %s", 653295367Sdes __func__, ssh_err(r)); 654295367Sdes goto err; 655295367Sdes } 656295367Sdes death = monotime() + seconds; 65798684Sdes break; 658113911Sdes case SSH_AGENT_CONSTRAIN_CONFIRM: 659113911Sdes confirm = 1; 660113911Sdes break; 66198684Sdes default: 662295367Sdes error("%s: Unknown constraint %d", __func__, ctype); 663295367Sdes err: 664295367Sdes sshbuf_reset(e->request); 665255767Sdes free(comment); 666295367Sdes sshkey_free(k); 667181111Sdes goto send; 66898684Sdes } 66998684Sdes } 670295367Sdes 671181111Sdes success = 1; 672113911Sdes if (lifetime && !death) 673255767Sdes death = monotime() + lifetime; 674181111Sdes if ((id = lookup_identity(k, version)) == NULL) { 675204917Sdes id = xcalloc(1, sizeof(Identity)); 67692559Sdes id->key = k; 67792559Sdes TAILQ_INSERT_TAIL(&tab->idlist, id, next); 67865674Skris /* Increment the number of identities. */ 67965674Skris tab->nentries++; 68065674Skris } else { 681295367Sdes sshkey_free(k); 682255767Sdes free(id->comment); 68365674Skris } 684181111Sdes id->comment = comment; 685181111Sdes id->death = death; 686181111Sdes id->confirm = confirm; 68765674Skrissend: 688295367Sdes send_status(e, success); 68957429Smarkm} 69057429Smarkm 69198684Sdes/* XXX todo: encrypt sensitive data with passphrase */ 69298684Sdesstatic void 69398684Sdesprocess_lock_agent(SocketEntry *e, int lock) 69498684Sdes{ 695295367Sdes int r, success = 0, delay; 696323124Sdes char *passwd; 697323124Sdes u_char passwdhash[LOCK_SIZE]; 698295367Sdes static u_int fail_count = 0; 699295367Sdes size_t pwlen; 70092559Sdes 701295367Sdes if ((r = sshbuf_get_cstring(e->request, &passwd, &pwlen)) != 0) 702295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 703295367Sdes if (pwlen == 0) { 704295367Sdes debug("empty password not supported"); 705295367Sdes } else if (locked && !lock) { 706295367Sdes if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt), 707295367Sdes passwdhash, sizeof(passwdhash), LOCK_ROUNDS) < 0) 708295367Sdes fatal("bcrypt_pbkdf"); 709323124Sdes if (timingsafe_bcmp(passwdhash, lock_pwhash, LOCK_SIZE) == 0) { 710295367Sdes debug("agent unlocked"); 711295367Sdes locked = 0; 712295367Sdes fail_count = 0; 713323124Sdes explicit_bzero(lock_pwhash, sizeof(lock_pwhash)); 714295367Sdes success = 1; 715295367Sdes } else { 716295367Sdes /* delay in 0.1s increments up to 10s */ 717295367Sdes if (fail_count < 100) 718295367Sdes fail_count++; 719295367Sdes delay = 100000 * fail_count; 720295367Sdes debug("unlock failed, delaying %0.1lf seconds", 721295367Sdes (double)delay/1000000); 722295367Sdes usleep(delay); 723295367Sdes } 724295367Sdes explicit_bzero(passwdhash, sizeof(passwdhash)); 72598684Sdes } else if (!locked && lock) { 726295367Sdes debug("agent locked"); 72798684Sdes locked = 1; 728295367Sdes arc4random_buf(lock_salt, sizeof(lock_salt)); 729295367Sdes if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt), 730323124Sdes lock_pwhash, sizeof(lock_pwhash), LOCK_ROUNDS) < 0) 731295367Sdes fatal("bcrypt_pbkdf"); 73298684Sdes success = 1; 73398684Sdes } 734295367Sdes explicit_bzero(passwd, pwlen); 735255767Sdes free(passwd); 736295367Sdes send_status(e, success); 73798684Sdes} 73898684Sdes 73998684Sdesstatic void 74098684Sdesno_identities(SocketEntry *e, u_int type) 74198684Sdes{ 742295367Sdes struct sshbuf *msg; 743295367Sdes int r; 74498684Sdes 745295367Sdes if ((msg = sshbuf_new()) == NULL) 746295367Sdes fatal("%s: sshbuf_new failed", __func__); 747295367Sdes if ((r = sshbuf_put_u8(msg, 74898684Sdes (type == SSH_AGENTC_REQUEST_RSA_IDENTITIES) ? 749295367Sdes SSH_AGENT_RSA_IDENTITIES_ANSWER : 750295367Sdes SSH2_AGENT_IDENTITIES_ANSWER)) != 0 || 751295367Sdes (r = sshbuf_put_u32(msg, 0)) != 0 || 752295367Sdes (r = sshbuf_put_stringb(e->output, msg)) != 0) 753295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 754295367Sdes sshbuf_free(msg); 75598684Sdes} 75698684Sdes 757204917Sdes#ifdef ENABLE_PKCS11 75892559Sdesstatic void 759181111Sdesprocess_add_smartcard_key(SocketEntry *e) 76092559Sdes{ 761323124Sdes char *provider = NULL, *pin; 762295367Sdes int r, i, version, count = 0, success = 0, confirm = 0; 763295367Sdes u_int seconds; 764255767Sdes time_t death = 0; 765295367Sdes u_char type; 766295367Sdes struct sshkey **keys = NULL, *k; 76798684Sdes Identity *id; 76892559Sdes Idtab *tab; 76992559Sdes 770295367Sdes if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 || 771295367Sdes (r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) 772295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 773124211Sdes 774295367Sdes while (sshbuf_len(e->request)) { 775295367Sdes if ((r = sshbuf_get_u8(e->request, &type)) != 0) 776295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 777295367Sdes switch (type) { 778124211Sdes case SSH_AGENT_CONSTRAIN_LIFETIME: 779295367Sdes if ((r = sshbuf_get_u32(e->request, &seconds)) != 0) 780295367Sdes fatal("%s: buffer error: %s", 781295367Sdes __func__, ssh_err(r)); 782295367Sdes death = monotime() + seconds; 783124211Sdes break; 784124211Sdes case SSH_AGENT_CONSTRAIN_CONFIRM: 785124211Sdes confirm = 1; 786124211Sdes break; 787124211Sdes default: 788181111Sdes error("process_add_smartcard_key: " 789181111Sdes "Unknown constraint type %d", type); 790181111Sdes goto send; 791124211Sdes } 792124211Sdes } 793124211Sdes if (lifetime && !death) 794255767Sdes death = monotime() + lifetime; 795124211Sdes 796323124Sdes count = pkcs11_add_provider(provider, pin, &keys); 797204917Sdes for (i = 0; i < count; i++) { 79898684Sdes k = keys[i]; 79998684Sdes version = k->type == KEY_RSA1 ? 1 : 2; 80098684Sdes tab = idtab_lookup(version); 80198684Sdes if (lookup_identity(k, version) == NULL) { 802204917Sdes id = xcalloc(1, sizeof(Identity)); 80398684Sdes id->key = k; 804323124Sdes id->provider = xstrdup(provider); 805323124Sdes id->comment = xstrdup(provider); /* XXX */ 806124211Sdes id->death = death; 807124211Sdes id->confirm = confirm; 80898684Sdes TAILQ_INSERT_TAIL(&tab->idlist, id, next); 80998684Sdes tab->nentries++; 81098684Sdes success = 1; 81198684Sdes } else { 812295367Sdes sshkey_free(k); 81398684Sdes } 81498684Sdes keys[i] = NULL; 81592559Sdes } 81692559Sdessend: 817255767Sdes free(pin); 818255767Sdes free(provider); 819255767Sdes free(keys); 820295367Sdes send_status(e, success); 82192559Sdes} 82292559Sdes 82392559Sdesstatic void 82492559Sdesprocess_remove_smartcard_key(SocketEntry *e) 82592559Sdes{ 826204917Sdes char *provider = NULL, *pin = NULL; 827295367Sdes int r, version, success = 0; 828204917Sdes Identity *id, *nxt; 82998684Sdes Idtab *tab; 83092559Sdes 831295367Sdes if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 || 832295367Sdes (r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) 833295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 834255767Sdes free(pin); 83592559Sdes 836204917Sdes for (version = 1; version < 3; version++) { 837204917Sdes tab = idtab_lookup(version); 838204917Sdes for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) { 839204917Sdes nxt = TAILQ_NEXT(id, next); 840262566Sdes /* Skip file--based keys */ 841262566Sdes if (id->provider == NULL) 842262566Sdes continue; 843204917Sdes if (!strcmp(provider, id->provider)) { 844204917Sdes TAILQ_REMOVE(&tab->idlist, id, next); 845204917Sdes free_identity(id); 846204917Sdes tab->nentries--; 847204917Sdes } 84892559Sdes } 84992559Sdes } 850204917Sdes if (pkcs11_del_provider(provider) == 0) 851204917Sdes success = 1; 852204917Sdes else 853204917Sdes error("process_remove_smartcard_key:" 854204917Sdes " pkcs11_del_provider failed"); 855255767Sdes free(provider); 856295367Sdes send_status(e, success); 85792559Sdes} 858204917Sdes#endif /* ENABLE_PKCS11 */ 85992559Sdes 86065674Skris/* dispatch incoming messages */ 86165674Skris 86292559Sdesstatic void 86357429Smarkmprocess_message(SocketEntry *e) 86457429Smarkm{ 865295367Sdes u_int msg_len; 866295367Sdes u_char type; 867295367Sdes const u_char *cp; 868295367Sdes int r; 86998684Sdes 870295367Sdes if (sshbuf_len(e->input) < 5) 87157429Smarkm return; /* Incomplete message. */ 872295367Sdes cp = sshbuf_ptr(e->input); 873295367Sdes msg_len = PEEK_U32(cp); 87457429Smarkm if (msg_len > 256 * 1024) { 875106130Sdes close_socket(e); 87657429Smarkm return; 87757429Smarkm } 878295367Sdes if (sshbuf_len(e->input) < msg_len + 4) 87957429Smarkm return; 88098684Sdes 88198684Sdes /* move the current input to e->request */ 882295367Sdes sshbuf_reset(e->request); 883295367Sdes if ((r = sshbuf_get_stringb(e->input, e->request)) != 0 || 884295367Sdes (r = sshbuf_get_u8(e->request, &type)) != 0) 885295367Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 88657429Smarkm 88798684Sdes /* check wheter agent is locked */ 88898684Sdes if (locked && type != SSH_AGENTC_UNLOCK) { 889295367Sdes sshbuf_reset(e->request); 89098684Sdes switch (type) { 89198684Sdes case SSH_AGENTC_REQUEST_RSA_IDENTITIES: 89298684Sdes case SSH2_AGENTC_REQUEST_IDENTITIES: 89398684Sdes /* send empty lists */ 89498684Sdes no_identities(e, type); 89598684Sdes break; 89698684Sdes default: 89798684Sdes /* send a fail message for all other request types */ 898295367Sdes send_status(e, 0); 89998684Sdes } 90098684Sdes return; 90198684Sdes } 90298684Sdes 90392559Sdes debug("type %d", type); 90457429Smarkm switch (type) { 90598684Sdes case SSH_AGENTC_LOCK: 90698684Sdes case SSH_AGENTC_UNLOCK: 90798684Sdes process_lock_agent(e, type == SSH_AGENTC_LOCK); 90898684Sdes break; 909295367Sdes#ifdef WITH_SSH1 91065674Skris /* ssh1 */ 91165674Skris case SSH_AGENTC_RSA_CHALLENGE: 91265674Skris process_authentication_challenge1(e); 91365674Skris break; 91457429Smarkm case SSH_AGENTC_REQUEST_RSA_IDENTITIES: 91565674Skris process_request_identities(e, 1); 91657429Smarkm break; 91757429Smarkm case SSH_AGENTC_ADD_RSA_IDENTITY: 91898684Sdes case SSH_AGENTC_ADD_RSA_ID_CONSTRAINED: 91965674Skris process_add_identity(e, 1); 92057429Smarkm break; 92157429Smarkm case SSH_AGENTC_REMOVE_RSA_IDENTITY: 92265674Skris process_remove_identity(e, 1); 92357429Smarkm break; 924295367Sdes#endif 92557429Smarkm case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: 926295367Sdes process_remove_all_identities(e, 1); /* safe for !WITH_SSH1 */ 92757429Smarkm break; 92865674Skris /* ssh2 */ 92965674Skris case SSH2_AGENTC_SIGN_REQUEST: 93065674Skris process_sign_request2(e); 93165674Skris break; 93265674Skris case SSH2_AGENTC_REQUEST_IDENTITIES: 93365674Skris process_request_identities(e, 2); 93465674Skris break; 93565674Skris case SSH2_AGENTC_ADD_IDENTITY: 93698684Sdes case SSH2_AGENTC_ADD_ID_CONSTRAINED: 93765674Skris process_add_identity(e, 2); 93865674Skris break; 93965674Skris case SSH2_AGENTC_REMOVE_IDENTITY: 94065674Skris process_remove_identity(e, 2); 94165674Skris break; 94265674Skris case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: 94365674Skris process_remove_all_identities(e, 2); 94465674Skris break; 945204917Sdes#ifdef ENABLE_PKCS11 94692559Sdes case SSH_AGENTC_ADD_SMARTCARD_KEY: 947124211Sdes case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED: 94892559Sdes process_add_smartcard_key(e); 94992559Sdes break; 95092559Sdes case SSH_AGENTC_REMOVE_SMARTCARD_KEY: 95192559Sdes process_remove_smartcard_key(e); 95292559Sdes break; 953204917Sdes#endif /* ENABLE_PKCS11 */ 95457429Smarkm default: 95557429Smarkm /* Unknown message. Respond with failure. */ 95657429Smarkm error("Unknown message %d", type); 957295367Sdes sshbuf_reset(e->request); 958295367Sdes send_status(e, 0); 95957429Smarkm break; 96057429Smarkm } 96157429Smarkm} 96257429Smarkm 96392559Sdesstatic void 96492559Sdesnew_socket(sock_type type, int fd) 96557429Smarkm{ 966120161Snectar u_int i, old_alloc, new_alloc; 96799063Sdes 968226103Sdes if (type == AUTH_CONNECTION) { 969226103Sdes debug("xcount %d -> %d", xcount, xcount + 1); 970226103Sdes ++xcount; 971226103Sdes } 972137019Sdes set_nonblock(fd); 97357429Smarkm 97457429Smarkm if (fd > max_fd) 97557429Smarkm max_fd = fd; 97657429Smarkm 97757429Smarkm for (i = 0; i < sockets_alloc; i++) 97857429Smarkm if (sockets[i].type == AUTH_UNUSED) { 97957429Smarkm sockets[i].fd = fd; 980295367Sdes if ((sockets[i].input = sshbuf_new()) == NULL) 981295367Sdes fatal("%s: sshbuf_new failed", __func__); 982295367Sdes if ((sockets[i].output = sshbuf_new()) == NULL) 983295367Sdes fatal("%s: sshbuf_new failed", __func__); 984295367Sdes if ((sockets[i].request = sshbuf_new()) == NULL) 985295367Sdes fatal("%s: sshbuf_new failed", __func__); 986120161Snectar sockets[i].type = type; 98757429Smarkm return; 98857429Smarkm } 98957429Smarkm old_alloc = sockets_alloc; 990120161Snectar new_alloc = sockets_alloc + 10; 991295367Sdes sockets = xreallocarray(sockets, new_alloc, sizeof(sockets[0])); 992120161Snectar for (i = old_alloc; i < new_alloc; i++) 99357429Smarkm sockets[i].type = AUTH_UNUSED; 994120161Snectar sockets_alloc = new_alloc; 99557429Smarkm sockets[old_alloc].fd = fd; 996295367Sdes if ((sockets[old_alloc].input = sshbuf_new()) == NULL) 997295367Sdes fatal("%s: sshbuf_new failed", __func__); 998295367Sdes if ((sockets[old_alloc].output = sshbuf_new()) == NULL) 999295367Sdes fatal("%s: sshbuf_new failed", __func__); 1000295367Sdes if ((sockets[old_alloc].request = sshbuf_new()) == NULL) 1001295367Sdes fatal("%s: sshbuf_new failed", __func__); 1002120161Snectar sockets[old_alloc].type = type; 100357429Smarkm} 100457429Smarkm 100592559Sdesstatic int 1006181111Sdesprepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl, u_int *nallocp, 1007181111Sdes struct timeval **tvpp) 100857429Smarkm{ 1009255767Sdes u_int i, sz; 101076262Sgreen int n = 0; 1011181111Sdes static struct timeval tv; 1012255767Sdes time_t deadline; 101376262Sgreen 101476262Sgreen for (i = 0; i < sockets_alloc; i++) { 101557429Smarkm switch (sockets[i].type) { 101657429Smarkm case AUTH_SOCKET: 101757429Smarkm case AUTH_CONNECTION: 101876262Sgreen n = MAX(n, sockets[i].fd); 101957429Smarkm break; 102057429Smarkm case AUTH_UNUSED: 102157429Smarkm break; 102257429Smarkm default: 102357429Smarkm fatal("Unknown socket type %d", sockets[i].type); 102457429Smarkm break; 102557429Smarkm } 102676262Sgreen } 102776262Sgreen 102876262Sgreen sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); 102992559Sdes if (*fdrp == NULL || sz > *nallocp) { 1030255767Sdes free(*fdrp); 1031255767Sdes free(*fdwp); 103276262Sgreen *fdrp = xmalloc(sz); 103376262Sgreen *fdwp = xmalloc(sz); 103492559Sdes *nallocp = sz; 103576262Sgreen } 103692559Sdes if (n < *fdl) 103792559Sdes debug("XXX shrink: %d < %d", n, *fdl); 103892559Sdes *fdl = n; 103976262Sgreen memset(*fdrp, 0, sz); 104076262Sgreen memset(*fdwp, 0, sz); 104176262Sgreen 104276262Sgreen for (i = 0; i < sockets_alloc; i++) { 104376262Sgreen switch (sockets[i].type) { 104476262Sgreen case AUTH_SOCKET: 104576262Sgreen case AUTH_CONNECTION: 104676262Sgreen FD_SET(sockets[i].fd, *fdrp); 1047295367Sdes if (sshbuf_len(sockets[i].output) > 0) 104876262Sgreen FD_SET(sockets[i].fd, *fdwp); 104976262Sgreen break; 105076262Sgreen default: 105176262Sgreen break; 105276262Sgreen } 105376262Sgreen } 1054181111Sdes deadline = reaper(); 1055181111Sdes if (parent_alive_interval != 0) 1056181111Sdes deadline = (deadline == 0) ? parent_alive_interval : 1057181111Sdes MIN(deadline, parent_alive_interval); 1058181111Sdes if (deadline == 0) { 1059181111Sdes *tvpp = NULL; 1060181111Sdes } else { 1061181111Sdes tv.tv_sec = deadline; 1062181111Sdes tv.tv_usec = 0; 1063181111Sdes *tvpp = &tv; 1064181111Sdes } 106576262Sgreen return (1); 106657429Smarkm} 106757429Smarkm 106892559Sdesstatic void 106957429Smarkmafter_select(fd_set *readset, fd_set *writeset) 107057429Smarkm{ 107199063Sdes struct sockaddr_un sunaddr; 107258585Skris socklen_t slen; 107357429Smarkm char buf[1024]; 1074295367Sdes int len, sock, r; 1075204917Sdes u_int i, orig_alloc; 1076106130Sdes uid_t euid; 1077106130Sdes gid_t egid; 107857429Smarkm 1079204917Sdes for (i = 0, orig_alloc = sockets_alloc; i < orig_alloc; i++) 108057429Smarkm switch (sockets[i].type) { 108157429Smarkm case AUTH_UNUSED: 108257429Smarkm break; 108357429Smarkm case AUTH_SOCKET: 108457429Smarkm if (FD_ISSET(sockets[i].fd, readset)) { 108558585Skris slen = sizeof(sunaddr); 108676262Sgreen sock = accept(sockets[i].fd, 1087162856Sdes (struct sockaddr *)&sunaddr, &slen); 108857429Smarkm if (sock < 0) { 108992559Sdes error("accept from AUTH_SOCKET: %s", 109092559Sdes strerror(errno)); 109157429Smarkm break; 109257429Smarkm } 1093106130Sdes if (getpeereid(sock, &euid, &egid) < 0) { 1094106130Sdes error("getpeereid %d failed: %s", 1095106130Sdes sock, strerror(errno)); 1096106130Sdes close(sock); 1097106130Sdes break; 1098106130Sdes } 1099106130Sdes if ((euid != 0) && (getuid() != euid)) { 1100106130Sdes error("uid mismatch: " 1101106130Sdes "peer euid %u != uid %u", 1102106130Sdes (u_int) euid, (u_int) getuid()); 1103106130Sdes close(sock); 1104106130Sdes break; 1105106130Sdes } 110657429Smarkm new_socket(AUTH_CONNECTION, sock); 110757429Smarkm } 110857429Smarkm break; 110957429Smarkm case AUTH_CONNECTION: 1110295367Sdes if (sshbuf_len(sockets[i].output) > 0 && 111157429Smarkm FD_ISSET(sockets[i].fd, writeset)) { 1112204917Sdes len = write(sockets[i].fd, 1113295367Sdes sshbuf_ptr(sockets[i].output), 1114295367Sdes sshbuf_len(sockets[i].output)); 1115204917Sdes if (len == -1 && (errno == EAGAIN || 1116204917Sdes errno == EWOULDBLOCK || 1117204917Sdes errno == EINTR)) 1118204917Sdes continue; 111957429Smarkm if (len <= 0) { 1120106130Sdes close_socket(&sockets[i]); 112157429Smarkm break; 112257429Smarkm } 1123295367Sdes if ((r = sshbuf_consume(sockets[i].output, 1124295367Sdes len)) != 0) 1125295367Sdes fatal("%s: buffer error: %s", 1126295367Sdes __func__, ssh_err(r)); 112757429Smarkm } 112857429Smarkm if (FD_ISSET(sockets[i].fd, readset)) { 1129204917Sdes len = read(sockets[i].fd, buf, sizeof(buf)); 1130204917Sdes if (len == -1 && (errno == EAGAIN || 1131204917Sdes errno == EWOULDBLOCK || 1132204917Sdes errno == EINTR)) 1133204917Sdes continue; 113457429Smarkm if (len <= 0) { 1135106130Sdes close_socket(&sockets[i]); 113657429Smarkm break; 113757429Smarkm } 1138295367Sdes if ((r = sshbuf_put(sockets[i].input, 1139295367Sdes buf, len)) != 0) 1140295367Sdes fatal("%s: buffer error: %s", 1141295367Sdes __func__, ssh_err(r)); 1142295367Sdes explicit_bzero(buf, sizeof(buf)); 114357429Smarkm process_message(&sockets[i]); 114457429Smarkm } 114557429Smarkm break; 114657429Smarkm default: 114757429Smarkm fatal("Unknown type %d", sockets[i].type); 114857429Smarkm } 114957429Smarkm} 115057429Smarkm 115192559Sdesstatic void 1152126277Sdescleanup_socket(void) 115357429Smarkm{ 1154295367Sdes if (cleanup_pid != 0 && getpid() != cleanup_pid) 1155295367Sdes return; 1156295367Sdes debug("%s: cleanup", __func__); 115776262Sgreen if (socket_name[0]) 115876262Sgreen unlink(socket_name); 115976262Sgreen if (socket_dir[0]) 116076262Sgreen rmdir(socket_dir); 116157429Smarkm} 116257429Smarkm 1163126277Sdesvoid 116457429Smarkmcleanup_exit(int i) 116557429Smarkm{ 1166126277Sdes cleanup_socket(); 1167126277Sdes _exit(i); 116857429Smarkm} 116957429Smarkm 1170162856Sdes/*ARGSUSED*/ 117192559Sdesstatic void 117276262Sgreencleanup_handler(int sig) 117357429Smarkm{ 1174126277Sdes cleanup_socket(); 1175204917Sdes#ifdef ENABLE_PKCS11 1176204917Sdes pkcs11_terminate(); 1177204917Sdes#endif 117876262Sgreen _exit(2); 117976262Sgreen} 118076262Sgreen 118192559Sdesstatic void 1182181111Sdescheck_parent_exists(void) 118392559Sdes{ 1184226046Sdes /* 1185226046Sdes * If our parent has exited then getppid() will return (pid_t)1, 1186226046Sdes * so testing for that should be safe. 1187226046Sdes */ 1188226046Sdes if (parent_pid != -1 && getppid() != parent_pid) { 118992559Sdes /* printf("Parent has died - Authentication agent exiting.\n"); */ 1190181111Sdes cleanup_socket(); 1191181111Sdes _exit(2); 119292559Sdes } 119392559Sdes} 119492559Sdes 119592559Sdesstatic void 119676262Sgreenusage(void) 119776262Sgreen{ 1198295367Sdes fprintf(stderr, 1199323124Sdes "usage: ssh-agent [-c | -s] [-Ddx] [-a bind_address] [-E fingerprint_hash]\n" 1200323124Sdes " [-t life] [command [arg ...]]\n" 1201295367Sdes " ssh-agent [-c | -s] -k\n"); 1202226103Sdes fprintf(stderr, " -x Exit when the last client disconnects.\n"); 120357429Smarkm exit(1); 120457429Smarkm} 120557429Smarkm 120657429Smarkmint 120757429Smarkmmain(int ac, char **av) 120857429Smarkm{ 1209295367Sdes int c_flag = 0, d_flag = 0, D_flag = 0, k_flag = 0, s_flag = 0; 1210181111Sdes int sock, fd, ch, result, saved_errno; 1211137019Sdes u_int nalloc; 121299063Sdes char *shell, *format, *pidstr, *agentsocket = NULL; 121399063Sdes fd_set *readsetp = NULL, *writesetp = NULL; 121498941Sdes#ifdef HAVE_SETRLIMIT 121576262Sgreen struct rlimit rlim; 121698941Sdes#endif 121799063Sdes extern int optind; 121899063Sdes extern char *optarg; 121957429Smarkm pid_t pid; 122099063Sdes char pidstrbuf[1 + 3 * sizeof pid]; 1221181111Sdes struct timeval *tvp = NULL; 1222197679Sdes size_t len; 1223295367Sdes mode_t prev_mask; 122457429Smarkm 1225296781Sdes ssh_malloc_init(); /* must be called before any mallocs */ 1226157019Sdes /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 1227157019Sdes sanitise_stdfd(); 1228157019Sdes 1229106130Sdes /* drop */ 1230106130Sdes setegid(getgid()); 1231106130Sdes setgid(getgid()); 1232110506Sdes setuid(geteuid()); 1233106130Sdes 1234323124Sdes platform_disable_tracing(0); /* strict=no */ 1235128460Sdes 1236295367Sdes#ifdef WITH_OPENSSL 1237221420Sdes OpenSSL_add_all_algorithms(); 1238295367Sdes#endif 123976262Sgreen 1240124211Sdes __progname = ssh_get_progname(av[0]); 124198941Sdes seed_rng(); 124298941Sdes 1243323124Sdes while ((ch = getopt(ac, av, "cDdksE:a:t:x")) != -1) { 124457429Smarkm switch (ch) { 1245295367Sdes case 'E': 1246295367Sdes fingerprint_hash = ssh_digest_alg_by_name(optarg); 1247295367Sdes if (fingerprint_hash == -1) 1248295367Sdes fatal("Invalid hash algorithm \"%s\"", optarg); 1249295367Sdes break; 125057429Smarkm case 'c': 125157429Smarkm if (s_flag) 125257429Smarkm usage(); 125357429Smarkm c_flag++; 125457429Smarkm break; 125557429Smarkm case 'k': 125657429Smarkm k_flag++; 125757429Smarkm break; 125857429Smarkm case 's': 125957429Smarkm if (c_flag) 126057429Smarkm usage(); 126157429Smarkm s_flag++; 126257429Smarkm break; 126392559Sdes case 'd': 1264295367Sdes if (d_flag || D_flag) 126592559Sdes usage(); 126692559Sdes d_flag++; 126792559Sdes break; 1268295367Sdes case 'D': 1269295367Sdes if (d_flag || D_flag) 1270295367Sdes usage(); 1271295367Sdes D_flag++; 1272295367Sdes break; 127398684Sdes case 'a': 127498684Sdes agentsocket = optarg; 127598684Sdes break; 1276113911Sdes case 't': 1277113911Sdes if ((lifetime = convtime(optarg)) == -1) { 1278113911Sdes fprintf(stderr, "Invalid lifetime\n"); 1279113911Sdes usage(); 1280113911Sdes } 1281113911Sdes break; 1282226103Sdes case 'x': 1283226103Sdes xcount = 0; 1284226103Sdes break; 128557429Smarkm default: 128657429Smarkm usage(); 128757429Smarkm } 128857429Smarkm } 128957429Smarkm ac -= optind; 129057429Smarkm av += optind; 129157429Smarkm 1292295367Sdes if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag)) 129357429Smarkm usage(); 129457429Smarkm 129598684Sdes if (ac == 0 && !c_flag && !s_flag) { 129657429Smarkm shell = getenv("SHELL"); 1297197679Sdes if (shell != NULL && (len = strlen(shell)) > 2 && 1298197679Sdes strncmp(shell + len - 3, "csh", 3) == 0) 129957429Smarkm c_flag = 1; 130057429Smarkm } 130157429Smarkm if (k_flag) { 1302162856Sdes const char *errstr = NULL; 1303162856Sdes 130457429Smarkm pidstr = getenv(SSH_AGENTPID_ENV_NAME); 130557429Smarkm if (pidstr == NULL) { 130657429Smarkm fprintf(stderr, "%s not set, cannot kill agent\n", 130776262Sgreen SSH_AGENTPID_ENV_NAME); 130857429Smarkm exit(1); 130957429Smarkm } 1310162856Sdes pid = (int)strtonum(pidstr, 2, INT_MAX, &errstr); 1311162856Sdes if (errstr) { 1312162856Sdes fprintf(stderr, 1313162856Sdes "%s=\"%s\", which is not a good PID: %s\n", 1314162856Sdes SSH_AGENTPID_ENV_NAME, pidstr, errstr); 131557429Smarkm exit(1); 131657429Smarkm } 131757429Smarkm if (kill(pid, SIGTERM) == -1) { 131857429Smarkm perror("kill"); 131957429Smarkm exit(1); 132057429Smarkm } 132157429Smarkm format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; 132257429Smarkm printf(format, SSH_AUTHSOCKET_ENV_NAME); 132357429Smarkm printf(format, SSH_AGENTPID_ENV_NAME); 132498684Sdes printf("echo Agent pid %ld killed;\n", (long)pid); 132557429Smarkm exit(0); 132657429Smarkm } 132757429Smarkm parent_pid = getpid(); 132857429Smarkm 132998684Sdes if (agentsocket == NULL) { 133098684Sdes /* Create private directory for agent socket */ 1331221420Sdes mktemp_proto(socket_dir, sizeof(socket_dir)); 133298684Sdes if (mkdtemp(socket_dir) == NULL) { 133398684Sdes perror("mkdtemp: private socket dir"); 133498684Sdes exit(1); 133598684Sdes } 133698684Sdes snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir, 133798684Sdes (long)parent_pid); 133898684Sdes } else { 133998684Sdes /* Try to use specified agent socket */ 134098684Sdes socket_dir[0] = '\0'; 134198684Sdes strlcpy(socket_name, agentsocket, sizeof socket_name); 134257429Smarkm } 134357429Smarkm 134457429Smarkm /* 134557429Smarkm * Create socket early so it will exist before command gets run from 134657429Smarkm * the parent. 134757429Smarkm */ 1348295367Sdes prev_mask = umask(0177); 1349295367Sdes sock = unix_listener(socket_name, SSH_LISTEN_BACKLOG, 0); 135057429Smarkm if (sock < 0) { 1351295367Sdes /* XXX - unix_listener() calls error() not perror() */ 1352147005Sdes *socket_name = '\0'; /* Don't unlink any existing file */ 135357429Smarkm cleanup_exit(1); 135457429Smarkm } 135598941Sdes umask(prev_mask); 135676262Sgreen 135757429Smarkm /* 135857429Smarkm * Fork, and have the parent execute the command, if any, or present 135957429Smarkm * the socket data. The child continues as the authentication agent. 136057429Smarkm */ 1361295367Sdes if (D_flag || d_flag) { 1362295367Sdes log_init(__progname, 1363295367Sdes d_flag ? SYSLOG_LEVEL_DEBUG3 : SYSLOG_LEVEL_INFO, 1364295367Sdes SYSLOG_FACILITY_AUTH, 1); 136592559Sdes format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; 136692559Sdes printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, 136792559Sdes SSH_AUTHSOCKET_ENV_NAME); 136898684Sdes printf("echo Agent pid %ld;\n", (long)parent_pid); 1369296781Sdes fflush(stdout); 137092559Sdes goto skip; 137192559Sdes } 137257429Smarkm pid = fork(); 137357429Smarkm if (pid == -1) { 137457429Smarkm perror("fork"); 137592559Sdes cleanup_exit(1); 137657429Smarkm } 137757429Smarkm if (pid != 0) { /* Parent - execute the given command. */ 137857429Smarkm close(sock); 137998684Sdes snprintf(pidstrbuf, sizeof pidstrbuf, "%ld", (long)pid); 138057429Smarkm if (ac == 0) { 138157429Smarkm format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; 138257429Smarkm printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, 138376262Sgreen SSH_AUTHSOCKET_ENV_NAME); 138457429Smarkm printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf, 138576262Sgreen SSH_AGENTPID_ENV_NAME); 138698684Sdes printf("echo Agent pid %ld;\n", (long)pid); 138757429Smarkm exit(0); 138857429Smarkm } 138969591Sgreen if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 || 139069591Sgreen setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) { 139169591Sgreen perror("setenv"); 139269591Sgreen exit(1); 139369591Sgreen } 139457429Smarkm execvp(av[0], av); 139557429Smarkm perror(av[0]); 139657429Smarkm exit(1); 139757429Smarkm } 139892559Sdes /* child */ 139992559Sdes log_init(__progname, SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 0); 140092559Sdes 140192559Sdes if (setsid() == -1) { 140292559Sdes error("setsid: %s", strerror(errno)); 140392559Sdes cleanup_exit(1); 140492559Sdes } 140592559Sdes 140692559Sdes (void)chdir("/"); 1407113911Sdes if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { 1408113911Sdes /* XXX might close listen socket */ 1409113911Sdes (void)dup2(fd, STDIN_FILENO); 1410113911Sdes (void)dup2(fd, STDOUT_FILENO); 1411113911Sdes (void)dup2(fd, STDERR_FILENO); 1412113911Sdes if (fd > 2) 1413113911Sdes close(fd); 1414113911Sdes } 141557429Smarkm 141698941Sdes#ifdef HAVE_SETRLIMIT 141776262Sgreen /* deny core dumps, since memory contains unencrypted private keys */ 141876262Sgreen rlim.rlim_cur = rlim.rlim_max = 0; 141976262Sgreen if (setrlimit(RLIMIT_CORE, &rlim) < 0) { 142092559Sdes error("setrlimit RLIMIT_CORE: %s", strerror(errno)); 142176262Sgreen cleanup_exit(1); 142276262Sgreen } 142398941Sdes#endif 142492559Sdes 142592559Sdesskip: 1426204917Sdes 1427295367Sdes cleanup_pid = getpid(); 1428295367Sdes 1429204917Sdes#ifdef ENABLE_PKCS11 1430204917Sdes pkcs11_init(0); 1431204917Sdes#endif 143257429Smarkm new_socket(AUTH_SOCKET, sock); 1433181111Sdes if (ac > 0) 1434181111Sdes parent_alive_interval = 10; 143565674Skris idtab_init(); 1436255767Sdes signal(SIGPIPE, SIG_IGN); 1437295367Sdes signal(SIGINT, (d_flag | D_flag) ? cleanup_handler : SIG_IGN); 143876262Sgreen signal(SIGHUP, cleanup_handler); 143976262Sgreen signal(SIGTERM, cleanup_handler); 144092559Sdes nalloc = 0; 144192559Sdes 1442323124Sdes if (pledge("stdio cpath unix id proc exec", NULL) == -1) 1443296781Sdes fatal("%s: pledge: %s", __progname, strerror(errno)); 1444296781Sdes platform_pledge_agent(); 1445296781Sdes 144657429Smarkm while (1) { 1447181111Sdes prepare_select(&readsetp, &writesetp, &max_fd, &nalloc, &tvp); 1448181111Sdes result = select(max_fd + 1, readsetp, writesetp, NULL, tvp); 1449181111Sdes saved_errno = errno; 1450181111Sdes if (parent_alive_interval != 0) 1451181111Sdes check_parent_exists(); 1452181111Sdes (void) reaper(); /* remove expired keys */ 1453181111Sdes if (result < 0) { 1454181111Sdes if (saved_errno == EINTR) 145557429Smarkm continue; 1456181111Sdes fatal("select: %s", strerror(saved_errno)); 1457181111Sdes } else if (result > 0) 1458181111Sdes after_select(readsetp, writesetp); 145957429Smarkm } 146057429Smarkm /* NOTREACHED */ 146157429Smarkm} 1462