1*ee116499SAntonio Huete Jimenez /* $OpenBSD: sshconnect2.c,v 1.361 2022/09/17 10:33:18 djm Exp $ */ 218de8d7fSPeter Avalos /* 318de8d7fSPeter Avalos * Copyright (c) 2000 Markus Friedl. All rights reserved. 4cb5eb4f1SPeter Avalos * Copyright (c) 2008 Damien Miller. All rights reserved. 518de8d7fSPeter Avalos * 618de8d7fSPeter Avalos * Redistribution and use in source and binary forms, with or without 718de8d7fSPeter Avalos * modification, are permitted provided that the following conditions 818de8d7fSPeter Avalos * are met: 918de8d7fSPeter Avalos * 1. Redistributions of source code must retain the above copyright 1018de8d7fSPeter Avalos * notice, this list of conditions and the following disclaimer. 1118de8d7fSPeter Avalos * 2. Redistributions in binary form must reproduce the above copyright 1218de8d7fSPeter Avalos * notice, this list of conditions and the following disclaimer in the 1318de8d7fSPeter Avalos * documentation and/or other materials provided with the distribution. 1418de8d7fSPeter Avalos * 1518de8d7fSPeter Avalos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1618de8d7fSPeter Avalos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1718de8d7fSPeter Avalos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1818de8d7fSPeter Avalos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1918de8d7fSPeter Avalos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2018de8d7fSPeter Avalos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2118de8d7fSPeter Avalos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2218de8d7fSPeter Avalos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2318de8d7fSPeter Avalos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2418de8d7fSPeter Avalos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2518de8d7fSPeter Avalos */ 2618de8d7fSPeter Avalos 2718de8d7fSPeter Avalos #include "includes.h" 2818de8d7fSPeter Avalos 2918de8d7fSPeter Avalos #include <sys/types.h> 3018de8d7fSPeter Avalos #include <sys/socket.h> 3118de8d7fSPeter Avalos #include <sys/wait.h> 3218de8d7fSPeter Avalos #include <sys/stat.h> 3318de8d7fSPeter Avalos 3418de8d7fSPeter Avalos #include <errno.h> 35856ea928SPeter Avalos #include <fcntl.h> 3650a69bb5SSascha Wildner #include <limits.h> 3718de8d7fSPeter Avalos #include <netdb.h> 3818de8d7fSPeter Avalos #include <pwd.h> 3918de8d7fSPeter Avalos #include <signal.h> 4018de8d7fSPeter Avalos #include <stdio.h> 4118de8d7fSPeter Avalos #include <string.h> 420cbfa66cSDaniel Fojt #include <stdarg.h> 4318de8d7fSPeter Avalos #include <unistd.h> 4436e94dc5SPeter Avalos #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) 4518de8d7fSPeter Avalos #include <vis.h> 4618de8d7fSPeter Avalos #endif 4718de8d7fSPeter Avalos 4818de8d7fSPeter Avalos #include "openbsd-compat/sys-queue.h" 4918de8d7fSPeter Avalos 5018de8d7fSPeter Avalos #include "xmalloc.h" 5118de8d7fSPeter Avalos #include "ssh.h" 5218de8d7fSPeter Avalos #include "ssh2.h" 53664f4763Szrj #include "sshbuf.h" 5418de8d7fSPeter Avalos #include "packet.h" 5518de8d7fSPeter Avalos #include "compat.h" 5618de8d7fSPeter Avalos #include "cipher.h" 57664f4763Szrj #include "sshkey.h" 5818de8d7fSPeter Avalos #include "kex.h" 5918de8d7fSPeter Avalos #include "myproposal.h" 6018de8d7fSPeter Avalos #include "sshconnect.h" 6118de8d7fSPeter Avalos #include "authfile.h" 6218de8d7fSPeter Avalos #include "dh.h" 6318de8d7fSPeter Avalos #include "authfd.h" 6418de8d7fSPeter Avalos #include "log.h" 6518de8d7fSPeter Avalos #include "misc.h" 6636e94dc5SPeter Avalos #include "readconf.h" 6718de8d7fSPeter Avalos #include "match.h" 6818de8d7fSPeter Avalos #include "dispatch.h" 6918de8d7fSPeter Avalos #include "canohost.h" 7018de8d7fSPeter Avalos #include "msg.h" 7118de8d7fSPeter Avalos #include "pathnames.h" 7218de8d7fSPeter Avalos #include "uidswap.h" 739f304aafSPeter Avalos #include "hostfile.h" 74e9778795SPeter Avalos #include "ssherr.h" 75e9778795SPeter Avalos #include "utf8.h" 760cbfa66cSDaniel Fojt #include "ssh-sk.h" 770cbfa66cSDaniel Fojt #include "sk-api.h" 7818de8d7fSPeter Avalos 7918de8d7fSPeter Avalos #ifdef GSSAPI 8018de8d7fSPeter Avalos #include "ssh-gss.h" 8118de8d7fSPeter Avalos #endif 8218de8d7fSPeter Avalos 8318de8d7fSPeter Avalos /* import */ 8418de8d7fSPeter Avalos extern char *client_version_string; 8518de8d7fSPeter Avalos extern char *server_version_string; 8618de8d7fSPeter Avalos extern Options options; 8718de8d7fSPeter Avalos 8818de8d7fSPeter Avalos /* 8918de8d7fSPeter Avalos * SSH2 key exchange 9018de8d7fSPeter Avalos */ 9118de8d7fSPeter Avalos 9250a69bb5SSascha Wildner static char *xxx_host; 9350a69bb5SSascha Wildner static struct sockaddr *xxx_hostaddr; 9450a69bb5SSascha Wildner static const struct ssh_conn_info *xxx_conn_info; 9518de8d7fSPeter Avalos 9618de8d7fSPeter Avalos static int 97ce74bacaSMatthew Dillon verify_host_key_callback(struct sshkey *hostkey, struct ssh *ssh) 9818de8d7fSPeter Avalos { 99*ee116499SAntonio Huete Jimenez int r; 100*ee116499SAntonio Huete Jimenez 101*ee116499SAntonio Huete Jimenez if ((r = sshkey_check_rsa_length(hostkey, 102*ee116499SAntonio Huete Jimenez options.required_rsa_size)) != 0) 103*ee116499SAntonio Huete Jimenez fatal_r(r, "Bad server host key"); 10450a69bb5SSascha Wildner if (verify_host_key(xxx_host, xxx_hostaddr, hostkey, 10550a69bb5SSascha Wildner xxx_conn_info) == -1) 10618de8d7fSPeter Avalos fatal("Host key verification failed."); 10718de8d7fSPeter Avalos return 0; 10818de8d7fSPeter Avalos } 10918de8d7fSPeter Avalos 11050a69bb5SSascha Wildner /* Returns the first item from a comma-separated algorithm list */ 1119f304aafSPeter Avalos static char * 11250a69bb5SSascha Wildner first_alg(const char *algs) 1139f304aafSPeter Avalos { 11450a69bb5SSascha Wildner char *ret, *cp; 11550a69bb5SSascha Wildner 11650a69bb5SSascha Wildner ret = xstrdup(algs); 11750a69bb5SSascha Wildner if ((cp = strchr(ret, ',')) != NULL) 11850a69bb5SSascha Wildner *cp = '\0'; 11950a69bb5SSascha Wildner return ret; 12050a69bb5SSascha Wildner } 12150a69bb5SSascha Wildner 12250a69bb5SSascha Wildner static char * 12350a69bb5SSascha Wildner order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port, 12450a69bb5SSascha Wildner const struct ssh_conn_info *cinfo) 12550a69bb5SSascha Wildner { 12650a69bb5SSascha Wildner char *oavail = NULL, *avail = NULL, *first = NULL, *last = NULL; 12750a69bb5SSascha Wildner char *alg = NULL, *hostname = NULL, *ret = NULL, *best = NULL; 1289f304aafSPeter Avalos size_t maxlen; 12950a69bb5SSascha Wildner struct hostkeys *hostkeys = NULL; 1309f304aafSPeter Avalos int ktype; 1311c188a7fSPeter Avalos u_int i; 1329f304aafSPeter Avalos 1339f304aafSPeter Avalos /* Find all hostkeys for this hostname */ 1349f304aafSPeter Avalos get_hostfile_hostname_ipaddr(host, hostaddr, port, &hostname, NULL); 1359f304aafSPeter Avalos hostkeys = init_hostkeys(); 1361c188a7fSPeter Avalos for (i = 0; i < options.num_user_hostfiles; i++) 13750a69bb5SSascha Wildner load_hostkeys(hostkeys, hostname, options.user_hostfiles[i], 0); 13850a69bb5SSascha Wildner for (i = 0; i < options.num_system_hostfiles; i++) { 13950a69bb5SSascha Wildner load_hostkeys(hostkeys, hostname, 14050a69bb5SSascha Wildner options.system_hostfiles[i], 0); 14150a69bb5SSascha Wildner } 14250a69bb5SSascha Wildner if (options.known_hosts_command != NULL) { 14350a69bb5SSascha Wildner load_hostkeys_command(hostkeys, options.known_hosts_command, 14450a69bb5SSascha Wildner "ORDER", cinfo, NULL, host); 14550a69bb5SSascha Wildner } 14650a69bb5SSascha Wildner /* 14750a69bb5SSascha Wildner * If a plain public key exists that matches the type of the best 14850a69bb5SSascha Wildner * preference HostkeyAlgorithms, then use the whole list as is. 14950a69bb5SSascha Wildner * Note that we ignore whether the best preference algorithm is a 15050a69bb5SSascha Wildner * certificate type, as sshconnect.c will downgrade certs to 15150a69bb5SSascha Wildner * plain keys if necessary. 15250a69bb5SSascha Wildner */ 15350a69bb5SSascha Wildner best = first_alg(options.hostkeyalgorithms); 15450a69bb5SSascha Wildner if (lookup_key_in_hostkeys_by_type(hostkeys, 15550a69bb5SSascha Wildner sshkey_type_plain(sshkey_type_from_name(best)), 15650a69bb5SSascha Wildner sshkey_ecdsa_nid_from_name(best), NULL)) { 15750a69bb5SSascha Wildner debug3_f("have matching best-preference key type %s, " 15850a69bb5SSascha Wildner "using HostkeyAlgorithms verbatim", best); 15950a69bb5SSascha Wildner ret = xstrdup(options.hostkeyalgorithms); 16050a69bb5SSascha Wildner goto out; 16150a69bb5SSascha Wildner } 1629f304aafSPeter Avalos 16350a69bb5SSascha Wildner /* 16450a69bb5SSascha Wildner * Otherwise, prefer the host key algorithms that match known keys 16550a69bb5SSascha Wildner * while keeping the ordering of HostkeyAlgorithms as much as possible. 16650a69bb5SSascha Wildner */ 1670cbfa66cSDaniel Fojt oavail = avail = xstrdup(options.hostkeyalgorithms); 1689f304aafSPeter Avalos maxlen = strlen(avail) + 1; 1699f304aafSPeter Avalos first = xmalloc(maxlen); 1709f304aafSPeter Avalos last = xmalloc(maxlen); 1719f304aafSPeter Avalos *first = *last = '\0'; 1729f304aafSPeter Avalos 1739f304aafSPeter Avalos #define ALG_APPEND(to, from) \ 1749f304aafSPeter Avalos do { \ 1759f304aafSPeter Avalos if (*to != '\0') \ 1769f304aafSPeter Avalos strlcat(to, ",", maxlen); \ 1779f304aafSPeter Avalos strlcat(to, from, maxlen); \ 1789f304aafSPeter Avalos } while (0) 1799f304aafSPeter Avalos 1809f304aafSPeter Avalos while ((alg = strsep(&avail, ",")) && *alg != '\0') { 181e9778795SPeter Avalos if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) 18250a69bb5SSascha Wildner fatal_f("unknown alg %s", alg); 18350a69bb5SSascha Wildner /* 18450a69bb5SSascha Wildner * If we have a @cert-authority marker in known_hosts then 18550a69bb5SSascha Wildner * prefer all certificate algorithms. 18650a69bb5SSascha Wildner */ 18750a69bb5SSascha Wildner if (sshkey_type_is_cert(ktype) && 18850a69bb5SSascha Wildner lookup_marker_in_hostkeys(hostkeys, MRK_CA)) { 1899f304aafSPeter Avalos ALG_APPEND(first, alg); 19050a69bb5SSascha Wildner continue; 19150a69bb5SSascha Wildner } 19250a69bb5SSascha Wildner /* If the key appears in known_hosts then prefer it */ 19350a69bb5SSascha Wildner if (lookup_key_in_hostkeys_by_type(hostkeys, 19450a69bb5SSascha Wildner sshkey_type_plain(ktype), 19550a69bb5SSascha Wildner sshkey_ecdsa_nid_from_name(alg), NULL)) { 19650a69bb5SSascha Wildner ALG_APPEND(first, alg); 19750a69bb5SSascha Wildner continue; 19850a69bb5SSascha Wildner } 19950a69bb5SSascha Wildner /* Otherwise, put it last */ 2009f304aafSPeter Avalos ALG_APPEND(last, alg); 2019f304aafSPeter Avalos } 2029f304aafSPeter Avalos #undef ALG_APPEND 203e9778795SPeter Avalos xasprintf(&ret, "%s%s%s", first, 204e9778795SPeter Avalos (*first == '\0' || *last == '\0') ? "" : ",", last); 2059f304aafSPeter Avalos if (*first != '\0') 20650a69bb5SSascha Wildner debug3_f("prefer hostkeyalgs: %s", first); 20750a69bb5SSascha Wildner else 20850a69bb5SSascha Wildner debug3_f("no algorithms matched; accept original"); 20950a69bb5SSascha Wildner out: 21050a69bb5SSascha Wildner free(best); 21136e94dc5SPeter Avalos free(first); 21236e94dc5SPeter Avalos free(last); 21336e94dc5SPeter Avalos free(hostname); 21436e94dc5SPeter Avalos free(oavail); 2159f304aafSPeter Avalos free_hostkeys(hostkeys); 2169f304aafSPeter Avalos 2179f304aafSPeter Avalos return ret; 2189f304aafSPeter Avalos } 2199f304aafSPeter Avalos 22018de8d7fSPeter Avalos void 22150a69bb5SSascha Wildner ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port, 22250a69bb5SSascha Wildner const struct ssh_conn_info *cinfo) 22318de8d7fSPeter Avalos { 22436e94dc5SPeter Avalos char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; 225664f4763Szrj char *s, *all_key; 226*ee116499SAntonio Huete Jimenez char *prop_kex = NULL, *prop_enc = NULL, *prop_hostkey = NULL; 2270cbfa66cSDaniel Fojt int r, use_known_hosts_order = 0; 22818de8d7fSPeter Avalos 22918de8d7fSPeter Avalos xxx_host = host; 23018de8d7fSPeter Avalos xxx_hostaddr = hostaddr; 23150a69bb5SSascha Wildner xxx_conn_info = cinfo; 23218de8d7fSPeter Avalos 2330cbfa66cSDaniel Fojt /* 2340cbfa66cSDaniel Fojt * If the user has not specified HostkeyAlgorithms, or has only 2350cbfa66cSDaniel Fojt * appended or removed algorithms from that list then prefer algorithms 2360cbfa66cSDaniel Fojt * that are in the list that are supported by known_hosts keys. 2370cbfa66cSDaniel Fojt */ 2380cbfa66cSDaniel Fojt if (options.hostkeyalgorithms == NULL || 2390cbfa66cSDaniel Fojt options.hostkeyalgorithms[0] == '-' || 2400cbfa66cSDaniel Fojt options.hostkeyalgorithms[0] == '+') 2410cbfa66cSDaniel Fojt use_known_hosts_order = 1; 2420cbfa66cSDaniel Fojt 2430cbfa66cSDaniel Fojt /* Expand or fill in HostkeyAlgorithms */ 2440cbfa66cSDaniel Fojt all_key = sshkey_alg_list(0, 0, 1, ','); 24550a69bb5SSascha Wildner if ((r = kex_assemble_names(&options.hostkeyalgorithms, 24650a69bb5SSascha Wildner kex_default_pk_alg(), all_key)) != 0) 24750a69bb5SSascha Wildner fatal_fr(r, "kex_assemble_namelist"); 2480cbfa66cSDaniel Fojt free(all_key); 2490cbfa66cSDaniel Fojt 250e9778795SPeter Avalos if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL) 25150a69bb5SSascha Wildner fatal_f("kex_names_cat"); 252*ee116499SAntonio Huete Jimenez myproposal[PROPOSAL_KEX_ALGS] = prop_kex = compat_kex_proposal(ssh, s); 25318de8d7fSPeter Avalos myproposal[PROPOSAL_ENC_ALGS_CTOS] = 254*ee116499SAntonio Huete Jimenez myproposal[PROPOSAL_ENC_ALGS_STOC] = prop_enc = 25550a69bb5SSascha Wildner compat_cipher_proposal(ssh, options.ciphers); 25618de8d7fSPeter Avalos myproposal[PROPOSAL_COMP_ALGS_CTOS] = 2570cbfa66cSDaniel Fojt myproposal[PROPOSAL_COMP_ALGS_STOC] = 2580cbfa66cSDaniel Fojt (char *)compression_alg_list(options.compression); 25918de8d7fSPeter Avalos myproposal[PROPOSAL_MAC_ALGS_CTOS] = 26018de8d7fSPeter Avalos myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; 2610cbfa66cSDaniel Fojt if (use_known_hosts_order) { 2620cbfa66cSDaniel Fojt /* Query known_hosts and prefer algorithms that appear there */ 263*ee116499SAntonio Huete Jimenez myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = prop_hostkey = 26450a69bb5SSascha Wildner compat_pkalg_proposal(ssh, 26550a69bb5SSascha Wildner order_hostkeyalgs(host, hostaddr, port, cinfo)); 2660cbfa66cSDaniel Fojt } else { 2670cbfa66cSDaniel Fojt /* Use specified HostkeyAlgorithms exactly */ 268*ee116499SAntonio Huete Jimenez myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = prop_hostkey = 26950a69bb5SSascha Wildner compat_pkalg_proposal(ssh, options.hostkeyalgorithms); 2709f304aafSPeter Avalos } 27118de8d7fSPeter Avalos 27236e94dc5SPeter Avalos if (options.rekey_limit || options.rekey_interval) 273664f4763Szrj ssh_packet_set_rekey_limits(ssh, options.rekey_limit, 274ce74bacaSMatthew Dillon options.rekey_interval); 27518de8d7fSPeter Avalos 27618de8d7fSPeter Avalos /* start key exchange */ 277664f4763Szrj if ((r = kex_setup(ssh, myproposal)) != 0) 27850a69bb5SSascha Wildner fatal_r(r, "kex_setup"); 27936e94dc5SPeter Avalos #ifdef WITH_OPENSSL 280664f4763Szrj ssh->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_client; 281664f4763Szrj ssh->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_client; 282664f4763Szrj ssh->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_client; 283664f4763Szrj ssh->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_client; 284664f4763Szrj ssh->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_client; 285664f4763Szrj ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; 286664f4763Szrj ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; 287e9778795SPeter Avalos # ifdef OPENSSL_HAS_ECC 288664f4763Szrj ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; 28936e94dc5SPeter Avalos # endif 290e9778795SPeter Avalos #endif 291664f4763Szrj ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; 29250a69bb5SSascha Wildner ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client; 293664f4763Szrj ssh->kex->verify_host_key=&verify_host_key_callback; 29418de8d7fSPeter Avalos 295664f4763Szrj ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done); 29618de8d7fSPeter Avalos 297e9778795SPeter Avalos /* remove ext-info from the KEX proposals for rekeying */ 298e9778795SPeter Avalos myproposal[PROPOSAL_KEX_ALGS] = 29950a69bb5SSascha Wildner compat_kex_proposal(ssh, options.kex_algorithms); 300664f4763Szrj if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0) 30150a69bb5SSascha Wildner fatal_r(r, "kex_prop2buf"); 30218de8d7fSPeter Avalos 30318de8d7fSPeter Avalos #ifdef DEBUG_KEXDH 30418de8d7fSPeter Avalos /* send 1st encrypted/maced/compressed message */ 305664f4763Szrj if ((r = sshpkt_start(ssh, SSH2_MSG_IGNORE)) != 0 || 306664f4763Szrj (r = sshpkt_put_cstring(ssh, "markus")) != 0 || 307664f4763Szrj (r = sshpkt_send(ssh)) != 0 || 308664f4763Szrj (r = ssh_packet_write_wait(ssh)) != 0) 30950a69bb5SSascha Wildner fatal_fr(r, "send packet"); 31018de8d7fSPeter Avalos #endif 311*ee116499SAntonio Huete Jimenez /* Free only parts of proposal that were dynamically allocated here. */ 312*ee116499SAntonio Huete Jimenez free(prop_kex); 313*ee116499SAntonio Huete Jimenez free(prop_enc); 314*ee116499SAntonio Huete Jimenez free(prop_hostkey); 31518de8d7fSPeter Avalos } 31618de8d7fSPeter Avalos 31718de8d7fSPeter Avalos /* 31818de8d7fSPeter Avalos * Authenticate user 31918de8d7fSPeter Avalos */ 32018de8d7fSPeter Avalos 321e9778795SPeter Avalos typedef struct cauthctxt Authctxt; 322e9778795SPeter Avalos typedef struct cauthmethod Authmethod; 32318de8d7fSPeter Avalos typedef struct identity Identity; 32418de8d7fSPeter Avalos typedef struct idlist Idlist; 32518de8d7fSPeter Avalos 32618de8d7fSPeter Avalos struct identity { 32718de8d7fSPeter Avalos TAILQ_ENTRY(identity) next; 328e9778795SPeter Avalos int agent_fd; /* >=0 if agent supports key */ 329e9778795SPeter Avalos struct sshkey *key; /* public/private key */ 33018de8d7fSPeter Avalos char *filename; /* comment for agent-only keys */ 33118de8d7fSPeter Avalos int tried; 33218de8d7fSPeter Avalos int isprivate; /* key points to the private key */ 33336e94dc5SPeter Avalos int userprovided; 33418de8d7fSPeter Avalos }; 33518de8d7fSPeter Avalos TAILQ_HEAD(idlist, identity); 33618de8d7fSPeter Avalos 337e9778795SPeter Avalos struct cauthctxt { 33818de8d7fSPeter Avalos const char *server_user; 33918de8d7fSPeter Avalos const char *local_user; 34018de8d7fSPeter Avalos const char *host; 34118de8d7fSPeter Avalos const char *service; 342e9778795SPeter Avalos struct cauthmethod *method; 343856ea928SPeter Avalos sig_atomic_t success; 34418de8d7fSPeter Avalos char *authlist; 345664f4763Szrj #ifdef GSSAPI 346664f4763Szrj /* gssapi */ 347664f4763Szrj gss_OID_set gss_supported_mechs; 348664f4763Szrj u_int mech_tried; 349664f4763Szrj #endif 35018de8d7fSPeter Avalos /* pubkey */ 351e9778795SPeter Avalos struct idlist keys; 352e9778795SPeter Avalos int agent_fd; 35318de8d7fSPeter Avalos /* hostbased */ 35418de8d7fSPeter Avalos Sensitive *sensitive; 355e9778795SPeter Avalos char *oktypes, *ktypes; 356e9778795SPeter Avalos const char *active_ktype; 35718de8d7fSPeter Avalos /* kbd-interactive */ 35818de8d7fSPeter Avalos int info_req_seen; 359664f4763Szrj int attempt_kbdint; 360664f4763Szrj /* password */ 361664f4763Szrj int attempt_passwd; 36218de8d7fSPeter Avalos /* generic */ 36318de8d7fSPeter Avalos void *methoddata; 36418de8d7fSPeter Avalos }; 365e9778795SPeter Avalos 366e9778795SPeter Avalos struct cauthmethod { 36718de8d7fSPeter Avalos char *name; /* string to compare against server's list */ 368664f4763Szrj int (*userauth)(struct ssh *ssh); 369664f4763Szrj void (*cleanup)(struct ssh *ssh); 37018de8d7fSPeter Avalos int *enabled; /* flag in option struct that enables method */ 37118de8d7fSPeter Avalos int *batch_flag; /* flag in option struct that disables method */ 37218de8d7fSPeter Avalos }; 37318de8d7fSPeter Avalos 374664f4763Szrj static int input_userauth_service_accept(int, u_int32_t, struct ssh *); 375664f4763Szrj static int input_userauth_ext_info(int, u_int32_t, struct ssh *); 376664f4763Szrj static int input_userauth_success(int, u_int32_t, struct ssh *); 377664f4763Szrj static int input_userauth_failure(int, u_int32_t, struct ssh *); 378664f4763Szrj static int input_userauth_banner(int, u_int32_t, struct ssh *); 379664f4763Szrj static int input_userauth_error(int, u_int32_t, struct ssh *); 380664f4763Szrj static int input_userauth_info_req(int, u_int32_t, struct ssh *); 381664f4763Szrj static int input_userauth_pk_ok(int, u_int32_t, struct ssh *); 382664f4763Szrj static int input_userauth_passwd_changereq(int, u_int32_t, struct ssh *); 38318de8d7fSPeter Avalos 384664f4763Szrj static int userauth_none(struct ssh *); 385664f4763Szrj static int userauth_pubkey(struct ssh *); 386664f4763Szrj static int userauth_passwd(struct ssh *); 387664f4763Szrj static int userauth_kbdint(struct ssh *); 388664f4763Szrj static int userauth_hostbased(struct ssh *); 38918de8d7fSPeter Avalos 39018de8d7fSPeter Avalos #ifdef GSSAPI 391664f4763Szrj static int userauth_gssapi(struct ssh *); 392664f4763Szrj static void userauth_gssapi_cleanup(struct ssh *); 393664f4763Szrj static int input_gssapi_response(int type, u_int32_t, struct ssh *); 394664f4763Szrj static int input_gssapi_token(int type, u_int32_t, struct ssh *); 395664f4763Szrj static int input_gssapi_error(int, u_int32_t, struct ssh *); 396664f4763Szrj static int input_gssapi_errtok(int, u_int32_t, struct ssh *); 39718de8d7fSPeter Avalos #endif 39818de8d7fSPeter Avalos 399664f4763Szrj void userauth(struct ssh *, char *); 40018de8d7fSPeter Avalos 401664f4763Szrj static void pubkey_cleanup(struct ssh *); 402664f4763Szrj static int sign_and_send_pubkey(struct ssh *ssh, Identity *); 403*ee116499SAntonio Huete Jimenez static void pubkey_prepare(struct ssh *, Authctxt *); 404ce74bacaSMatthew Dillon static void pubkey_reset(Authctxt *); 405ce74bacaSMatthew Dillon static struct sshkey *load_identity_file(Identity *); 40618de8d7fSPeter Avalos 40718de8d7fSPeter Avalos static Authmethod *authmethod_get(char *authlist); 40818de8d7fSPeter Avalos static Authmethod *authmethod_lookup(const char *name); 40918de8d7fSPeter Avalos static char *authmethods_get(void); 41018de8d7fSPeter Avalos 41118de8d7fSPeter Avalos Authmethod authmethods[] = { 41218de8d7fSPeter Avalos #ifdef GSSAPI 41318de8d7fSPeter Avalos {"gssapi-with-mic", 41418de8d7fSPeter Avalos userauth_gssapi, 415664f4763Szrj userauth_gssapi_cleanup, 41618de8d7fSPeter Avalos &options.gss_authentication, 41718de8d7fSPeter Avalos NULL}, 41818de8d7fSPeter Avalos #endif 41918de8d7fSPeter Avalos {"hostbased", 42018de8d7fSPeter Avalos userauth_hostbased, 421cb5eb4f1SPeter Avalos NULL, 42218de8d7fSPeter Avalos &options.hostbased_authentication, 42318de8d7fSPeter Avalos NULL}, 42418de8d7fSPeter Avalos {"publickey", 42518de8d7fSPeter Avalos userauth_pubkey, 426cb5eb4f1SPeter Avalos NULL, 42718de8d7fSPeter Avalos &options.pubkey_authentication, 42818de8d7fSPeter Avalos NULL}, 42918de8d7fSPeter Avalos {"keyboard-interactive", 43018de8d7fSPeter Avalos userauth_kbdint, 431cb5eb4f1SPeter Avalos NULL, 43218de8d7fSPeter Avalos &options.kbd_interactive_authentication, 43318de8d7fSPeter Avalos &options.batch_mode}, 43418de8d7fSPeter Avalos {"password", 43518de8d7fSPeter Avalos userauth_passwd, 436cb5eb4f1SPeter Avalos NULL, 43718de8d7fSPeter Avalos &options.password_authentication, 43818de8d7fSPeter Avalos &options.batch_mode}, 43918de8d7fSPeter Avalos {"none", 44018de8d7fSPeter Avalos userauth_none, 44118de8d7fSPeter Avalos NULL, 442cb5eb4f1SPeter Avalos NULL, 44318de8d7fSPeter Avalos NULL}, 444cb5eb4f1SPeter Avalos {NULL, NULL, NULL, NULL, NULL} 44518de8d7fSPeter Avalos }; 44618de8d7fSPeter Avalos 44718de8d7fSPeter Avalos void 448664f4763Szrj ssh_userauth2(struct ssh *ssh, const char *local_user, 449664f4763Szrj const char *server_user, char *host, Sensitive *sensitive) 45018de8d7fSPeter Avalos { 45118de8d7fSPeter Avalos Authctxt authctxt; 452e9778795SPeter Avalos int r; 45318de8d7fSPeter Avalos 45418de8d7fSPeter Avalos if (options.preferred_authentications == NULL) 45518de8d7fSPeter Avalos options.preferred_authentications = authmethods_get(); 45618de8d7fSPeter Avalos 45718de8d7fSPeter Avalos /* setup authentication context */ 45818de8d7fSPeter Avalos memset(&authctxt, 0, sizeof(authctxt)); 45918de8d7fSPeter Avalos authctxt.server_user = server_user; 46018de8d7fSPeter Avalos authctxt.local_user = local_user; 46118de8d7fSPeter Avalos authctxt.host = host; 46218de8d7fSPeter Avalos authctxt.service = "ssh-connection"; /* service name */ 46318de8d7fSPeter Avalos authctxt.success = 0; 46418de8d7fSPeter Avalos authctxt.method = authmethod_lookup("none"); 46518de8d7fSPeter Avalos authctxt.authlist = NULL; 46618de8d7fSPeter Avalos authctxt.methoddata = NULL; 46718de8d7fSPeter Avalos authctxt.sensitive = sensitive; 468e9778795SPeter Avalos authctxt.active_ktype = authctxt.oktypes = authctxt.ktypes = NULL; 46918de8d7fSPeter Avalos authctxt.info_req_seen = 0; 470664f4763Szrj authctxt.attempt_kbdint = 0; 471664f4763Szrj authctxt.attempt_passwd = 0; 472664f4763Szrj #if GSSAPI 473664f4763Szrj authctxt.gss_supported_mechs = NULL; 474664f4763Szrj authctxt.mech_tried = 0; 475664f4763Szrj #endif 476e9778795SPeter Avalos authctxt.agent_fd = -1; 477*ee116499SAntonio Huete Jimenez pubkey_prepare(ssh, &authctxt); 478664f4763Szrj if (authctxt.method == NULL) { 47950a69bb5SSascha Wildner fatal_f("internal error: cannot send userauth none request"); 480664f4763Szrj } 48118de8d7fSPeter Avalos 482e9778795SPeter Avalos if ((r = sshpkt_start(ssh, SSH2_MSG_SERVICE_REQUEST)) != 0 || 483e9778795SPeter Avalos (r = sshpkt_put_cstring(ssh, "ssh-userauth")) != 0 || 484e9778795SPeter Avalos (r = sshpkt_send(ssh)) != 0) 48550a69bb5SSascha Wildner fatal_fr(r, "send packet"); 48618de8d7fSPeter Avalos 487ce74bacaSMatthew Dillon ssh->authctxt = &authctxt; 488e9778795SPeter Avalos ssh_dispatch_init(ssh, &input_userauth_error); 489e9778795SPeter Avalos ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_ext_info); 490e9778795SPeter Avalos ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_ACCEPT, &input_userauth_service_accept); 491ce74bacaSMatthew Dillon ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt.success); /* loop until success */ 492664f4763Szrj pubkey_cleanup(ssh); 493ce74bacaSMatthew Dillon ssh->authctxt = NULL; 49418de8d7fSPeter Avalos 495e9778795SPeter Avalos ssh_dispatch_range(ssh, SSH2_MSG_USERAUTH_MIN, SSH2_MSG_USERAUTH_MAX, NULL); 49618de8d7fSPeter Avalos 497ce74bacaSMatthew Dillon if (!authctxt.success) 498ce74bacaSMatthew Dillon fatal("Authentication failed."); 49950a69bb5SSascha Wildner if (ssh_packet_connection_is_on_socket(ssh)) { 50050a69bb5SSascha Wildner verbose("Authenticated to %s ([%s]:%d) using \"%s\".", host, 50150a69bb5SSascha Wildner ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), 50250a69bb5SSascha Wildner authctxt.method->name); 50350a69bb5SSascha Wildner } else { 50450a69bb5SSascha Wildner verbose("Authenticated to %s (via proxy) using \"%s\".", host, 50550a69bb5SSascha Wildner authctxt.method->name); 50650a69bb5SSascha Wildner } 50718de8d7fSPeter Avalos } 50818de8d7fSPeter Avalos 509e9778795SPeter Avalos /* ARGSUSED */ 510664f4763Szrj static int 511ce74bacaSMatthew Dillon input_userauth_service_accept(int type, u_int32_t seq, struct ssh *ssh) 512e9778795SPeter Avalos { 513e9778795SPeter Avalos int r; 514e9778795SPeter Avalos 515e9778795SPeter Avalos if (ssh_packet_remaining(ssh) > 0) { 516e9778795SPeter Avalos char *reply; 517e9778795SPeter Avalos 518e9778795SPeter Avalos if ((r = sshpkt_get_cstring(ssh, &reply, NULL)) != 0) 519e9778795SPeter Avalos goto out; 520e9778795SPeter Avalos debug2("service_accept: %s", reply); 521e9778795SPeter Avalos free(reply); 522e9778795SPeter Avalos } else { 523e9778795SPeter Avalos debug2("buggy server: service_accept w/o service"); 524e9778795SPeter Avalos } 525e9778795SPeter Avalos if ((r = sshpkt_get_end(ssh)) != 0) 526e9778795SPeter Avalos goto out; 527e9778795SPeter Avalos debug("SSH2_MSG_SERVICE_ACCEPT received"); 528e9778795SPeter Avalos 529e9778795SPeter Avalos /* initial userauth request */ 530664f4763Szrj userauth_none(ssh); 531e9778795SPeter Avalos 532e9778795SPeter Avalos ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_error); 533e9778795SPeter Avalos ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success); 534e9778795SPeter Avalos ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure); 535e9778795SPeter Avalos ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner); 536e9778795SPeter Avalos r = 0; 537e9778795SPeter Avalos out: 538e9778795SPeter Avalos return r; 539e9778795SPeter Avalos } 540e9778795SPeter Avalos 541e9778795SPeter Avalos /* ARGSUSED */ 542664f4763Szrj static int 543ce74bacaSMatthew Dillon input_userauth_ext_info(int type, u_int32_t seqnr, struct ssh *ssh) 544e9778795SPeter Avalos { 545ce74bacaSMatthew Dillon return kex_input_ext_info(type, seqnr, ssh); 546e9778795SPeter Avalos } 547e9778795SPeter Avalos 54818de8d7fSPeter Avalos void 549664f4763Szrj userauth(struct ssh *ssh, char *authlist) 55018de8d7fSPeter Avalos { 551664f4763Szrj Authctxt *authctxt = (Authctxt *)ssh->authctxt; 552664f4763Szrj 553cb5eb4f1SPeter Avalos if (authctxt->method != NULL && authctxt->method->cleanup != NULL) 554664f4763Szrj authctxt->method->cleanup(ssh); 555cb5eb4f1SPeter Avalos 55636e94dc5SPeter Avalos free(authctxt->methoddata); 55718de8d7fSPeter Avalos authctxt->methoddata = NULL; 55818de8d7fSPeter Avalos if (authlist == NULL) { 55918de8d7fSPeter Avalos authlist = authctxt->authlist; 56018de8d7fSPeter Avalos } else { 56136e94dc5SPeter Avalos free(authctxt->authlist); 56218de8d7fSPeter Avalos authctxt->authlist = authlist; 56318de8d7fSPeter Avalos } 56418de8d7fSPeter Avalos for (;;) { 56518de8d7fSPeter Avalos Authmethod *method = authmethod_get(authlist); 56618de8d7fSPeter Avalos if (method == NULL) 567ce74bacaSMatthew Dillon fatal("%s@%s: Permission denied (%s).", 568ce74bacaSMatthew Dillon authctxt->server_user, authctxt->host, authlist); 56918de8d7fSPeter Avalos authctxt->method = method; 57018de8d7fSPeter Avalos 57118de8d7fSPeter Avalos /* reset the per method handler */ 572664f4763Szrj ssh_dispatch_range(ssh, SSH2_MSG_USERAUTH_PER_METHOD_MIN, 57318de8d7fSPeter Avalos SSH2_MSG_USERAUTH_PER_METHOD_MAX, NULL); 57418de8d7fSPeter Avalos 57518de8d7fSPeter Avalos /* and try new method */ 576664f4763Szrj if (method->userauth(ssh) != 0) { 57718de8d7fSPeter Avalos debug2("we sent a %s packet, wait for reply", method->name); 57818de8d7fSPeter Avalos break; 57918de8d7fSPeter Avalos } else { 58018de8d7fSPeter Avalos debug2("we did not send a packet, disable method"); 58118de8d7fSPeter Avalos method->enabled = NULL; 58218de8d7fSPeter Avalos } 58318de8d7fSPeter Avalos } 58418de8d7fSPeter Avalos } 58518de8d7fSPeter Avalos 586cb5eb4f1SPeter Avalos /* ARGSUSED */ 587664f4763Szrj static int 588ce74bacaSMatthew Dillon input_userauth_error(int type, u_int32_t seq, struct ssh *ssh) 58918de8d7fSPeter Avalos { 59050a69bb5SSascha Wildner fatal_f("bad message during authentication: type %d", type); 591e9778795SPeter Avalos return 0; 59218de8d7fSPeter Avalos } 59318de8d7fSPeter Avalos 594cb5eb4f1SPeter Avalos /* ARGSUSED */ 595664f4763Szrj static int 596ce74bacaSMatthew Dillon input_userauth_banner(int type, u_int32_t seq, struct ssh *ssh) 59718de8d7fSPeter Avalos { 598664f4763Szrj char *msg = NULL; 599664f4763Szrj size_t len; 600664f4763Szrj int r; 60118de8d7fSPeter Avalos 60250a69bb5SSascha Wildner debug3_f("entering"); 603664f4763Szrj if ((r = sshpkt_get_cstring(ssh, &msg, &len)) != 0 || 604664f4763Szrj (r = sshpkt_get_cstring(ssh, NULL, NULL)) != 0) 605664f4763Szrj goto out; 606e9778795SPeter Avalos if (len > 0 && options.log_level >= SYSLOG_LEVEL_INFO) 607e9778795SPeter Avalos fmprintf(stderr, "%s", msg); 608664f4763Szrj r = 0; 609664f4763Szrj out: 61036e94dc5SPeter Avalos free(msg); 611664f4763Szrj return r; 61218de8d7fSPeter Avalos } 61318de8d7fSPeter Avalos 614cb5eb4f1SPeter Avalos /* ARGSUSED */ 615664f4763Szrj static int 616ce74bacaSMatthew Dillon input_userauth_success(int type, u_int32_t seq, struct ssh *ssh) 61718de8d7fSPeter Avalos { 618ce74bacaSMatthew Dillon Authctxt *authctxt = ssh->authctxt; 619856ea928SPeter Avalos 62018de8d7fSPeter Avalos if (authctxt == NULL) 62150a69bb5SSascha Wildner fatal_f("no authentication context"); 62236e94dc5SPeter Avalos free(authctxt->authlist); 62318de8d7fSPeter Avalos authctxt->authlist = NULL; 624856ea928SPeter Avalos if (authctxt->method != NULL && authctxt->method->cleanup != NULL) 625664f4763Szrj authctxt->method->cleanup(ssh); 62636e94dc5SPeter Avalos free(authctxt->methoddata); 62718de8d7fSPeter Avalos authctxt->methoddata = NULL; 62818de8d7fSPeter Avalos authctxt->success = 1; /* break out */ 629e9778795SPeter Avalos return 0; 63018de8d7fSPeter Avalos } 63118de8d7fSPeter Avalos 632664f4763Szrj #if 0 633664f4763Szrj static int 634ce74bacaSMatthew Dillon input_userauth_success_unexpected(int type, u_int32_t seq, struct ssh *ssh) 635856ea928SPeter Avalos { 636ce74bacaSMatthew Dillon Authctxt *authctxt = ssh->authctxt; 637856ea928SPeter Avalos 638856ea928SPeter Avalos if (authctxt == NULL) 63950a69bb5SSascha Wildner fatal_f("no authentication context"); 640856ea928SPeter Avalos 641856ea928SPeter Avalos fatal("Unexpected authentication success during %s.", 642856ea928SPeter Avalos authctxt->method->name); 643e9778795SPeter Avalos return 0; 644856ea928SPeter Avalos } 645664f4763Szrj #endif 646856ea928SPeter Avalos 647cb5eb4f1SPeter Avalos /* ARGSUSED */ 648664f4763Szrj static int 649ce74bacaSMatthew Dillon input_userauth_failure(int type, u_int32_t seq, struct ssh *ssh) 65018de8d7fSPeter Avalos { 651ce74bacaSMatthew Dillon Authctxt *authctxt = ssh->authctxt; 65218de8d7fSPeter Avalos char *authlist = NULL; 653664f4763Szrj u_char partial; 65418de8d7fSPeter Avalos 65518de8d7fSPeter Avalos if (authctxt == NULL) 65618de8d7fSPeter Avalos fatal("input_userauth_failure: no authentication context"); 65718de8d7fSPeter Avalos 6580cbfa66cSDaniel Fojt if (sshpkt_get_cstring(ssh, &authlist, NULL) != 0 || 6590cbfa66cSDaniel Fojt sshpkt_get_u8(ssh, &partial) != 0 || 6600cbfa66cSDaniel Fojt sshpkt_get_end(ssh) != 0) 661664f4763Szrj goto out; 66218de8d7fSPeter Avalos 66336e94dc5SPeter Avalos if (partial != 0) { 66450a69bb5SSascha Wildner verbose("Authenticated using \"%s\" with partial success.", 66550a69bb5SSascha Wildner authctxt->method->name); 66636e94dc5SPeter Avalos /* reset state */ 667ce74bacaSMatthew Dillon pubkey_reset(authctxt); 66836e94dc5SPeter Avalos } 66918de8d7fSPeter Avalos debug("Authentications that can continue: %s", authlist); 67018de8d7fSPeter Avalos 671664f4763Szrj userauth(ssh, authlist); 672664f4763Szrj authlist = NULL; 673664f4763Szrj out: 674664f4763Szrj free(authlist); 675e9778795SPeter Avalos return 0; 67618de8d7fSPeter Avalos } 677cb5eb4f1SPeter Avalos 678664f4763Szrj /* 679664f4763Szrj * Format an identity for logging including filename, key type, fingerprint 680664f4763Szrj * and location (agent, etc.). Caller must free. 681664f4763Szrj */ 682664f4763Szrj static char * 683664f4763Szrj format_identity(Identity *id) 684664f4763Szrj { 685664f4763Szrj char *fp = NULL, *ret = NULL; 6860cbfa66cSDaniel Fojt const char *note = ""; 687664f4763Szrj 688664f4763Szrj if (id->key != NULL) { 689664f4763Szrj fp = sshkey_fingerprint(id->key, options.fingerprint_hash, 690664f4763Szrj SSH_FP_DEFAULT); 691664f4763Szrj } 6920cbfa66cSDaniel Fojt if (id->key) { 6930cbfa66cSDaniel Fojt if ((id->key->flags & SSHKEY_FLAG_EXT) != 0) 6940cbfa66cSDaniel Fojt note = " token"; 6950cbfa66cSDaniel Fojt else if (sshkey_is_sk(id->key)) 6960cbfa66cSDaniel Fojt note = " authenticator"; 6970cbfa66cSDaniel Fojt } 698664f4763Szrj xasprintf(&ret, "%s %s%s%s%s%s%s", 699664f4763Szrj id->filename, 700664f4763Szrj id->key ? sshkey_type(id->key) : "", id->key ? " " : "", 701664f4763Szrj fp ? fp : "", 7020cbfa66cSDaniel Fojt id->userprovided ? " explicit" : "", note, 703664f4763Szrj id->agent_fd != -1 ? " agent" : ""); 704664f4763Szrj free(fp); 705664f4763Szrj return ret; 706664f4763Szrj } 707664f4763Szrj 708cb5eb4f1SPeter Avalos /* ARGSUSED */ 709664f4763Szrj static int 710ce74bacaSMatthew Dillon input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh) 71118de8d7fSPeter Avalos { 712ce74bacaSMatthew Dillon Authctxt *authctxt = ssh->authctxt; 713ce74bacaSMatthew Dillon struct sshkey *key = NULL; 71418de8d7fSPeter Avalos Identity *id = NULL; 715664f4763Szrj int pktype, found = 0, sent = 0; 716664f4763Szrj size_t blen; 717664f4763Szrj char *pkalg = NULL, *fp = NULL, *ident = NULL; 718664f4763Szrj u_char *pkblob = NULL; 719664f4763Szrj int r; 72018de8d7fSPeter Avalos 72118de8d7fSPeter Avalos if (authctxt == NULL) 72218de8d7fSPeter Avalos fatal("input_userauth_pk_ok: no authentication context"); 72318de8d7fSPeter Avalos 724664f4763Szrj if ((r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 || 725664f4763Szrj (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0 || 726664f4763Szrj (r = sshpkt_get_end(ssh)) != 0) 727664f4763Szrj goto done; 72818de8d7fSPeter Avalos 729664f4763Szrj if ((pktype = sshkey_type_from_name(pkalg)) == KEY_UNSPEC) { 73050a69bb5SSascha Wildner debug_f("server sent unknown pkalg %s", pkalg); 73118de8d7fSPeter Avalos goto done; 73218de8d7fSPeter Avalos } 733664f4763Szrj if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) { 73450a69bb5SSascha Wildner debug_r(r, "no key from blob. pkalg %s", pkalg); 73518de8d7fSPeter Avalos goto done; 73618de8d7fSPeter Avalos } 73718de8d7fSPeter Avalos if (key->type != pktype) { 73818de8d7fSPeter Avalos error("input_userauth_pk_ok: type mismatch " 73918de8d7fSPeter Avalos "for decoded key (received %d, expected %d)", 74018de8d7fSPeter Avalos key->type, pktype); 74118de8d7fSPeter Avalos goto done; 74218de8d7fSPeter Avalos } 74318de8d7fSPeter Avalos 74418de8d7fSPeter Avalos /* 74518de8d7fSPeter Avalos * search keys in the reverse order, because last candidate has been 74618de8d7fSPeter Avalos * moved to the end of the queue. this also avoids confusion by 74718de8d7fSPeter Avalos * duplicate keys 74818de8d7fSPeter Avalos */ 74918de8d7fSPeter Avalos TAILQ_FOREACH_REVERSE(id, &authctxt->keys, idlist, next) { 750664f4763Szrj if (sshkey_equal(key, id->key)) { 751664f4763Szrj found = 1; 75218de8d7fSPeter Avalos break; 75318de8d7fSPeter Avalos } 75418de8d7fSPeter Avalos } 755664f4763Szrj if (!found || id == NULL) { 756664f4763Szrj fp = sshkey_fingerprint(key, options.fingerprint_hash, 757664f4763Szrj SSH_FP_DEFAULT); 75850a69bb5SSascha Wildner error_f("server replied with unknown key: %s %s", 759664f4763Szrj sshkey_type(key), fp == NULL ? "<ERROR>" : fp); 760664f4763Szrj goto done; 761664f4763Szrj } 762664f4763Szrj ident = format_identity(id); 763664f4763Szrj debug("Server accepts key: %s", ident); 764664f4763Szrj sent = sign_and_send_pubkey(ssh, id); 765664f4763Szrj r = 0; 76618de8d7fSPeter Avalos done: 767664f4763Szrj sshkey_free(key); 768664f4763Szrj free(ident); 769664f4763Szrj free(fp); 77036e94dc5SPeter Avalos free(pkalg); 77136e94dc5SPeter Avalos free(pkblob); 77218de8d7fSPeter Avalos 77318de8d7fSPeter Avalos /* try another method if we did not send a packet */ 774664f4763Szrj if (r == 0 && sent == 0) 775664f4763Szrj userauth(ssh, NULL); 776664f4763Szrj return r; 77718de8d7fSPeter Avalos } 77818de8d7fSPeter Avalos 77918de8d7fSPeter Avalos #ifdef GSSAPI 780664f4763Szrj static int 781664f4763Szrj userauth_gssapi(struct ssh *ssh) 78218de8d7fSPeter Avalos { 783664f4763Szrj Authctxt *authctxt = (Authctxt *)ssh->authctxt; 78418de8d7fSPeter Avalos Gssctxt *gssctxt = NULL; 78518de8d7fSPeter Avalos OM_uint32 min; 786664f4763Szrj int r, ok = 0; 787664f4763Szrj gss_OID mech = NULL; 78818de8d7fSPeter Avalos 78918de8d7fSPeter Avalos /* Try one GSSAPI method at a time, rather than sending them all at 79018de8d7fSPeter Avalos * once. */ 79118de8d7fSPeter Avalos 792664f4763Szrj if (authctxt->gss_supported_mechs == NULL) 793664f4763Szrj gss_indicate_mechs(&min, &authctxt->gss_supported_mechs); 79418de8d7fSPeter Avalos 795664f4763Szrj /* Check to see whether the mechanism is usable before we offer it */ 796664f4763Szrj while (authctxt->mech_tried < authctxt->gss_supported_mechs->count && 797664f4763Szrj !ok) { 798664f4763Szrj mech = &authctxt->gss_supported_mechs-> 799664f4763Szrj elements[authctxt->mech_tried]; 80018de8d7fSPeter Avalos /* My DER encoding requires length<128 */ 801664f4763Szrj if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt, 802664f4763Szrj mech, authctxt->host)) { 80318de8d7fSPeter Avalos ok = 1; /* Mechanism works */ 80418de8d7fSPeter Avalos } else { 805664f4763Szrj authctxt->mech_tried++; 80618de8d7fSPeter Avalos } 80718de8d7fSPeter Avalos } 80818de8d7fSPeter Avalos 809664f4763Szrj if (!ok || mech == NULL) 81018de8d7fSPeter Avalos return 0; 81118de8d7fSPeter Avalos 81218de8d7fSPeter Avalos authctxt->methoddata=(void *)gssctxt; 81318de8d7fSPeter Avalos 814664f4763Szrj if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || 815664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || 816664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || 817664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || 818664f4763Szrj (r = sshpkt_put_u32(ssh, 1)) != 0 || 819664f4763Szrj (r = sshpkt_put_u32(ssh, (mech->length) + 2)) != 0 || 820664f4763Szrj (r = sshpkt_put_u8(ssh, SSH_GSS_OIDTYPE)) != 0 || 821664f4763Szrj (r = sshpkt_put_u8(ssh, mech->length)) != 0 || 822664f4763Szrj (r = sshpkt_put(ssh, mech->elements, mech->length)) != 0 || 823664f4763Szrj (r = sshpkt_send(ssh)) != 0) 82450a69bb5SSascha Wildner fatal_fr(r, "send packet"); 82518de8d7fSPeter Avalos 826664f4763Szrj ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_RESPONSE, &input_gssapi_response); 827664f4763Szrj ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token); 828664f4763Szrj ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERROR, &input_gssapi_error); 829664f4763Szrj ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok); 83018de8d7fSPeter Avalos 831664f4763Szrj authctxt->mech_tried++; /* Move along to next candidate */ 83218de8d7fSPeter Avalos 83318de8d7fSPeter Avalos return 1; 83418de8d7fSPeter Avalos } 83518de8d7fSPeter Avalos 836664f4763Szrj static void 837664f4763Szrj userauth_gssapi_cleanup(struct ssh *ssh) 838664f4763Szrj { 839664f4763Szrj Authctxt *authctxt = (Authctxt *)ssh->authctxt; 840664f4763Szrj Gssctxt *gssctxt = (Gssctxt *)authctxt->methoddata; 841664f4763Szrj 842664f4763Szrj ssh_gssapi_delete_ctx(&gssctxt); 843664f4763Szrj authctxt->methoddata = NULL; 844664f4763Szrj 845664f4763Szrj free(authctxt->gss_supported_mechs); 846664f4763Szrj authctxt->gss_supported_mechs = NULL; 847664f4763Szrj } 848664f4763Szrj 84918de8d7fSPeter Avalos static OM_uint32 850ce74bacaSMatthew Dillon process_gssapi_token(struct ssh *ssh, gss_buffer_t recv_tok) 85118de8d7fSPeter Avalos { 852ce74bacaSMatthew Dillon Authctxt *authctxt = ssh->authctxt; 85318de8d7fSPeter Avalos Gssctxt *gssctxt = authctxt->methoddata; 85418de8d7fSPeter Avalos gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; 85518de8d7fSPeter Avalos gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; 85618de8d7fSPeter Avalos gss_buffer_desc gssbuf; 85718de8d7fSPeter Avalos OM_uint32 status, ms, flags; 858664f4763Szrj int r; 85918de8d7fSPeter Avalos 86018de8d7fSPeter Avalos status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds, 86118de8d7fSPeter Avalos recv_tok, &send_tok, &flags); 86218de8d7fSPeter Avalos 86318de8d7fSPeter Avalos if (send_tok.length > 0) { 864664f4763Szrj u_char type = GSS_ERROR(status) ? 865664f4763Szrj SSH2_MSG_USERAUTH_GSSAPI_ERRTOK : 866664f4763Szrj SSH2_MSG_USERAUTH_GSSAPI_TOKEN; 86718de8d7fSPeter Avalos 868664f4763Szrj if ((r = sshpkt_start(ssh, type)) != 0 || 869664f4763Szrj (r = sshpkt_put_string(ssh, send_tok.value, 870664f4763Szrj send_tok.length)) != 0 || 871664f4763Szrj (r = sshpkt_send(ssh)) != 0) 87250a69bb5SSascha Wildner fatal_fr(r, "send %u packet", type); 873664f4763Szrj 87418de8d7fSPeter Avalos gss_release_buffer(&ms, &send_tok); 87518de8d7fSPeter Avalos } 87618de8d7fSPeter Avalos 87718de8d7fSPeter Avalos if (status == GSS_S_COMPLETE) { 87818de8d7fSPeter Avalos /* send either complete or MIC, depending on mechanism */ 87918de8d7fSPeter Avalos if (!(flags & GSS_C_INTEG_FLAG)) { 880664f4763Szrj if ((r = sshpkt_start(ssh, 881664f4763Szrj SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE)) != 0 || 882664f4763Szrj (r = sshpkt_send(ssh)) != 0) 88350a69bb5SSascha Wildner fatal_fr(r, "send completion"); 88418de8d7fSPeter Avalos } else { 885664f4763Szrj struct sshbuf *b; 886664f4763Szrj 887664f4763Szrj if ((b = sshbuf_new()) == NULL) 88850a69bb5SSascha Wildner fatal_f("sshbuf_new failed"); 889664f4763Szrj ssh_gssapi_buildmic(b, authctxt->server_user, 89050a69bb5SSascha Wildner authctxt->service, "gssapi-with-mic", 89150a69bb5SSascha Wildner ssh->kex->session_id); 89218de8d7fSPeter Avalos 893664f4763Szrj if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL) 89450a69bb5SSascha Wildner fatal_f("sshbuf_mutable_ptr failed"); 895664f4763Szrj gssbuf.length = sshbuf_len(b); 89618de8d7fSPeter Avalos 89718de8d7fSPeter Avalos status = ssh_gssapi_sign(gssctxt, &gssbuf, &mic); 89818de8d7fSPeter Avalos 89918de8d7fSPeter Avalos if (!GSS_ERROR(status)) { 900664f4763Szrj if ((r = sshpkt_start(ssh, 901664f4763Szrj SSH2_MSG_USERAUTH_GSSAPI_MIC)) != 0 || 902664f4763Szrj (r = sshpkt_put_string(ssh, mic.value, 903664f4763Szrj mic.length)) != 0 || 904664f4763Szrj (r = sshpkt_send(ssh)) != 0) 90550a69bb5SSascha Wildner fatal_fr(r, "send MIC"); 90618de8d7fSPeter Avalos } 90718de8d7fSPeter Avalos 908664f4763Szrj sshbuf_free(b); 90918de8d7fSPeter Avalos gss_release_buffer(&ms, &mic); 91018de8d7fSPeter Avalos } 91118de8d7fSPeter Avalos } 91218de8d7fSPeter Avalos 91318de8d7fSPeter Avalos return status; 91418de8d7fSPeter Avalos } 91518de8d7fSPeter Avalos 916cb5eb4f1SPeter Avalos /* ARGSUSED */ 917664f4763Szrj static int 918ce74bacaSMatthew Dillon input_gssapi_response(int type, u_int32_t plen, struct ssh *ssh) 91918de8d7fSPeter Avalos { 920ce74bacaSMatthew Dillon Authctxt *authctxt = ssh->authctxt; 92118de8d7fSPeter Avalos Gssctxt *gssctxt; 922664f4763Szrj size_t oidlen; 923664f4763Szrj u_char *oidv = NULL; 924664f4763Szrj int r; 92518de8d7fSPeter Avalos 92618de8d7fSPeter Avalos if (authctxt == NULL) 92718de8d7fSPeter Avalos fatal("input_gssapi_response: no authentication context"); 92818de8d7fSPeter Avalos gssctxt = authctxt->methoddata; 92918de8d7fSPeter Avalos 93018de8d7fSPeter Avalos /* Setup our OID */ 931664f4763Szrj if ((r = sshpkt_get_string(ssh, &oidv, &oidlen)) != 0) 932664f4763Szrj goto done; 93318de8d7fSPeter Avalos 93418de8d7fSPeter Avalos if (oidlen <= 2 || 93518de8d7fSPeter Avalos oidv[0] != SSH_GSS_OIDTYPE || 93618de8d7fSPeter Avalos oidv[1] != oidlen - 2) { 93718de8d7fSPeter Avalos debug("Badly encoded mechanism OID received"); 938664f4763Szrj userauth(ssh, NULL); 939664f4763Szrj goto ok; 94018de8d7fSPeter Avalos } 94118de8d7fSPeter Avalos 94218de8d7fSPeter Avalos if (!ssh_gssapi_check_oid(gssctxt, oidv + 2, oidlen - 2)) 94318de8d7fSPeter Avalos fatal("Server returned different OID than expected"); 94418de8d7fSPeter Avalos 945664f4763Szrj if ((r = sshpkt_get_end(ssh)) != 0) 946664f4763Szrj goto done; 94718de8d7fSPeter Avalos 948ce74bacaSMatthew Dillon if (GSS_ERROR(process_gssapi_token(ssh, GSS_C_NO_BUFFER))) { 94918de8d7fSPeter Avalos /* Start again with next method on list */ 95018de8d7fSPeter Avalos debug("Trying to start again"); 951664f4763Szrj userauth(ssh, NULL); 952664f4763Szrj goto ok; 95318de8d7fSPeter Avalos } 954664f4763Szrj ok: 955664f4763Szrj r = 0; 956664f4763Szrj done: 957664f4763Szrj free(oidv); 958664f4763Szrj return r; 95918de8d7fSPeter Avalos } 96018de8d7fSPeter Avalos 961cb5eb4f1SPeter Avalos /* ARGSUSED */ 962664f4763Szrj static int 963ce74bacaSMatthew Dillon input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh) 96418de8d7fSPeter Avalos { 965ce74bacaSMatthew Dillon Authctxt *authctxt = ssh->authctxt; 96618de8d7fSPeter Avalos gss_buffer_desc recv_tok; 967664f4763Szrj u_char *p = NULL; 968664f4763Szrj size_t len; 96918de8d7fSPeter Avalos OM_uint32 status; 970664f4763Szrj int r; 97118de8d7fSPeter Avalos 97218de8d7fSPeter Avalos if (authctxt == NULL) 97318de8d7fSPeter Avalos fatal("input_gssapi_response: no authentication context"); 97418de8d7fSPeter Avalos 975664f4763Szrj if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 || 976664f4763Szrj (r = sshpkt_get_end(ssh)) != 0) 977664f4763Szrj goto out; 97818de8d7fSPeter Avalos 979664f4763Szrj recv_tok.value = p; 980664f4763Szrj recv_tok.length = len; 981ce74bacaSMatthew Dillon status = process_gssapi_token(ssh, &recv_tok); 98218de8d7fSPeter Avalos 98318de8d7fSPeter Avalos /* Start again with the next method in the list */ 984664f4763Szrj if (GSS_ERROR(status)) { 985664f4763Szrj userauth(ssh, NULL); 986664f4763Szrj /* ok */ 98718de8d7fSPeter Avalos } 988664f4763Szrj r = 0; 989664f4763Szrj out: 990664f4763Szrj free(p); 991664f4763Szrj return r; 99218de8d7fSPeter Avalos } 99318de8d7fSPeter Avalos 994cb5eb4f1SPeter Avalos /* ARGSUSED */ 995664f4763Szrj static int 996ce74bacaSMatthew Dillon input_gssapi_errtok(int type, u_int32_t plen, struct ssh *ssh) 99718de8d7fSPeter Avalos { 998ce74bacaSMatthew Dillon Authctxt *authctxt = ssh->authctxt; 99918de8d7fSPeter Avalos Gssctxt *gssctxt; 100018de8d7fSPeter Avalos gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; 100118de8d7fSPeter Avalos gss_buffer_desc recv_tok; 100236e94dc5SPeter Avalos OM_uint32 ms; 1003664f4763Szrj u_char *p = NULL; 1004664f4763Szrj size_t len; 1005664f4763Szrj int r; 100618de8d7fSPeter Avalos 100718de8d7fSPeter Avalos if (authctxt == NULL) 100818de8d7fSPeter Avalos fatal("input_gssapi_response: no authentication context"); 100918de8d7fSPeter Avalos gssctxt = authctxt->methoddata; 101018de8d7fSPeter Avalos 1011664f4763Szrj if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 || 1012664f4763Szrj (r = sshpkt_get_end(ssh)) != 0) { 1013664f4763Szrj free(p); 1014664f4763Szrj return r; 1015664f4763Szrj } 101618de8d7fSPeter Avalos 101718de8d7fSPeter Avalos /* Stick it into GSSAPI and see what it says */ 1018664f4763Szrj recv_tok.value = p; 1019664f4763Szrj recv_tok.length = len; 102036e94dc5SPeter Avalos (void)ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds, 102118de8d7fSPeter Avalos &recv_tok, &send_tok, NULL); 1022664f4763Szrj free(p); 102318de8d7fSPeter Avalos gss_release_buffer(&ms, &send_tok); 102418de8d7fSPeter Avalos 102518de8d7fSPeter Avalos /* Server will be returning a failed packet after this one */ 1026e9778795SPeter Avalos return 0; 102718de8d7fSPeter Avalos } 102818de8d7fSPeter Avalos 1029cb5eb4f1SPeter Avalos /* ARGSUSED */ 1030664f4763Szrj static int 1031ce74bacaSMatthew Dillon input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh) 103218de8d7fSPeter Avalos { 1033664f4763Szrj char *msg = NULL; 1034664f4763Szrj char *lang = NULL; 1035664f4763Szrj int r; 103618de8d7fSPeter Avalos 1037664f4763Szrj if ((r = sshpkt_get_u32(ssh, NULL)) != 0 || /* maj */ 1038664f4763Szrj (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* min */ 1039664f4763Szrj (r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 || 1040664f4763Szrj (r = sshpkt_get_cstring(ssh, &lang, NULL)) != 0) 1041664f4763Szrj goto out; 1042664f4763Szrj r = sshpkt_get_end(ssh); 104318de8d7fSPeter Avalos debug("Server GSSAPI Error:\n%s", msg); 1044664f4763Szrj out: 104536e94dc5SPeter Avalos free(msg); 104636e94dc5SPeter Avalos free(lang); 1047664f4763Szrj return r; 104818de8d7fSPeter Avalos } 104918de8d7fSPeter Avalos #endif /* GSSAPI */ 105018de8d7fSPeter Avalos 1051664f4763Szrj static int 1052664f4763Szrj userauth_none(struct ssh *ssh) 105318de8d7fSPeter Avalos { 1054664f4763Szrj Authctxt *authctxt = (Authctxt *)ssh->authctxt; 1055664f4763Szrj int r; 1056664f4763Szrj 105718de8d7fSPeter Avalos /* initial userauth request */ 1058664f4763Szrj if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || 1059664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || 1060664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || 1061664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || 1062664f4763Szrj (r = sshpkt_send(ssh)) != 0) 106350a69bb5SSascha Wildner fatal_fr(r, "send packet"); 106418de8d7fSPeter Avalos return 1; 106518de8d7fSPeter Avalos } 106618de8d7fSPeter Avalos 1067664f4763Szrj static int 1068664f4763Szrj userauth_passwd(struct ssh *ssh) 106918de8d7fSPeter Avalos { 1070664f4763Szrj Authctxt *authctxt = (Authctxt *)ssh->authctxt; 1071664f4763Szrj char *password, *prompt = NULL; 1072856ea928SPeter Avalos const char *host = options.host_key_alias ? options.host_key_alias : 1073856ea928SPeter Avalos authctxt->host; 1074664f4763Szrj int r; 107518de8d7fSPeter Avalos 1076664f4763Szrj if (authctxt->attempt_passwd++ >= options.number_of_password_prompts) 107718de8d7fSPeter Avalos return 0; 107818de8d7fSPeter Avalos 1079664f4763Szrj if (authctxt->attempt_passwd != 1) 108018de8d7fSPeter Avalos error("Permission denied, please try again."); 108118de8d7fSPeter Avalos 1082664f4763Szrj xasprintf(&prompt, "%s@%s's password: ", authctxt->server_user, host); 108318de8d7fSPeter Avalos password = read_passphrase(prompt, 0); 1084664f4763Szrj if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || 1085664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || 1086664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || 1087664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || 1088664f4763Szrj (r = sshpkt_put_u8(ssh, 0)) != 0 || 1089664f4763Szrj (r = sshpkt_put_cstring(ssh, password)) != 0 || 1090664f4763Szrj (r = sshpkt_add_padding(ssh, 64)) != 0 || 1091664f4763Szrj (r = sshpkt_send(ssh)) != 0) 109250a69bb5SSascha Wildner fatal_fr(r, "send packet"); 109318de8d7fSPeter Avalos 1094664f4763Szrj free(prompt); 1095664f4763Szrj if (password != NULL) 1096664f4763Szrj freezero(password, strlen(password)); 1097664f4763Szrj 1098664f4763Szrj ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, 109918de8d7fSPeter Avalos &input_userauth_passwd_changereq); 110018de8d7fSPeter Avalos 110118de8d7fSPeter Avalos return 1; 110218de8d7fSPeter Avalos } 1103cb5eb4f1SPeter Avalos 110418de8d7fSPeter Avalos /* 110518de8d7fSPeter Avalos * parse PASSWD_CHANGEREQ, prompt user and send SSH2_MSG_USERAUTH_REQUEST 110618de8d7fSPeter Avalos */ 1107cb5eb4f1SPeter Avalos /* ARGSUSED */ 1108664f4763Szrj static int 1109ce74bacaSMatthew Dillon input_userauth_passwd_changereq(int type, u_int32_t seqnr, struct ssh *ssh) 111018de8d7fSPeter Avalos { 1111ce74bacaSMatthew Dillon Authctxt *authctxt = ssh->authctxt; 1112664f4763Szrj char *info = NULL, *lang = NULL, *password = NULL, *retype = NULL; 1113ce74bacaSMatthew Dillon char prompt[256]; 1114ce74bacaSMatthew Dillon const char *host; 1115664f4763Szrj int r; 111618de8d7fSPeter Avalos 111718de8d7fSPeter Avalos debug2("input_userauth_passwd_changereq"); 111818de8d7fSPeter Avalos 111918de8d7fSPeter Avalos if (authctxt == NULL) 112018de8d7fSPeter Avalos fatal("input_userauth_passwd_changereq: " 112118de8d7fSPeter Avalos "no authentication context"); 1122ce74bacaSMatthew Dillon host = options.host_key_alias ? options.host_key_alias : authctxt->host; 112318de8d7fSPeter Avalos 1124664f4763Szrj if ((r = sshpkt_get_cstring(ssh, &info, NULL)) != 0 || 1125664f4763Szrj (r = sshpkt_get_cstring(ssh, &lang, NULL)) != 0) 1126664f4763Szrj goto out; 112718de8d7fSPeter Avalos if (strlen(info) > 0) 112818de8d7fSPeter Avalos logit("%s", info); 1129664f4763Szrj if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || 1130664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || 1131664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || 1132664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || 1133664f4763Szrj (r = sshpkt_put_u8(ssh, 1)) != 0) /* additional info */ 1134664f4763Szrj goto out; 1135664f4763Szrj 113618de8d7fSPeter Avalos snprintf(prompt, sizeof(prompt), 113718de8d7fSPeter Avalos "Enter %.30s@%.128s's old password: ", 1138856ea928SPeter Avalos authctxt->server_user, host); 113918de8d7fSPeter Avalos password = read_passphrase(prompt, 0); 1140664f4763Szrj if ((r = sshpkt_put_cstring(ssh, password)) != 0) 1141664f4763Szrj goto out; 1142664f4763Szrj 1143664f4763Szrj freezero(password, strlen(password)); 114418de8d7fSPeter Avalos password = NULL; 114518de8d7fSPeter Avalos while (password == NULL) { 114618de8d7fSPeter Avalos snprintf(prompt, sizeof(prompt), 114718de8d7fSPeter Avalos "Enter %.30s@%.128s's new password: ", 1148856ea928SPeter Avalos authctxt->server_user, host); 114918de8d7fSPeter Avalos password = read_passphrase(prompt, RP_ALLOW_EOF); 115018de8d7fSPeter Avalos if (password == NULL) { 115118de8d7fSPeter Avalos /* bail out */ 1152664f4763Szrj r = 0; 1153664f4763Szrj goto out; 115418de8d7fSPeter Avalos } 115518de8d7fSPeter Avalos snprintf(prompt, sizeof(prompt), 115618de8d7fSPeter Avalos "Retype %.30s@%.128s's new password: ", 1157856ea928SPeter Avalos authctxt->server_user, host); 115818de8d7fSPeter Avalos retype = read_passphrase(prompt, 0); 115918de8d7fSPeter Avalos if (strcmp(password, retype) != 0) { 1160664f4763Szrj freezero(password, strlen(password)); 116118de8d7fSPeter Avalos logit("Mismatch; try again, EOF to quit."); 116218de8d7fSPeter Avalos password = NULL; 116318de8d7fSPeter Avalos } 1164664f4763Szrj freezero(retype, strlen(retype)); 116518de8d7fSPeter Avalos } 1166664f4763Szrj if ((r = sshpkt_put_cstring(ssh, password)) != 0 || 1167664f4763Szrj (r = sshpkt_add_padding(ssh, 64)) != 0 || 1168664f4763Szrj (r = sshpkt_send(ssh)) != 0) 1169664f4763Szrj goto out; 117018de8d7fSPeter Avalos 1171664f4763Szrj ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, 117218de8d7fSPeter Avalos &input_userauth_passwd_changereq); 1173664f4763Szrj r = 0; 1174664f4763Szrj out: 1175664f4763Szrj if (password) 1176664f4763Szrj freezero(password, strlen(password)); 1177664f4763Szrj free(info); 1178664f4763Szrj free(lang); 1179664f4763Szrj return r; 1180e9778795SPeter Avalos } 1181e9778795SPeter Avalos 1182664f4763Szrj /* 1183664f4763Szrj * Select an algorithm for publickey signatures. 1184664f4763Szrj * Returns algorithm (caller must free) or NULL if no mutual algorithm found. 1185664f4763Szrj * 1186664f4763Szrj * Call with ssh==NULL to ignore server-sig-algs extension list and 1187664f4763Szrj * only attempt with the key's base signature type. 1188664f4763Szrj */ 1189664f4763Szrj static char * 1190664f4763Szrj key_sig_algorithm(struct ssh *ssh, const struct sshkey *key) 1191e9778795SPeter Avalos { 1192664f4763Szrj char *allowed, *oallowed, *cp, *tmp, *alg = NULL; 119350a69bb5SSascha Wildner const char *server_sig_algs; 1194e9778795SPeter Avalos 1195664f4763Szrj /* 1196664f4763Szrj * The signature algorithm will only differ from the key algorithm 1197664f4763Szrj * for RSA keys/certs and when the server advertises support for 1198664f4763Szrj * newer (SHA2) algorithms. 1199664f4763Szrj */ 1200664f4763Szrj if (ssh == NULL || ssh->kex->server_sig_algs == NULL || 1201664f4763Szrj (key->type != KEY_RSA && key->type != KEY_RSA_CERT) || 120250a69bb5SSascha Wildner (key->type == KEY_RSA_CERT && (ssh->compat & SSH_BUG_SIGTYPE))) { 1203664f4763Szrj /* Filter base key signature alg against our configuration */ 1204664f4763Szrj return match_list(sshkey_ssh_name(key), 120550a69bb5SSascha Wildner options.pubkey_accepted_algos, NULL); 1206e9778795SPeter Avalos } 1207664f4763Szrj 1208664f4763Szrj /* 120950a69bb5SSascha Wildner * Workaround OpenSSH 7.4 bug: this version supports RSA/SHA-2 but 121050a69bb5SSascha Wildner * fails to advertise it via SSH2_MSG_EXT_INFO. 121150a69bb5SSascha Wildner */ 121250a69bb5SSascha Wildner server_sig_algs = ssh->kex->server_sig_algs; 121350a69bb5SSascha Wildner if (key->type == KEY_RSA && (ssh->compat & SSH_BUG_SIGTYPE74)) 121450a69bb5SSascha Wildner server_sig_algs = "rsa-sha2-256,rsa-sha2-512"; 121550a69bb5SSascha Wildner 121650a69bb5SSascha Wildner /* 1217664f4763Szrj * For RSA keys/certs, since these might have a different sig type: 121850a69bb5SSascha Wildner * find the first entry in PubkeyAcceptedAlgorithms of the right type 1219664f4763Szrj * that also appears in the supported signature algorithms list from 1220664f4763Szrj * the server. 1221664f4763Szrj */ 122250a69bb5SSascha Wildner oallowed = allowed = xstrdup(options.pubkey_accepted_algos); 1223664f4763Szrj while ((cp = strsep(&allowed, ",")) != NULL) { 1224664f4763Szrj if (sshkey_type_from_name(cp) != key->type) 1225664f4763Szrj continue; 122650a69bb5SSascha Wildner tmp = match_list(sshkey_sigalg_by_name(cp), 122750a69bb5SSascha Wildner server_sig_algs, NULL); 1228664f4763Szrj if (tmp != NULL) 1229664f4763Szrj alg = xstrdup(cp); 1230664f4763Szrj free(tmp); 1231664f4763Szrj if (alg != NULL) 1232664f4763Szrj break; 1233e9778795SPeter Avalos } 1234664f4763Szrj free(oallowed); 1235664f4763Szrj return alg; 123618de8d7fSPeter Avalos } 123718de8d7fSPeter Avalos 123818de8d7fSPeter Avalos static int 1239e9778795SPeter Avalos identity_sign(struct identity *id, u_char **sigp, size_t *lenp, 1240664f4763Szrj const u_char *data, size_t datalen, u_int compat, const char *alg) 124118de8d7fSPeter Avalos { 12420cbfa66cSDaniel Fojt struct sshkey *sign_key = NULL, *prv = NULL; 1243*ee116499SAntonio Huete Jimenez int is_agent = 0, retried = 0, r = SSH_ERR_INTERNAL_ERROR; 12440cbfa66cSDaniel Fojt struct notifier_ctx *notifier = NULL; 124550a69bb5SSascha Wildner char *fp = NULL, *pin = NULL, *prompt = NULL; 12460cbfa66cSDaniel Fojt 12470cbfa66cSDaniel Fojt *sigp = NULL; 12480cbfa66cSDaniel Fojt *lenp = 0; 124918de8d7fSPeter Avalos 1250664f4763Szrj /* The agent supports this key. */ 1251664f4763Szrj if (id->key != NULL && id->agent_fd != -1) { 1252e9778795SPeter Avalos return ssh_agent_sign(id->agent_fd, id->key, sigp, lenp, 1253664f4763Szrj data, datalen, alg, compat); 1254664f4763Szrj } 1255e9778795SPeter Avalos 125618de8d7fSPeter Avalos /* 1257664f4763Szrj * We have already loaded the private key or the private key is 1258664f4763Szrj * stored in external hardware. 125918de8d7fSPeter Avalos */ 1260ce74bacaSMatthew Dillon if (id->key != NULL && 1261664f4763Szrj (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))) { 12620cbfa66cSDaniel Fojt sign_key = id->key; 1263*ee116499SAntonio Huete Jimenez is_agent = 1; 12640cbfa66cSDaniel Fojt } else { 1265664f4763Szrj /* Load the private key from the file. */ 1266e9778795SPeter Avalos if ((prv = load_identity_file(id)) == NULL) 1267e9778795SPeter Avalos return SSH_ERR_KEY_NOT_FOUND; 1268ce74bacaSMatthew Dillon if (id->key != NULL && !sshkey_equal_public(prv, id->key)) { 126950a69bb5SSascha Wildner error_f("private key %s contents do not match public", 127050a69bb5SSascha Wildner id->filename); 12710cbfa66cSDaniel Fojt r = SSH_ERR_KEY_NOT_FOUND; 12720cbfa66cSDaniel Fojt goto out; 1273ce74bacaSMatthew Dillon } 12740cbfa66cSDaniel Fojt sign_key = prv; 127550a69bb5SSascha Wildner } 1276*ee116499SAntonio Huete Jimenez retry_pin: 1277*ee116499SAntonio Huete Jimenez /* Prompt for touch for non-agent FIDO keys that request UP */ 1278*ee116499SAntonio Huete Jimenez if (!is_agent && sshkey_is_sk(sign_key) && 1279*ee116499SAntonio Huete Jimenez (sign_key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { 128050a69bb5SSascha Wildner /* XXX should batch mode just skip these? */ 12810cbfa66cSDaniel Fojt if ((fp = sshkey_fingerprint(sign_key, 1282*ee116499SAntonio Huete Jimenez options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 128350a69bb5SSascha Wildner fatal_f("fingerprint failed"); 12840cbfa66cSDaniel Fojt notifier = notify_start(options.batch_mode, 12850cbfa66cSDaniel Fojt "Confirm user presence for key %s %s", 12860cbfa66cSDaniel Fojt sshkey_type(sign_key), fp); 12870cbfa66cSDaniel Fojt free(fp); 12880cbfa66cSDaniel Fojt } 12890cbfa66cSDaniel Fojt if ((r = sshkey_sign(sign_key, sigp, lenp, data, datalen, 129050a69bb5SSascha Wildner alg, options.sk_provider, pin, compat)) != 0) { 129150a69bb5SSascha Wildner debug_fr(r, "sshkey_sign"); 1292*ee116499SAntonio Huete Jimenez if (!retried && pin == NULL && !is_agent && 1293*ee116499SAntonio Huete Jimenez sshkey_is_sk(sign_key) && 129450a69bb5SSascha Wildner r == SSH_ERR_KEY_WRONG_PASSPHRASE) { 129550a69bb5SSascha Wildner notify_complete(notifier, NULL); 129650a69bb5SSascha Wildner notifier = NULL; 1297*ee116499SAntonio Huete Jimenez xasprintf(&prompt, "Enter PIN for %s key %s: ", 1298*ee116499SAntonio Huete Jimenez sshkey_type(sign_key), id->filename); 1299*ee116499SAntonio Huete Jimenez pin = read_passphrase(prompt, 0); 130050a69bb5SSascha Wildner retried = 1; 130150a69bb5SSascha Wildner goto retry_pin; 130250a69bb5SSascha Wildner } 13030cbfa66cSDaniel Fojt goto out; 13040cbfa66cSDaniel Fojt } 130550a69bb5SSascha Wildner 13060cbfa66cSDaniel Fojt /* 13070cbfa66cSDaniel Fojt * PKCS#11 tokens may not support all signature algorithms, 13080cbfa66cSDaniel Fojt * so check what we get back. 13090cbfa66cSDaniel Fojt */ 13100cbfa66cSDaniel Fojt if ((r = sshkey_check_sigtype(*sigp, *lenp, alg)) != 0) { 131150a69bb5SSascha Wildner debug_fr(r, "sshkey_check_sigtype"); 13120cbfa66cSDaniel Fojt goto out; 13130cbfa66cSDaniel Fojt } 13140cbfa66cSDaniel Fojt /* success */ 13150cbfa66cSDaniel Fojt r = 0; 13160cbfa66cSDaniel Fojt out: 131750a69bb5SSascha Wildner free(prompt); 131850a69bb5SSascha Wildner if (pin != NULL) 131950a69bb5SSascha Wildner freezero(pin, strlen(pin)); 132050a69bb5SSascha Wildner notify_complete(notifier, r == 0 ? "User presence confirmed" : NULL); 1321e9778795SPeter Avalos sshkey_free(prv); 1322664f4763Szrj return r; 132318de8d7fSPeter Avalos } 132418de8d7fSPeter Avalos 132518de8d7fSPeter Avalos static int 1326ce74bacaSMatthew Dillon id_filename_matches(Identity *id, Identity *private_id) 1327ce74bacaSMatthew Dillon { 1328*ee116499SAntonio Huete Jimenez static const char * const suffixes[] = { ".pub", "-cert.pub", NULL }; 1329ce74bacaSMatthew Dillon size_t len = strlen(id->filename), plen = strlen(private_id->filename); 1330ce74bacaSMatthew Dillon size_t i, slen; 1331ce74bacaSMatthew Dillon 1332ce74bacaSMatthew Dillon if (strcmp(id->filename, private_id->filename) == 0) 1333ce74bacaSMatthew Dillon return 1; 1334ce74bacaSMatthew Dillon for (i = 0; suffixes[i]; i++) { 1335ce74bacaSMatthew Dillon slen = strlen(suffixes[i]); 1336ce74bacaSMatthew Dillon if (len > slen && plen == len - slen && 1337ce74bacaSMatthew Dillon strcmp(id->filename + (len - slen), suffixes[i]) == 0 && 1338ce74bacaSMatthew Dillon memcmp(id->filename, private_id->filename, plen) == 0) 1339ce74bacaSMatthew Dillon return 1; 1340ce74bacaSMatthew Dillon } 1341ce74bacaSMatthew Dillon return 0; 1342ce74bacaSMatthew Dillon } 1343ce74bacaSMatthew Dillon 1344ce74bacaSMatthew Dillon static int 1345664f4763Szrj sign_and_send_pubkey(struct ssh *ssh, Identity *id) 134618de8d7fSPeter Avalos { 1347664f4763Szrj Authctxt *authctxt = (Authctxt *)ssh->authctxt; 1348664f4763Szrj struct sshbuf *b = NULL; 1349664f4763Szrj Identity *private_id, *sign_id = NULL; 1350664f4763Szrj u_char *signature = NULL; 1351664f4763Szrj size_t slen = 0, skip = 0; 1352664f4763Szrj int r, fallback_sigtype, sent = 0; 1353664f4763Szrj char *alg = NULL, *fp = NULL; 1354*ee116499SAntonio Huete Jimenez const char *loc = "", *method = "publickey"; 1355*ee116499SAntonio Huete Jimenez int hostbound = 0; 1356*ee116499SAntonio Huete Jimenez 1357*ee116499SAntonio Huete Jimenez /* prefer host-bound pubkey signatures if supported by server */ 1358*ee116499SAntonio Huete Jimenez if ((ssh->kex->flags & KEX_HAS_PUBKEY_HOSTBOUND) != 0 && 1359*ee116499SAntonio Huete Jimenez (options.pubkey_authentication & SSH_PUBKEY_AUTH_HBOUND) != 0) { 1360*ee116499SAntonio Huete Jimenez hostbound = 1; 1361*ee116499SAntonio Huete Jimenez method = "publickey-hostbound-v00@openssh.com"; 1362*ee116499SAntonio Huete Jimenez } 136318de8d7fSPeter Avalos 1364e9778795SPeter Avalos if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash, 1365e9778795SPeter Avalos SSH_FP_DEFAULT)) == NULL) 1366e9778795SPeter Avalos return 0; 136718de8d7fSPeter Avalos 1368*ee116499SAntonio Huete Jimenez debug3_f("using %s with %s %s", method, sshkey_type(id->key), fp); 136918de8d7fSPeter Avalos 1370e9778795SPeter Avalos /* 1371e9778795SPeter Avalos * If the key is an certificate, try to find a matching private key 1372e9778795SPeter Avalos * and use it to complete the signature. 1373e9778795SPeter Avalos * If no such private key exists, fall back to trying the certificate 1374e9778795SPeter Avalos * key itself in case it has a private half already loaded. 1375664f4763Szrj * This will try to set sign_id to the private key that will perform 1376664f4763Szrj * the signature. 1377e9778795SPeter Avalos */ 1378664f4763Szrj if (sshkey_is_cert(id->key)) { 1379e9778795SPeter Avalos TAILQ_FOREACH(private_id, &authctxt->keys, next) { 1380e9778795SPeter Avalos if (sshkey_equal_public(id->key, private_id->key) && 1381e9778795SPeter Avalos id->key->type != private_id->key->type) { 1382664f4763Szrj sign_id = private_id; 1383e9778795SPeter Avalos break; 1384e9778795SPeter Avalos } 1385e9778795SPeter Avalos } 1386ce74bacaSMatthew Dillon /* 1387ce74bacaSMatthew Dillon * Exact key matches are preferred, but also allow 1388ce74bacaSMatthew Dillon * filename matches for non-PKCS#11/agent keys that 1389ce74bacaSMatthew Dillon * didn't load public keys. This supports the case 1390ce74bacaSMatthew Dillon * of keeping just a private key file and public 1391ce74bacaSMatthew Dillon * certificate on disk. 1392ce74bacaSMatthew Dillon */ 1393664f4763Szrj if (sign_id == NULL && 1394664f4763Szrj !id->isprivate && id->agent_fd == -1 && 1395ce74bacaSMatthew Dillon (id->key->flags & SSHKEY_FLAG_EXT) == 0) { 1396ce74bacaSMatthew Dillon TAILQ_FOREACH(private_id, &authctxt->keys, next) { 1397ce74bacaSMatthew Dillon if (private_id->key == NULL && 1398ce74bacaSMatthew Dillon id_filename_matches(id, private_id)) { 1399664f4763Szrj sign_id = private_id; 1400ce74bacaSMatthew Dillon break; 1401ce74bacaSMatthew Dillon } 1402ce74bacaSMatthew Dillon } 1403ce74bacaSMatthew Dillon } 1404664f4763Szrj if (sign_id != NULL) { 140550a69bb5SSascha Wildner debug2_f("using private key \"%s\"%s for " 140650a69bb5SSascha Wildner "certificate", sign_id->filename, 140750a69bb5SSascha Wildner sign_id->agent_fd != -1 ? " from agent" : ""); 1408e9778795SPeter Avalos } else { 140950a69bb5SSascha Wildner debug_f("no separate private key for certificate " 141050a69bb5SSascha Wildner "\"%s\"", id->filename); 1411e9778795SPeter Avalos } 1412e9778795SPeter Avalos } 1413e9778795SPeter Avalos 1414664f4763Szrj /* 1415664f4763Szrj * If the above didn't select another identity to do the signing 1416664f4763Szrj * then default to the one we started with. 1417664f4763Szrj */ 1418664f4763Szrj if (sign_id == NULL) 1419664f4763Szrj sign_id = id; 1420664f4763Szrj 1421664f4763Szrj /* assemble and sign data */ 1422664f4763Szrj for (fallback_sigtype = 0; fallback_sigtype <= 1; fallback_sigtype++) { 1423664f4763Szrj free(alg); 1424664f4763Szrj slen = 0; 1425664f4763Szrj signature = NULL; 1426664f4763Szrj if ((alg = key_sig_algorithm(fallback_sigtype ? NULL : ssh, 1427664f4763Szrj id->key)) == NULL) { 142850a69bb5SSascha Wildner error_f("no mutual signature supported"); 1429664f4763Szrj goto out; 1430664f4763Szrj } 143150a69bb5SSascha Wildner debug3_f("signing using %s %s", alg, fp); 1432664f4763Szrj 1433664f4763Szrj sshbuf_free(b); 1434664f4763Szrj if ((b = sshbuf_new()) == NULL) 143550a69bb5SSascha Wildner fatal_f("sshbuf_new failed"); 143650a69bb5SSascha Wildner if (ssh->compat & SSH_OLD_SESSIONID) { 143750a69bb5SSascha Wildner if ((r = sshbuf_putb(b, ssh->kex->session_id)) != 0) 143850a69bb5SSascha Wildner fatal_fr(r, "sshbuf_putb"); 1439664f4763Szrj } else { 144050a69bb5SSascha Wildner if ((r = sshbuf_put_stringb(b, 144150a69bb5SSascha Wildner ssh->kex->session_id)) != 0) 144250a69bb5SSascha Wildner fatal_fr(r, "sshbuf_put_stringb"); 1443664f4763Szrj } 1444664f4763Szrj skip = sshbuf_len(b); 1445664f4763Szrj if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || 1446664f4763Szrj (r = sshbuf_put_cstring(b, authctxt->server_user)) != 0 || 1447664f4763Szrj (r = sshbuf_put_cstring(b, authctxt->service)) != 0 || 1448*ee116499SAntonio Huete Jimenez (r = sshbuf_put_cstring(b, method)) != 0 || 1449664f4763Szrj (r = sshbuf_put_u8(b, 1)) != 0 || 1450664f4763Szrj (r = sshbuf_put_cstring(b, alg)) != 0 || 1451664f4763Szrj (r = sshkey_puts(id->key, b)) != 0) { 145250a69bb5SSascha Wildner fatal_fr(r, "assemble signed data"); 1453664f4763Szrj } 1454*ee116499SAntonio Huete Jimenez if (hostbound) { 1455*ee116499SAntonio Huete Jimenez if (ssh->kex->initial_hostkey == NULL) { 1456*ee116499SAntonio Huete Jimenez fatal_f("internal error: initial hostkey " 1457*ee116499SAntonio Huete Jimenez "not recorded"); 1458*ee116499SAntonio Huete Jimenez } 1459*ee116499SAntonio Huete Jimenez if ((r = sshkey_puts(ssh->kex->initial_hostkey, b)) != 0) 1460*ee116499SAntonio Huete Jimenez fatal_fr(r, "assemble %s hostkey", method); 1461*ee116499SAntonio Huete Jimenez } 146218de8d7fSPeter Avalos /* generate signature */ 1463664f4763Szrj r = identity_sign(sign_id, &signature, &slen, 146450a69bb5SSascha Wildner sshbuf_ptr(b), sshbuf_len(b), ssh->compat, alg); 1465664f4763Szrj if (r == 0) 1466664f4763Szrj break; 1467664f4763Szrj else if (r == SSH_ERR_KEY_NOT_FOUND) 1468664f4763Szrj goto out; /* soft failure */ 1469664f4763Szrj else if (r == SSH_ERR_SIGN_ALG_UNSUPPORTED && 1470664f4763Szrj !fallback_sigtype) { 1471664f4763Szrj if (sign_id->agent_fd != -1) 1472664f4763Szrj loc = "agent "; 1473664f4763Szrj else if ((sign_id->key->flags & SSHKEY_FLAG_EXT) != 0) 1474664f4763Szrj loc = "token "; 1475664f4763Szrj logit("%skey %s %s returned incorrect signature type", 1476664f4763Szrj loc, sshkey_type(id->key), fp); 1477664f4763Szrj continue; 147818de8d7fSPeter Avalos } 147950a69bb5SSascha Wildner error_fr(r, "signing failed for %s \"%s\"%s", 14800cbfa66cSDaniel Fojt sshkey_type(sign_id->key), sign_id->filename, 148150a69bb5SSascha Wildner id->agent_fd != -1 ? " from agent" : ""); 1482664f4763Szrj goto out; 148318de8d7fSPeter Avalos } 1484664f4763Szrj if (slen == 0 || signature == NULL) /* shouldn't happen */ 148550a69bb5SSascha Wildner fatal_f("no signature"); 148618de8d7fSPeter Avalos 148718de8d7fSPeter Avalos /* append signature */ 1488664f4763Szrj if ((r = sshbuf_put_string(b, signature, slen)) != 0) 148950a69bb5SSascha Wildner fatal_fr(r, "append signature"); 149018de8d7fSPeter Avalos 1491664f4763Szrj #ifdef DEBUG_PK 1492664f4763Szrj sshbuf_dump(b, stderr); 1493664f4763Szrj #endif 149418de8d7fSPeter Avalos /* skip session id and packet type */ 1495664f4763Szrj if ((r = sshbuf_consume(b, skip + 1)) != 0) 149650a69bb5SSascha Wildner fatal_fr(r, "consume"); 149718de8d7fSPeter Avalos 149818de8d7fSPeter Avalos /* put remaining data from buffer into packet */ 1499664f4763Szrj if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || 1500664f4763Szrj (r = sshpkt_putb(ssh, b)) != 0 || 1501664f4763Szrj (r = sshpkt_send(ssh)) != 0) 150250a69bb5SSascha Wildner fatal_fr(r, "enqueue request"); 150318de8d7fSPeter Avalos 1504664f4763Szrj /* success */ 1505664f4763Szrj sent = 1; 1506664f4763Szrj 1507664f4763Szrj out: 1508664f4763Szrj free(fp); 1509664f4763Szrj free(alg); 1510664f4763Szrj sshbuf_free(b); 1511664f4763Szrj freezero(signature, slen); 1512664f4763Szrj return sent; 151318de8d7fSPeter Avalos } 151418de8d7fSPeter Avalos 151518de8d7fSPeter Avalos static int 1516664f4763Szrj send_pubkey_test(struct ssh *ssh, Identity *id) 151718de8d7fSPeter Avalos { 1518664f4763Szrj Authctxt *authctxt = (Authctxt *)ssh->authctxt; 1519664f4763Szrj u_char *blob = NULL; 1520664f4763Szrj char *alg = NULL; 1521664f4763Szrj size_t bloblen; 1522664f4763Szrj u_int have_sig = 0; 1523664f4763Szrj int sent = 0, r; 152418de8d7fSPeter Avalos 1525664f4763Szrj if ((alg = key_sig_algorithm(ssh, id->key)) == NULL) { 152650a69bb5SSascha Wildner debug_f("no mutual signature algorithm"); 1527664f4763Szrj goto out; 1528664f4763Szrj } 152918de8d7fSPeter Avalos 1530664f4763Szrj if ((r = sshkey_to_blob(id->key, &blob, &bloblen)) != 0) { 153118de8d7fSPeter Avalos /* we cannot handle this key */ 153250a69bb5SSascha Wildner debug3_f("cannot handle key"); 1533664f4763Szrj goto out; 153418de8d7fSPeter Avalos } 153518de8d7fSPeter Avalos /* register callback for USERAUTH_PK_OK message */ 1536664f4763Szrj ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok); 153718de8d7fSPeter Avalos 1538664f4763Szrj if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || 1539664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || 1540664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || 1541664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || 1542664f4763Szrj (r = sshpkt_put_u8(ssh, have_sig)) != 0 || 1543664f4763Szrj (r = sshpkt_put_cstring(ssh, alg)) != 0 || 1544664f4763Szrj (r = sshpkt_put_string(ssh, blob, bloblen)) != 0 || 1545664f4763Szrj (r = sshpkt_send(ssh)) != 0) 154650a69bb5SSascha Wildner fatal_fr(r, "send packet"); 1547664f4763Szrj sent = 1; 1548664f4763Szrj 1549664f4763Szrj out: 1550664f4763Szrj free(alg); 155136e94dc5SPeter Avalos free(blob); 1552664f4763Szrj return sent; 155318de8d7fSPeter Avalos } 155418de8d7fSPeter Avalos 1555ce74bacaSMatthew Dillon static struct sshkey * 1556e9778795SPeter Avalos load_identity_file(Identity *id) 155718de8d7fSPeter Avalos { 1558ce74bacaSMatthew Dillon struct sshkey *private = NULL; 1559e9778795SPeter Avalos char prompt[300], *passphrase, *comment; 15600cbfa66cSDaniel Fojt int r, quit = 0, i; 156118de8d7fSPeter Avalos struct stat st; 156218de8d7fSPeter Avalos 15630cbfa66cSDaniel Fojt if (stat(id->filename, &st) == -1) { 156450a69bb5SSascha Wildner do_log2(id->userprovided ? 156550a69bb5SSascha Wildner SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_DEBUG3, 156650a69bb5SSascha Wildner "no such identity: %s: %s", id->filename, strerror(errno)); 156718de8d7fSPeter Avalos return NULL; 156818de8d7fSPeter Avalos } 156918de8d7fSPeter Avalos snprintf(prompt, sizeof prompt, 1570e9778795SPeter Avalos "Enter passphrase for key '%.100s': ", id->filename); 1571e9778795SPeter Avalos for (i = 0; i <= options.number_of_password_prompts; i++) { 1572e9778795SPeter Avalos if (i == 0) 1573e9778795SPeter Avalos passphrase = ""; 1574e9778795SPeter Avalos else { 157518de8d7fSPeter Avalos passphrase = read_passphrase(prompt, 0); 1576e9778795SPeter Avalos if (*passphrase == '\0') { 157718de8d7fSPeter Avalos debug2("no passphrase given, try next key"); 1578e9778795SPeter Avalos free(passphrase); 1579e9778795SPeter Avalos break; 158018de8d7fSPeter Avalos } 1581e9778795SPeter Avalos } 1582e9778795SPeter Avalos switch ((r = sshkey_load_private_type(KEY_UNSPEC, id->filename, 15830cbfa66cSDaniel Fojt passphrase, &private, &comment))) { 1584e9778795SPeter Avalos case 0: 1585e9778795SPeter Avalos break; 1586e9778795SPeter Avalos case SSH_ERR_KEY_WRONG_PASSPHRASE: 1587e9778795SPeter Avalos if (options.batch_mode) { 1588e9778795SPeter Avalos quit = 1; 1589e9778795SPeter Avalos break; 1590e9778795SPeter Avalos } 1591e9778795SPeter Avalos if (i != 0) 1592e9778795SPeter Avalos debug2("bad passphrase given, try again..."); 1593e9778795SPeter Avalos break; 1594e9778795SPeter Avalos case SSH_ERR_SYSTEM_ERROR: 1595e9778795SPeter Avalos if (errno == ENOENT) { 159650a69bb5SSascha Wildner debug2_r(r, "Load key \"%s\"", id->filename); 1597e9778795SPeter Avalos quit = 1; 1598e9778795SPeter Avalos break; 1599e9778795SPeter Avalos } 1600e9778795SPeter Avalos /* FALLTHROUGH */ 1601e9778795SPeter Avalos default: 160250a69bb5SSascha Wildner error_r(r, "Load key \"%s\"", id->filename); 1603e9778795SPeter Avalos quit = 1; 1604e9778795SPeter Avalos break; 1605e9778795SPeter Avalos } 16060cbfa66cSDaniel Fojt if (private != NULL && sshkey_is_sk(private) && 16070cbfa66cSDaniel Fojt options.sk_provider == NULL) { 16080cbfa66cSDaniel Fojt debug("key \"%s\" is an authenticator-hosted key, " 16090cbfa66cSDaniel Fojt "but no provider specified", id->filename); 16100cbfa66cSDaniel Fojt sshkey_free(private); 16110cbfa66cSDaniel Fojt private = NULL; 16120cbfa66cSDaniel Fojt quit = 1; 16130cbfa66cSDaniel Fojt } 1614*ee116499SAntonio Huete Jimenez if (!quit && (r = sshkey_check_rsa_length(private, 1615*ee116499SAntonio Huete Jimenez options.required_rsa_size)) != 0) { 1616*ee116499SAntonio Huete Jimenez debug_fr(r, "Skipping key %s", id->filename); 1617*ee116499SAntonio Huete Jimenez sshkey_free(private); 1618*ee116499SAntonio Huete Jimenez private = NULL; 1619*ee116499SAntonio Huete Jimenez quit = 1; 1620*ee116499SAntonio Huete Jimenez } 1621e9778795SPeter Avalos if (!quit && private != NULL && id->agent_fd == -1 && 1622e9778795SPeter Avalos !(id->key && id->isprivate)) 1623e9778795SPeter Avalos maybe_add_key_to_agent(id->filename, private, comment, 1624e9778795SPeter Avalos passphrase); 1625664f4763Szrj if (i > 0) 1626664f4763Szrj freezero(passphrase, strlen(passphrase)); 1627e9778795SPeter Avalos free(comment); 162818de8d7fSPeter Avalos if (private != NULL || quit) 162918de8d7fSPeter Avalos break; 163018de8d7fSPeter Avalos } 163118de8d7fSPeter Avalos return private; 163218de8d7fSPeter Avalos } 163318de8d7fSPeter Avalos 1634664f4763Szrj static int 1635664f4763Szrj key_type_allowed_by_config(struct sshkey *key) 1636664f4763Szrj { 1637664f4763Szrj if (match_pattern_list(sshkey_ssh_name(key), 163850a69bb5SSascha Wildner options.pubkey_accepted_algos, 0) == 1) 1639664f4763Szrj return 1; 1640664f4763Szrj 1641664f4763Szrj /* RSA keys/certs might be allowed by alternate signature types */ 1642664f4763Szrj switch (key->type) { 1643664f4763Szrj case KEY_RSA: 1644664f4763Szrj if (match_pattern_list("rsa-sha2-512", 164550a69bb5SSascha Wildner options.pubkey_accepted_algos, 0) == 1) 1646664f4763Szrj return 1; 1647664f4763Szrj if (match_pattern_list("rsa-sha2-256", 164850a69bb5SSascha Wildner options.pubkey_accepted_algos, 0) == 1) 1649664f4763Szrj return 1; 1650664f4763Szrj break; 1651664f4763Szrj case KEY_RSA_CERT: 1652664f4763Szrj if (match_pattern_list("rsa-sha2-512-cert-v01@openssh.com", 165350a69bb5SSascha Wildner options.pubkey_accepted_algos, 0) == 1) 1654664f4763Szrj return 1; 1655664f4763Szrj if (match_pattern_list("rsa-sha2-256-cert-v01@openssh.com", 165650a69bb5SSascha Wildner options.pubkey_accepted_algos, 0) == 1) 1657664f4763Szrj return 1; 1658664f4763Szrj break; 1659664f4763Szrj } 1660664f4763Szrj return 0; 1661664f4763Szrj } 1662664f4763Szrj 1663*ee116499SAntonio Huete Jimenez /* obtain a list of keys from the agent */ 1664*ee116499SAntonio Huete Jimenez static int 1665*ee116499SAntonio Huete Jimenez get_agent_identities(struct ssh *ssh, int *agent_fdp, 1666*ee116499SAntonio Huete Jimenez struct ssh_identitylist **idlistp) 1667*ee116499SAntonio Huete Jimenez { 1668*ee116499SAntonio Huete Jimenez int r, agent_fd; 1669*ee116499SAntonio Huete Jimenez struct ssh_identitylist *idlist; 1670*ee116499SAntonio Huete Jimenez 1671*ee116499SAntonio Huete Jimenez if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) { 1672*ee116499SAntonio Huete Jimenez if (r != SSH_ERR_AGENT_NOT_PRESENT) 1673*ee116499SAntonio Huete Jimenez debug_fr(r, "ssh_get_authentication_socket"); 1674*ee116499SAntonio Huete Jimenez return r; 1675*ee116499SAntonio Huete Jimenez } 1676*ee116499SAntonio Huete Jimenez if ((r = ssh_agent_bind_hostkey(agent_fd, ssh->kex->initial_hostkey, 1677*ee116499SAntonio Huete Jimenez ssh->kex->session_id, ssh->kex->initial_sig, 0)) == 0) 1678*ee116499SAntonio Huete Jimenez debug_f("bound agent to hostkey"); 1679*ee116499SAntonio Huete Jimenez else 1680*ee116499SAntonio Huete Jimenez debug2_fr(r, "ssh_agent_bind_hostkey"); 1681*ee116499SAntonio Huete Jimenez 1682*ee116499SAntonio Huete Jimenez if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) { 1683*ee116499SAntonio Huete Jimenez debug_fr(r, "ssh_fetch_identitylist"); 1684*ee116499SAntonio Huete Jimenez close(agent_fd); 1685*ee116499SAntonio Huete Jimenez return r; 1686*ee116499SAntonio Huete Jimenez } 1687*ee116499SAntonio Huete Jimenez /* success */ 1688*ee116499SAntonio Huete Jimenez *agent_fdp = agent_fd; 1689*ee116499SAntonio Huete Jimenez *idlistp = idlist; 1690*ee116499SAntonio Huete Jimenez debug_f("agent returned %zu keys", idlist->nkeys); 1691*ee116499SAntonio Huete Jimenez return 0; 1692*ee116499SAntonio Huete Jimenez } 1693664f4763Szrj 169418de8d7fSPeter Avalos /* 169518de8d7fSPeter Avalos * try keys in the following order: 1696e9778795SPeter Avalos * 1. certificates listed in the config file 1697e9778795SPeter Avalos * 2. other input certificates 1698e9778795SPeter Avalos * 3. agent keys that are found in the config file 1699e9778795SPeter Avalos * 4. other agent keys 1700e9778795SPeter Avalos * 5. keys that are only listed in the config file 170118de8d7fSPeter Avalos */ 170218de8d7fSPeter Avalos static void 1703*ee116499SAntonio Huete Jimenez pubkey_prepare(struct ssh *ssh, Authctxt *authctxt) 170418de8d7fSPeter Avalos { 1705e9778795SPeter Avalos struct identity *id, *id2, *tmp; 1706e9778795SPeter Avalos struct idlist agent, files, *preferred; 1707e9778795SPeter Avalos struct sshkey *key; 1708e9778795SPeter Avalos int agent_fd = -1, i, r, found; 1709e9778795SPeter Avalos size_t j; 1710e9778795SPeter Avalos struct ssh_identitylist *idlist; 1711664f4763Szrj char *ident; 171218de8d7fSPeter Avalos 171318de8d7fSPeter Avalos TAILQ_INIT(&agent); /* keys from the agent */ 171418de8d7fSPeter Avalos TAILQ_INIT(&files); /* keys from the config file */ 171518de8d7fSPeter Avalos preferred = &authctxt->keys; 171618de8d7fSPeter Avalos TAILQ_INIT(preferred); /* preferred order of keys */ 171718de8d7fSPeter Avalos 171836e94dc5SPeter Avalos /* list of keys stored in the filesystem and PKCS#11 */ 171918de8d7fSPeter Avalos for (i = 0; i < options.num_identity_files; i++) { 172018de8d7fSPeter Avalos key = options.identity_keys[i]; 17210cbfa66cSDaniel Fojt if (key && key->cert && 17220cbfa66cSDaniel Fojt key->cert->type != SSH2_CERT_TYPE_USER) { 172350a69bb5SSascha Wildner debug_f("ignoring certificate %s: not a user " 172450a69bb5SSascha Wildner "certificate", options.identity_files[i]); 1725856ea928SPeter Avalos continue; 17260cbfa66cSDaniel Fojt } 17270cbfa66cSDaniel Fojt if (key && sshkey_is_sk(key) && options.sk_provider == NULL) { 172850a69bb5SSascha Wildner debug_f("ignoring authenticator-hosted key %s as no " 17290cbfa66cSDaniel Fojt "SecurityKeyProvider has been specified", 173050a69bb5SSascha Wildner options.identity_files[i]); 17310cbfa66cSDaniel Fojt continue; 17320cbfa66cSDaniel Fojt } 173318de8d7fSPeter Avalos options.identity_keys[i] = NULL; 173418de8d7fSPeter Avalos id = xcalloc(1, sizeof(*id)); 1735e9778795SPeter Avalos id->agent_fd = -1; 173618de8d7fSPeter Avalos id->key = key; 173718de8d7fSPeter Avalos id->filename = xstrdup(options.identity_files[i]); 173836e94dc5SPeter Avalos id->userprovided = options.identity_file_userprovided[i]; 173918de8d7fSPeter Avalos TAILQ_INSERT_TAIL(&files, id, next); 174018de8d7fSPeter Avalos } 1741e9778795SPeter Avalos /* list of certificates specified by user */ 1742e9778795SPeter Avalos for (i = 0; i < options.num_certificate_files; i++) { 1743e9778795SPeter Avalos key = options.certificates[i]; 1744664f4763Szrj if (!sshkey_is_cert(key) || key->cert == NULL || 17450cbfa66cSDaniel Fojt key->cert->type != SSH2_CERT_TYPE_USER) { 174650a69bb5SSascha Wildner debug_f("ignoring certificate %s: not a user " 174750a69bb5SSascha Wildner "certificate", options.identity_files[i]); 1748e9778795SPeter Avalos continue; 17490cbfa66cSDaniel Fojt } 17500cbfa66cSDaniel Fojt if (key && sshkey_is_sk(key) && options.sk_provider == NULL) { 175150a69bb5SSascha Wildner debug_f("ignoring authenticator-hosted key " 17520cbfa66cSDaniel Fojt "certificate %s as no " 17530cbfa66cSDaniel Fojt "SecurityKeyProvider has been specified", 175450a69bb5SSascha Wildner options.identity_files[i]); 17550cbfa66cSDaniel Fojt continue; 17560cbfa66cSDaniel Fojt } 1757e9778795SPeter Avalos id = xcalloc(1, sizeof(*id)); 1758e9778795SPeter Avalos id->agent_fd = -1; 1759e9778795SPeter Avalos id->key = key; 1760e9778795SPeter Avalos id->filename = xstrdup(options.certificate_files[i]); 1761e9778795SPeter Avalos id->userprovided = options.certificate_file_userprovided[i]; 1762e9778795SPeter Avalos TAILQ_INSERT_TAIL(preferred, id, next); 1763e9778795SPeter Avalos } 1764e9778795SPeter Avalos /* list of keys supported by the agent */ 1765*ee116499SAntonio Huete Jimenez if ((r = get_agent_identities(ssh, &agent_fd, &idlist)) == 0) { 1766e9778795SPeter Avalos for (j = 0; j < idlist->nkeys; j++) { 1767*ee116499SAntonio Huete Jimenez if ((r = sshkey_check_rsa_length(idlist->keys[j], 1768*ee116499SAntonio Huete Jimenez options.required_rsa_size)) != 0) { 1769*ee116499SAntonio Huete Jimenez debug_fr(r, "ignoring %s agent key", 1770*ee116499SAntonio Huete Jimenez sshkey_ssh_name(idlist->keys[j])); 1771*ee116499SAntonio Huete Jimenez continue; 1772*ee116499SAntonio Huete Jimenez } 1773e9778795SPeter Avalos found = 0; 1774e9778795SPeter Avalos TAILQ_FOREACH(id, &files, next) { 1775e9778795SPeter Avalos /* 1776e9778795SPeter Avalos * agent keys from the config file are 1777e9778795SPeter Avalos * preferred 1778e9778795SPeter Avalos */ 1779e9778795SPeter Avalos if (sshkey_equal(idlist->keys[j], id->key)) { 1780e9778795SPeter Avalos TAILQ_REMOVE(&files, id, next); 1781e9778795SPeter Avalos TAILQ_INSERT_TAIL(preferred, id, next); 1782e9778795SPeter Avalos id->agent_fd = agent_fd; 1783e9778795SPeter Avalos found = 1; 1784e9778795SPeter Avalos break; 1785e9778795SPeter Avalos } 1786e9778795SPeter Avalos } 1787e9778795SPeter Avalos if (!found && !options.identities_only) { 1788e9778795SPeter Avalos id = xcalloc(1, sizeof(*id)); 1789e9778795SPeter Avalos /* XXX "steals" key/comment from idlist */ 1790e9778795SPeter Avalos id->key = idlist->keys[j]; 1791e9778795SPeter Avalos id->filename = idlist->comments[j]; 1792e9778795SPeter Avalos idlist->keys[j] = NULL; 1793e9778795SPeter Avalos idlist->comments[j] = NULL; 1794e9778795SPeter Avalos id->agent_fd = agent_fd; 1795e9778795SPeter Avalos TAILQ_INSERT_TAIL(&agent, id, next); 1796e9778795SPeter Avalos } 1797e9778795SPeter Avalos } 1798e9778795SPeter Avalos ssh_free_identitylist(idlist); 1799e9778795SPeter Avalos /* append remaining agent keys */ 180050a69bb5SSascha Wildner TAILQ_CONCAT(preferred, &agent, next); 1801e9778795SPeter Avalos authctxt->agent_fd = agent_fd; 1802e9778795SPeter Avalos } 180336e94dc5SPeter Avalos /* Prefer PKCS11 keys that are explicitly listed */ 180436e94dc5SPeter Avalos TAILQ_FOREACH_SAFE(id, &files, next, tmp) { 180536e94dc5SPeter Avalos if (id->key == NULL || (id->key->flags & SSHKEY_FLAG_EXT) == 0) 180636e94dc5SPeter Avalos continue; 180736e94dc5SPeter Avalos found = 0; 180836e94dc5SPeter Avalos TAILQ_FOREACH(id2, &files, next) { 180936e94dc5SPeter Avalos if (id2->key == NULL || 18100cbfa66cSDaniel Fojt (id2->key->flags & SSHKEY_FLAG_EXT) != 0) 181136e94dc5SPeter Avalos continue; 1812e9778795SPeter Avalos if (sshkey_equal(id->key, id2->key)) { 181336e94dc5SPeter Avalos TAILQ_REMOVE(&files, id, next); 181436e94dc5SPeter Avalos TAILQ_INSERT_TAIL(preferred, id, next); 181536e94dc5SPeter Avalos found = 1; 181636e94dc5SPeter Avalos break; 181736e94dc5SPeter Avalos } 181836e94dc5SPeter Avalos } 181936e94dc5SPeter Avalos /* If IdentitiesOnly set and key not found then don't use it */ 182036e94dc5SPeter Avalos if (!found && options.identities_only) { 182136e94dc5SPeter Avalos TAILQ_REMOVE(&files, id, next); 1822664f4763Szrj freezero(id, sizeof(*id)); 182336e94dc5SPeter Avalos } 182436e94dc5SPeter Avalos } 182518de8d7fSPeter Avalos /* append remaining keys from the config file */ 182650a69bb5SSascha Wildner TAILQ_CONCAT(preferred, &files, next); 182750a69bb5SSascha Wildner /* finally, filter by PubkeyAcceptedAlgorithms */ 1828e9778795SPeter Avalos TAILQ_FOREACH_SAFE(id, preferred, next, id2) { 1829664f4763Szrj if (id->key != NULL && !key_type_allowed_by_config(id->key)) { 1830e9778795SPeter Avalos debug("Skipping %s key %s - " 183150a69bb5SSascha Wildner "corresponding algo not in PubkeyAcceptedAlgorithms", 1832e9778795SPeter Avalos sshkey_ssh_name(id->key), id->filename); 1833e9778795SPeter Avalos TAILQ_REMOVE(preferred, id, next); 1834e9778795SPeter Avalos sshkey_free(id->key); 1835e9778795SPeter Avalos free(id->filename); 1836e9778795SPeter Avalos memset(id, 0, sizeof(*id)); 1837e9778795SPeter Avalos continue; 1838e9778795SPeter Avalos } 183918de8d7fSPeter Avalos } 1840664f4763Szrj /* List the keys we plan on using */ 1841664f4763Szrj TAILQ_FOREACH_SAFE(id, preferred, next, id2) { 1842664f4763Szrj ident = format_identity(id); 1843664f4763Szrj debug("Will attempt key: %s", ident); 1844664f4763Szrj free(ident); 1845664f4763Szrj } 184650a69bb5SSascha Wildner debug2_f("done"); 184718de8d7fSPeter Avalos } 184818de8d7fSPeter Avalos 184918de8d7fSPeter Avalos static void 1850664f4763Szrj pubkey_cleanup(struct ssh *ssh) 185118de8d7fSPeter Avalos { 1852664f4763Szrj Authctxt *authctxt = (Authctxt *)ssh->authctxt; 185318de8d7fSPeter Avalos Identity *id; 185418de8d7fSPeter Avalos 1855664f4763Szrj if (authctxt->agent_fd != -1) { 1856e9778795SPeter Avalos ssh_close_authentication_socket(authctxt->agent_fd); 1857664f4763Szrj authctxt->agent_fd = -1; 1858664f4763Szrj } 185918de8d7fSPeter Avalos for (id = TAILQ_FIRST(&authctxt->keys); id; 186018de8d7fSPeter Avalos id = TAILQ_FIRST(&authctxt->keys)) { 186118de8d7fSPeter Avalos TAILQ_REMOVE(&authctxt->keys, id, next); 1862e9778795SPeter Avalos sshkey_free(id->key); 186336e94dc5SPeter Avalos free(id->filename); 186436e94dc5SPeter Avalos free(id); 186518de8d7fSPeter Avalos } 186618de8d7fSPeter Avalos } 186718de8d7fSPeter Avalos 1868ce74bacaSMatthew Dillon static void 1869ce74bacaSMatthew Dillon pubkey_reset(Authctxt *authctxt) 1870ce74bacaSMatthew Dillon { 1871ce74bacaSMatthew Dillon Identity *id; 1872ce74bacaSMatthew Dillon 1873ce74bacaSMatthew Dillon TAILQ_FOREACH(id, &authctxt->keys, next) 1874ce74bacaSMatthew Dillon id->tried = 0; 1875ce74bacaSMatthew Dillon } 1876ce74bacaSMatthew Dillon 1877e9778795SPeter Avalos static int 187850a69bb5SSascha Wildner try_identity(struct ssh *ssh, Identity *id) 1879e9778795SPeter Avalos { 1880e9778795SPeter Avalos if (!id->key) 1881e9778795SPeter Avalos return (0); 1882664f4763Szrj if (sshkey_type_plain(id->key->type) == KEY_RSA && 188350a69bb5SSascha Wildner (ssh->compat & SSH_BUG_RSASIGMD5) != 0) { 1884e9778795SPeter Avalos debug("Skipped %s key %s for RSA/MD5 server", 1885664f4763Szrj sshkey_type(id->key), id->filename); 1886e9778795SPeter Avalos return (0); 1887e9778795SPeter Avalos } 1888ce74bacaSMatthew Dillon return 1; 1889e9778795SPeter Avalos } 1890e9778795SPeter Avalos 1891664f4763Szrj static int 1892664f4763Szrj userauth_pubkey(struct ssh *ssh) 189318de8d7fSPeter Avalos { 1894664f4763Szrj Authctxt *authctxt = (Authctxt *)ssh->authctxt; 189518de8d7fSPeter Avalos Identity *id; 189618de8d7fSPeter Avalos int sent = 0; 1897664f4763Szrj char *ident; 189818de8d7fSPeter Avalos 189918de8d7fSPeter Avalos while ((id = TAILQ_FIRST(&authctxt->keys))) { 190018de8d7fSPeter Avalos if (id->tried++) 190118de8d7fSPeter Avalos return (0); 190218de8d7fSPeter Avalos /* move key to the end of the queue */ 190318de8d7fSPeter Avalos TAILQ_REMOVE(&authctxt->keys, id, next); 190418de8d7fSPeter Avalos TAILQ_INSERT_TAIL(&authctxt->keys, id, next); 190518de8d7fSPeter Avalos /* 190618de8d7fSPeter Avalos * send a test message if we have the public key. for 190718de8d7fSPeter Avalos * encrypted keys we cannot do this and have to load the 190818de8d7fSPeter Avalos * private key instead 190918de8d7fSPeter Avalos */ 191036e94dc5SPeter Avalos if (id->key != NULL) { 191150a69bb5SSascha Wildner if (try_identity(ssh, id)) { 1912664f4763Szrj ident = format_identity(id); 1913664f4763Szrj debug("Offering public key: %s", ident); 1914664f4763Szrj free(ident); 1915664f4763Szrj sent = send_pubkey_test(ssh, id); 191636e94dc5SPeter Avalos } 191736e94dc5SPeter Avalos } else { 191818de8d7fSPeter Avalos debug("Trying private key: %s", id->filename); 1919e9778795SPeter Avalos id->key = load_identity_file(id); 192018de8d7fSPeter Avalos if (id->key != NULL) { 192150a69bb5SSascha Wildner if (try_identity(ssh, id)) { 192218de8d7fSPeter Avalos id->isprivate = 1; 1923664f4763Szrj sent = sign_and_send_pubkey(ssh, id); 192436e94dc5SPeter Avalos } 1925664f4763Szrj sshkey_free(id->key); 192618de8d7fSPeter Avalos id->key = NULL; 1927ce74bacaSMatthew Dillon id->isprivate = 0; 192818de8d7fSPeter Avalos } 192918de8d7fSPeter Avalos } 193018de8d7fSPeter Avalos if (sent) 193118de8d7fSPeter Avalos return (sent); 193218de8d7fSPeter Avalos } 193318de8d7fSPeter Avalos return (0); 193418de8d7fSPeter Avalos } 193518de8d7fSPeter Avalos 193618de8d7fSPeter Avalos /* 193718de8d7fSPeter Avalos * Send userauth request message specifying keyboard-interactive method. 193818de8d7fSPeter Avalos */ 1939664f4763Szrj static int 1940664f4763Szrj userauth_kbdint(struct ssh *ssh) 194118de8d7fSPeter Avalos { 1942664f4763Szrj Authctxt *authctxt = (Authctxt *)ssh->authctxt; 1943664f4763Szrj int r; 194418de8d7fSPeter Avalos 1945664f4763Szrj if (authctxt->attempt_kbdint++ >= options.number_of_password_prompts) 194618de8d7fSPeter Avalos return 0; 194718de8d7fSPeter Avalos /* disable if no SSH2_MSG_USERAUTH_INFO_REQUEST has been seen */ 1948664f4763Szrj if (authctxt->attempt_kbdint > 1 && !authctxt->info_req_seen) { 194918de8d7fSPeter Avalos debug3("userauth_kbdint: disable: no info_req_seen"); 1950664f4763Szrj ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_INFO_REQUEST, NULL); 195118de8d7fSPeter Avalos return 0; 195218de8d7fSPeter Avalos } 195318de8d7fSPeter Avalos 195418de8d7fSPeter Avalos debug2("userauth_kbdint"); 1955664f4763Szrj if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || 1956664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || 1957664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || 1958664f4763Szrj (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || 1959664f4763Szrj (r = sshpkt_put_cstring(ssh, "")) != 0 || /* lang */ 1960664f4763Szrj (r = sshpkt_put_cstring(ssh, options.kbd_interactive_devices ? 1961664f4763Szrj options.kbd_interactive_devices : "")) != 0 || 1962664f4763Szrj (r = sshpkt_send(ssh)) != 0) 196350a69bb5SSascha Wildner fatal_fr(r, "send packet"); 196418de8d7fSPeter Avalos 1965664f4763Szrj ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req); 196618de8d7fSPeter Avalos return 1; 196718de8d7fSPeter Avalos } 196818de8d7fSPeter Avalos 196918de8d7fSPeter Avalos /* 197018de8d7fSPeter Avalos * parse INFO_REQUEST, prompt user and send INFO_RESPONSE 197118de8d7fSPeter Avalos */ 1972664f4763Szrj static int 1973ce74bacaSMatthew Dillon input_userauth_info_req(int type, u_int32_t seq, struct ssh *ssh) 197418de8d7fSPeter Avalos { 1975ce74bacaSMatthew Dillon Authctxt *authctxt = ssh->authctxt; 1976664f4763Szrj char *name = NULL, *inst = NULL, *lang = NULL, *prompt = NULL; 197750a69bb5SSascha Wildner char *display_prompt = NULL, *response = NULL; 1978664f4763Szrj u_char echo = 0; 197918de8d7fSPeter Avalos u_int num_prompts, i; 1980664f4763Szrj int r; 198118de8d7fSPeter Avalos 198250a69bb5SSascha Wildner debug2_f("entering"); 198318de8d7fSPeter Avalos 198418de8d7fSPeter Avalos if (authctxt == NULL) 198550a69bb5SSascha Wildner fatal_f("no authentication context"); 198618de8d7fSPeter Avalos 198718de8d7fSPeter Avalos authctxt->info_req_seen = 1; 198818de8d7fSPeter Avalos 1989664f4763Szrj if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0 || 1990664f4763Szrj (r = sshpkt_get_cstring(ssh, &inst, NULL)) != 0 || 1991664f4763Szrj (r = sshpkt_get_cstring(ssh, &lang, NULL)) != 0) 1992664f4763Szrj goto out; 199318de8d7fSPeter Avalos if (strlen(name) > 0) 199418de8d7fSPeter Avalos logit("%s", name); 199518de8d7fSPeter Avalos if (strlen(inst) > 0) 199618de8d7fSPeter Avalos logit("%s", inst); 199718de8d7fSPeter Avalos 1998664f4763Szrj if ((r = sshpkt_get_u32(ssh, &num_prompts)) != 0) 1999664f4763Szrj goto out; 200018de8d7fSPeter Avalos /* 200118de8d7fSPeter Avalos * Begin to build info response packet based on prompts requested. 200218de8d7fSPeter Avalos * We commit to providing the correct number of responses, so if 200318de8d7fSPeter Avalos * further on we run into a problem that prevents this, we have to 200418de8d7fSPeter Avalos * be sure and clean this up and send a correct error response. 200518de8d7fSPeter Avalos */ 2006664f4763Szrj if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_INFO_RESPONSE)) != 0 || 2007664f4763Szrj (r = sshpkt_put_u32(ssh, num_prompts)) != 0) 2008664f4763Szrj goto out; 200918de8d7fSPeter Avalos 201050a69bb5SSascha Wildner debug2_f("num_prompts %d", num_prompts); 201118de8d7fSPeter Avalos for (i = 0; i < num_prompts; i++) { 2012664f4763Szrj if ((r = sshpkt_get_cstring(ssh, &prompt, NULL)) != 0 || 2013664f4763Szrj (r = sshpkt_get_u8(ssh, &echo)) != 0) 2014664f4763Szrj goto out; 201550a69bb5SSascha Wildner if (asmprintf(&display_prompt, INT_MAX, NULL, "(%s@%s) %s", 201650a69bb5SSascha Wildner authctxt->server_user, options.host_key_alias ? 201750a69bb5SSascha Wildner options.host_key_alias : authctxt->host, prompt) == -1) 201850a69bb5SSascha Wildner fatal_f("asmprintf failed"); 201950a69bb5SSascha Wildner response = read_passphrase(display_prompt, echo ? RP_ECHO : 0); 2020664f4763Szrj if ((r = sshpkt_put_cstring(ssh, response)) != 0) 2021664f4763Szrj goto out; 2022664f4763Szrj freezero(response, strlen(response)); 202336e94dc5SPeter Avalos free(prompt); 202450a69bb5SSascha Wildner free(display_prompt); 202550a69bb5SSascha Wildner display_prompt = response = prompt = NULL; 202618de8d7fSPeter Avalos } 2027664f4763Szrj /* done with parsing incoming message. */ 2028664f4763Szrj if ((r = sshpkt_get_end(ssh)) != 0 || 2029664f4763Szrj (r = sshpkt_add_padding(ssh, 64)) != 0) 2030664f4763Szrj goto out; 2031664f4763Szrj r = sshpkt_send(ssh); 2032664f4763Szrj out: 2033664f4763Szrj if (response) 2034664f4763Szrj freezero(response, strlen(response)); 2035664f4763Szrj free(prompt); 203650a69bb5SSascha Wildner free(display_prompt); 2037664f4763Szrj free(name); 2038664f4763Szrj free(inst); 2039664f4763Szrj free(lang); 2040664f4763Szrj return r; 204118de8d7fSPeter Avalos } 204218de8d7fSPeter Avalos 204318de8d7fSPeter Avalos static int 2044664f4763Szrj ssh_keysign(struct ssh *ssh, struct sshkey *key, u_char **sigp, size_t *lenp, 2045e9778795SPeter Avalos const u_char *data, size_t datalen) 204618de8d7fSPeter Avalos { 2047e9778795SPeter Avalos struct sshbuf *b; 204818de8d7fSPeter Avalos struct stat st; 204918de8d7fSPeter Avalos pid_t pid; 20500cbfa66cSDaniel Fojt int r, to[2], from[2], status; 2051664f4763Szrj int sock = ssh_packet_get_connection_in(ssh); 2052e9778795SPeter Avalos u_char rversion = 0, version = 2; 2053e9778795SPeter Avalos void (*osigchld)(int); 205418de8d7fSPeter Avalos 2055e9778795SPeter Avalos *sigp = NULL; 2056e9778795SPeter Avalos *lenp = 0; 205718de8d7fSPeter Avalos 20580cbfa66cSDaniel Fojt if (stat(_PATH_SSH_KEY_SIGN, &st) == -1) { 205950a69bb5SSascha Wildner error_f("not installed: %s", strerror(errno)); 206018de8d7fSPeter Avalos return -1; 206118de8d7fSPeter Avalos } 2062e9778795SPeter Avalos if (fflush(stdout) != 0) { 206350a69bb5SSascha Wildner error_f("fflush: %s", strerror(errno)); 2064e9778795SPeter Avalos return -1; 2065e9778795SPeter Avalos } 20660cbfa66cSDaniel Fojt if (pipe(to) == -1) { 206750a69bb5SSascha Wildner error_f("pipe: %s", strerror(errno)); 206818de8d7fSPeter Avalos return -1; 206918de8d7fSPeter Avalos } 20700cbfa66cSDaniel Fojt if (pipe(from) == -1) { 207150a69bb5SSascha Wildner error_f("pipe: %s", strerror(errno)); 207218de8d7fSPeter Avalos return -1; 207318de8d7fSPeter Avalos } 20740cbfa66cSDaniel Fojt if ((pid = fork()) == -1) { 207550a69bb5SSascha Wildner error_f("fork: %s", strerror(errno)); 207618de8d7fSPeter Avalos return -1; 207718de8d7fSPeter Avalos } 20780cbfa66cSDaniel Fojt osigchld = ssh_signal(SIGCHLD, SIG_DFL); 207918de8d7fSPeter Avalos if (pid == 0) { 208018de8d7fSPeter Avalos close(from[0]); 20810cbfa66cSDaniel Fojt if (dup2(from[1], STDOUT_FILENO) == -1) 208250a69bb5SSascha Wildner fatal_f("dup2: %s", strerror(errno)); 208318de8d7fSPeter Avalos close(to[1]); 20840cbfa66cSDaniel Fojt if (dup2(to[0], STDIN_FILENO) == -1) 208550a69bb5SSascha Wildner fatal_f("dup2: %s", strerror(errno)); 208618de8d7fSPeter Avalos close(from[1]); 208718de8d7fSPeter Avalos close(to[0]); 20880cbfa66cSDaniel Fojt 20890cbfa66cSDaniel Fojt if (dup2(sock, STDERR_FILENO + 1) == -1) 209050a69bb5SSascha Wildner fatal_f("dup2: %s", strerror(errno)); 20910cbfa66cSDaniel Fojt sock = STDERR_FILENO + 1; 20920cbfa66cSDaniel Fojt fcntl(sock, F_SETFD, 0); /* keep the socket on exec */ 2093e9778795SPeter Avalos closefrom(sock + 1); 20940cbfa66cSDaniel Fojt 209550a69bb5SSascha Wildner debug3_f("[child] pid=%ld, exec %s", 209650a69bb5SSascha Wildner (long)getpid(), _PATH_SSH_KEY_SIGN); 2097e9778795SPeter Avalos execl(_PATH_SSH_KEY_SIGN, _PATH_SSH_KEY_SIGN, (char *)NULL); 209850a69bb5SSascha Wildner fatal_f("exec(%s): %s", _PATH_SSH_KEY_SIGN, 209918de8d7fSPeter Avalos strerror(errno)); 210018de8d7fSPeter Avalos } 210118de8d7fSPeter Avalos close(from[1]); 210218de8d7fSPeter Avalos close(to[0]); 21030cbfa66cSDaniel Fojt sock = STDERR_FILENO + 1; 210418de8d7fSPeter Avalos 2105e9778795SPeter Avalos if ((b = sshbuf_new()) == NULL) 210650a69bb5SSascha Wildner fatal_f("sshbuf_new failed"); 2107e9778795SPeter Avalos /* send # of sock, data to be signed */ 2108ce74bacaSMatthew Dillon if ((r = sshbuf_put_u32(b, sock)) != 0 || 2109e9778795SPeter Avalos (r = sshbuf_put_string(b, data, datalen)) != 0) 211050a69bb5SSascha Wildner fatal_fr(r, "buffer error"); 2111e9778795SPeter Avalos if (ssh_msg_send(to[1], version, b) == -1) 211250a69bb5SSascha Wildner fatal_f("couldn't send request"); 2113e9778795SPeter Avalos sshbuf_reset(b); 2114e9778795SPeter Avalos r = ssh_msg_recv(from[0], b); 211518de8d7fSPeter Avalos close(from[0]); 211618de8d7fSPeter Avalos close(to[1]); 2117e9778795SPeter Avalos if (r < 0) { 211850a69bb5SSascha Wildner error_f("no reply"); 2119e9778795SPeter Avalos goto fail; 2120e9778795SPeter Avalos } 212118de8d7fSPeter Avalos 2122e9778795SPeter Avalos errno = 0; 21230cbfa66cSDaniel Fojt while (waitpid(pid, &status, 0) == -1) { 2124e9778795SPeter Avalos if (errno != EINTR) { 212550a69bb5SSascha Wildner error_f("waitpid %ld: %s", (long)pid, strerror(errno)); 2126e9778795SPeter Avalos goto fail; 2127e9778795SPeter Avalos } 2128e9778795SPeter Avalos } 2129e9778795SPeter Avalos if (!WIFEXITED(status)) { 213050a69bb5SSascha Wildner error_f("exited abnormally"); 2131e9778795SPeter Avalos goto fail; 2132e9778795SPeter Avalos } 2133e9778795SPeter Avalos if (WEXITSTATUS(status) != 0) { 213450a69bb5SSascha Wildner error_f("exited with status %d", WEXITSTATUS(status)); 2135e9778795SPeter Avalos goto fail; 2136e9778795SPeter Avalos } 2137e9778795SPeter Avalos if ((r = sshbuf_get_u8(b, &rversion)) != 0) { 213850a69bb5SSascha Wildner error_fr(r, "buffer error"); 2139e9778795SPeter Avalos goto fail; 2140e9778795SPeter Avalos } 2141e9778795SPeter Avalos if (rversion != version) { 214250a69bb5SSascha Wildner error_f("bad version"); 2143e9778795SPeter Avalos goto fail; 2144e9778795SPeter Avalos } 2145e9778795SPeter Avalos if ((r = sshbuf_get_string(b, sigp, lenp)) != 0) { 214650a69bb5SSascha Wildner error_fr(r, "buffer error"); 2147e9778795SPeter Avalos fail: 21480cbfa66cSDaniel Fojt ssh_signal(SIGCHLD, osigchld); 2149e9778795SPeter Avalos sshbuf_free(b); 215018de8d7fSPeter Avalos return -1; 215118de8d7fSPeter Avalos } 21520cbfa66cSDaniel Fojt ssh_signal(SIGCHLD, osigchld); 2153e9778795SPeter Avalos sshbuf_free(b); 215418de8d7fSPeter Avalos 215518de8d7fSPeter Avalos return 0; 215618de8d7fSPeter Avalos } 215718de8d7fSPeter Avalos 2158664f4763Szrj static int 2159664f4763Szrj userauth_hostbased(struct ssh *ssh) 216018de8d7fSPeter Avalos { 2161664f4763Szrj Authctxt *authctxt = (Authctxt *)ssh->authctxt; 2162e9778795SPeter Avalos struct sshkey *private = NULL; 2163e9778795SPeter Avalos struct sshbuf *b = NULL; 2164e9778795SPeter Avalos u_char *sig = NULL, *keyblob = NULL; 2165e9778795SPeter Avalos char *fp = NULL, *chost = NULL, *lname = NULL; 2166e9778795SPeter Avalos size_t siglen = 0, keylen = 0; 2167e9778795SPeter Avalos int i, r, success = 0; 2168e9778795SPeter Avalos 2169e9778795SPeter Avalos if (authctxt->ktypes == NULL) { 217050a69bb5SSascha Wildner authctxt->oktypes = xstrdup(options.hostbased_accepted_algos); 2171e9778795SPeter Avalos authctxt->ktypes = authctxt->oktypes; 2172e9778795SPeter Avalos } 2173e9778795SPeter Avalos 2174e9778795SPeter Avalos /* 217550a69bb5SSascha Wildner * Work through each listed type pattern in HostbasedAcceptedAlgorithms, 2176e9778795SPeter Avalos * trying each hostkey that matches the type in turn. 2177e9778795SPeter Avalos */ 2178e9778795SPeter Avalos for (;;) { 2179e9778795SPeter Avalos if (authctxt->active_ktype == NULL) 2180e9778795SPeter Avalos authctxt->active_ktype = strsep(&authctxt->ktypes, ","); 2181e9778795SPeter Avalos if (authctxt->active_ktype == NULL || 2182e9778795SPeter Avalos *authctxt->active_ktype == '\0') 2183e9778795SPeter Avalos break; 218450a69bb5SSascha Wildner debug3_f("trying key type %s", authctxt->active_ktype); 218518de8d7fSPeter Avalos 218618de8d7fSPeter Avalos /* check for a useful key */ 2187e9778795SPeter Avalos private = NULL; 2188e9778795SPeter Avalos for (i = 0; i < authctxt->sensitive->nkeys; i++) { 2189e9778795SPeter Avalos if (authctxt->sensitive->keys[i] == NULL || 2190e9778795SPeter Avalos authctxt->sensitive->keys[i]->type == KEY_UNSPEC) 2191e9778795SPeter Avalos continue; 2192*ee116499SAntonio Huete Jimenez if (!sshkey_match_keyname_to_sigalgs( 2193e9778795SPeter Avalos sshkey_ssh_name(authctxt->sensitive->keys[i]), 2194*ee116499SAntonio Huete Jimenez authctxt->active_ktype)) 2195e9778795SPeter Avalos continue; 219618de8d7fSPeter Avalos /* we take and free the key */ 2197e9778795SPeter Avalos private = authctxt->sensitive->keys[i]; 2198e9778795SPeter Avalos authctxt->sensitive->keys[i] = NULL; 219918de8d7fSPeter Avalos break; 220018de8d7fSPeter Avalos } 2201e9778795SPeter Avalos /* Found one */ 2202e9778795SPeter Avalos if (private != NULL) 2203e9778795SPeter Avalos break; 2204e9778795SPeter Avalos /* No more keys of this type; advance */ 2205e9778795SPeter Avalos authctxt->active_ktype = NULL; 220618de8d7fSPeter Avalos } 2207e9778795SPeter Avalos if (private == NULL) { 2208e9778795SPeter Avalos free(authctxt->oktypes); 2209e9778795SPeter Avalos authctxt->oktypes = authctxt->ktypes = NULL; 2210e9778795SPeter Avalos authctxt->active_ktype = NULL; 221118de8d7fSPeter Avalos debug("No more client hostkeys for hostbased authentication."); 2212e9778795SPeter Avalos goto out; 221318de8d7fSPeter Avalos } 2214e9778795SPeter Avalos 2215e9778795SPeter Avalos if ((fp = sshkey_fingerprint(private, options.fingerprint_hash, 2216e9778795SPeter Avalos SSH_FP_DEFAULT)) == NULL) { 221750a69bb5SSascha Wildner error_f("sshkey_fingerprint failed"); 2218e9778795SPeter Avalos goto out; 221918de8d7fSPeter Avalos } 2220*ee116499SAntonio Huete Jimenez debug_f("trying hostkey %s %s using sigalg %s", 2221*ee116499SAntonio Huete Jimenez sshkey_ssh_name(private), fp, authctxt->active_ktype); 2222e9778795SPeter Avalos 222318de8d7fSPeter Avalos /* figure out a name for the client host */ 2224664f4763Szrj lname = get_local_name(ssh_packet_get_connection_in(ssh)); 2225664f4763Szrj if (lname == NULL) { 222650a69bb5SSascha Wildner error_f("cannot get local ipaddr/name"); 2227e9778795SPeter Avalos goto out; 222818de8d7fSPeter Avalos } 2229e9778795SPeter Avalos 2230e9778795SPeter Avalos /* XXX sshbuf_put_stringf? */ 2231e9778795SPeter Avalos xasprintf(&chost, "%s.", lname); 223250a69bb5SSascha Wildner debug2_f("chost %s", chost); 223318de8d7fSPeter Avalos 2234e9778795SPeter Avalos /* construct data */ 2235e9778795SPeter Avalos if ((b = sshbuf_new()) == NULL) { 223650a69bb5SSascha Wildner error_f("sshbuf_new failed"); 2237e9778795SPeter Avalos goto out; 2238e9778795SPeter Avalos } 2239e9778795SPeter Avalos if ((r = sshkey_to_blob(private, &keyblob, &keylen)) != 0) { 224050a69bb5SSascha Wildner error_fr(r, "sshkey_to_blob"); 2241e9778795SPeter Avalos goto out; 2242e9778795SPeter Avalos } 224350a69bb5SSascha Wildner if ((r = sshbuf_put_stringb(b, ssh->kex->session_id)) != 0 || 2244e9778795SPeter Avalos (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || 2245e9778795SPeter Avalos (r = sshbuf_put_cstring(b, authctxt->server_user)) != 0 || 2246664f4763Szrj (r = sshbuf_put_cstring(b, authctxt->service)) != 0 || 2247e9778795SPeter Avalos (r = sshbuf_put_cstring(b, authctxt->method->name)) != 0 || 224850a69bb5SSascha Wildner (r = sshbuf_put_cstring(b, authctxt->active_ktype)) != 0 || 2249e9778795SPeter Avalos (r = sshbuf_put_string(b, keyblob, keylen)) != 0 || 2250e9778795SPeter Avalos (r = sshbuf_put_cstring(b, chost)) != 0 || 2251e9778795SPeter Avalos (r = sshbuf_put_cstring(b, authctxt->local_user)) != 0) { 225250a69bb5SSascha Wildner error_fr(r, "buffer error"); 2253e9778795SPeter Avalos goto out; 2254e9778795SPeter Avalos } 2255e9778795SPeter Avalos 2256e9778795SPeter Avalos #ifdef DEBUG_PK 2257e9778795SPeter Avalos sshbuf_dump(b, stderr); 2258e9778795SPeter Avalos #endif 2259664f4763Szrj if ((r = ssh_keysign(ssh, private, &sig, &siglen, 2260664f4763Szrj sshbuf_ptr(b), sshbuf_len(b))) != 0) { 2261e9778795SPeter Avalos error("sign using hostkey %s %s failed", 2262e9778795SPeter Avalos sshkey_ssh_name(private), fp); 2263e9778795SPeter Avalos goto out; 2264e9778795SPeter Avalos } 2265e9778795SPeter Avalos if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || 2266e9778795SPeter Avalos (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || 2267e9778795SPeter Avalos (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || 2268e9778795SPeter Avalos (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || 226950a69bb5SSascha Wildner (r = sshpkt_put_cstring(ssh, authctxt->active_ktype)) != 0 || 2270e9778795SPeter Avalos (r = sshpkt_put_string(ssh, keyblob, keylen)) != 0 || 2271e9778795SPeter Avalos (r = sshpkt_put_cstring(ssh, chost)) != 0 || 2272e9778795SPeter Avalos (r = sshpkt_put_cstring(ssh, authctxt->local_user)) != 0 || 2273e9778795SPeter Avalos (r = sshpkt_put_string(ssh, sig, siglen)) != 0 || 2274e9778795SPeter Avalos (r = sshpkt_send(ssh)) != 0) { 227550a69bb5SSascha Wildner error_fr(r, "packet error"); 2276e9778795SPeter Avalos goto out; 2277e9778795SPeter Avalos } 2278e9778795SPeter Avalos success = 1; 2279e9778795SPeter Avalos 2280e9778795SPeter Avalos out: 2281664f4763Szrj if (sig != NULL) 2282664f4763Szrj freezero(sig, siglen); 2283e9778795SPeter Avalos free(keyblob); 2284e9778795SPeter Avalos free(lname); 2285e9778795SPeter Avalos free(fp); 2286e9778795SPeter Avalos free(chost); 2287e9778795SPeter Avalos sshkey_free(private); 2288e9778795SPeter Avalos sshbuf_free(b); 2289e9778795SPeter Avalos 2290e9778795SPeter Avalos return success; 229118de8d7fSPeter Avalos } 229218de8d7fSPeter Avalos 229318de8d7fSPeter Avalos /* find auth method */ 229418de8d7fSPeter Avalos 229518de8d7fSPeter Avalos /* 229618de8d7fSPeter Avalos * given auth method name, if configurable options permit this method fill 229718de8d7fSPeter Avalos * in auth_ident field and return true, otherwise return false. 229818de8d7fSPeter Avalos */ 229918de8d7fSPeter Avalos static int 230018de8d7fSPeter Avalos authmethod_is_enabled(Authmethod *method) 230118de8d7fSPeter Avalos { 230218de8d7fSPeter Avalos if (method == NULL) 230318de8d7fSPeter Avalos return 0; 230418de8d7fSPeter Avalos /* return false if options indicate this method is disabled */ 230518de8d7fSPeter Avalos if (method->enabled == NULL || *method->enabled == 0) 230618de8d7fSPeter Avalos return 0; 230718de8d7fSPeter Avalos /* return false if batch mode is enabled but method needs interactive mode */ 230818de8d7fSPeter Avalos if (method->batch_flag != NULL && *method->batch_flag != 0) 230918de8d7fSPeter Avalos return 0; 231018de8d7fSPeter Avalos return 1; 231118de8d7fSPeter Avalos } 231218de8d7fSPeter Avalos 231318de8d7fSPeter Avalos static Authmethod * 231418de8d7fSPeter Avalos authmethod_lookup(const char *name) 231518de8d7fSPeter Avalos { 231618de8d7fSPeter Avalos Authmethod *method = NULL; 231718de8d7fSPeter Avalos if (name != NULL) 231818de8d7fSPeter Avalos for (method = authmethods; method->name != NULL; method++) 231918de8d7fSPeter Avalos if (strcmp(name, method->name) == 0) 232018de8d7fSPeter Avalos return method; 232118de8d7fSPeter Avalos debug2("Unrecognized authentication method name: %s", name ? name : "NULL"); 232218de8d7fSPeter Avalos return NULL; 232318de8d7fSPeter Avalos } 232418de8d7fSPeter Avalos 232518de8d7fSPeter Avalos /* XXX internal state */ 232618de8d7fSPeter Avalos static Authmethod *current = NULL; 232718de8d7fSPeter Avalos static char *supported = NULL; 232818de8d7fSPeter Avalos static char *preferred = NULL; 232918de8d7fSPeter Avalos 233018de8d7fSPeter Avalos /* 233118de8d7fSPeter Avalos * Given the authentication method list sent by the server, return the 233218de8d7fSPeter Avalos * next method we should try. If the server initially sends a nil list, 233318de8d7fSPeter Avalos * use a built-in default list. 233418de8d7fSPeter Avalos */ 233518de8d7fSPeter Avalos static Authmethod * 233618de8d7fSPeter Avalos authmethod_get(char *authlist) 233718de8d7fSPeter Avalos { 233818de8d7fSPeter Avalos char *name = NULL; 233918de8d7fSPeter Avalos u_int next; 234018de8d7fSPeter Avalos 234118de8d7fSPeter Avalos /* Use a suitable default if we're passed a nil list. */ 234218de8d7fSPeter Avalos if (authlist == NULL || strlen(authlist) == 0) 234318de8d7fSPeter Avalos authlist = options.preferred_authentications; 234418de8d7fSPeter Avalos 234518de8d7fSPeter Avalos if (supported == NULL || strcmp(authlist, supported) != 0) { 234618de8d7fSPeter Avalos debug3("start over, passed a different list %s", authlist); 234736e94dc5SPeter Avalos free(supported); 234818de8d7fSPeter Avalos supported = xstrdup(authlist); 234918de8d7fSPeter Avalos preferred = options.preferred_authentications; 235018de8d7fSPeter Avalos debug3("preferred %s", preferred); 235118de8d7fSPeter Avalos current = NULL; 235218de8d7fSPeter Avalos } else if (current != NULL && authmethod_is_enabled(current)) 235318de8d7fSPeter Avalos return current; 235418de8d7fSPeter Avalos 235518de8d7fSPeter Avalos for (;;) { 235618de8d7fSPeter Avalos if ((name = match_list(preferred, supported, &next)) == NULL) { 235718de8d7fSPeter Avalos debug("No more authentication methods to try."); 235818de8d7fSPeter Avalos current = NULL; 235918de8d7fSPeter Avalos return NULL; 236018de8d7fSPeter Avalos } 236118de8d7fSPeter Avalos preferred += next; 236218de8d7fSPeter Avalos debug3("authmethod_lookup %s", name); 236318de8d7fSPeter Avalos debug3("remaining preferred: %s", preferred); 236418de8d7fSPeter Avalos if ((current = authmethod_lookup(name)) != NULL && 236518de8d7fSPeter Avalos authmethod_is_enabled(current)) { 236618de8d7fSPeter Avalos debug3("authmethod_is_enabled %s", name); 236718de8d7fSPeter Avalos debug("Next authentication method: %s", name); 236836e94dc5SPeter Avalos free(name); 236918de8d7fSPeter Avalos return current; 237018de8d7fSPeter Avalos } 237136e94dc5SPeter Avalos free(name); 237218de8d7fSPeter Avalos } 237318de8d7fSPeter Avalos } 237418de8d7fSPeter Avalos 237518de8d7fSPeter Avalos static char * 237618de8d7fSPeter Avalos authmethods_get(void) 237718de8d7fSPeter Avalos { 237818de8d7fSPeter Avalos Authmethod *method = NULL; 2379664f4763Szrj struct sshbuf *b; 238018de8d7fSPeter Avalos char *list; 2381664f4763Szrj int r; 238218de8d7fSPeter Avalos 2383664f4763Szrj if ((b = sshbuf_new()) == NULL) 238450a69bb5SSascha Wildner fatal_f("sshbuf_new failed"); 238518de8d7fSPeter Avalos for (method = authmethods; method->name != NULL; method++) { 238618de8d7fSPeter Avalos if (authmethod_is_enabled(method)) { 2387664f4763Szrj if ((r = sshbuf_putf(b, "%s%s", 2388664f4763Szrj sshbuf_len(b) ? "," : "", method->name)) != 0) 238950a69bb5SSascha Wildner fatal_fr(r, "buffer error"); 239018de8d7fSPeter Avalos } 239118de8d7fSPeter Avalos } 2392664f4763Szrj if ((list = sshbuf_dup_string(b)) == NULL) 239350a69bb5SSascha Wildner fatal_f("sshbuf_dup_string failed"); 2394664f4763Szrj sshbuf_free(b); 239518de8d7fSPeter Avalos return list; 239618de8d7fSPeter Avalos } 2397