1323136Sdes/* $OpenBSD: ssh-agent.c,v 1.218 2017/03/15 03:52:30 deraadt 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/11/crypto/openssh/ssh-agent.c 323136 2017-09-02 23:39:51Z des $"); 39162856Sdes 40162856Sdes#include <sys/types.h> 41162856Sdes#include <sys/param.h> 42162856Sdes#include <sys/resource.h> 43162856Sdes#include <sys/stat.h> 44162856Sdes#include <sys/socket.h> 45162856Sdes#ifdef HAVE_SYS_TIME_H 46162856Sdes# include <sys/time.h> 47162856Sdes#endif 48162856Sdes#ifdef HAVE_SYS_UN_H 49162856Sdes# include <sys/un.h> 50162856Sdes#endif 51106130Sdes#include "openbsd-compat/sys-queue.h" 5257429Smarkm 53294328Sdes#ifdef WITH_OPENSSL 5476262Sgreen#include <openssl/evp.h> 55181111Sdes#include "openbsd-compat/openssl-compat.h" 56294328Sdes#endif 5776262Sgreen 58162856Sdes#include <errno.h> 59162856Sdes#include <fcntl.h> 60294332Sdes#include <limits.h> 61162856Sdes#ifdef HAVE_PATHS_H 62162856Sdes# include <paths.h> 63162856Sdes#endif 64162856Sdes#include <signal.h> 65162856Sdes#include <stdarg.h> 66162856Sdes#include <stdio.h> 67162856Sdes#include <stdlib.h> 68162856Sdes#include <time.h> 69162856Sdes#include <string.h> 70162856Sdes#include <unistd.h> 71294336Sdes#ifdef HAVE_UTIL_H 72294336Sdes# include <util.h> 73294336Sdes#endif 74162856Sdes 75162856Sdes#include "xmalloc.h" 7657429Smarkm#include "ssh.h" 7757429Smarkm#include "rsa.h" 78294332Sdes#include "sshbuf.h" 79294332Sdes#include "sshkey.h" 8065674Skris#include "authfd.h" 8169591Sgreen#include "compat.h" 8276262Sgreen#include "log.h" 83113911Sdes#include "misc.h" 84263712Sdes#include "digest.h" 85294332Sdes#include "ssherr.h" 86323134Sdes#include "match.h" 8757429Smarkm 88204917Sdes#ifdef ENABLE_PKCS11 89204917Sdes#include "ssh-pkcs11.h" 9092559Sdes#endif 9192559Sdes 92323134Sdes#ifndef DEFAULT_PKCS11_WHITELIST 93323136Sdes# define DEFAULT_PKCS11_WHITELIST "/usr/lib*/*,/usr/local/lib*/*" 94323134Sdes#endif 95323134Sdes 9692559Sdestypedef enum { 9792559Sdes AUTH_UNUSED, 9892559Sdes AUTH_SOCKET, 9992559Sdes AUTH_CONNECTION 10092559Sdes} sock_type; 10192559Sdes 10257429Smarkmtypedef struct { 10357429Smarkm int fd; 10492559Sdes sock_type type; 105294332Sdes struct sshbuf *input; 106294332Sdes struct sshbuf *output; 107294332Sdes struct sshbuf *request; 10857429Smarkm} SocketEntry; 10957429Smarkm 11076262Sgreenu_int sockets_alloc = 0; 11157429SmarkmSocketEntry *sockets = NULL; 11257429Smarkm 11392559Sdestypedef struct identity { 11492559Sdes TAILQ_ENTRY(identity) next; 115294332Sdes struct sshkey *key; 11657429Smarkm char *comment; 117204917Sdes char *provider; 118255767Sdes time_t death; 119113911Sdes u_int confirm; 12057429Smarkm} Identity; 12157429Smarkm 12265674Skristypedef struct { 12365674Skris int nentries; 12492559Sdes TAILQ_HEAD(idqueue, identity) idlist; 12565674Skris} Idtab; 12657429Smarkm 12765674Skris/* private key table, one per protocol version */ 12865674SkrisIdtab idtable[3]; 12965674Skris 13057429Smarkmint max_fd = 0; 13157429Smarkm 13257429Smarkm/* pid of shell == parent of agent */ 13360576Skrispid_t parent_pid = -1; 134255767Sdestime_t parent_alive_interval = 0; 13557429Smarkm 136294328Sdes/* pid of process for which cleanup_socket is applicable */ 137294328Sdespid_t cleanup_pid = 0; 138294328Sdes 13957429Smarkm/* pathname and directory for AUTH_SOCKET */ 140294332Sdeschar socket_name[PATH_MAX]; 141294332Sdeschar socket_dir[PATH_MAX]; 14257429Smarkm 143323134Sdes/* PKCS#11 path whitelist */ 144323134Sdesstatic char *pkcs11_whitelist; 145323134Sdes 14698684Sdes/* locking */ 147294336Sdes#define LOCK_SIZE 32 148294336Sdes#define LOCK_SALT_SIZE 16 149294336Sdes#define LOCK_ROUNDS 1 15098684Sdesint locked = 0; 151323129Sdesu_char lock_pwhash[LOCK_SIZE]; 152323129Sdesu_char lock_salt[LOCK_SALT_SIZE]; 15398684Sdes 15457429Smarkmextern char *__progname; 15557429Smarkm 156255767Sdes/* Default lifetime in seconds (0 == forever) */ 157255767Sdesstatic long lifetime = 0; 158113911Sdes 159294332Sdesstatic int fingerprint_hash = SSH_FP_HASH_DEFAULT; 160294332Sdes 161226103Sdes/* 162226103Sdes * Client connection count; incremented in new_socket() and decremented in 163226103Sdes * close_socket(). When it reaches 0, ssh-agent will exit. Since it is 164226103Sdes * normally initialized to 1, it will never reach 0. However, if the -x 165226103Sdes * option is specified, it is initialized to 0 in main(); in that case, 166226103Sdes * ssh-agent will exit as soon as it has had at least one client but no 167226103Sdes * longer has any. 168226103Sdes */ 169226103Sdesstatic int xcount = 1; 170226103Sdes 17192559Sdesstatic void 172106130Sdesclose_socket(SocketEntry *e) 173106130Sdes{ 174226103Sdes int last = 0; 175226103Sdes 176226103Sdes if (e->type == AUTH_CONNECTION) { 177226103Sdes debug("xcount %d -> %d", xcount, xcount - 1); 178226103Sdes if (--xcount == 0) 179226103Sdes last = 1; 180226103Sdes } 181106130Sdes close(e->fd); 182106130Sdes e->fd = -1; 183106130Sdes e->type = AUTH_UNUSED; 184294332Sdes sshbuf_free(e->input); 185294332Sdes sshbuf_free(e->output); 186294332Sdes sshbuf_free(e->request); 187226103Sdes if (last) 188226103Sdes cleanup_exit(0); 189106130Sdes} 190106130Sdes 191106130Sdesstatic void 19265674Skrisidtab_init(void) 19357429Smarkm{ 19465674Skris int i; 19599063Sdes 19692559Sdes for (i = 0; i <=2; i++) { 19792559Sdes TAILQ_INIT(&idtable[i].idlist); 19865674Skris idtable[i].nentries = 0; 19965674Skris } 20065674Skris} 20165674Skris 20265674Skris/* return private key table for requested protocol version */ 20392559Sdesstatic Idtab * 20465674Skrisidtab_lookup(int version) 20565674Skris{ 20665674Skris if (version < 1 || version > 2) 20765674Skris fatal("internal error, bad protocol version %d", version); 20865674Skris return &idtable[version]; 20965674Skris} 21065674Skris 21198684Sdesstatic void 21298684Sdesfree_identity(Identity *id) 21398684Sdes{ 214294332Sdes sshkey_free(id->key); 215255767Sdes free(id->provider); 216255767Sdes free(id->comment); 217255767Sdes free(id); 21898684Sdes} 21998684Sdes 22065674Skris/* return matching private key for given public key */ 22192559Sdesstatic Identity * 222294332Sdeslookup_identity(struct sshkey *key, int version) 22365674Skris{ 22492559Sdes Identity *id; 22592559Sdes 22665674Skris Idtab *tab = idtab_lookup(version); 22792559Sdes TAILQ_FOREACH(id, &tab->idlist, next) { 228294332Sdes if (sshkey_equal(key, id->key)) 22992559Sdes return (id); 23065674Skris } 23192559Sdes return (NULL); 23265674Skris} 23365674Skris 234113911Sdes/* Check confirmation of keysign request */ 235113911Sdesstatic int 236113911Sdesconfirm_key(Identity *id) 237113911Sdes{ 238147005Sdes char *p; 239113911Sdes int ret = -1; 240113911Sdes 241294332Sdes p = sshkey_fingerprint(id->key, fingerprint_hash, SSH_FP_DEFAULT); 242294332Sdes if (p != NULL && 243294332Sdes ask_permission("Allow use of key %s?\nKey fingerprint %s.", 244147005Sdes id->comment, p)) 245147005Sdes ret = 0; 246255767Sdes free(p); 247147005Sdes 248113911Sdes return (ret); 249113911Sdes} 250113911Sdes 251294332Sdesstatic void 252294332Sdessend_status(SocketEntry *e, int success) 253294332Sdes{ 254294332Sdes int r; 255294332Sdes 256294332Sdes if ((r = sshbuf_put_u32(e->output, 1)) != 0 || 257294332Sdes (r = sshbuf_put_u8(e->output, success ? 258294332Sdes SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE)) != 0) 259294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 260294332Sdes} 261294332Sdes 26265674Skris/* send list of supported public keys to 'client' */ 26392559Sdesstatic void 26465674Skrisprocess_request_identities(SocketEntry *e, int version) 26565674Skris{ 26665674Skris Idtab *tab = idtab_lookup(version); 26799063Sdes Identity *id; 268294332Sdes struct sshbuf *msg; 269294332Sdes int r; 27057429Smarkm 271294332Sdes if ((msg = sshbuf_new()) == NULL) 272294332Sdes fatal("%s: sshbuf_new failed", __func__); 273294332Sdes if ((r = sshbuf_put_u8(msg, (version == 1) ? 274294332Sdes SSH_AGENT_RSA_IDENTITIES_ANSWER : 275294332Sdes SSH2_AGENT_IDENTITIES_ANSWER)) != 0 || 276294332Sdes (r = sshbuf_put_u32(msg, tab->nentries)) != 0) 277294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 27892559Sdes TAILQ_FOREACH(id, &tab->idlist, next) { 27976262Sgreen if (id->key->type == KEY_RSA1) { 280294328Sdes#ifdef WITH_SSH1 281294332Sdes if ((r = sshbuf_put_u32(msg, 282294332Sdes BN_num_bits(id->key->rsa->n))) != 0 || 283294332Sdes (r = sshbuf_put_bignum1(msg, 284294332Sdes id->key->rsa->e)) != 0 || 285294332Sdes (r = sshbuf_put_bignum1(msg, 286294332Sdes id->key->rsa->n)) != 0) 287294332Sdes fatal("%s: buffer error: %s", 288294332Sdes __func__, ssh_err(r)); 289294328Sdes#endif 29065674Skris } else { 29176262Sgreen u_char *blob; 292294332Sdes size_t blen; 293294332Sdes 294294332Sdes if ((r = sshkey_to_blob(id->key, &blob, &blen)) != 0) { 295294332Sdes error("%s: sshkey_to_blob: %s", __func__, 296294332Sdes ssh_err(r)); 297294332Sdes continue; 298294332Sdes } 299294332Sdes if ((r = sshbuf_put_string(msg, blob, blen)) != 0) 300294332Sdes fatal("%s: buffer error: %s", 301294332Sdes __func__, ssh_err(r)); 302255767Sdes free(blob); 30365674Skris } 304294332Sdes if ((r = sshbuf_put_cstring(msg, id->comment)) != 0) 305294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 30657429Smarkm } 307294332Sdes if ((r = sshbuf_put_stringb(e->output, msg)) != 0) 308294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 309294332Sdes sshbuf_free(msg); 31057429Smarkm} 31157429Smarkm 312294328Sdes#ifdef WITH_SSH1 31365674Skris/* ssh1 only */ 31492559Sdesstatic void 31565674Skrisprocess_authentication_challenge1(SocketEntry *e) 31657429Smarkm{ 31799063Sdes u_char buf[32], mdbuf[16], session_id[16]; 31899063Sdes u_int response_type; 31999063Sdes BIGNUM *challenge; 32092559Sdes Identity *id; 321294332Sdes int r, len; 322294332Sdes struct sshbuf *msg; 323263712Sdes struct ssh_digest_ctx *md; 324294332Sdes struct sshkey *key; 32557429Smarkm 326294332Sdes if ((msg = sshbuf_new()) == NULL) 327294332Sdes fatal("%s: sshbuf_new failed", __func__); 328294332Sdes if ((key = sshkey_new(KEY_RSA1)) == NULL) 329294332Sdes fatal("%s: sshkey_new failed", __func__); 33092559Sdes if ((challenge = BN_new()) == NULL) 331294332Sdes fatal("%s: BN_new failed", __func__); 33265674Skris 333294332Sdes if ((r = sshbuf_get_u32(e->request, NULL)) != 0 || /* ignored */ 334294332Sdes (r = sshbuf_get_bignum1(e->request, key->rsa->e)) != 0 || 335294332Sdes (r = sshbuf_get_bignum1(e->request, key->rsa->n)) != 0 || 336294332Sdes (r = sshbuf_get_bignum1(e->request, challenge))) 337294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 33857429Smarkm 33965674Skris /* Only protocol 1.1 is supported */ 340294332Sdes if (sshbuf_len(e->request) == 0) 34165674Skris goto failure; 342294332Sdes if ((r = sshbuf_get(e->request, session_id, sizeof(session_id))) != 0 || 343294332Sdes (r = sshbuf_get_u32(e->request, &response_type)) != 0) 344294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 34565674Skris if (response_type != 1) 34665674Skris goto failure; 34757429Smarkm 34892559Sdes id = lookup_identity(key, 1); 349113911Sdes if (id != NULL && (!id->confirm || confirm_key(id) == 0)) { 350294332Sdes struct sshkey *private = id->key; 35165674Skris /* Decrypt the challenge using the private key. */ 352294332Sdes if ((r = rsa_private_decrypt(challenge, challenge, 353294332Sdes private->rsa) != 0)) { 354294332Sdes fatal("%s: rsa_public_encrypt: %s", __func__, 355294332Sdes ssh_err(r)); 356294332Sdes goto failure; /* XXX ? */ 357294332Sdes } 35857429Smarkm 359294332Sdes /* The response is MD5 of decrypted challenge plus session id */ 36065674Skris len = BN_num_bytes(challenge); 36165674Skris if (len <= 0 || len > 32) { 362294332Sdes logit("%s: bad challenge length %d", __func__, len); 36365674Skris goto failure; 36465674Skris } 36565674Skris memset(buf, 0, 32); 36665674Skris BN_bn2bin(challenge, buf + 32 - len); 367263712Sdes if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL || 368263712Sdes ssh_digest_update(md, buf, 32) < 0 || 369263712Sdes ssh_digest_update(md, session_id, 16) < 0 || 370263712Sdes ssh_digest_final(md, mdbuf, sizeof(mdbuf)) < 0) 371263712Sdes fatal("%s: md5 failed", __func__); 372263712Sdes ssh_digest_free(md); 37357429Smarkm 37465674Skris /* Send the response. */ 375294332Sdes if ((r = sshbuf_put_u8(msg, SSH_AGENT_RSA_RESPONSE)) != 0 || 376294332Sdes (r = sshbuf_put(msg, mdbuf, sizeof(mdbuf))) != 0) 377294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 37865674Skris goto send; 37965674Skris } 38057429Smarkm 381294332Sdes failure: 38265674Skris /* Unknown identity or protocol error. Send failure. */ 383294332Sdes if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0) 384294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 385294332Sdes send: 386294332Sdes if ((r = sshbuf_put_stringb(e->output, msg)) != 0) 387294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 388294332Sdes sshkey_free(key); 38965674Skris BN_clear_free(challenge); 390294332Sdes sshbuf_free(msg); 39165674Skris} 392294328Sdes#endif 39365674Skris 394296633Sdesstatic char * 395296633Sdesagent_decode_alg(struct sshkey *key, u_int flags) 396296633Sdes{ 397296633Sdes if (key->type == KEY_RSA) { 398296633Sdes if (flags & SSH_AGENT_RSA_SHA2_256) 399296633Sdes return "rsa-sha2-256"; 400296633Sdes else if (flags & SSH_AGENT_RSA_SHA2_512) 401296633Sdes return "rsa-sha2-512"; 402296633Sdes } 403296633Sdes return NULL; 404296633Sdes} 405296633Sdes 40665674Skris/* ssh2 only */ 40792559Sdesstatic void 40865674Skrisprocess_sign_request2(SocketEntry *e) 40965674Skris{ 41076262Sgreen u_char *blob, *data, *signature = NULL; 411294332Sdes size_t blen, dlen, slen = 0; 412294332Sdes u_int compat = 0, flags; 413294332Sdes int r, ok = -1; 414294332Sdes struct sshbuf *msg; 415294332Sdes struct sshkey *key; 416294332Sdes struct identity *id; 41765674Skris 418294332Sdes if ((msg = sshbuf_new()) == NULL) 419294332Sdes fatal("%s: sshbuf_new failed", __func__); 420294332Sdes if ((r = sshbuf_get_string(e->request, &blob, &blen)) != 0 || 421294332Sdes (r = sshbuf_get_string(e->request, &data, &dlen)) != 0 || 422294332Sdes (r = sshbuf_get_u32(e->request, &flags)) != 0) 423294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 42469591Sgreen if (flags & SSH_AGENT_OLD_SIGNATURE) 425294332Sdes compat = SSH_BUG_SIGBLOB; 426294332Sdes if ((r = sshkey_from_blob(blob, blen, &key)) != 0) { 427296633Sdes error("%s: cannot parse key blob: %s", __func__, ssh_err(r)); 428294332Sdes goto send; 42965674Skris } 430294332Sdes if ((id = lookup_identity(key, 2)) == NULL) { 431294332Sdes verbose("%s: %s key not found", __func__, sshkey_type(key)); 432294332Sdes goto send; 433294332Sdes } 434294332Sdes if (id->confirm && confirm_key(id) != 0) { 435294332Sdes verbose("%s: user refused key", __func__); 436294332Sdes goto send; 437294332Sdes } 438294332Sdes if ((r = sshkey_sign(id->key, &signature, &slen, 439296633Sdes data, dlen, agent_decode_alg(key, flags), compat)) != 0) { 440296633Sdes error("%s: sshkey_sign: %s", __func__, ssh_err(r)); 441294332Sdes goto send; 442294332Sdes } 443294332Sdes /* Success */ 444294332Sdes ok = 0; 445294332Sdes send: 446294332Sdes sshkey_free(key); 44765674Skris if (ok == 0) { 448294332Sdes if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE)) != 0 || 449294332Sdes (r = sshbuf_put_string(msg, signature, slen)) != 0) 450294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 451294332Sdes } else if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0) 452294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 453294332Sdes 454294332Sdes if ((r = sshbuf_put_stringb(e->output, msg)) != 0) 455294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 456294332Sdes 457294332Sdes sshbuf_free(msg); 458255767Sdes free(data); 459255767Sdes free(blob); 460255767Sdes free(signature); 46157429Smarkm} 46257429Smarkm 46365674Skris/* shared */ 46492559Sdesstatic void 46565674Skrisprocess_remove_identity(SocketEntry *e, int version) 46657429Smarkm{ 467294332Sdes size_t blen; 468294332Sdes int r, success = 0; 469294332Sdes struct sshkey *key = NULL; 47076262Sgreen u_char *blob; 471294328Sdes#ifdef WITH_SSH1 472294328Sdes u_int bits; 473294328Sdes#endif /* WITH_SSH1 */ 47457429Smarkm 47592559Sdes switch (version) { 476294328Sdes#ifdef WITH_SSH1 47765674Skris case 1: 478294332Sdes if ((key = sshkey_new(KEY_RSA1)) == NULL) { 479294332Sdes error("%s: sshkey_new failed", __func__); 480294332Sdes return; 481294332Sdes } 482294332Sdes if ((r = sshbuf_get_u32(e->request, &bits)) != 0 || 483294332Sdes (r = sshbuf_get_bignum1(e->request, key->rsa->e)) != 0 || 484294332Sdes (r = sshbuf_get_bignum1(e->request, key->rsa->n)) != 0) 485294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 48657429Smarkm 487294332Sdes if (bits != sshkey_size(key)) 488294332Sdes logit("Warning: identity keysize mismatch: " 489294332Sdes "actual %u, announced %u", 490294332Sdes sshkey_size(key), bits); 49165674Skris break; 492294328Sdes#endif /* WITH_SSH1 */ 49365674Skris case 2: 494294332Sdes if ((r = sshbuf_get_string(e->request, &blob, &blen)) != 0) 495294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 496294332Sdes if ((r = sshkey_from_blob(blob, blen, &key)) != 0) 497294332Sdes error("%s: sshkey_from_blob failed: %s", 498294332Sdes __func__, ssh_err(r)); 499255767Sdes free(blob); 50065674Skris break; 50165674Skris } 50265674Skris if (key != NULL) { 50392559Sdes Identity *id = lookup_identity(key, version); 50492559Sdes if (id != NULL) { 50557429Smarkm /* 50657429Smarkm * We have this key. Free the old key. Since we 507157019Sdes * don't want to leave empty slots in the middle of 50876262Sgreen * the array, we actually free the key there and move 50976262Sgreen * all the entries between the empty slot and the end 51076262Sgreen * of the array. 51157429Smarkm */ 51265674Skris Idtab *tab = idtab_lookup(version); 51376262Sgreen if (tab->nentries < 1) 51476262Sgreen fatal("process_remove_identity: " 51576262Sgreen "internal error: tab->nentries %d", 51676262Sgreen tab->nentries); 51792559Sdes TAILQ_REMOVE(&tab->idlist, id, next); 51892559Sdes free_identity(id); 51965674Skris tab->nentries--; 52065674Skris success = 1; 52157429Smarkm } 522294332Sdes sshkey_free(key); 52365674Skris } 524294332Sdes send_status(e, success); 52557429Smarkm} 52657429Smarkm 52792559Sdesstatic void 52865674Skrisprocess_remove_all_identities(SocketEntry *e, int version) 52957429Smarkm{ 53065674Skris Idtab *tab = idtab_lookup(version); 53192559Sdes Identity *id; 53257429Smarkm 53357429Smarkm /* Loop over all identities and clear the keys. */ 53492559Sdes for (id = TAILQ_FIRST(&tab->idlist); id; 53592559Sdes id = TAILQ_FIRST(&tab->idlist)) { 53692559Sdes TAILQ_REMOVE(&tab->idlist, id, next); 53792559Sdes free_identity(id); 53857429Smarkm } 53957429Smarkm 54057429Smarkm /* Mark that there are no identities. */ 54165674Skris tab->nentries = 0; 54257429Smarkm 54357429Smarkm /* Send success. */ 544294332Sdes send_status(e, 1); 54557429Smarkm} 54657429Smarkm 547181111Sdes/* removes expired keys and returns number of seconds until the next expiry */ 548255767Sdesstatic time_t 54998684Sdesreaper(void) 55098684Sdes{ 551255767Sdes time_t deadline = 0, now = monotime(); 55298684Sdes Identity *id, *nxt; 55398684Sdes int version; 55499063Sdes Idtab *tab; 55598684Sdes 55698684Sdes for (version = 1; version < 3; version++) { 55798684Sdes tab = idtab_lookup(version); 55898684Sdes for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) { 55998684Sdes nxt = TAILQ_NEXT(id, next); 560181111Sdes if (id->death == 0) 561181111Sdes continue; 562181111Sdes if (now >= id->death) { 563181111Sdes debug("expiring key '%s'", id->comment); 56498684Sdes TAILQ_REMOVE(&tab->idlist, id, next); 56598684Sdes free_identity(id); 56698684Sdes tab->nentries--; 567181111Sdes } else 568181111Sdes deadline = (deadline == 0) ? id->death : 569323134Sdes MINIMUM(deadline, id->death); 57098684Sdes } 57198684Sdes } 572181111Sdes if (deadline == 0 || deadline <= now) 573181111Sdes return 0; 574181111Sdes else 575181111Sdes return (deadline - now); 57698684Sdes} 57798684Sdes 578294332Sdes/* 579294332Sdes * XXX this and the corresponding serialisation function probably belongs 580294332Sdes * in key.c 581294332Sdes */ 582294332Sdes#ifdef WITH_SSH1 583294332Sdesstatic int 584294332Sdesagent_decode_rsa1(struct sshbuf *m, struct sshkey **kp) 585294332Sdes{ 586294332Sdes struct sshkey *k = NULL; 587294332Sdes int r = SSH_ERR_INTERNAL_ERROR; 588294332Sdes 589294332Sdes *kp = NULL; 590294332Sdes if ((k = sshkey_new_private(KEY_RSA1)) == NULL) 591294332Sdes return SSH_ERR_ALLOC_FAIL; 592294332Sdes 593294332Sdes if ((r = sshbuf_get_u32(m, NULL)) != 0 || /* ignored */ 594294332Sdes (r = sshbuf_get_bignum1(m, k->rsa->n)) != 0 || 595294332Sdes (r = sshbuf_get_bignum1(m, k->rsa->e)) != 0 || 596294332Sdes (r = sshbuf_get_bignum1(m, k->rsa->d)) != 0 || 597294332Sdes (r = sshbuf_get_bignum1(m, k->rsa->iqmp)) != 0 || 598294332Sdes /* SSH1 and SSL have p and q swapped */ 599294332Sdes (r = sshbuf_get_bignum1(m, k->rsa->q)) != 0 || /* p */ 600294332Sdes (r = sshbuf_get_bignum1(m, k->rsa->p)) != 0) /* q */ 601294332Sdes goto out; 602294332Sdes 603294332Sdes /* Generate additional parameters */ 604294332Sdes if ((r = rsa_generate_additional_parameters(k->rsa)) != 0) 605294332Sdes goto out; 606294332Sdes /* enable blinding */ 607294332Sdes if (RSA_blinding_on(k->rsa, NULL) != 1) { 608294332Sdes r = SSH_ERR_LIBCRYPTO_ERROR; 609294332Sdes goto out; 610294332Sdes } 611294332Sdes 612294332Sdes r = 0; /* success */ 613294332Sdes out: 614294332Sdes if (r == 0) 615294332Sdes *kp = k; 616294332Sdes else 617294332Sdes sshkey_free(k); 618294332Sdes return r; 619294332Sdes} 620294332Sdes#endif /* WITH_SSH1 */ 621294332Sdes 62298684Sdesstatic void 62365674Skrisprocess_add_identity(SocketEntry *e, int version) 62457429Smarkm{ 62599063Sdes Idtab *tab = idtab_lookup(version); 626181111Sdes Identity *id; 627294332Sdes int success = 0, confirm = 0; 628294332Sdes u_int seconds; 629294332Sdes char *comment = NULL; 630255767Sdes time_t death = 0; 631294332Sdes struct sshkey *k = NULL; 632294332Sdes u_char ctype; 633294332Sdes int r = SSH_ERR_INTERNAL_ERROR; 63457429Smarkm 63565674Skris switch (version) { 636294328Sdes#ifdef WITH_SSH1 63765674Skris case 1: 638294332Sdes r = agent_decode_rsa1(e->request, &k); 639113911Sdes break; 640294328Sdes#endif /* WITH_SSH1 */ 641261320Sdes case 2: 642294332Sdes r = sshkey_private_deserialize(e->request, &k); 643261320Sdes break; 644113911Sdes } 645294332Sdes if (r != 0 || k == NULL || 646294332Sdes (r = sshbuf_get_cstring(e->request, &comment, NULL)) != 0) { 647294332Sdes error("%s: decode private key: %s", __func__, ssh_err(r)); 648294332Sdes goto err; 649294332Sdes } 650294328Sdes 651294332Sdes while (sshbuf_len(e->request)) { 652294332Sdes if ((r = sshbuf_get_u8(e->request, &ctype)) != 0) { 653294332Sdes error("%s: buffer error: %s", __func__, ssh_err(r)); 654294332Sdes goto err; 655294332Sdes } 656294332Sdes switch (ctype) { 65798684Sdes case SSH_AGENT_CONSTRAIN_LIFETIME: 658294332Sdes if ((r = sshbuf_get_u32(e->request, &seconds)) != 0) { 659294332Sdes error("%s: bad lifetime constraint: %s", 660294332Sdes __func__, ssh_err(r)); 661294332Sdes goto err; 662294332Sdes } 663294332Sdes death = monotime() + seconds; 66498684Sdes break; 665113911Sdes case SSH_AGENT_CONSTRAIN_CONFIRM: 666113911Sdes confirm = 1; 667113911Sdes break; 66898684Sdes default: 669294332Sdes error("%s: Unknown constraint %d", __func__, ctype); 670294332Sdes err: 671294332Sdes sshbuf_reset(e->request); 672255767Sdes free(comment); 673294332Sdes sshkey_free(k); 674181111Sdes goto send; 67598684Sdes } 67698684Sdes } 677294332Sdes 678181111Sdes success = 1; 679113911Sdes if (lifetime && !death) 680255767Sdes death = monotime() + lifetime; 681181111Sdes if ((id = lookup_identity(k, version)) == NULL) { 682204917Sdes id = xcalloc(1, sizeof(Identity)); 68392559Sdes id->key = k; 68492559Sdes TAILQ_INSERT_TAIL(&tab->idlist, id, next); 68565674Skris /* Increment the number of identities. */ 68665674Skris tab->nentries++; 68765674Skris } else { 688294332Sdes sshkey_free(k); 689255767Sdes free(id->comment); 69065674Skris } 691181111Sdes id->comment = comment; 692181111Sdes id->death = death; 693181111Sdes id->confirm = confirm; 69465674Skrissend: 695294332Sdes send_status(e, success); 69657429Smarkm} 69757429Smarkm 69898684Sdes/* XXX todo: encrypt sensitive data with passphrase */ 69998684Sdesstatic void 70098684Sdesprocess_lock_agent(SocketEntry *e, int lock) 70198684Sdes{ 702294336Sdes int r, success = 0, delay; 703323129Sdes char *passwd; 704323129Sdes u_char passwdhash[LOCK_SIZE]; 705294336Sdes static u_int fail_count = 0; 706294336Sdes size_t pwlen; 70792559Sdes 708294336Sdes if ((r = sshbuf_get_cstring(e->request, &passwd, &pwlen)) != 0) 709294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 710294336Sdes if (pwlen == 0) { 711294336Sdes debug("empty password not supported"); 712294336Sdes } else if (locked && !lock) { 713294336Sdes if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt), 714294336Sdes passwdhash, sizeof(passwdhash), LOCK_ROUNDS) < 0) 715294336Sdes fatal("bcrypt_pbkdf"); 716323129Sdes if (timingsafe_bcmp(passwdhash, lock_pwhash, LOCK_SIZE) == 0) { 717294336Sdes debug("agent unlocked"); 718294336Sdes locked = 0; 719294336Sdes fail_count = 0; 720323129Sdes explicit_bzero(lock_pwhash, sizeof(lock_pwhash)); 721294336Sdes success = 1; 722294336Sdes } else { 723294336Sdes /* delay in 0.1s increments up to 10s */ 724294336Sdes if (fail_count < 100) 725294336Sdes fail_count++; 726294336Sdes delay = 100000 * fail_count; 727294336Sdes debug("unlock failed, delaying %0.1lf seconds", 728294336Sdes (double)delay/1000000); 729294336Sdes usleep(delay); 730294336Sdes } 731294336Sdes explicit_bzero(passwdhash, sizeof(passwdhash)); 73298684Sdes } else if (!locked && lock) { 733294336Sdes debug("agent locked"); 73498684Sdes locked = 1; 735294336Sdes arc4random_buf(lock_salt, sizeof(lock_salt)); 736294336Sdes if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt), 737323129Sdes lock_pwhash, sizeof(lock_pwhash), LOCK_ROUNDS) < 0) 738294336Sdes fatal("bcrypt_pbkdf"); 73998684Sdes success = 1; 74098684Sdes } 741294336Sdes explicit_bzero(passwd, pwlen); 742255767Sdes free(passwd); 743294332Sdes send_status(e, success); 74498684Sdes} 74598684Sdes 74698684Sdesstatic void 74798684Sdesno_identities(SocketEntry *e, u_int type) 74898684Sdes{ 749294332Sdes struct sshbuf *msg; 750294332Sdes int r; 75198684Sdes 752294332Sdes if ((msg = sshbuf_new()) == NULL) 753294332Sdes fatal("%s: sshbuf_new failed", __func__); 754294332Sdes if ((r = sshbuf_put_u8(msg, 75598684Sdes (type == SSH_AGENTC_REQUEST_RSA_IDENTITIES) ? 756294332Sdes SSH_AGENT_RSA_IDENTITIES_ANSWER : 757294332Sdes SSH2_AGENT_IDENTITIES_ANSWER)) != 0 || 758294332Sdes (r = sshbuf_put_u32(msg, 0)) != 0 || 759294332Sdes (r = sshbuf_put_stringb(e->output, msg)) != 0) 760294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 761294332Sdes sshbuf_free(msg); 76298684Sdes} 76398684Sdes 764204917Sdes#ifdef ENABLE_PKCS11 76592559Sdesstatic void 766181111Sdesprocess_add_smartcard_key(SocketEntry *e) 76792559Sdes{ 768323134Sdes char *provider = NULL, *pin, canonical_provider[PATH_MAX]; 769294332Sdes int r, i, version, count = 0, success = 0, confirm = 0; 770294332Sdes u_int seconds; 771255767Sdes time_t death = 0; 772294332Sdes u_char type; 773294332Sdes struct sshkey **keys = NULL, *k; 77498684Sdes Identity *id; 77592559Sdes Idtab *tab; 77692559Sdes 777294332Sdes if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 || 778294332Sdes (r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) 779294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 780124211Sdes 781294332Sdes while (sshbuf_len(e->request)) { 782294332Sdes if ((r = sshbuf_get_u8(e->request, &type)) != 0) 783294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 784294332Sdes switch (type) { 785124211Sdes case SSH_AGENT_CONSTRAIN_LIFETIME: 786294332Sdes if ((r = sshbuf_get_u32(e->request, &seconds)) != 0) 787294332Sdes fatal("%s: buffer error: %s", 788294332Sdes __func__, ssh_err(r)); 789294332Sdes death = monotime() + seconds; 790124211Sdes break; 791124211Sdes case SSH_AGENT_CONSTRAIN_CONFIRM: 792124211Sdes confirm = 1; 793124211Sdes break; 794124211Sdes default: 795181111Sdes error("process_add_smartcard_key: " 796181111Sdes "Unknown constraint type %d", type); 797181111Sdes goto send; 798124211Sdes } 799124211Sdes } 800323134Sdes if (realpath(provider, canonical_provider) == NULL) { 801323134Sdes verbose("failed PKCS#11 add of \"%.100s\": realpath: %s", 802323134Sdes provider, strerror(errno)); 803323134Sdes goto send; 804323134Sdes } 805323134Sdes if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { 806323134Sdes verbose("refusing PKCS#11 add of \"%.100s\": " 807323134Sdes "provider not whitelisted", canonical_provider); 808323134Sdes goto send; 809323134Sdes } 810323134Sdes debug("%s: add %.100s", __func__, canonical_provider); 811124211Sdes if (lifetime && !death) 812255767Sdes death = monotime() + lifetime; 813124211Sdes 814323134Sdes count = pkcs11_add_provider(canonical_provider, pin, &keys); 815204917Sdes for (i = 0; i < count; i++) { 81698684Sdes k = keys[i]; 81798684Sdes version = k->type == KEY_RSA1 ? 1 : 2; 81898684Sdes tab = idtab_lookup(version); 81998684Sdes if (lookup_identity(k, version) == NULL) { 820204917Sdes id = xcalloc(1, sizeof(Identity)); 82198684Sdes id->key = k; 822323134Sdes id->provider = xstrdup(canonical_provider); 823323134Sdes id->comment = xstrdup(canonical_provider); /* XXX */ 824124211Sdes id->death = death; 825124211Sdes id->confirm = confirm; 82698684Sdes TAILQ_INSERT_TAIL(&tab->idlist, id, next); 82798684Sdes tab->nentries++; 82898684Sdes success = 1; 82998684Sdes } else { 830294332Sdes sshkey_free(k); 83198684Sdes } 83298684Sdes keys[i] = NULL; 83392559Sdes } 83492559Sdessend: 835255767Sdes free(pin); 836255767Sdes free(provider); 837255767Sdes free(keys); 838294332Sdes send_status(e, success); 83992559Sdes} 84092559Sdes 84192559Sdesstatic void 84292559Sdesprocess_remove_smartcard_key(SocketEntry *e) 84392559Sdes{ 844323136Sdes char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; 845294332Sdes int r, version, success = 0; 846204917Sdes Identity *id, *nxt; 84798684Sdes Idtab *tab; 84892559Sdes 849294332Sdes if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 || 850294332Sdes (r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) 851294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 852255767Sdes free(pin); 85392559Sdes 854323136Sdes if (realpath(provider, canonical_provider) == NULL) { 855323136Sdes verbose("failed PKCS#11 add of \"%.100s\": realpath: %s", 856323136Sdes provider, strerror(errno)); 857323136Sdes goto send; 858323136Sdes } 859323136Sdes 860323136Sdes debug("%s: remove %.100s", __func__, canonical_provider); 861204917Sdes for (version = 1; version < 3; version++) { 862204917Sdes tab = idtab_lookup(version); 863204917Sdes for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) { 864204917Sdes nxt = TAILQ_NEXT(id, next); 865261320Sdes /* Skip file--based keys */ 866261320Sdes if (id->provider == NULL) 867261320Sdes continue; 868323136Sdes if (!strcmp(canonical_provider, id->provider)) { 869204917Sdes TAILQ_REMOVE(&tab->idlist, id, next); 870204917Sdes free_identity(id); 871204917Sdes tab->nentries--; 872204917Sdes } 87392559Sdes } 87492559Sdes } 875323136Sdes if (pkcs11_del_provider(canonical_provider) == 0) 876204917Sdes success = 1; 877204917Sdes else 878204917Sdes error("process_remove_smartcard_key:" 879204917Sdes " pkcs11_del_provider failed"); 880323136Sdessend: 881255767Sdes free(provider); 882294332Sdes send_status(e, success); 88392559Sdes} 884204917Sdes#endif /* ENABLE_PKCS11 */ 88592559Sdes 88665674Skris/* dispatch incoming messages */ 88765674Skris 88892559Sdesstatic void 88957429Smarkmprocess_message(SocketEntry *e) 89057429Smarkm{ 891294332Sdes u_int msg_len; 892294332Sdes u_char type; 893294332Sdes const u_char *cp; 894294332Sdes int r; 89598684Sdes 896294332Sdes if (sshbuf_len(e->input) < 5) 89757429Smarkm return; /* Incomplete message. */ 898294332Sdes cp = sshbuf_ptr(e->input); 899294332Sdes msg_len = PEEK_U32(cp); 90057429Smarkm if (msg_len > 256 * 1024) { 901106130Sdes close_socket(e); 90257429Smarkm return; 90357429Smarkm } 904294332Sdes if (sshbuf_len(e->input) < msg_len + 4) 90557429Smarkm return; 90698684Sdes 90798684Sdes /* move the current input to e->request */ 908294332Sdes sshbuf_reset(e->request); 909294332Sdes if ((r = sshbuf_get_stringb(e->input, e->request)) != 0 || 910294332Sdes (r = sshbuf_get_u8(e->request, &type)) != 0) 911294332Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 91257429Smarkm 91398684Sdes /* check wheter agent is locked */ 91498684Sdes if (locked && type != SSH_AGENTC_UNLOCK) { 915294332Sdes sshbuf_reset(e->request); 91698684Sdes switch (type) { 91798684Sdes case SSH_AGENTC_REQUEST_RSA_IDENTITIES: 91898684Sdes case SSH2_AGENTC_REQUEST_IDENTITIES: 91998684Sdes /* send empty lists */ 92098684Sdes no_identities(e, type); 92198684Sdes break; 92298684Sdes default: 92398684Sdes /* send a fail message for all other request types */ 924294332Sdes send_status(e, 0); 92598684Sdes } 92698684Sdes return; 92798684Sdes } 92898684Sdes 92992559Sdes debug("type %d", type); 93057429Smarkm switch (type) { 93198684Sdes case SSH_AGENTC_LOCK: 93298684Sdes case SSH_AGENTC_UNLOCK: 93398684Sdes process_lock_agent(e, type == SSH_AGENTC_LOCK); 93498684Sdes break; 935294328Sdes#ifdef WITH_SSH1 93665674Skris /* ssh1 */ 93765674Skris case SSH_AGENTC_RSA_CHALLENGE: 93865674Skris process_authentication_challenge1(e); 93965674Skris break; 94057429Smarkm case SSH_AGENTC_REQUEST_RSA_IDENTITIES: 94165674Skris process_request_identities(e, 1); 94257429Smarkm break; 94357429Smarkm case SSH_AGENTC_ADD_RSA_IDENTITY: 94498684Sdes case SSH_AGENTC_ADD_RSA_ID_CONSTRAINED: 94565674Skris process_add_identity(e, 1); 94657429Smarkm break; 94757429Smarkm case SSH_AGENTC_REMOVE_RSA_IDENTITY: 94865674Skris process_remove_identity(e, 1); 94957429Smarkm break; 950294332Sdes#endif 95157429Smarkm case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: 952294332Sdes process_remove_all_identities(e, 1); /* safe for !WITH_SSH1 */ 95357429Smarkm break; 95465674Skris /* ssh2 */ 95565674Skris case SSH2_AGENTC_SIGN_REQUEST: 95665674Skris process_sign_request2(e); 95765674Skris break; 95865674Skris case SSH2_AGENTC_REQUEST_IDENTITIES: 95965674Skris process_request_identities(e, 2); 96065674Skris break; 96165674Skris case SSH2_AGENTC_ADD_IDENTITY: 96298684Sdes case SSH2_AGENTC_ADD_ID_CONSTRAINED: 96365674Skris process_add_identity(e, 2); 96465674Skris break; 96565674Skris case SSH2_AGENTC_REMOVE_IDENTITY: 96665674Skris process_remove_identity(e, 2); 96765674Skris break; 96865674Skris case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: 96965674Skris process_remove_all_identities(e, 2); 97065674Skris break; 971204917Sdes#ifdef ENABLE_PKCS11 97292559Sdes case SSH_AGENTC_ADD_SMARTCARD_KEY: 973124211Sdes case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED: 97492559Sdes process_add_smartcard_key(e); 97592559Sdes break; 97692559Sdes case SSH_AGENTC_REMOVE_SMARTCARD_KEY: 97792559Sdes process_remove_smartcard_key(e); 97892559Sdes break; 979204917Sdes#endif /* ENABLE_PKCS11 */ 98057429Smarkm default: 98157429Smarkm /* Unknown message. Respond with failure. */ 98257429Smarkm error("Unknown message %d", type); 983294332Sdes sshbuf_reset(e->request); 984294332Sdes send_status(e, 0); 98557429Smarkm break; 98657429Smarkm } 98757429Smarkm} 98857429Smarkm 98992559Sdesstatic void 99092559Sdesnew_socket(sock_type type, int fd) 99157429Smarkm{ 992120161Snectar u_int i, old_alloc, new_alloc; 99399063Sdes 994226103Sdes if (type == AUTH_CONNECTION) { 995226103Sdes debug("xcount %d -> %d", xcount, xcount + 1); 996226103Sdes ++xcount; 997226103Sdes } 998137019Sdes set_nonblock(fd); 99957429Smarkm 100057429Smarkm if (fd > max_fd) 100157429Smarkm max_fd = fd; 100257429Smarkm 100357429Smarkm for (i = 0; i < sockets_alloc; i++) 100457429Smarkm if (sockets[i].type == AUTH_UNUSED) { 100557429Smarkm sockets[i].fd = fd; 1006294332Sdes if ((sockets[i].input = sshbuf_new()) == NULL) 1007294332Sdes fatal("%s: sshbuf_new failed", __func__); 1008294332Sdes if ((sockets[i].output = sshbuf_new()) == NULL) 1009294332Sdes fatal("%s: sshbuf_new failed", __func__); 1010294332Sdes if ((sockets[i].request = sshbuf_new()) == NULL) 1011294332Sdes fatal("%s: sshbuf_new failed", __func__); 1012120161Snectar sockets[i].type = type; 101357429Smarkm return; 101457429Smarkm } 101557429Smarkm old_alloc = sockets_alloc; 1016120161Snectar new_alloc = sockets_alloc + 10; 1017294336Sdes sockets = xreallocarray(sockets, new_alloc, sizeof(sockets[0])); 1018120161Snectar for (i = old_alloc; i < new_alloc; i++) 101957429Smarkm sockets[i].type = AUTH_UNUSED; 1020120161Snectar sockets_alloc = new_alloc; 102157429Smarkm sockets[old_alloc].fd = fd; 1022294332Sdes if ((sockets[old_alloc].input = sshbuf_new()) == NULL) 1023294332Sdes fatal("%s: sshbuf_new failed", __func__); 1024294332Sdes if ((sockets[old_alloc].output = sshbuf_new()) == NULL) 1025294332Sdes fatal("%s: sshbuf_new failed", __func__); 1026294332Sdes if ((sockets[old_alloc].request = sshbuf_new()) == NULL) 1027294332Sdes fatal("%s: sshbuf_new failed", __func__); 1028120161Snectar sockets[old_alloc].type = type; 102957429Smarkm} 103057429Smarkm 103192559Sdesstatic int 1032181111Sdesprepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl, u_int *nallocp, 1033181111Sdes struct timeval **tvpp) 103457429Smarkm{ 1035255767Sdes u_int i, sz; 103676262Sgreen int n = 0; 1037181111Sdes static struct timeval tv; 1038255767Sdes time_t deadline; 103976262Sgreen 104076262Sgreen for (i = 0; i < sockets_alloc; i++) { 104157429Smarkm switch (sockets[i].type) { 104257429Smarkm case AUTH_SOCKET: 104357429Smarkm case AUTH_CONNECTION: 1044323134Sdes n = MAXIMUM(n, sockets[i].fd); 104557429Smarkm break; 104657429Smarkm case AUTH_UNUSED: 104757429Smarkm break; 104857429Smarkm default: 104957429Smarkm fatal("Unknown socket type %d", sockets[i].type); 105057429Smarkm break; 105157429Smarkm } 105276262Sgreen } 105376262Sgreen 105476262Sgreen sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); 105592559Sdes if (*fdrp == NULL || sz > *nallocp) { 1056255767Sdes free(*fdrp); 1057255767Sdes free(*fdwp); 105876262Sgreen *fdrp = xmalloc(sz); 105976262Sgreen *fdwp = xmalloc(sz); 106092559Sdes *nallocp = sz; 106176262Sgreen } 106292559Sdes if (n < *fdl) 106392559Sdes debug("XXX shrink: %d < %d", n, *fdl); 106492559Sdes *fdl = n; 106576262Sgreen memset(*fdrp, 0, sz); 106676262Sgreen memset(*fdwp, 0, sz); 106776262Sgreen 106876262Sgreen for (i = 0; i < sockets_alloc; i++) { 106976262Sgreen switch (sockets[i].type) { 107076262Sgreen case AUTH_SOCKET: 107176262Sgreen case AUTH_CONNECTION: 107276262Sgreen FD_SET(sockets[i].fd, *fdrp); 1073294332Sdes if (sshbuf_len(sockets[i].output) > 0) 107476262Sgreen FD_SET(sockets[i].fd, *fdwp); 107576262Sgreen break; 107676262Sgreen default: 107776262Sgreen break; 107876262Sgreen } 107976262Sgreen } 1080181111Sdes deadline = reaper(); 1081181111Sdes if (parent_alive_interval != 0) 1082181111Sdes deadline = (deadline == 0) ? parent_alive_interval : 1083323134Sdes MINIMUM(deadline, parent_alive_interval); 1084181111Sdes if (deadline == 0) { 1085181111Sdes *tvpp = NULL; 1086181111Sdes } else { 1087181111Sdes tv.tv_sec = deadline; 1088181111Sdes tv.tv_usec = 0; 1089181111Sdes *tvpp = &tv; 1090181111Sdes } 109176262Sgreen return (1); 109257429Smarkm} 109357429Smarkm 109492559Sdesstatic void 109557429Smarkmafter_select(fd_set *readset, fd_set *writeset) 109657429Smarkm{ 109799063Sdes struct sockaddr_un sunaddr; 109858585Skris socklen_t slen; 109957429Smarkm char buf[1024]; 1100294332Sdes int len, sock, r; 1101204917Sdes u_int i, orig_alloc; 1102106130Sdes uid_t euid; 1103106130Sdes gid_t egid; 110457429Smarkm 1105204917Sdes for (i = 0, orig_alloc = sockets_alloc; i < orig_alloc; i++) 110657429Smarkm switch (sockets[i].type) { 110757429Smarkm case AUTH_UNUSED: 110857429Smarkm break; 110957429Smarkm case AUTH_SOCKET: 111057429Smarkm if (FD_ISSET(sockets[i].fd, readset)) { 111158585Skris slen = sizeof(sunaddr); 111276262Sgreen sock = accept(sockets[i].fd, 1113162856Sdes (struct sockaddr *)&sunaddr, &slen); 111457429Smarkm if (sock < 0) { 111592559Sdes error("accept from AUTH_SOCKET: %s", 111692559Sdes strerror(errno)); 111757429Smarkm break; 111857429Smarkm } 1119106130Sdes if (getpeereid(sock, &euid, &egid) < 0) { 1120106130Sdes error("getpeereid %d failed: %s", 1121106130Sdes sock, strerror(errno)); 1122106130Sdes close(sock); 1123106130Sdes break; 1124106130Sdes } 1125106130Sdes if ((euid != 0) && (getuid() != euid)) { 1126106130Sdes error("uid mismatch: " 1127106130Sdes "peer euid %u != uid %u", 1128106130Sdes (u_int) euid, (u_int) getuid()); 1129106130Sdes close(sock); 1130106130Sdes break; 1131106130Sdes } 113257429Smarkm new_socket(AUTH_CONNECTION, sock); 113357429Smarkm } 113457429Smarkm break; 113557429Smarkm case AUTH_CONNECTION: 1136294332Sdes if (sshbuf_len(sockets[i].output) > 0 && 113757429Smarkm FD_ISSET(sockets[i].fd, writeset)) { 1138204917Sdes len = write(sockets[i].fd, 1139294332Sdes sshbuf_ptr(sockets[i].output), 1140294332Sdes sshbuf_len(sockets[i].output)); 1141204917Sdes if (len == -1 && (errno == EAGAIN || 1142204917Sdes errno == EWOULDBLOCK || 1143204917Sdes errno == EINTR)) 1144204917Sdes continue; 114557429Smarkm if (len <= 0) { 1146106130Sdes close_socket(&sockets[i]); 114757429Smarkm break; 114857429Smarkm } 1149294332Sdes if ((r = sshbuf_consume(sockets[i].output, 1150294332Sdes len)) != 0) 1151294332Sdes fatal("%s: buffer error: %s", 1152294332Sdes __func__, ssh_err(r)); 115357429Smarkm } 115457429Smarkm if (FD_ISSET(sockets[i].fd, readset)) { 1155204917Sdes len = read(sockets[i].fd, buf, sizeof(buf)); 1156204917Sdes if (len == -1 && (errno == EAGAIN || 1157204917Sdes errno == EWOULDBLOCK || 1158204917Sdes errno == EINTR)) 1159204917Sdes continue; 116057429Smarkm if (len <= 0) { 1161106130Sdes close_socket(&sockets[i]); 116257429Smarkm break; 116357429Smarkm } 1164294332Sdes if ((r = sshbuf_put(sockets[i].input, 1165294332Sdes buf, len)) != 0) 1166294332Sdes fatal("%s: buffer error: %s", 1167294332Sdes __func__, ssh_err(r)); 1168294328Sdes explicit_bzero(buf, sizeof(buf)); 116957429Smarkm process_message(&sockets[i]); 117057429Smarkm } 117157429Smarkm break; 117257429Smarkm default: 117357429Smarkm fatal("Unknown type %d", sockets[i].type); 117457429Smarkm } 117557429Smarkm} 117657429Smarkm 117792559Sdesstatic void 1178126277Sdescleanup_socket(void) 117957429Smarkm{ 1180294328Sdes if (cleanup_pid != 0 && getpid() != cleanup_pid) 1181294328Sdes return; 1182294328Sdes debug("%s: cleanup", __func__); 118376262Sgreen if (socket_name[0]) 118476262Sgreen unlink(socket_name); 118576262Sgreen if (socket_dir[0]) 118676262Sgreen rmdir(socket_dir); 118757429Smarkm} 118857429Smarkm 1189126277Sdesvoid 119057429Smarkmcleanup_exit(int i) 119157429Smarkm{ 1192126277Sdes cleanup_socket(); 1193126277Sdes _exit(i); 119457429Smarkm} 119557429Smarkm 1196162856Sdes/*ARGSUSED*/ 119792559Sdesstatic void 119876262Sgreencleanup_handler(int sig) 119957429Smarkm{ 1200126277Sdes cleanup_socket(); 1201204917Sdes#ifdef ENABLE_PKCS11 1202204917Sdes pkcs11_terminate(); 1203204917Sdes#endif 120476262Sgreen _exit(2); 120576262Sgreen} 120676262Sgreen 120792559Sdesstatic void 1208181111Sdescheck_parent_exists(void) 120992559Sdes{ 1210226046Sdes /* 1211226046Sdes * If our parent has exited then getppid() will return (pid_t)1, 1212226046Sdes * so testing for that should be safe. 1213226046Sdes */ 1214226046Sdes if (parent_pid != -1 && getppid() != parent_pid) { 121592559Sdes /* printf("Parent has died - Authentication agent exiting.\n"); */ 1216181111Sdes cleanup_socket(); 1217181111Sdes _exit(2); 121892559Sdes } 121992559Sdes} 122092559Sdes 122192559Sdesstatic void 122276262Sgreenusage(void) 122376262Sgreen{ 1224294328Sdes fprintf(stderr, 1225323136Sdes "usage: ssh-agent [-c | -s] [-Ddx] [-a bind_address] [-E fingerprint_hash]\n" 1226323134Sdes " [-P pkcs11_whitelist] [-t life] [command [arg ...]]\n" 1227294328Sdes " ssh-agent [-c | -s] -k\n"); 122857429Smarkm exit(1); 122957429Smarkm} 123057429Smarkm 123157429Smarkmint 123257429Smarkmmain(int ac, char **av) 123357429Smarkm{ 1234294336Sdes int c_flag = 0, d_flag = 0, D_flag = 0, k_flag = 0, s_flag = 0; 1235181111Sdes int sock, fd, ch, result, saved_errno; 1236137019Sdes u_int nalloc; 123799063Sdes char *shell, *format, *pidstr, *agentsocket = NULL; 123899063Sdes fd_set *readsetp = NULL, *writesetp = NULL; 123998941Sdes#ifdef HAVE_SETRLIMIT 124076262Sgreen struct rlimit rlim; 124198941Sdes#endif 124299063Sdes extern int optind; 124399063Sdes extern char *optarg; 124457429Smarkm pid_t pid; 124599063Sdes char pidstrbuf[1 + 3 * sizeof pid]; 1246181111Sdes struct timeval *tvp = NULL; 1247197679Sdes size_t len; 1248294328Sdes mode_t prev_mask; 124957429Smarkm 1250296633Sdes ssh_malloc_init(); /* must be called before any mallocs */ 1251157019Sdes /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 1252157019Sdes sanitise_stdfd(); 1253157019Sdes 1254106130Sdes /* drop */ 1255106130Sdes setegid(getgid()); 1256106130Sdes setgid(getgid()); 1257110506Sdes setuid(geteuid()); 1258106130Sdes 1259323129Sdes platform_disable_tracing(0); /* strict=no */ 1260128460Sdes 1261294328Sdes#ifdef WITH_OPENSSL 1262221420Sdes OpenSSL_add_all_algorithms(); 1263294328Sdes#endif 126476262Sgreen 1265124211Sdes __progname = ssh_get_progname(av[0]); 126698941Sdes seed_rng(); 126798941Sdes 1268323134Sdes while ((ch = getopt(ac, av, "cDdksE:a:P:t:x")) != -1) { 126957429Smarkm switch (ch) { 1270294332Sdes case 'E': 1271294332Sdes fingerprint_hash = ssh_digest_alg_by_name(optarg); 1272294332Sdes if (fingerprint_hash == -1) 1273294332Sdes fatal("Invalid hash algorithm \"%s\"", optarg); 1274294332Sdes break; 127557429Smarkm case 'c': 127657429Smarkm if (s_flag) 127757429Smarkm usage(); 127857429Smarkm c_flag++; 127957429Smarkm break; 128057429Smarkm case 'k': 128157429Smarkm k_flag++; 128257429Smarkm break; 1283323134Sdes case 'P': 1284323134Sdes if (pkcs11_whitelist != NULL) 1285323134Sdes fatal("-P option already specified"); 1286323134Sdes pkcs11_whitelist = xstrdup(optarg); 1287323134Sdes break; 128857429Smarkm case 's': 128957429Smarkm if (c_flag) 129057429Smarkm usage(); 129157429Smarkm s_flag++; 129257429Smarkm break; 129392559Sdes case 'd': 1294294336Sdes if (d_flag || D_flag) 129592559Sdes usage(); 129692559Sdes d_flag++; 129792559Sdes break; 1298294336Sdes case 'D': 1299294336Sdes if (d_flag || D_flag) 1300294336Sdes usage(); 1301294336Sdes D_flag++; 1302294336Sdes break; 130398684Sdes case 'a': 130498684Sdes agentsocket = optarg; 130598684Sdes break; 1306113911Sdes case 't': 1307113911Sdes if ((lifetime = convtime(optarg)) == -1) { 1308113911Sdes fprintf(stderr, "Invalid lifetime\n"); 1309113911Sdes usage(); 1310113911Sdes } 1311113911Sdes break; 1312226103Sdes case 'x': 1313226103Sdes xcount = 0; 1314226103Sdes break; 131557429Smarkm default: 131657429Smarkm usage(); 131757429Smarkm } 131857429Smarkm } 131957429Smarkm ac -= optind; 132057429Smarkm av += optind; 132157429Smarkm 1322294336Sdes if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag)) 132357429Smarkm usage(); 132457429Smarkm 1325323134Sdes if (pkcs11_whitelist == NULL) 1326323134Sdes pkcs11_whitelist = xstrdup(DEFAULT_PKCS11_WHITELIST); 1327323134Sdes 132898684Sdes if (ac == 0 && !c_flag && !s_flag) { 132957429Smarkm shell = getenv("SHELL"); 1330197679Sdes if (shell != NULL && (len = strlen(shell)) > 2 && 1331197679Sdes strncmp(shell + len - 3, "csh", 3) == 0) 133257429Smarkm c_flag = 1; 133357429Smarkm } 133457429Smarkm if (k_flag) { 1335162856Sdes const char *errstr = NULL; 1336162856Sdes 133757429Smarkm pidstr = getenv(SSH_AGENTPID_ENV_NAME); 133857429Smarkm if (pidstr == NULL) { 133957429Smarkm fprintf(stderr, "%s not set, cannot kill agent\n", 134076262Sgreen SSH_AGENTPID_ENV_NAME); 134157429Smarkm exit(1); 134257429Smarkm } 1343162856Sdes pid = (int)strtonum(pidstr, 2, INT_MAX, &errstr); 1344162856Sdes if (errstr) { 1345162856Sdes fprintf(stderr, 1346162856Sdes "%s=\"%s\", which is not a good PID: %s\n", 1347162856Sdes SSH_AGENTPID_ENV_NAME, pidstr, errstr); 134857429Smarkm exit(1); 134957429Smarkm } 135057429Smarkm if (kill(pid, SIGTERM) == -1) { 135157429Smarkm perror("kill"); 135257429Smarkm exit(1); 135357429Smarkm } 135457429Smarkm format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; 135557429Smarkm printf(format, SSH_AUTHSOCKET_ENV_NAME); 135657429Smarkm printf(format, SSH_AGENTPID_ENV_NAME); 135798684Sdes printf("echo Agent pid %ld killed;\n", (long)pid); 135857429Smarkm exit(0); 135957429Smarkm } 136057429Smarkm parent_pid = getpid(); 136157429Smarkm 136298684Sdes if (agentsocket == NULL) { 136398684Sdes /* Create private directory for agent socket */ 1364221420Sdes mktemp_proto(socket_dir, sizeof(socket_dir)); 136598684Sdes if (mkdtemp(socket_dir) == NULL) { 136698684Sdes perror("mkdtemp: private socket dir"); 136798684Sdes exit(1); 136898684Sdes } 136998684Sdes snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir, 137098684Sdes (long)parent_pid); 137198684Sdes } else { 137298684Sdes /* Try to use specified agent socket */ 137398684Sdes socket_dir[0] = '\0'; 137498684Sdes strlcpy(socket_name, agentsocket, sizeof socket_name); 137557429Smarkm } 137657429Smarkm 137757429Smarkm /* 137857429Smarkm * Create socket early so it will exist before command gets run from 137957429Smarkm * the parent. 138057429Smarkm */ 1381294328Sdes prev_mask = umask(0177); 1382294328Sdes sock = unix_listener(socket_name, SSH_LISTEN_BACKLOG, 0); 138357429Smarkm if (sock < 0) { 1384294328Sdes /* XXX - unix_listener() calls error() not perror() */ 1385147005Sdes *socket_name = '\0'; /* Don't unlink any existing file */ 138657429Smarkm cleanup_exit(1); 138757429Smarkm } 138898941Sdes umask(prev_mask); 138976262Sgreen 139057429Smarkm /* 139157429Smarkm * Fork, and have the parent execute the command, if any, or present 139257429Smarkm * the socket data. The child continues as the authentication agent. 139357429Smarkm */ 1394294336Sdes if (D_flag || d_flag) { 1395294336Sdes log_init(__progname, 1396294336Sdes d_flag ? SYSLOG_LEVEL_DEBUG3 : SYSLOG_LEVEL_INFO, 1397294336Sdes SYSLOG_FACILITY_AUTH, 1); 139892559Sdes format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; 139992559Sdes printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, 140092559Sdes SSH_AUTHSOCKET_ENV_NAME); 140198684Sdes printf("echo Agent pid %ld;\n", (long)parent_pid); 1402296633Sdes fflush(stdout); 140392559Sdes goto skip; 140492559Sdes } 140557429Smarkm pid = fork(); 140657429Smarkm if (pid == -1) { 140757429Smarkm perror("fork"); 140892559Sdes cleanup_exit(1); 140957429Smarkm } 141057429Smarkm if (pid != 0) { /* Parent - execute the given command. */ 141157429Smarkm close(sock); 141298684Sdes snprintf(pidstrbuf, sizeof pidstrbuf, "%ld", (long)pid); 141357429Smarkm if (ac == 0) { 141457429Smarkm format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; 141557429Smarkm printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, 141676262Sgreen SSH_AUTHSOCKET_ENV_NAME); 141757429Smarkm printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf, 141876262Sgreen SSH_AGENTPID_ENV_NAME); 141998684Sdes printf("echo Agent pid %ld;\n", (long)pid); 142057429Smarkm exit(0); 142157429Smarkm } 142269591Sgreen if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 || 142369591Sgreen setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) { 142469591Sgreen perror("setenv"); 142569591Sgreen exit(1); 142669591Sgreen } 142757429Smarkm execvp(av[0], av); 142857429Smarkm perror(av[0]); 142957429Smarkm exit(1); 143057429Smarkm } 143192559Sdes /* child */ 143292559Sdes log_init(__progname, SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 0); 143392559Sdes 143492559Sdes if (setsid() == -1) { 143592559Sdes error("setsid: %s", strerror(errno)); 143692559Sdes cleanup_exit(1); 143792559Sdes } 143892559Sdes 143992559Sdes (void)chdir("/"); 1440113911Sdes if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { 1441113911Sdes /* XXX might close listen socket */ 1442113911Sdes (void)dup2(fd, STDIN_FILENO); 1443113911Sdes (void)dup2(fd, STDOUT_FILENO); 1444113911Sdes (void)dup2(fd, STDERR_FILENO); 1445113911Sdes if (fd > 2) 1446113911Sdes close(fd); 1447113911Sdes } 144857429Smarkm 144998941Sdes#ifdef HAVE_SETRLIMIT 145076262Sgreen /* deny core dumps, since memory contains unencrypted private keys */ 145176262Sgreen rlim.rlim_cur = rlim.rlim_max = 0; 145276262Sgreen if (setrlimit(RLIMIT_CORE, &rlim) < 0) { 145392559Sdes error("setrlimit RLIMIT_CORE: %s", strerror(errno)); 145476262Sgreen cleanup_exit(1); 145576262Sgreen } 145698941Sdes#endif 145792559Sdes 145892559Sdesskip: 1459204917Sdes 1460294328Sdes cleanup_pid = getpid(); 1461294328Sdes 1462204917Sdes#ifdef ENABLE_PKCS11 1463204917Sdes pkcs11_init(0); 1464204917Sdes#endif 146557429Smarkm new_socket(AUTH_SOCKET, sock); 1466181111Sdes if (ac > 0) 1467181111Sdes parent_alive_interval = 10; 146865674Skris idtab_init(); 1469255767Sdes signal(SIGPIPE, SIG_IGN); 1470294336Sdes signal(SIGINT, (d_flag | D_flag) ? cleanup_handler : SIG_IGN); 147176262Sgreen signal(SIGHUP, cleanup_handler); 147276262Sgreen signal(SIGTERM, cleanup_handler); 147392559Sdes nalloc = 0; 147492559Sdes 1475323134Sdes if (pledge("stdio rpath cpath unix id proc exec", NULL) == -1) 1476296633Sdes fatal("%s: pledge: %s", __progname, strerror(errno)); 1477296633Sdes platform_pledge_agent(); 1478296633Sdes 147957429Smarkm while (1) { 1480181111Sdes prepare_select(&readsetp, &writesetp, &max_fd, &nalloc, &tvp); 1481181111Sdes result = select(max_fd + 1, readsetp, writesetp, NULL, tvp); 1482181111Sdes saved_errno = errno; 1483181111Sdes if (parent_alive_interval != 0) 1484181111Sdes check_parent_exists(); 1485181111Sdes (void) reaper(); /* remove expired keys */ 1486181111Sdes if (result < 0) { 1487181111Sdes if (saved_errno == EINTR) 148857429Smarkm continue; 1489181111Sdes fatal("select: %s", strerror(saved_errno)); 1490181111Sdes } else if (result > 0) 1491181111Sdes after_select(readsetp, writesetp); 149257429Smarkm } 149357429Smarkm /* NOTREACHED */ 149457429Smarkm} 1495