sshconnect2.c revision 69587
160573Skris/* 260573Skris * Copyright (c) 2000 Markus Friedl. All rights reserved. 360573Skris * 460573Skris * Redistribution and use in source and binary forms, with or without 560573Skris * modification, are permitted provided that the following conditions 660573Skris * are met: 760573Skris * 1. Redistributions of source code must retain the above copyright 860573Skris * notice, this list of conditions and the following disclaimer. 960573Skris * 2. Redistributions in binary form must reproduce the above copyright 1060573Skris * notice, this list of conditions and the following disclaimer in the 1160573Skris * documentation and/or other materials provided with the distribution. 1260573Skris * 1360573Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1460573Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1560573Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1660573Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1760573Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1860573Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1960573Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2060573Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2160573Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2260573Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2360573Skris */ 2460573Skris 2560573Skris#include "includes.h" 2669587SgreenRCSID("$OpenBSD: sshconnect2.c,v 1.27 2000/10/19 16:45:16 provos Exp $"); 2760573Skris 2860573Skris#include <openssl/bn.h> 2960573Skris#include <openssl/rsa.h> 3060573Skris#include <openssl/dsa.h> 3160573Skris#include <openssl/md5.h> 3260573Skris#include <openssl/dh.h> 3360573Skris#include <openssl/hmac.h> 3460573Skris 3560573Skris#include "ssh.h" 3660573Skris#include "xmalloc.h" 3760573Skris#include "rsa.h" 3860573Skris#include "buffer.h" 3960573Skris#include "packet.h" 4060573Skris#include "uidswap.h" 4160573Skris#include "compat.h" 4260573Skris#include "readconf.h" 4360573Skris#include "bufaux.h" 4460573Skris#include "ssh2.h" 4560573Skris#include "kex.h" 4660573Skris#include "myproposal.h" 4760573Skris#include "key.h" 4860573Skris#include "dsa.h" 4960573Skris#include "sshconnect.h" 5060573Skris#include "authfile.h" 5169587Sgreen#include "cli.h" 5269587Sgreen#include "dispatch.h" 5365668Skris#include "authfd.h" 5460573Skris 5569587Sgreenvoid ssh_dh1_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *); 5669587Sgreenvoid ssh_dhgex_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *); 5769587Sgreen 5860573Skris/* import */ 5960573Skrisextern char *client_version_string; 6060573Skrisextern char *server_version_string; 6160573Skrisextern Options options; 6260573Skris 6360573Skris/* 6460573Skris * SSH2 key exchange 6560573Skris */ 6660573Skris 6760573Skrisunsigned char *session_id2 = NULL; 6860573Skrisint session_id2_len = 0; 6960573Skris 7060573Skrisvoid 7169587Sgreenssh_kex2(char *host, struct sockaddr *hostaddr) 7260573Skris{ 7369587Sgreen int i, plen; 7469587Sgreen Kex *kex; 7569587Sgreen Buffer *client_kexinit, *server_kexinit; 7669587Sgreen char *sprop[PROPOSAL_MAX]; 7769587Sgreen 7869587Sgreen if (options.ciphers == NULL) { 7969587Sgreen if (options.cipher == SSH_CIPHER_3DES) { 8069587Sgreen options.ciphers = "3des-cbc"; 8169587Sgreen } else if (options.cipher == SSH_CIPHER_BLOWFISH) { 8269587Sgreen options.ciphers = "blowfish-cbc"; 8369587Sgreen } else if (options.cipher == SSH_CIPHER_DES) { 8469587Sgreen fatal("cipher DES not supported for protocol version 2"); 8569587Sgreen } 8669587Sgreen } 8769587Sgreen if (options.ciphers != NULL) { 8869587Sgreen myproposal[PROPOSAL_ENC_ALGS_CTOS] = 8969587Sgreen myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; 9069587Sgreen } 9169587Sgreen if (options.compression) { 9269587Sgreen myproposal[PROPOSAL_COMP_ALGS_CTOS] = "zlib"; 9369587Sgreen myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib"; 9469587Sgreen } else { 9569587Sgreen myproposal[PROPOSAL_COMP_ALGS_CTOS] = "none"; 9669587Sgreen myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; 9769587Sgreen } 9869587Sgreen 9969587Sgreen /* buffers with raw kexinit messages */ 10069587Sgreen server_kexinit = xmalloc(sizeof(*server_kexinit)); 10169587Sgreen buffer_init(server_kexinit); 10269587Sgreen client_kexinit = kex_init(myproposal); 10369587Sgreen 10469587Sgreen /* algorithm negotiation */ 10569587Sgreen kex_exchange_kexinit(client_kexinit, server_kexinit, sprop); 10669587Sgreen kex = kex_choose_conf(myproposal, sprop, 0); 10769587Sgreen for (i = 0; i < PROPOSAL_MAX; i++) 10869587Sgreen xfree(sprop[i]); 10969587Sgreen 11069587Sgreen /* server authentication and session key agreement */ 11169587Sgreen switch(kex->kex_type) { 11269587Sgreen case DH_GRP1_SHA1: 11369587Sgreen ssh_dh1_client(kex, host, hostaddr, 11469587Sgreen client_kexinit, server_kexinit); 11569587Sgreen break; 11669587Sgreen case DH_GEX_SHA1: 11769587Sgreen ssh_dhgex_client(kex, host, hostaddr, client_kexinit, 11869587Sgreen server_kexinit); 11969587Sgreen break; 12069587Sgreen default: 12169587Sgreen fatal("Unsupported key exchange %d", kex->kex_type); 12269587Sgreen } 12369587Sgreen 12469587Sgreen buffer_free(client_kexinit); 12569587Sgreen buffer_free(server_kexinit); 12669587Sgreen xfree(client_kexinit); 12769587Sgreen xfree(server_kexinit); 12869587Sgreen 12969587Sgreen debug("Wait SSH2_MSG_NEWKEYS."); 13069587Sgreen packet_read_expect(&plen, SSH2_MSG_NEWKEYS); 13169587Sgreen packet_done(); 13269587Sgreen debug("GOT SSH2_MSG_NEWKEYS."); 13369587Sgreen 13469587Sgreen debug("send SSH2_MSG_NEWKEYS."); 13569587Sgreen packet_start(SSH2_MSG_NEWKEYS); 13669587Sgreen packet_send(); 13769587Sgreen packet_write_wait(); 13869587Sgreen debug("done: send SSH2_MSG_NEWKEYS."); 13969587Sgreen 14069587Sgreen#ifdef DEBUG_KEXDH 14169587Sgreen /* send 1st encrypted/maced/compressed message */ 14269587Sgreen packet_start(SSH2_MSG_IGNORE); 14369587Sgreen packet_put_cstring("markus"); 14469587Sgreen packet_send(); 14569587Sgreen packet_write_wait(); 14669587Sgreen#endif 14769587Sgreen debug("done: KEX2."); 14869587Sgreen} 14969587Sgreen 15069587Sgreen/* diffie-hellman-group1-sha1 */ 15169587Sgreen 15269587Sgreenvoid 15369587Sgreenssh_dh1_client(Kex *kex, char *host, struct sockaddr *hostaddr, 15469587Sgreen Buffer *client_kexinit, Buffer *server_kexinit) 15569587Sgreen{ 15669587Sgreen#ifdef DEBUG_KEXDH 15769587Sgreen int i; 15869587Sgreen#endif 15961209Skris int plen, dlen; 16060573Skris unsigned int klen, kout; 16160573Skris char *signature = NULL; 16260573Skris unsigned int slen; 16360573Skris char *server_host_key_blob = NULL; 16460573Skris Key *server_host_key; 16560573Skris unsigned int sbloblen; 16660573Skris DH *dh; 16760573Skris BIGNUM *dh_server_pub = 0; 16860573Skris BIGNUM *shared_secret = 0; 16960573Skris unsigned char *kbuf; 17060573Skris unsigned char *hash; 17160573Skris 17260573Skris debug("Sending SSH2_MSG_KEXDH_INIT."); 17360573Skris /* generate and send 'e', client DH public key */ 17460573Skris dh = dh_new_group1(); 17560573Skris packet_start(SSH2_MSG_KEXDH_INIT); 17660573Skris packet_put_bignum2(dh->pub_key); 17760573Skris packet_send(); 17860573Skris packet_write_wait(); 17960573Skris 18060573Skris#ifdef DEBUG_KEXDH 18160573Skris fprintf(stderr, "\np= "); 18269587Sgreen BN_print_fp(stderr, dh->p); 18360573Skris fprintf(stderr, "\ng= "); 18469587Sgreen BN_print_fp(stderr, dh->g); 18560573Skris fprintf(stderr, "\npub= "); 18669587Sgreen BN_print_fp(stderr, dh->pub_key); 18760573Skris fprintf(stderr, "\n"); 18860573Skris DHparams_print_fp(stderr, dh); 18960573Skris#endif 19060573Skris 19160573Skris debug("Wait SSH2_MSG_KEXDH_REPLY."); 19260573Skris 19361209Skris packet_read_expect(&plen, SSH2_MSG_KEXDH_REPLY); 19460573Skris 19560573Skris debug("Got SSH2_MSG_KEXDH_REPLY."); 19660573Skris 19760573Skris /* key, cert */ 19860573Skris server_host_key_blob = packet_get_string(&sbloblen); 19960573Skris server_host_key = dsa_key_from_blob(server_host_key_blob, sbloblen); 20060573Skris if (server_host_key == NULL) 20160573Skris fatal("cannot decode server_host_key_blob"); 20260573Skris 20360573Skris check_host_key(host, hostaddr, server_host_key, 20469587Sgreen options.user_hostfile2, options.system_hostfile2); 20560573Skris 20660573Skris /* DH paramter f, server public DH key */ 20760573Skris dh_server_pub = BN_new(); 20860573Skris if (dh_server_pub == NULL) 20960573Skris fatal("dh_server_pub == NULL"); 21060573Skris packet_get_bignum2(dh_server_pub, &dlen); 21160573Skris 21260573Skris#ifdef DEBUG_KEXDH 21360573Skris fprintf(stderr, "\ndh_server_pub= "); 21469587Sgreen BN_print_fp(stderr, dh_server_pub); 21560573Skris fprintf(stderr, "\n"); 21660573Skris debug("bits %d", BN_num_bits(dh_server_pub)); 21760573Skris#endif 21860573Skris 21960573Skris /* signed H */ 22060573Skris signature = packet_get_string(&slen); 22160573Skris packet_done(); 22260573Skris 22360573Skris if (!dh_pub_is_valid(dh, dh_server_pub)) 22460573Skris packet_disconnect("bad server public DH value"); 22560573Skris 22660573Skris klen = DH_size(dh); 22760573Skris kbuf = xmalloc(klen); 22860573Skris kout = DH_compute_key(kbuf, dh_server_pub, dh); 22960573Skris#ifdef DEBUG_KEXDH 23060573Skris debug("shared secret: len %d/%d", klen, kout); 23160573Skris fprintf(stderr, "shared secret == "); 23260573Skris for (i = 0; i< kout; i++) 23360573Skris fprintf(stderr, "%02x", (kbuf[i])&0xff); 23460573Skris fprintf(stderr, "\n"); 23560573Skris#endif 23660573Skris shared_secret = BN_new(); 23760573Skris 23860573Skris BN_bin2bn(kbuf, kout, shared_secret); 23960573Skris memset(kbuf, 0, klen); 24060573Skris xfree(kbuf); 24160573Skris 24260573Skris /* calc and verify H */ 24360573Skris hash = kex_hash( 24460573Skris client_version_string, 24560573Skris server_version_string, 24660573Skris buffer_ptr(client_kexinit), buffer_len(client_kexinit), 24760573Skris buffer_ptr(server_kexinit), buffer_len(server_kexinit), 24860573Skris server_host_key_blob, sbloblen, 24960573Skris dh->pub_key, 25060573Skris dh_server_pub, 25160573Skris shared_secret 25260573Skris ); 25360573Skris xfree(server_host_key_blob); 25461209Skris DH_free(dh); 25560573Skris#ifdef DEBUG_KEXDH 25660573Skris fprintf(stderr, "hash == "); 25760573Skris for (i = 0; i< 20; i++) 25860573Skris fprintf(stderr, "%02x", (hash[i])&0xff); 25960573Skris fprintf(stderr, "\n"); 26060573Skris#endif 26160573Skris if (dsa_verify(server_host_key, (unsigned char *)signature, slen, hash, 20) != 1) 26260573Skris fatal("dsa_verify failed for server_host_key"); 26360573Skris key_free(server_host_key); 26460573Skris 26560573Skris kex_derive_keys(kex, hash, shared_secret); 26660573Skris packet_set_kex(kex); 26760573Skris 26860573Skris /* save session id */ 26960573Skris session_id2_len = 20; 27060573Skris session_id2 = xmalloc(session_id2_len); 27160573Skris memcpy(session_id2, hash, session_id2_len); 27261209Skris} 27360573Skris 27469587Sgreen/* diffie-hellman-group-exchange-sha1 */ 27569587Sgreen 27669587Sgreen/* 27769587Sgreen * Estimates the group order for a Diffie-Hellman group that has an 27869587Sgreen * attack complexity approximately the same as O(2**bits). Estimate 27969587Sgreen * with: O(exp(1.9223 * (ln q)^(1/3) (ln ln q)^(2/3))) 28069587Sgreen */ 28169587Sgreen 28269587Sgreenint 28369587Sgreendh_estimate(int bits) 28469587Sgreen{ 28569587Sgreen 28669587Sgreen if (bits < 64) 28769587Sgreen return (512); /* O(2**63) */ 28869587Sgreen if (bits < 128) 28969587Sgreen return (1024); /* O(2**86) */ 29069587Sgreen if (bits < 192) 29169587Sgreen return (2048); /* O(2**116) */ 29269587Sgreen return (4096); /* O(2**156) */ 29369587Sgreen} 29469587Sgreen 29561209Skrisvoid 29669587Sgreenssh_dhgex_client(Kex *kex, char *host, struct sockaddr *hostaddr, 29769587Sgreen Buffer *client_kexinit, Buffer *server_kexinit) 29861209Skris{ 29969587Sgreen#ifdef DEBUG_KEXDH 30069587Sgreen int i; 30169587Sgreen#endif 30269587Sgreen int plen, dlen; 30369587Sgreen unsigned int klen, kout; 30469587Sgreen char *signature = NULL; 30569587Sgreen unsigned int slen, nbits; 30669587Sgreen char *server_host_key_blob = NULL; 30769587Sgreen Key *server_host_key; 30869587Sgreen unsigned int sbloblen; 30969587Sgreen DH *dh; 31069587Sgreen BIGNUM *dh_server_pub = 0; 31169587Sgreen BIGNUM *shared_secret = 0; 31269587Sgreen BIGNUM *p = 0, *g = 0; 31369587Sgreen unsigned char *kbuf; 31469587Sgreen unsigned char *hash; 31561209Skris 31669587Sgreen nbits = dh_estimate(kex->enc[MODE_OUT].cipher->key_len * 8); 31761209Skris 31869587Sgreen debug("Sending SSH2_MSG_KEX_DH_GEX_REQUEST."); 31969587Sgreen packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST); 32069587Sgreen packet_put_int(nbits); 32169587Sgreen packet_send(); 32269587Sgreen packet_write_wait(); 32361209Skris 32469587Sgreen#ifdef DEBUG_KEXDH 32569587Sgreen fprintf(stderr, "\nnbits = %d", nbits); 32669587Sgreen#endif 32761209Skris 32869587Sgreen debug("Wait SSH2_MSG_KEX_DH_GEX_GROUP."); 32961209Skris 33069587Sgreen packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_GROUP); 33161209Skris 33269587Sgreen debug("Got SSH2_MSG_KEX_DH_GEX_GROUP."); 33360573Skris 33469587Sgreen if ((p = BN_new()) == NULL) 33569587Sgreen fatal("BN_new"); 33669587Sgreen packet_get_bignum2(p, &dlen); 33769587Sgreen if ((g = BN_new()) == NULL) 33869587Sgreen fatal("BN_new"); 33969587Sgreen packet_get_bignum2(g, &dlen); 34069587Sgreen if ((dh = dh_new_group(g, p)) == NULL) 34169587Sgreen fatal("dh_new_group"); 34269587Sgreen 34369587Sgreen#ifdef DEBUG_KEXDH 34469587Sgreen fprintf(stderr, "\np= "); 34569587Sgreen BN_print_fp(stderr, dh->p); 34669587Sgreen fprintf(stderr, "\ng= "); 34769587Sgreen BN_print_fp(stderr, dh->g); 34869587Sgreen fprintf(stderr, "\npub= "); 34969587Sgreen BN_print_fp(stderr, dh->pub_key); 35069587Sgreen fprintf(stderr, "\n"); 35169587Sgreen DHparams_print_fp(stderr, dh); 35269587Sgreen#endif 35369587Sgreen 35469587Sgreen debug("Sending SSH2_MSG_KEX_DH_GEX_INIT."); 35569587Sgreen /* generate and send 'e', client DH public key */ 35669587Sgreen packet_start(SSH2_MSG_KEX_DH_GEX_INIT); 35769587Sgreen packet_put_bignum2(dh->pub_key); 35860573Skris packet_send(); 35960573Skris packet_write_wait(); 36060573Skris 36169587Sgreen debug("Wait SSH2_MSG_KEX_DH_GEX_REPLY."); 36269587Sgreen 36369587Sgreen packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_REPLY); 36469587Sgreen 36569587Sgreen debug("Got SSH2_MSG_KEXDH_REPLY."); 36669587Sgreen 36769587Sgreen /* key, cert */ 36869587Sgreen server_host_key_blob = packet_get_string(&sbloblen); 36969587Sgreen server_host_key = dsa_key_from_blob(server_host_key_blob, sbloblen); 37069587Sgreen if (server_host_key == NULL) 37169587Sgreen fatal("cannot decode server_host_key_blob"); 37269587Sgreen 37369587Sgreen check_host_key(host, hostaddr, server_host_key, 37469587Sgreen options.user_hostfile2, options.system_hostfile2); 37569587Sgreen 37669587Sgreen /* DH paramter f, server public DH key */ 37769587Sgreen dh_server_pub = BN_new(); 37869587Sgreen if (dh_server_pub == NULL) 37969587Sgreen fatal("dh_server_pub == NULL"); 38069587Sgreen packet_get_bignum2(dh_server_pub, &dlen); 38169587Sgreen 38260573Skris#ifdef DEBUG_KEXDH 38369587Sgreen fprintf(stderr, "\ndh_server_pub= "); 38469587Sgreen BN_print_fp(stderr, dh_server_pub); 38569587Sgreen fprintf(stderr, "\n"); 38669587Sgreen debug("bits %d", BN_num_bits(dh_server_pub)); 38760573Skris#endif 38869587Sgreen 38969587Sgreen /* signed H */ 39069587Sgreen signature = packet_get_string(&slen); 39169587Sgreen packet_done(); 39269587Sgreen 39369587Sgreen if (!dh_pub_is_valid(dh, dh_server_pub)) 39469587Sgreen packet_disconnect("bad server public DH value"); 39569587Sgreen 39669587Sgreen klen = DH_size(dh); 39769587Sgreen kbuf = xmalloc(klen); 39869587Sgreen kout = DH_compute_key(kbuf, dh_server_pub, dh); 39969587Sgreen#ifdef DEBUG_KEXDH 40069587Sgreen debug("shared secret: len %d/%d", klen, kout); 40169587Sgreen fprintf(stderr, "shared secret == "); 40269587Sgreen for (i = 0; i< kout; i++) 40369587Sgreen fprintf(stderr, "%02x", (kbuf[i])&0xff); 40469587Sgreen fprintf(stderr, "\n"); 40569587Sgreen#endif 40669587Sgreen shared_secret = BN_new(); 40769587Sgreen 40869587Sgreen BN_bin2bn(kbuf, kout, shared_secret); 40969587Sgreen memset(kbuf, 0, klen); 41069587Sgreen xfree(kbuf); 41169587Sgreen 41269587Sgreen /* calc and verify H */ 41369587Sgreen hash = kex_hash_gex( 41469587Sgreen client_version_string, 41569587Sgreen server_version_string, 41669587Sgreen buffer_ptr(client_kexinit), buffer_len(client_kexinit), 41769587Sgreen buffer_ptr(server_kexinit), buffer_len(server_kexinit), 41869587Sgreen server_host_key_blob, sbloblen, 41969587Sgreen nbits, dh->p, dh->g, 42069587Sgreen dh->pub_key, 42169587Sgreen dh_server_pub, 42269587Sgreen shared_secret 42369587Sgreen ); 42469587Sgreen xfree(server_host_key_blob); 42569587Sgreen DH_free(dh); 42669587Sgreen#ifdef DEBUG_KEXDH 42769587Sgreen fprintf(stderr, "hash == "); 42869587Sgreen for (i = 0; i< 20; i++) 42969587Sgreen fprintf(stderr, "%02x", (hash[i])&0xff); 43069587Sgreen fprintf(stderr, "\n"); 43169587Sgreen#endif 43269587Sgreen if (dsa_verify(server_host_key, (unsigned char *)signature, slen, hash, 20) != 1) 43369587Sgreen fatal("dsa_verify failed for server_host_key"); 43469587Sgreen key_free(server_host_key); 43569587Sgreen 43669587Sgreen kex_derive_keys(kex, hash, shared_secret); 43769587Sgreen packet_set_kex(kex); 43869587Sgreen 43969587Sgreen /* save session id */ 44069587Sgreen session_id2_len = 20; 44169587Sgreen session_id2 = xmalloc(session_id2_len); 44269587Sgreen memcpy(session_id2, hash, session_id2_len); 44360573Skris} 44461209Skris 44560573Skris/* 44660573Skris * Authenticate user 44760573Skris */ 44869587Sgreen 44969587Sgreentypedef struct Authctxt Authctxt; 45069587Sgreentypedef struct Authmethod Authmethod; 45169587Sgreen 45269587Sgreentypedef int sign_cb_fn( 45369587Sgreen Authctxt *authctxt, Key *key, 45469587Sgreen unsigned char **sigp, int *lenp, unsigned char *data, int datalen); 45569587Sgreen 45669587Sgreenstruct Authctxt { 45769587Sgreen const char *server_user; 45869587Sgreen const char *host; 45969587Sgreen const char *service; 46069587Sgreen AuthenticationConnection *agent; 46169587Sgreen Authmethod *method; 46269587Sgreen int success; 46369587Sgreen}; 46469587Sgreenstruct Authmethod { 46569587Sgreen char *name; /* string to compare against server's list */ 46669587Sgreen int (*userauth)(Authctxt *authctxt); 46769587Sgreen int *enabled; /* flag in option struct that enables method */ 46869587Sgreen int *batch_flag; /* flag in option struct that disables method */ 46969587Sgreen}; 47069587Sgreen 47169587Sgreenvoid input_userauth_success(int type, int plen, void *ctxt); 47269587Sgreenvoid input_userauth_failure(int type, int plen, void *ctxt); 47369587Sgreenvoid input_userauth_error(int type, int plen, void *ctxt); 47469587Sgreenvoid input_userauth_info_req(int type, int plen, void *ctxt); 47569587Sgreen 47669587Sgreenint userauth_none(Authctxt *authctxt); 47769587Sgreenint userauth_pubkey(Authctxt *authctxt); 47869587Sgreenint userauth_passwd(Authctxt *authctxt); 47969587Sgreenint userauth_kbdint(Authctxt *authctxt); 48069587Sgreen 48169587Sgreenvoid authmethod_clear(); 48269587SgreenAuthmethod *authmethod_get(char *authlist); 48369587SgreenAuthmethod *authmethod_lookup(const char *name); 48469587Sgreen 48569587SgreenAuthmethod authmethods[] = { 48669587Sgreen {"publickey", 48769587Sgreen userauth_pubkey, 48869587Sgreen &options.dsa_authentication, 48969587Sgreen NULL}, 49069587Sgreen {"password", 49169587Sgreen userauth_passwd, 49269587Sgreen &options.password_authentication, 49369587Sgreen &options.batch_mode}, 49469587Sgreen {"keyboard-interactive", 49569587Sgreen userauth_kbdint, 49669587Sgreen &options.kbd_interactive_authentication, 49769587Sgreen &options.batch_mode}, 49869587Sgreen {"none", 49969587Sgreen userauth_none, 50069587Sgreen NULL, 50169587Sgreen NULL}, 50269587Sgreen {NULL, NULL, NULL, NULL} 50369587Sgreen}; 50469587Sgreen 50569587Sgreenvoid 50669587Sgreenssh_userauth2(const char *server_user, char *host) 50769587Sgreen{ 50869587Sgreen Authctxt authctxt; 50969587Sgreen int type; 51069587Sgreen int plen; 51169587Sgreen 51269587Sgreen debug("send SSH2_MSG_SERVICE_REQUEST"); 51369587Sgreen packet_start(SSH2_MSG_SERVICE_REQUEST); 51469587Sgreen packet_put_cstring("ssh-userauth"); 51569587Sgreen packet_send(); 51669587Sgreen packet_write_wait(); 51769587Sgreen type = packet_read(&plen); 51869587Sgreen if (type != SSH2_MSG_SERVICE_ACCEPT) { 51969587Sgreen fatal("denied SSH2_MSG_SERVICE_ACCEPT: %d", type); 52069587Sgreen } 52169587Sgreen if (packet_remaining() > 0) { 52269587Sgreen char *reply = packet_get_string(&plen); 52369587Sgreen debug("service_accept: %s", reply); 52469587Sgreen xfree(reply); 52569587Sgreen packet_done(); 52669587Sgreen } else { 52769587Sgreen debug("buggy server: service_accept w/o service"); 52869587Sgreen } 52969587Sgreen packet_done(); 53069587Sgreen debug("got SSH2_MSG_SERVICE_ACCEPT"); 53169587Sgreen 53269587Sgreen /* setup authentication context */ 53369587Sgreen authctxt.agent = ssh_get_authentication_connection(); 53469587Sgreen authctxt.server_user = server_user; 53569587Sgreen authctxt.host = host; 53669587Sgreen authctxt.service = "ssh-connection"; /* service name */ 53769587Sgreen authctxt.success = 0; 53869587Sgreen authctxt.method = authmethod_lookup("none"); 53969587Sgreen if (authctxt.method == NULL) 54069587Sgreen fatal("ssh_userauth2: internal error: cannot send userauth none request"); 54169587Sgreen authmethod_clear(); 54269587Sgreen 54369587Sgreen /* initial userauth request */ 54469587Sgreen userauth_none(&authctxt); 54569587Sgreen 54669587Sgreen dispatch_init(&input_userauth_error); 54769587Sgreen dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success); 54869587Sgreen dispatch_set(SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure); 54969587Sgreen dispatch_run(DISPATCH_BLOCK, &authctxt.success, &authctxt); /* loop until success */ 55069587Sgreen 55169587Sgreen if (authctxt.agent != NULL) 55269587Sgreen ssh_close_authentication_connection(authctxt.agent); 55369587Sgreen 55469587Sgreen debug("ssh-userauth2 successfull: method %s", authctxt.method->name); 55569587Sgreen} 55669587Sgreenvoid 55769587Sgreeninput_userauth_error(int type, int plen, void *ctxt) 55869587Sgreen{ 55969587Sgreen fatal("input_userauth_error: bad message during authentication"); 56069587Sgreen} 56169587Sgreenvoid 56269587Sgreeninput_userauth_success(int type, int plen, void *ctxt) 56369587Sgreen{ 56469587Sgreen Authctxt *authctxt = ctxt; 56569587Sgreen if (authctxt == NULL) 56669587Sgreen fatal("input_userauth_success: no authentication context"); 56769587Sgreen authctxt->success = 1; /* break out */ 56869587Sgreen} 56969587Sgreenvoid 57069587Sgreeninput_userauth_failure(int type, int plen, void *ctxt) 57169587Sgreen{ 57269587Sgreen Authmethod *method = NULL; 57369587Sgreen Authctxt *authctxt = ctxt; 57469587Sgreen char *authlist = NULL; 57569587Sgreen int partial; 57669587Sgreen 57769587Sgreen if (authctxt == NULL) 57869587Sgreen fatal("input_userauth_failure: no authentication context"); 57969587Sgreen 58069587Sgreen authlist = packet_get_string(NULL); 58169587Sgreen partial = packet_get_char(); 58269587Sgreen packet_done(); 58369587Sgreen 58469587Sgreen if (partial != 0) 58569587Sgreen debug("partial success"); 58669587Sgreen debug("authentications that can continue: %s", authlist); 58769587Sgreen 58869587Sgreen for (;;) { 58969587Sgreen method = authmethod_get(authlist); 59069587Sgreen if (method == NULL) 59169587Sgreen fatal("Unable to find an authentication method"); 59269587Sgreen authctxt->method = method; 59369587Sgreen if (method->userauth(authctxt) != 0) { 59469587Sgreen debug2("we sent a %s packet, wait for reply", method->name); 59569587Sgreen break; 59669587Sgreen } else { 59769587Sgreen debug2("we did not send a packet, disable method"); 59869587Sgreen method->enabled = NULL; 59969587Sgreen } 60069587Sgreen } 60169587Sgreen xfree(authlist); 60269587Sgreen} 60369587Sgreen 60460573Skrisint 60569587Sgreenuserauth_none(Authctxt *authctxt) 60660573Skris{ 60769587Sgreen /* initial userauth request */ 60869587Sgreen packet_start(SSH2_MSG_USERAUTH_REQUEST); 60969587Sgreen packet_put_cstring(authctxt->server_user); 61069587Sgreen packet_put_cstring(authctxt->service); 61169587Sgreen packet_put_cstring(authctxt->method->name); 61269587Sgreen packet_send(); 61369587Sgreen packet_write_wait(); 61469587Sgreen return 1; 61569587Sgreen} 61669587Sgreen 61769587Sgreenint 61869587Sgreenuserauth_passwd(Authctxt *authctxt) 61969587Sgreen{ 62060573Skris static int attempt = 0; 62160573Skris char prompt[80]; 62260573Skris char *password; 62360573Skris 62465668Skris if (attempt++ >= options.number_of_password_prompts) 62560573Skris return 0; 62660573Skris 62765668Skris if(attempt != 1) 62865668Skris error("Permission denied, please try again."); 62965668Skris 63060573Skris snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ", 63169587Sgreen authctxt->server_user, authctxt->host); 63260573Skris password = read_passphrase(prompt, 0); 63360573Skris packet_start(SSH2_MSG_USERAUTH_REQUEST); 63469587Sgreen packet_put_cstring(authctxt->server_user); 63569587Sgreen packet_put_cstring(authctxt->service); 63669587Sgreen packet_put_cstring(authctxt->method->name); 63760573Skris packet_put_char(0); 63860573Skris packet_put_cstring(password); 63960573Skris memset(password, 0, strlen(password)); 64060573Skris xfree(password); 64160573Skris packet_send(); 64260573Skris packet_write_wait(); 64360573Skris return 1; 64460573Skris} 64560573Skris 64660573Skrisint 64769587Sgreensign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) 64860573Skris{ 64960573Skris Buffer b; 65060573Skris unsigned char *blob, *signature; 65160573Skris int bloblen, slen; 65265668Skris int skip = 0; 65365668Skris int ret = -1; 65469587Sgreen int have_sig = 1; 65560573Skris 65660573Skris dsa_make_key_blob(k, &blob, &bloblen); 65760573Skris 65860573Skris /* data to be signed */ 65960573Skris buffer_init(&b); 66069587Sgreen if (datafellows & SSH_OLD_SESSIONID) { 66169587Sgreen buffer_append(&b, session_id2, session_id2_len); 66269587Sgreen skip = session_id2_len; 66369587Sgreen } else { 66465668Skris buffer_put_string(&b, session_id2, session_id2_len); 66565668Skris skip = buffer_len(&b); 66665668Skris } 66760573Skris buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); 66869587Sgreen buffer_put_cstring(&b, authctxt->server_user); 66960573Skris buffer_put_cstring(&b, 67060573Skris datafellows & SSH_BUG_PUBKEYAUTH ? 67160573Skris "ssh-userauth" : 67269587Sgreen authctxt->service); 67369587Sgreen buffer_put_cstring(&b, authctxt->method->name); 67469587Sgreen buffer_put_char(&b, have_sig); 67560573Skris buffer_put_cstring(&b, KEX_DSS); 67660573Skris buffer_put_string(&b, blob, bloblen); 67760573Skris 67860573Skris /* generate signature */ 67969587Sgreen ret = (*sign_callback)(authctxt, k, &signature, &slen, buffer_ptr(&b), buffer_len(&b)); 68065668Skris if (ret == -1) { 68165668Skris xfree(blob); 68265668Skris buffer_free(&b); 68365668Skris return 0; 68465668Skris } 68560573Skris#ifdef DEBUG_DSS 68660573Skris buffer_dump(&b); 68760573Skris#endif 68860573Skris if (datafellows & SSH_BUG_PUBKEYAUTH) { 68960573Skris buffer_clear(&b); 69060573Skris buffer_append(&b, session_id2, session_id2_len); 69160573Skris buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); 69269587Sgreen buffer_put_cstring(&b, authctxt->server_user); 69369587Sgreen buffer_put_cstring(&b, authctxt->service); 69469587Sgreen buffer_put_cstring(&b, authctxt->method->name); 69569587Sgreen buffer_put_char(&b, have_sig); 69660573Skris buffer_put_cstring(&b, KEX_DSS); 69760573Skris buffer_put_string(&b, blob, bloblen); 69860573Skris } 69960573Skris xfree(blob); 70060573Skris /* append signature */ 70160573Skris buffer_put_string(&b, signature, slen); 70260573Skris xfree(signature); 70360573Skris 70460573Skris /* skip session id and packet type */ 70565668Skris if (buffer_len(&b) < skip + 1) 70669587Sgreen fatal("userauth_pubkey: internal error"); 70765668Skris buffer_consume(&b, skip + 1); 70860573Skris 70960573Skris /* put remaining data from buffer into packet */ 71060573Skris packet_start(SSH2_MSG_USERAUTH_REQUEST); 71160573Skris packet_put_raw(buffer_ptr(&b), buffer_len(&b)); 71260573Skris buffer_free(&b); 71360573Skris 71460573Skris /* send */ 71560573Skris packet_send(); 71660573Skris packet_write_wait(); 71765668Skris 71860573Skris return 1; 71960573Skris} 72060573Skris 72169587Sgreen/* sign callback */ 72269587Sgreenint dsa_sign_cb(Authctxt *authctxt, Key *key, unsigned char **sigp, int *lenp, 72369587Sgreen unsigned char *data, int datalen) 72469587Sgreen{ 72569587Sgreen return dsa_sign(key, sigp, lenp, data, datalen); 72669587Sgreen} 72769587Sgreen 72865668Skrisint 72969587Sgreenuserauth_pubkey_identity(Authctxt *authctxt, char *filename) 73065668Skris{ 73165668Skris Key *k; 73269587Sgreen int i, ret, try_next; 73365668Skris struct stat st; 73465668Skris 73565668Skris if (stat(filename, &st) != 0) { 73665668Skris debug("key does not exist: %s", filename); 73765668Skris return 0; 73865668Skris } 73965668Skris debug("try pubkey: %s", filename); 74065668Skris 74165668Skris k = key_new(KEY_DSA); 74265668Skris if (!load_private_key(filename, "", k, NULL)) { 74365668Skris int success = 0; 74465668Skris char *passphrase; 74565668Skris char prompt[300]; 74665668Skris snprintf(prompt, sizeof prompt, 74769587Sgreen "Enter passphrase for %s key '%.100s': ", 74869587Sgreen key_type(k), filename); 74969587Sgreen for (i = 0; i < options.number_of_password_prompts; i++) { 75069587Sgreen passphrase = read_passphrase(prompt, 0); 75169587Sgreen if (strcmp(passphrase, "") != 0) { 75269587Sgreen success = load_private_key(filename, passphrase, k, NULL); 75369587Sgreen try_next = 0; 75469587Sgreen } else { 75569587Sgreen debug2("no passphrase given, try next key"); 75669587Sgreen try_next = 1; 75769587Sgreen } 75869587Sgreen memset(passphrase, 0, strlen(passphrase)); 75969587Sgreen xfree(passphrase); 76069587Sgreen if (success || try_next) 76169587Sgreen break; 76269587Sgreen debug2("bad passphrase given, try again..."); 76369587Sgreen } 76465668Skris if (!success) { 76565668Skris key_free(k); 76665668Skris return 0; 76765668Skris } 76865668Skris } 76969587Sgreen ret = sign_and_send_pubkey(authctxt, k, dsa_sign_cb); 77065668Skris key_free(k); 77165668Skris return ret; 77265668Skris} 77365668Skris 77469587Sgreen/* sign callback */ 77569587Sgreenint agent_sign_cb(Authctxt *authctxt, Key *key, unsigned char **sigp, int *lenp, 77665668Skris unsigned char *data, int datalen) 77765668Skris{ 77869587Sgreen return ssh_agent_sign(authctxt->agent, key, sigp, lenp, data, datalen); 77965668Skris} 78065668Skris 78165668Skrisint 78269587Sgreenuserauth_pubkey_agent(Authctxt *authctxt) 78365668Skris{ 78465668Skris static int called = 0; 78565668Skris char *comment; 78665668Skris Key *k; 78765668Skris int ret; 78865668Skris 78965668Skris if (called == 0) { 79069587Sgreen k = ssh_get_first_identity(authctxt->agent, &comment, 2); 79169587Sgreen called = 1; 79265668Skris } else { 79369587Sgreen k = ssh_get_next_identity(authctxt->agent, &comment, 2); 79465668Skris } 79569587Sgreen if (k == NULL) { 79669587Sgreen debug2("no more DSA keys from agent"); 79765668Skris return 0; 79869587Sgreen } 79965668Skris debug("trying DSA agent key %s", comment); 80065668Skris xfree(comment); 80169587Sgreen ret = sign_and_send_pubkey(authctxt, k, agent_sign_cb); 80265668Skris key_free(k); 80365668Skris return ret; 80465668Skris} 80565668Skris 80669587Sgreenint 80769587Sgreenuserauth_pubkey(Authctxt *authctxt) 80869587Sgreen{ 80969587Sgreen static int idx = 0; 81069587Sgreen int sent = 0; 81169587Sgreen 81269587Sgreen if (authctxt->agent != NULL) 81369587Sgreen sent = userauth_pubkey_agent(authctxt); 81469587Sgreen while (sent == 0 && idx < options.num_identity_files2) 81569587Sgreen sent = userauth_pubkey_identity(authctxt, options.identity_files2[idx++]); 81669587Sgreen return sent; 81769587Sgreen} 81869587Sgreen 81969587Sgreen/* 82069587Sgreen * Send userauth request message specifying keyboard-interactive method. 82169587Sgreen */ 82269587Sgreenint 82369587Sgreenuserauth_kbdint(Authctxt *authctxt) 82469587Sgreen{ 82569587Sgreen static int attempt = 0; 82669587Sgreen 82769587Sgreen if (attempt++ >= options.number_of_password_prompts) 82869587Sgreen return 0; 82969587Sgreen 83069587Sgreen debug2("userauth_kbdint"); 83169587Sgreen packet_start(SSH2_MSG_USERAUTH_REQUEST); 83269587Sgreen packet_put_cstring(authctxt->server_user); 83369587Sgreen packet_put_cstring(authctxt->service); 83469587Sgreen packet_put_cstring(authctxt->method->name); 83569587Sgreen packet_put_cstring(""); /* lang */ 83669587Sgreen packet_put_cstring(options.kbd_interactive_devices ? 83769587Sgreen options.kbd_interactive_devices : ""); 83869587Sgreen packet_send(); 83969587Sgreen packet_write_wait(); 84069587Sgreen 84169587Sgreen dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req); 84269587Sgreen return 1; 84369587Sgreen} 84469587Sgreen 84569587Sgreen/* 84669587Sgreen * parse SSH2_MSG_USERAUTH_INFO_REQUEST, prompt user and send 84769587Sgreen * SSH2_MSG_USERAUTH_INFO_RESPONSE 84869587Sgreen */ 84960573Skrisvoid 85069587Sgreeninput_userauth_info_req(int type, int plen, void *ctxt) 85160573Skris{ 85269587Sgreen Authctxt *authctxt = ctxt; 85369587Sgreen char *name = NULL; 85469587Sgreen char *inst = NULL; 85569587Sgreen char *lang = NULL; 85669587Sgreen char *prompt = NULL; 85769587Sgreen char *response = NULL; 85869587Sgreen unsigned int num_prompts, i; 85969587Sgreen int echo = 0; 86060573Skris 86169587Sgreen debug2("input_userauth_info_req"); 86269587Sgreen 86369587Sgreen if (authctxt == NULL) 86469587Sgreen fatal("input_userauth_info_req: no authentication context"); 86569587Sgreen 86669587Sgreen name = packet_get_string(NULL); 86769587Sgreen inst = packet_get_string(NULL); 86869587Sgreen lang = packet_get_string(NULL); 86969587Sgreen 87069587Sgreen if (strlen(name) > 0) 87169587Sgreen cli_mesg(name); 87269587Sgreen xfree(name); 87369587Sgreen 87469587Sgreen if (strlen(inst) > 0) 87569587Sgreen cli_mesg(inst); 87669587Sgreen xfree(inst); 87769587Sgreen xfree(lang); /* unused */ 87869587Sgreen 87969587Sgreen num_prompts = packet_get_int(); 88069587Sgreen /* 88169587Sgreen * Begin to build info response packet based on prompts requested. 88269587Sgreen * We commit to providing the correct number of responses, so if 88369587Sgreen * further on we run into a problem that prevents this, we have to 88469587Sgreen * be sure and clean this up and send a correct error response. 88569587Sgreen */ 88669587Sgreen packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE); 88769587Sgreen packet_put_int(num_prompts); 88869587Sgreen 88969587Sgreen for (i = 0; i < num_prompts; i++) { 89069587Sgreen prompt = packet_get_string(NULL); 89169587Sgreen echo = packet_get_char(); 89269587Sgreen 89369587Sgreen response = cli_prompt(prompt, echo); 89469587Sgreen 89569587Sgreen packet_put_cstring(response); 89669587Sgreen memset(response, 0, strlen(response)); 89769587Sgreen xfree(response); 89869587Sgreen xfree(prompt); 89969587Sgreen } 90069587Sgreen packet_done(); /* done with parsing incoming message. */ 90169587Sgreen 90260573Skris packet_send(); 90360573Skris packet_write_wait(); 90469587Sgreen} 90560573Skris 90669587Sgreen/* find auth method */ 90769587Sgreen 90869587Sgreen#define DELIM "," 90969587Sgreen 91069587Sgreenstatic char *def_authlist = "publickey,password"; 91169587Sgreenstatic char *authlist_current = NULL; /* clean copy used for comparison */ 91269587Sgreenstatic char *authname_current = NULL; /* last used auth method */ 91369587Sgreenstatic char *authlist_working = NULL; /* copy that gets modified by strtok_r() */ 91469587Sgreenstatic char *authlist_state = NULL; /* state variable for strtok_r() */ 91569587Sgreen 91669587Sgreen/* 91769587Sgreen * Before starting to use a new authentication method list sent by the 91869587Sgreen * server, reset internal variables. This should also be called when 91969587Sgreen * finished processing server list to free resources. 92069587Sgreen */ 92169587Sgreenvoid 92269587Sgreenauthmethod_clear() 92369587Sgreen{ 92469587Sgreen if (authlist_current != NULL) { 92569587Sgreen xfree(authlist_current); 92669587Sgreen authlist_current = NULL; 92760573Skris } 92869587Sgreen if (authlist_working != NULL) { 92969587Sgreen xfree(authlist_working); 93069587Sgreen authlist_working = NULL; 93169587Sgreen } 93269587Sgreen if (authname_current != NULL) { 93369587Sgreen xfree(authname_current); 93469587Sgreen authlist_state = NULL; 93569587Sgreen } 93669587Sgreen if (authlist_state != NULL) 93769587Sgreen authlist_state = NULL; 93869587Sgreen return; 93969587Sgreen} 94069587Sgreen 94169587Sgreen/* 94269587Sgreen * given auth method name, if configurable options permit this method fill 94369587Sgreen * in auth_ident field and return true, otherwise return false. 94469587Sgreen */ 94569587Sgreenint 94669587Sgreenauthmethod_is_enabled(Authmethod *method) 94769587Sgreen{ 94869587Sgreen if (method == NULL) 94969587Sgreen return 0; 95069587Sgreen /* return false if options indicate this method is disabled */ 95169587Sgreen if (method->enabled == NULL || *method->enabled == 0) 95269587Sgreen return 0; 95369587Sgreen /* return false if batch mode is enabled but method needs interactive mode */ 95469587Sgreen if (method->batch_flag != NULL && *method->batch_flag != 0) 95569587Sgreen return 0; 95669587Sgreen return 1; 95769587Sgreen} 95869587Sgreen 95969587SgreenAuthmethod * 96069587Sgreenauthmethod_lookup(const char *name) 96169587Sgreen{ 96269587Sgreen Authmethod *method = NULL; 96369587Sgreen if (name != NULL) 96469587Sgreen for (method = authmethods; method->name != NULL; method++) 96569587Sgreen if (strcmp(name, method->name) == 0) 96669587Sgreen return method; 96769587Sgreen debug2("Unrecognized authentication method name: %s", name ? name : "NULL"); 96869587Sgreen return NULL; 96969587Sgreen} 97069587Sgreen 97169587Sgreen/* 97269587Sgreen * Given the authentication method list sent by the server, return the 97369587Sgreen * next method we should try. If the server initially sends a nil list, 97469587Sgreen * use a built-in default list. If the server sends a nil list after 97569587Sgreen * previously sending a valid list, continue using the list originally 97669587Sgreen * sent. 97769587Sgreen */ 97869587Sgreen 97969587SgreenAuthmethod * 98069587Sgreenauthmethod_get(char *authlist) 98169587Sgreen{ 98269587Sgreen char *name = NULL, *authname_old; 98369587Sgreen Authmethod *method = NULL; 98469587Sgreen 98569587Sgreen /* Use a suitable default if we're passed a nil list. */ 98669587Sgreen if (authlist == NULL || strlen(authlist) == 0) 98769587Sgreen authlist = def_authlist; 98869587Sgreen 98969587Sgreen if (authlist_current == NULL || strcmp(authlist, authlist_current) != 0) { 99069587Sgreen /* start over if passed a different list */ 99169587Sgreen debug3("start over, passed a different list"); 99269587Sgreen authmethod_clear(); 99369587Sgreen authlist_current = xstrdup(authlist); 99469587Sgreen authlist_working = xstrdup(authlist); 99569587Sgreen name = strtok_r(authlist_working, DELIM, &authlist_state); 99660573Skris } else { 99769587Sgreen /* 99869587Sgreen * try to use previously used authentication method 99969587Sgreen * or continue to use previously passed list 100069587Sgreen */ 100169587Sgreen name = (authname_current != NULL) ? 100269587Sgreen authname_current : strtok_r(NULL, DELIM, &authlist_state); 100360573Skris } 100460573Skris 100569587Sgreen while (name != NULL) { 100669587Sgreen debug3("authmethod_lookup %s", name); 100769587Sgreen method = authmethod_lookup(name); 100869587Sgreen if (method != NULL && authmethod_is_enabled(method)) { 100969587Sgreen debug3("authmethod_is_enabled %s", name); 101060573Skris break; 101160573Skris } 101269587Sgreen name = strtok_r(NULL, DELIM, &authlist_state); 101369587Sgreen method = NULL; 101460573Skris } 101569587Sgreen 101669587Sgreen authname_old = authname_current; 101769587Sgreen if (method != NULL) { 101869587Sgreen debug("next auth method to try is %s", name); 101969587Sgreen authname_current = xstrdup(name); 102069587Sgreen } else { 102169587Sgreen debug("no more auth methods to try"); 102269587Sgreen authname_current = NULL; 102369587Sgreen } 102469587Sgreen 102569587Sgreen if (authname_old != NULL) 102669587Sgreen xfree(authname_old); 102769587Sgreen 102869587Sgreen return (method); 102960573Skris} 1030