1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389 |
- /* $OpenBSD: sshconnect2.c,v 1.336 2020/11/13 07:30:44 djm Exp $ */
- /*
- * Copyright (c) 2000 Markus Friedl. All rights reserved.
- * Copyright (c) 2008 Damien Miller. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #include "includes.h"
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/wait.h>
- #include <sys/stat.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <limits.h>
- #include <netdb.h>
- #include <pwd.h>
- #include <signal.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdarg.h>
- #include <unistd.h>
- #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
- #include <vis.h>
- #endif
- #include "openbsd-compat/sys-queue.h"
- #include "xmalloc.h"
- #include "ssh.h"
- #include "ssh2.h"
- #include "sshbuf.h"
- #include "packet.h"
- #include "compat.h"
- #include "cipher.h"
- #include "sshkey.h"
- #include "kex.h"
- #include "myproposal.h"
- #include "sshconnect.h"
- #include "authfile.h"
- #include "dh.h"
- #include "authfd.h"
- #include "log.h"
- #include "misc.h"
- #include "readconf.h"
- #include "match.h"
- #include "dispatch.h"
- #include "canohost.h"
- #include "msg.h"
- #include "pathnames.h"
- #include "uidswap.h"
- #include "hostfile.h"
- #include "ssherr.h"
- #include "utf8.h"
- #include "ssh-sk.h"
- #include "sk-api.h"
- #ifdef GSSAPI
- #include "ssh-gss.h"
- #endif
- /* import */
- extern char *client_version_string;
- extern char *server_version_string;
- extern Options options;
- /*
- * tty_flag is set in ssh.c. Use this in ssh_userauth2:
- * if it is set, then prevent the switch to the null cipher.
- */
- extern int tty_flag;
- /*
- * SSH2 key exchange
- */
- static char *xxx_host;
- static struct sockaddr *xxx_hostaddr;
- static const struct ssh_conn_info *xxx_conn_info;
- static int
- verify_host_key_callback(struct sshkey *hostkey, struct ssh *ssh)
- {
- if (verify_host_key(xxx_host, xxx_hostaddr, hostkey,
- xxx_conn_info) == -1)
- fatal("Host key verification failed.");
- return 0;
- }
- /* Returns the first item from a comma-separated algorithm list */
- static char *
- first_alg(const char *algs)
- {
- char *ret, *cp;
- ret = xstrdup(algs);
- if ((cp = strchr(ret, ',')) != NULL)
- *cp = '\0';
- return ret;
- }
- static char *
- order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port,
- const struct ssh_conn_info *cinfo)
- {
- char *oavail = NULL, *avail = NULL, *first = NULL, *last = NULL;
- char *alg = NULL, *hostname = NULL, *ret = NULL, *best = NULL;
- size_t maxlen;
- struct hostkeys *hostkeys = NULL;
- int ktype;
- u_int i;
- /* Find all hostkeys for this hostname */
- get_hostfile_hostname_ipaddr(host, hostaddr, port, &hostname, NULL);
- hostkeys = init_hostkeys();
- for (i = 0; i < options.num_user_hostfiles; i++)
- load_hostkeys(hostkeys, hostname, options.user_hostfiles[i]);
- for (i = 0; i < options.num_system_hostfiles; i++)
- load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]);
- /*
- * If a plain public key exists that matches the type of the best
- * preference HostkeyAlgorithms, then use the whole list as is.
- * Note that we ignore whether the best preference algorithm is a
- * certificate type, as sshconnect.c will downgrade certs to
- * plain keys if necessary.
- */
- best = first_alg(options.hostkeyalgorithms);
- if (lookup_key_in_hostkeys_by_type(hostkeys,
- sshkey_type_plain(sshkey_type_from_name(best)),
- sshkey_ecdsa_nid_from_name(best), NULL)) {
- debug3("%s: have matching best-preference key type %s, "
- "using HostkeyAlgorithms verbatim", __func__, best);
- ret = xstrdup(options.hostkeyalgorithms);
- goto out;
- }
- /*
- * Otherwise, prefer the host key algorithms that match known keys
- * while keeping the ordering of HostkeyAlgorithms as much as possible.
- */
- oavail = avail = xstrdup(options.hostkeyalgorithms);
- maxlen = strlen(avail) + 1;
- first = xmalloc(maxlen);
- last = xmalloc(maxlen);
- *first = *last = '\0';
- #define ALG_APPEND(to, from) \
- do { \
- if (*to != '\0') \
- strlcat(to, ",", maxlen); \
- strlcat(to, from, maxlen); \
- } while (0)
- while ((alg = strsep(&avail, ",")) && *alg != '\0') {
- if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC)
- fatal("%s: unknown alg %s", __func__, alg);
- /*
- * If we have a @cert-authority marker in known_hosts then
- * prefer all certificate algorithms.
- */
- if (sshkey_type_is_cert(ktype) &&
- lookup_marker_in_hostkeys(hostkeys, MRK_CA)) {
- ALG_APPEND(first, alg);
- continue;
- }
- /* If the key appears in known_hosts then prefer it */
- if (lookup_key_in_hostkeys_by_type(hostkeys,
- sshkey_type_plain(ktype),
- sshkey_ecdsa_nid_from_name(alg), NULL)) {
- ALG_APPEND(first, alg);
- continue;
- }
- /* Otherwise, put it last */
- ALG_APPEND(last, alg);
- }
- #undef ALG_APPEND
- xasprintf(&ret, "%s%s%s", first,
- (*first == '\0' || *last == '\0') ? "" : ",", last);
- if (*first != '\0')
- debug3("%s: prefer hostkeyalgs: %s", __func__, first);
- out:
- free(best);
- free(first);
- free(last);
- free(hostname);
- free(oavail);
- free_hostkeys(hostkeys);
- return ret;
- }
- static char *myproposal[PROPOSAL_MAX];
- static const char *myproposal_default[PROPOSAL_MAX] = { KEX_CLIENT };
- void
- ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port,
- const struct ssh_conn_info *cinfo)
- {
- char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT };
- char *s, *all_key;
- int r, use_known_hosts_order = 0;
- memcpy(&myproposal, &myproposal_default, sizeof(myproposal));
- memcpy(&myproposal, &myproposal_default, sizeof(myproposal));
- xxx_host = host;
- xxx_hostaddr = hostaddr;
- xxx_conn_info = cinfo;
- /*
- * If the user has not specified HostkeyAlgorithms, or has only
- * appended or removed algorithms from that list then prefer algorithms
- * that are in the list that are supported by known_hosts keys.
- */
- if (options.hostkeyalgorithms == NULL ||
- options.hostkeyalgorithms[0] == '-' ||
- options.hostkeyalgorithms[0] == '+')
- use_known_hosts_order = 1;
- /* Expand or fill in HostkeyAlgorithms */
- all_key = sshkey_alg_list(0, 0, 1, ',');
- if (kex_assemble_names(&options.hostkeyalgorithms,
- kex_default_pk_alg(), all_key) != 0)
- fatal("%s: kex_assemble_namelist", __func__);
- free(all_key);
- if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL)
- fatal("%s: kex_names_cat", __func__);
- myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(ssh, s);
- myproposal[PROPOSAL_ENC_ALGS_CTOS] =
- compat_cipher_proposal(ssh, options.ciphers);
- myproposal[PROPOSAL_ENC_ALGS_STOC] =
- compat_cipher_proposal(ssh, options.ciphers);
- myproposal[PROPOSAL_COMP_ALGS_CTOS] =
- myproposal[PROPOSAL_COMP_ALGS_STOC] =
- (char *)compression_alg_list(options.compression);
- myproposal[PROPOSAL_MAC_ALGS_CTOS] =
- myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
- if (use_known_hosts_order) {
- /* Query known_hosts and prefer algorithms that appear there */
- myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
- compat_pkalg_proposal(ssh,
- order_hostkeyalgs(host, hostaddr, port, cinfo));
- } else {
- /* Use specified HostkeyAlgorithms exactly */
- myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
- compat_pkalg_proposal(ssh, options.hostkeyalgorithms);
- }
- if (options.rekey_limit || options.rekey_interval)
- ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
- options.rekey_interval);
- /* start key exchange */
- if ((r = kex_setup(ssh, myproposal)) != 0)
- fatal("kex_setup: %s", ssh_err(r));
- #ifdef WITH_OPENSSL
- ssh->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_client;
- ssh->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_client;
- ssh->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_client;
- ssh->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_client;
- ssh->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_client;
- ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
- ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
- # ifdef OPENSSL_HAS_ECC
- ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client;
- # endif
- #endif
- ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
- ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client;
- ssh->kex->verify_host_key=&verify_host_key_callback;
- ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done);
- /* remove ext-info from the KEX proposals for rekeying */
- myproposal[PROPOSAL_KEX_ALGS] =
- compat_kex_proposal(ssh, options.kex_algorithms);
- if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0)
- fatal("kex_prop2buf: %s", ssh_err(r));
- #ifdef DEBUG_KEXDH
- /* send 1st encrypted/maced/compressed message */
- if ((r = sshpkt_start(ssh, SSH2_MSG_IGNORE)) != 0 ||
- (r = sshpkt_put_cstring(ssh, "markus")) != 0 ||
- (r = sshpkt_send(ssh)) != 0 ||
- (r = ssh_packet_write_wait(ssh)) != 0)
- fatal("%s: %s", __func__, ssh_err(r));
- #endif
- }
- /*
- * Authenticate user
- */
- typedef struct cauthctxt Authctxt;
- typedef struct cauthmethod Authmethod;
- typedef struct identity Identity;
- typedef struct idlist Idlist;
- struct identity {
- TAILQ_ENTRY(identity) next;
- int agent_fd; /* >=0 if agent supports key */
- struct sshkey *key; /* public/private key */
- char *filename; /* comment for agent-only keys */
- int tried;
- int isprivate; /* key points to the private key */
- int userprovided;
- };
- TAILQ_HEAD(idlist, identity);
- struct cauthctxt {
- const char *server_user;
- const char *local_user;
- const char *host;
- const char *service;
- struct cauthmethod *method;
- sig_atomic_t success;
- char *authlist;
- #ifdef GSSAPI
- /* gssapi */
- gss_OID_set gss_supported_mechs;
- u_int mech_tried;
- #endif
- /* pubkey */
- struct idlist keys;
- int agent_fd;
- /* hostbased */
- Sensitive *sensitive;
- char *oktypes, *ktypes;
- const char *active_ktype;
- /* kbd-interactive */
- int info_req_seen;
- int attempt_kbdint;
- /* password */
- int attempt_passwd;
- /* generic */
- void *methoddata;
- };
- struct cauthmethod {
- char *name; /* string to compare against server's list */
- int (*userauth)(struct ssh *ssh);
- void (*cleanup)(struct ssh *ssh);
- int *enabled; /* flag in option struct that enables method */
- int *batch_flag; /* flag in option struct that disables method */
- };
- static int input_userauth_service_accept(int, u_int32_t, struct ssh *);
- static int input_userauth_ext_info(int, u_int32_t, struct ssh *);
- static int input_userauth_success(int, u_int32_t, struct ssh *);
- static int input_userauth_failure(int, u_int32_t, struct ssh *);
- static int input_userauth_banner(int, u_int32_t, struct ssh *);
- static int input_userauth_error(int, u_int32_t, struct ssh *);
- static int input_userauth_info_req(int, u_int32_t, struct ssh *);
- static int input_userauth_pk_ok(int, u_int32_t, struct ssh *);
- static int input_userauth_passwd_changereq(int, u_int32_t, struct ssh *);
- static int userauth_none(struct ssh *);
- static int userauth_pubkey(struct ssh *);
- static int userauth_passwd(struct ssh *);
- static int userauth_kbdint(struct ssh *);
- static int userauth_hostbased(struct ssh *);
- #ifdef GSSAPI
- static int userauth_gssapi(struct ssh *);
- static void userauth_gssapi_cleanup(struct ssh *);
- static int input_gssapi_response(int type, u_int32_t, struct ssh *);
- static int input_gssapi_token(int type, u_int32_t, struct ssh *);
- static int input_gssapi_error(int, u_int32_t, struct ssh *);
- static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
- #endif
- void userauth(struct ssh *, char *);
- static void pubkey_cleanup(struct ssh *);
- static int sign_and_send_pubkey(struct ssh *ssh, Identity *);
- static void pubkey_prepare(Authctxt *);
- static void pubkey_reset(Authctxt *);
- static struct sshkey *load_identity_file(Identity *);
- static Authmethod *authmethod_get(char *authlist);
- static Authmethod *authmethod_lookup(const char *name);
- static char *authmethods_get(void);
- Authmethod authmethods[] = {
- #ifdef GSSAPI
- {"gssapi-with-mic",
- userauth_gssapi,
- userauth_gssapi_cleanup,
- &options.gss_authentication,
- NULL},
- #endif
- {"hostbased",
- userauth_hostbased,
- NULL,
- &options.hostbased_authentication,
- NULL},
- {"publickey",
- userauth_pubkey,
- NULL,
- &options.pubkey_authentication,
- NULL},
- {"keyboard-interactive",
- userauth_kbdint,
- NULL,
- &options.kbd_interactive_authentication,
- &options.batch_mode},
- {"password",
- userauth_passwd,
- NULL,
- &options.password_authentication,
- &options.batch_mode},
- {"none",
- userauth_none,
- NULL,
- NULL,
- NULL},
- {NULL, NULL, NULL, NULL, NULL}
- };
- void
- ssh_userauth2(struct ssh *ssh, const char *local_user,
- const char *server_user, char *host, Sensitive *sensitive)
- {
- Authctxt authctxt;
- int r;
- if (options.challenge_response_authentication)
- options.kbd_interactive_authentication = 1;
- if (options.preferred_authentications == NULL)
- options.preferred_authentications = authmethods_get();
- /* setup authentication context */
- memset(&authctxt, 0, sizeof(authctxt));
- authctxt.server_user = server_user;
- authctxt.local_user = local_user;
- authctxt.host = host;
- authctxt.service = "ssh-connection"; /* service name */
- authctxt.success = 0;
- authctxt.method = authmethod_lookup("none");
- authctxt.authlist = NULL;
- authctxt.methoddata = NULL;
- authctxt.sensitive = sensitive;
- authctxt.active_ktype = authctxt.oktypes = authctxt.ktypes = NULL;
- authctxt.info_req_seen = 0;
- authctxt.attempt_kbdint = 0;
- authctxt.attempt_passwd = 0;
- #if GSSAPI
- authctxt.gss_supported_mechs = NULL;
- authctxt.mech_tried = 0;
- #endif
- authctxt.agent_fd = -1;
- pubkey_prepare(&authctxt);
- if (authctxt.method == NULL) {
- fatal("%s: internal error: cannot send userauth none request",
- __func__);
- }
- if ((r = sshpkt_start(ssh, SSH2_MSG_SERVICE_REQUEST)) != 0 ||
- (r = sshpkt_put_cstring(ssh, "ssh-userauth")) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- fatal("%s: %s", __func__, ssh_err(r));
- ssh->authctxt = &authctxt;
- ssh_dispatch_init(ssh, &input_userauth_error);
- ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_ext_info);
- ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_ACCEPT, &input_userauth_service_accept);
- ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt.success); /* loop until success */
- pubkey_cleanup(ssh);
- ssh->authctxt = NULL;
- ssh_dispatch_range(ssh, SSH2_MSG_USERAUTH_MIN, SSH2_MSG_USERAUTH_MAX, NULL);
- if (!authctxt.success)
- fatal("Authentication failed.");
- /*
- * If the user wants to use the none cipher and/or none mac, do it post authentication
- * and only if the right conditions are met -- both of the NONE commands
- * must be true and there must be no tty allocated.
- */
- if (options.none_switch == 1 && options.none_enabled == 1) {
- if (!tty_flag) { /* no null on tty sessions */
- debug("Requesting none rekeying...");
- memcpy(&myproposal, &myproposal_default, sizeof(myproposal));
- myproposal[PROPOSAL_ENC_ALGS_STOC] = "none";
- myproposal[PROPOSAL_ENC_ALGS_CTOS] = "none";
- fprintf(stderr, "WARNING: ENABLED NONE CIPHER!!!\n");
- /* NONEMAC can only be used in context of the NONE CIPHER */
- if (options.nonemac_enabled == 1) {
- myproposal[PROPOSAL_MAC_ALGS_STOC] = "none";
- myproposal[PROPOSAL_MAC_ALGS_CTOS] = "none";
- fprintf(stderr, "WARNING: ENABLED NONE MAC\n");
- }
- kex_prop2buf(ssh->kex->my, myproposal);
- packet_request_rekeying();
- } else {
- /* requested NONE cipher when in a tty */
- debug("Cannot switch to NONE cipher with tty allocated");
- fprintf(stderr, "NONE cipher switch disabled when a TTY is allocated\n");
- }
- }
-
- #ifdef WITH_OPENSSL
- if (options.disable_multithreaded == 0) {
- /* if we are using aes-ctr there can be issues in either a fork or sandbox
- * so the initial aes-ctr is defined to point to the original single process
- * evp. After authentication we'll be past the fork and the sandboxed privsep
- * so we repoint the define to the multithreaded evp. To start the threads we
- * then force a rekey
- */
- const void *cc = ssh_packet_get_send_context(ssh);
-
- /* only do this for the ctr cipher. otherwise gcm mode breaks. Don't know why though */
- if (strstr(cipher_ctx_name(cc), "ctr")) {
- debug("Single to Multithread CTR cipher swap - client request");
- cipher_reset_multithreaded();
- packet_request_rekeying();
- }
- }
- #endif
- debug("Authentication succeeded (%s).", authctxt.method->name);
- }
- /* ARGSUSED */
- static int
- input_userauth_service_accept(int type, u_int32_t seq, struct ssh *ssh)
- {
- int r;
- if (ssh_packet_remaining(ssh) > 0) {
- char *reply;
- if ((r = sshpkt_get_cstring(ssh, &reply, NULL)) != 0)
- goto out;
- debug2("service_accept: %s", reply);
- free(reply);
- } else {
- debug2("buggy server: service_accept w/o service");
- }
- if ((r = sshpkt_get_end(ssh)) != 0)
- goto out;
- debug("SSH2_MSG_SERVICE_ACCEPT received");
- /* initial userauth request */
- userauth_none(ssh);
- ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_error);
- ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success);
- ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure);
- ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner);
- r = 0;
- out:
- return r;
- }
- /* ARGSUSED */
- static int
- input_userauth_ext_info(int type, u_int32_t seqnr, struct ssh *ssh)
- {
- return kex_input_ext_info(type, seqnr, ssh);
- }
- void
- userauth(struct ssh *ssh, char *authlist)
- {
- Authctxt *authctxt = (Authctxt *)ssh->authctxt;
- if (authctxt->method != NULL && authctxt->method->cleanup != NULL)
- authctxt->method->cleanup(ssh);
- free(authctxt->methoddata);
- authctxt->methoddata = NULL;
- if (authlist == NULL) {
- authlist = authctxt->authlist;
- } else {
- free(authctxt->authlist);
- authctxt->authlist = authlist;
- }
- for (;;) {
- Authmethod *method = authmethod_get(authlist);
- if (method == NULL)
- fatal("%s@%s: Permission denied (%s).",
- authctxt->server_user, authctxt->host, authlist);
- authctxt->method = method;
- /* reset the per method handler */
- ssh_dispatch_range(ssh, SSH2_MSG_USERAUTH_PER_METHOD_MIN,
- SSH2_MSG_USERAUTH_PER_METHOD_MAX, NULL);
- /* and try new method */
- if (method->userauth(ssh) != 0) {
- debug2("we sent a %s packet, wait for reply", method->name);
- break;
- } else {
- debug2("we did not send a packet, disable method");
- method->enabled = NULL;
- }
- }
- }
- /* ARGSUSED */
- static int
- input_userauth_error(int type, u_int32_t seq, struct ssh *ssh)
- {
- fatal("%s: bad message during authentication: type %d", __func__, type);
- return 0;
- }
- /* ARGSUSED */
- static int
- input_userauth_banner(int type, u_int32_t seq, struct ssh *ssh)
- {
- char *msg = NULL;
- size_t len;
- int r;
- debug3("%s", __func__);
- if ((r = sshpkt_get_cstring(ssh, &msg, &len)) != 0 ||
- (r = sshpkt_get_cstring(ssh, NULL, NULL)) != 0)
- goto out;
- if (len > 0 && options.log_level >= SYSLOG_LEVEL_INFO)
- fmprintf(stderr, "%s", msg);
- r = 0;
- out:
- free(msg);
- return r;
- }
- /* ARGSUSED */
- static int
- input_userauth_success(int type, u_int32_t seq, struct ssh *ssh)
- {
- Authctxt *authctxt = ssh->authctxt;
- if (authctxt == NULL)
- fatal("%s: no authentication context", __func__);
- free(authctxt->authlist);
- authctxt->authlist = NULL;
- if (authctxt->method != NULL && authctxt->method->cleanup != NULL)
- authctxt->method->cleanup(ssh);
- free(authctxt->methoddata);
- authctxt->methoddata = NULL;
- authctxt->success = 1; /* break out */
- return 0;
- }
- #if 0
- static int
- input_userauth_success_unexpected(int type, u_int32_t seq, struct ssh *ssh)
- {
- Authctxt *authctxt = ssh->authctxt;
- if (authctxt == NULL)
- fatal("%s: no authentication context", __func__);
- fatal("Unexpected authentication success during %s.",
- authctxt->method->name);
- return 0;
- }
- #endif
- /* ARGSUSED */
- static int
- input_userauth_failure(int type, u_int32_t seq, struct ssh *ssh)
- {
- Authctxt *authctxt = ssh->authctxt;
- char *authlist = NULL;
- u_char partial;
- if (authctxt == NULL)
- fatal("input_userauth_failure: no authentication context");
- if (sshpkt_get_cstring(ssh, &authlist, NULL) != 0 ||
- sshpkt_get_u8(ssh, &partial) != 0 ||
- sshpkt_get_end(ssh) != 0)
- goto out;
- if (partial != 0) {
- verbose("Authenticated with partial success.");
- /* reset state */
- pubkey_reset(authctxt);
- }
- debug("Authentications that can continue: %s", authlist);
- userauth(ssh, authlist);
- authlist = NULL;
- out:
- free(authlist);
- return 0;
- }
- /*
- * Format an identity for logging including filename, key type, fingerprint
- * and location (agent, etc.). Caller must free.
- */
- static char *
- format_identity(Identity *id)
- {
- char *fp = NULL, *ret = NULL;
- const char *note = "";
- if (id->key != NULL) {
- fp = sshkey_fingerprint(id->key, options.fingerprint_hash,
- SSH_FP_DEFAULT);
- }
- if (id->key) {
- if ((id->key->flags & SSHKEY_FLAG_EXT) != 0)
- note = " token";
- else if (sshkey_is_sk(id->key))
- note = " authenticator";
- }
- xasprintf(&ret, "%s %s%s%s%s%s%s",
- id->filename,
- id->key ? sshkey_type(id->key) : "", id->key ? " " : "",
- fp ? fp : "",
- id->userprovided ? " explicit" : "", note,
- id->agent_fd != -1 ? " agent" : "");
- free(fp);
- return ret;
- }
- /* ARGSUSED */
- static int
- input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh)
- {
- Authctxt *authctxt = ssh->authctxt;
- struct sshkey *key = NULL;
- Identity *id = NULL;
- int pktype, found = 0, sent = 0;
- size_t blen;
- char *pkalg = NULL, *fp = NULL, *ident = NULL;
- u_char *pkblob = NULL;
- int r;
- if (authctxt == NULL)
- fatal("input_userauth_pk_ok: no authentication context");
- if ((r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 ||
- (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0 ||
- (r = sshpkt_get_end(ssh)) != 0)
- goto done;
- if ((pktype = sshkey_type_from_name(pkalg)) == KEY_UNSPEC) {
- debug("%s: server sent unknown pkalg %s", __func__, pkalg);
- goto done;
- }
- if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
- debug("no key from blob. pkalg %s: %s", pkalg, ssh_err(r));
- goto done;
- }
- if (key->type != pktype) {
- error("input_userauth_pk_ok: type mismatch "
- "for decoded key (received %d, expected %d)",
- key->type, pktype);
- goto done;
- }
- /*
- * search keys in the reverse order, because last candidate has been
- * moved to the end of the queue. this also avoids confusion by
- * duplicate keys
- */
- TAILQ_FOREACH_REVERSE(id, &authctxt->keys, idlist, next) {
- if (sshkey_equal(key, id->key)) {
- found = 1;
- break;
- }
- }
- if (!found || id == NULL) {
- fp = sshkey_fingerprint(key, options.fingerprint_hash,
- SSH_FP_DEFAULT);
- error("%s: server replied with unknown key: %s %s", __func__,
- sshkey_type(key), fp == NULL ? "<ERROR>" : fp);
- goto done;
- }
- ident = format_identity(id);
- debug("Server accepts key: %s", ident);
- sent = sign_and_send_pubkey(ssh, id);
- r = 0;
- done:
- sshkey_free(key);
- free(ident);
- free(fp);
- free(pkalg);
- free(pkblob);
- /* try another method if we did not send a packet */
- if (r == 0 && sent == 0)
- userauth(ssh, NULL);
- return r;
- }
- #ifdef GSSAPI
- static int
- userauth_gssapi(struct ssh *ssh)
- {
- Authctxt *authctxt = (Authctxt *)ssh->authctxt;
- Gssctxt *gssctxt = NULL;
- OM_uint32 min;
- int r, ok = 0;
- gss_OID mech = NULL;
- /* Try one GSSAPI method at a time, rather than sending them all at
- * once. */
- if (authctxt->gss_supported_mechs == NULL)
- gss_indicate_mechs(&min, &authctxt->gss_supported_mechs);
- /* Check to see whether the mechanism is usable before we offer it */
- while (authctxt->mech_tried < authctxt->gss_supported_mechs->count &&
- !ok) {
- mech = &authctxt->gss_supported_mechs->
- elements[authctxt->mech_tried];
- /* My DER encoding requires length<128 */
- if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt,
- mech, authctxt->host)) {
- ok = 1; /* Mechanism works */
- } else {
- authctxt->mech_tried++;
- }
- }
- if (!ok || mech == NULL)
- return 0;
- authctxt->methoddata=(void *)gssctxt;
- if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
- (r = sshpkt_put_u32(ssh, 1)) != 0 ||
- (r = sshpkt_put_u32(ssh, (mech->length) + 2)) != 0 ||
- (r = sshpkt_put_u8(ssh, SSH_GSS_OIDTYPE)) != 0 ||
- (r = sshpkt_put_u8(ssh, mech->length)) != 0 ||
- (r = sshpkt_put(ssh, mech->elements, mech->length)) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- fatal("%s: %s", __func__, ssh_err(r));
- ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_RESPONSE, &input_gssapi_response);
- ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token);
- ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERROR, &input_gssapi_error);
- ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok);
- authctxt->mech_tried++; /* Move along to next candidate */
- return 1;
- }
- static void
- userauth_gssapi_cleanup(struct ssh *ssh)
- {
- Authctxt *authctxt = (Authctxt *)ssh->authctxt;
- Gssctxt *gssctxt = (Gssctxt *)authctxt->methoddata;
- ssh_gssapi_delete_ctx(&gssctxt);
- authctxt->methoddata = NULL;
- free(authctxt->gss_supported_mechs);
- authctxt->gss_supported_mechs = NULL;
- }
- static OM_uint32
- process_gssapi_token(struct ssh *ssh, gss_buffer_t recv_tok)
- {
- Authctxt *authctxt = ssh->authctxt;
- Gssctxt *gssctxt = authctxt->methoddata;
- gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
- gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
- gss_buffer_desc gssbuf;
- OM_uint32 status, ms, flags;
- int r;
- status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
- recv_tok, &send_tok, &flags);
- if (send_tok.length > 0) {
- u_char type = GSS_ERROR(status) ?
- SSH2_MSG_USERAUTH_GSSAPI_ERRTOK :
- SSH2_MSG_USERAUTH_GSSAPI_TOKEN;
- if ((r = sshpkt_start(ssh, type)) != 0 ||
- (r = sshpkt_put_string(ssh, send_tok.value,
- send_tok.length)) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- fatal("%s: %s", __func__, ssh_err(r));
- gss_release_buffer(&ms, &send_tok);
- }
- if (status == GSS_S_COMPLETE) {
- /* send either complete or MIC, depending on mechanism */
- if (!(flags & GSS_C_INTEG_FLAG)) {
- if ((r = sshpkt_start(ssh,
- SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE)) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- fatal("%s: %s", __func__, ssh_err(r));
- } else {
- struct sshbuf *b;
- if ((b = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
- ssh_gssapi_buildmic(b, authctxt->server_user,
- authctxt->service, "gssapi-with-mic",
- ssh->kex->session_id);
- if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
- fatal("%s: sshbuf_mutable_ptr failed", __func__);
- gssbuf.length = sshbuf_len(b);
- status = ssh_gssapi_sign(gssctxt, &gssbuf, &mic);
- if (!GSS_ERROR(status)) {
- if ((r = sshpkt_start(ssh,
- SSH2_MSG_USERAUTH_GSSAPI_MIC)) != 0 ||
- (r = sshpkt_put_string(ssh, mic.value,
- mic.length)) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- fatal("%s: %s", __func__, ssh_err(r));
- }
- sshbuf_free(b);
- gss_release_buffer(&ms, &mic);
- }
- }
- return status;
- }
- /* ARGSUSED */
- static int
- input_gssapi_response(int type, u_int32_t plen, struct ssh *ssh)
- {
- Authctxt *authctxt = ssh->authctxt;
- Gssctxt *gssctxt;
- size_t oidlen;
- u_char *oidv = NULL;
- int r;
- if (authctxt == NULL)
- fatal("input_gssapi_response: no authentication context");
- gssctxt = authctxt->methoddata;
- /* Setup our OID */
- if ((r = sshpkt_get_string(ssh, &oidv, &oidlen)) != 0)
- goto done;
- if (oidlen <= 2 ||
- oidv[0] != SSH_GSS_OIDTYPE ||
- oidv[1] != oidlen - 2) {
- debug("Badly encoded mechanism OID received");
- userauth(ssh, NULL);
- goto ok;
- }
- if (!ssh_gssapi_check_oid(gssctxt, oidv + 2, oidlen - 2))
- fatal("Server returned different OID than expected");
- if ((r = sshpkt_get_end(ssh)) != 0)
- goto done;
- if (GSS_ERROR(process_gssapi_token(ssh, GSS_C_NO_BUFFER))) {
- /* Start again with next method on list */
- debug("Trying to start again");
- userauth(ssh, NULL);
- goto ok;
- }
- ok:
- r = 0;
- done:
- free(oidv);
- return r;
- }
- /* ARGSUSED */
- static int
- input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh)
- {
- Authctxt *authctxt = ssh->authctxt;
- gss_buffer_desc recv_tok;
- u_char *p = NULL;
- size_t len;
- OM_uint32 status;
- int r;
- if (authctxt == NULL)
- fatal("input_gssapi_response: no authentication context");
- if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
- (r = sshpkt_get_end(ssh)) != 0)
- goto out;
- recv_tok.value = p;
- recv_tok.length = len;
- status = process_gssapi_token(ssh, &recv_tok);
- /* Start again with the next method in the list */
- if (GSS_ERROR(status)) {
- userauth(ssh, NULL);
- /* ok */
- }
- r = 0;
- out:
- free(p);
- return r;
- }
- /* ARGSUSED */
- static int
- input_gssapi_errtok(int type, u_int32_t plen, struct ssh *ssh)
- {
- Authctxt *authctxt = ssh->authctxt;
- Gssctxt *gssctxt;
- gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
- gss_buffer_desc recv_tok;
- OM_uint32 ms;
- u_char *p = NULL;
- size_t len;
- int r;
- if (authctxt == NULL)
- fatal("input_gssapi_response: no authentication context");
- gssctxt = authctxt->methoddata;
- if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
- (r = sshpkt_get_end(ssh)) != 0) {
- free(p);
- return r;
- }
- /* Stick it into GSSAPI and see what it says */
- recv_tok.value = p;
- recv_tok.length = len;
- (void)ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
- &recv_tok, &send_tok, NULL);
- free(p);
- gss_release_buffer(&ms, &send_tok);
- /* Server will be returning a failed packet after this one */
- return 0;
- }
- /* ARGSUSED */
- static int
- input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh)
- {
- char *msg = NULL;
- char *lang = NULL;
- int r;
- if ((r = sshpkt_get_u32(ssh, NULL)) != 0 || /* maj */
- (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* min */
- (r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 ||
- (r = sshpkt_get_cstring(ssh, &lang, NULL)) != 0)
- goto out;
- r = sshpkt_get_end(ssh);
- debug("Server GSSAPI Error:\n%s", msg);
- out:
- free(msg);
- free(lang);
- return r;
- }
- #endif /* GSSAPI */
- static int
- userauth_none(struct ssh *ssh)
- {
- Authctxt *authctxt = (Authctxt *)ssh->authctxt;
- int r;
- /* initial userauth request */
- if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- fatal("%s: %s", __func__, ssh_err(r));
- return 1;
- }
- static int
- userauth_passwd(struct ssh *ssh)
- {
- Authctxt *authctxt = (Authctxt *)ssh->authctxt;
- char *password, *prompt = NULL;
- const char *host = options.host_key_alias ? options.host_key_alias :
- authctxt->host;
- int r;
- if (authctxt->attempt_passwd++ >= options.number_of_password_prompts)
- return 0;
- if (authctxt->attempt_passwd != 1)
- error("Permission denied, please try again.");
- xasprintf(&prompt, "%s@%s's password: ", authctxt->server_user, host);
- password = read_passphrase(prompt, 0);
- if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
- (r = sshpkt_put_u8(ssh, 0)) != 0 ||
- (r = sshpkt_put_cstring(ssh, password)) != 0 ||
- (r = sshpkt_add_padding(ssh, 64)) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- fatal("%s: %s", __func__, ssh_err(r));
- free(prompt);
- if (password != NULL)
- freezero(password, strlen(password));
- ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ,
- &input_userauth_passwd_changereq);
- return 1;
- }
- /*
- * parse PASSWD_CHANGEREQ, prompt user and send SSH2_MSG_USERAUTH_REQUEST
- */
- /* ARGSUSED */
- static int
- input_userauth_passwd_changereq(int type, u_int32_t seqnr, struct ssh *ssh)
- {
- Authctxt *authctxt = ssh->authctxt;
- char *info = NULL, *lang = NULL, *password = NULL, *retype = NULL;
- char prompt[256];
- const char *host;
- int r;
- debug2("input_userauth_passwd_changereq");
- if (authctxt == NULL)
- fatal("input_userauth_passwd_changereq: "
- "no authentication context");
- host = options.host_key_alias ? options.host_key_alias : authctxt->host;
- if ((r = sshpkt_get_cstring(ssh, &info, NULL)) != 0 ||
- (r = sshpkt_get_cstring(ssh, &lang, NULL)) != 0)
- goto out;
- if (strlen(info) > 0)
- logit("%s", info);
- if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
- (r = sshpkt_put_u8(ssh, 1)) != 0) /* additional info */
- goto out;
- snprintf(prompt, sizeof(prompt),
- "Enter %.30s@%.128s's old password: ",
- authctxt->server_user, host);
- password = read_passphrase(prompt, 0);
- if ((r = sshpkt_put_cstring(ssh, password)) != 0)
- goto out;
- freezero(password, strlen(password));
- password = NULL;
- while (password == NULL) {
- snprintf(prompt, sizeof(prompt),
- "Enter %.30s@%.128s's new password: ",
- authctxt->server_user, host);
- password = read_passphrase(prompt, RP_ALLOW_EOF);
- if (password == NULL) {
- /* bail out */
- r = 0;
- goto out;
- }
- snprintf(prompt, sizeof(prompt),
- "Retype %.30s@%.128s's new password: ",
- authctxt->server_user, host);
- retype = read_passphrase(prompt, 0);
- if (strcmp(password, retype) != 0) {
- freezero(password, strlen(password));
- logit("Mismatch; try again, EOF to quit.");
- password = NULL;
- }
- freezero(retype, strlen(retype));
- }
- if ((r = sshpkt_put_cstring(ssh, password)) != 0 ||
- (r = sshpkt_add_padding(ssh, 64)) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- goto out;
- ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ,
- &input_userauth_passwd_changereq);
- r = 0;
- out:
- if (password)
- freezero(password, strlen(password));
- free(info);
- free(lang);
- return r;
- }
- /*
- * Select an algorithm for publickey signatures.
- * Returns algorithm (caller must free) or NULL if no mutual algorithm found.
- *
- * Call with ssh==NULL to ignore server-sig-algs extension list and
- * only attempt with the key's base signature type.
- */
- static char *
- key_sig_algorithm(struct ssh *ssh, const struct sshkey *key)
- {
- char *allowed, *oallowed, *cp, *tmp, *alg = NULL;
- /*
- * The signature algorithm will only differ from the key algorithm
- * for RSA keys/certs and when the server advertises support for
- * newer (SHA2) algorithms.
- */
- if (ssh == NULL || ssh->kex->server_sig_algs == NULL ||
- (key->type != KEY_RSA && key->type != KEY_RSA_CERT) ||
- (key->type == KEY_RSA_CERT && (ssh->compat & SSH_BUG_SIGTYPE))) {
- /* Filter base key signature alg against our configuration */
- return match_list(sshkey_ssh_name(key),
- options.pubkey_accepted_algos, NULL);
- }
- /*
- * For RSA keys/certs, since these might have a different sig type:
- * find the first entry in PubkeyAcceptedAlgorithms of the right type
- * that also appears in the supported signature algorithms list from
- * the server.
- */
- oallowed = allowed = xstrdup(options.pubkey_accepted_algos);
- while ((cp = strsep(&allowed, ",")) != NULL) {
- if (sshkey_type_from_name(cp) != key->type)
- continue;
- tmp = match_list(sshkey_sigalg_by_name(cp),
- ssh->kex->server_sig_algs, NULL);
- if (tmp != NULL)
- alg = xstrdup(cp);
- free(tmp);
- if (alg != NULL)
- break;
- }
- free(oallowed);
- return alg;
- }
- static int
- identity_sign(struct identity *id, u_char **sigp, size_t *lenp,
- const u_char *data, size_t datalen, u_int compat, const char *alg)
- {
- struct sshkey *sign_key = NULL, *prv = NULL;
- int retried = 0, r = SSH_ERR_INTERNAL_ERROR;
- struct notifier_ctx *notifier = NULL;
- char *fp = NULL, *pin = NULL, *prompt = NULL;
- *sigp = NULL;
- *lenp = 0;
- /* The agent supports this key. */
- if (id->key != NULL && id->agent_fd != -1) {
- return ssh_agent_sign(id->agent_fd, id->key, sigp, lenp,
- data, datalen, alg, compat);
- }
- /*
- * We have already loaded the private key or the private key is
- * stored in external hardware.
- */
- if (id->key != NULL &&
- (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))) {
- sign_key = id->key;
- } else {
- /* Load the private key from the file. */
- if ((prv = load_identity_file(id)) == NULL)
- return SSH_ERR_KEY_NOT_FOUND;
- if (id->key != NULL && !sshkey_equal_public(prv, id->key)) {
- error("%s: private key %s contents do not match public",
- __func__, id->filename);
- r = SSH_ERR_KEY_NOT_FOUND;
- goto out;
- }
- sign_key = prv;
- if (sshkey_is_sk(sign_key)) {
- if ((sign_key->sk_flags &
- SSH_SK_USER_VERIFICATION_REQD)) {
- retry_pin:
- xasprintf(&prompt, "Enter PIN for %s key %s: ",
- sshkey_type(sign_key), id->filename);
- pin = read_passphrase(prompt, 0);
- }
- if ((sign_key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
- /* XXX should batch mode just skip these? */
- if ((fp = sshkey_fingerprint(sign_key,
- options.fingerprint_hash,
- SSH_FP_DEFAULT)) == NULL)
- fatal("%s: fingerprint", __func__);
- notifier = notify_start(options.batch_mode,
- "Confirm user presence for key %s %s",
- sshkey_type(sign_key), fp);
- free(fp);
- }
- }
- }
- if ((r = sshkey_sign(sign_key, sigp, lenp, data, datalen,
- alg, options.sk_provider, pin, compat)) != 0) {
- debug("%s: sshkey_sign: %s", __func__, ssh_err(r));
- if (pin == NULL && !retried && sshkey_is_sk(sign_key) &&
- r == SSH_ERR_KEY_WRONG_PASSPHRASE) {
- //notify_complete(notifier, NULL);
- notifier = NULL;
- retried = 1;
- goto retry_pin;
- }
- goto out;
- }
- /*
- * PKCS#11 tokens may not support all signature algorithms,
- * so check what we get back.
- */
- if ((r = sshkey_check_sigtype(*sigp, *lenp, alg)) != 0) {
- debug("%s: sshkey_check_sigtype: %s", __func__, ssh_err(r));
- goto out;
- }
- /* success */
- r = 0;
- out:
- free(prompt);
- if (pin != NULL)
- freezero(pin, strlen(pin));
- //notify_complete(notifier, r == 0 ? "User presence confirmed" : NULL);
- sshkey_free(prv);
- return r;
- }
- static int
- id_filename_matches(Identity *id, Identity *private_id)
- {
- const char *suffixes[] = { ".pub", "-cert.pub", NULL };
- size_t len = strlen(id->filename), plen = strlen(private_id->filename);
- size_t i, slen;
- if (strcmp(id->filename, private_id->filename) == 0)
- return 1;
- for (i = 0; suffixes[i]; i++) {
- slen = strlen(suffixes[i]);
- if (len > slen && plen == len - slen &&
- strcmp(id->filename + (len - slen), suffixes[i]) == 0 &&
- memcmp(id->filename, private_id->filename, plen) == 0)
- return 1;
- }
- return 0;
- }
- static int
- sign_and_send_pubkey(struct ssh *ssh, Identity *id)
- {
- Authctxt *authctxt = (Authctxt *)ssh->authctxt;
- struct sshbuf *b = NULL;
- Identity *private_id, *sign_id = NULL;
- u_char *signature = NULL;
- size_t slen = 0, skip = 0;
- int r, fallback_sigtype, sent = 0;
- char *alg = NULL, *fp = NULL;
- const char *loc = "";
- if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash,
- SSH_FP_DEFAULT)) == NULL)
- return 0;
- debug3("%s: %s %s", __func__, sshkey_type(id->key), fp);
- /*
- * If the key is an certificate, try to find a matching private key
- * and use it to complete the signature.
- * If no such private key exists, fall back to trying the certificate
- * key itself in case it has a private half already loaded.
- * This will try to set sign_id to the private key that will perform
- * the signature.
- */
- if (sshkey_is_cert(id->key)) {
- TAILQ_FOREACH(private_id, &authctxt->keys, next) {
- if (sshkey_equal_public(id->key, private_id->key) &&
- id->key->type != private_id->key->type) {
- sign_id = private_id;
- break;
- }
- }
- /*
- * Exact key matches are preferred, but also allow
- * filename matches for non-PKCS#11/agent keys that
- * didn't load public keys. This supports the case
- * of keeping just a private key file and public
- * certificate on disk.
- */
- if (sign_id == NULL &&
- !id->isprivate && id->agent_fd == -1 &&
- (id->key->flags & SSHKEY_FLAG_EXT) == 0) {
- TAILQ_FOREACH(private_id, &authctxt->keys, next) {
- if (private_id->key == NULL &&
- id_filename_matches(id, private_id)) {
- sign_id = private_id;
- break;
- }
- }
- }
- if (sign_id != NULL) {
- debug2("%s: using private key \"%s\"%s for "
- "certificate", __func__, id->filename,
- id->agent_fd != -1 ? " from agent" : "");
- } else {
- debug("%s: no separate private key for certificate "
- "\"%s\"", __func__, id->filename);
- }
- }
- /*
- * If the above didn't select another identity to do the signing
- * then default to the one we started with.
- */
- if (sign_id == NULL)
- sign_id = id;
- /* assemble and sign data */
- for (fallback_sigtype = 0; fallback_sigtype <= 1; fallback_sigtype++) {
- free(alg);
- slen = 0;
- signature = NULL;
- if ((alg = key_sig_algorithm(fallback_sigtype ? NULL : ssh,
- id->key)) == NULL) {
- error("%s: no mutual signature supported", __func__);
- goto out;
- }
- debug3("%s: signing using %s %s", __func__, alg, fp);
- sshbuf_free(b);
- if ((b = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
- if (ssh->compat & SSH_OLD_SESSIONID) {
- if ((r = sshbuf_putb(b, ssh->kex->session_id)) != 0)
- fatal(r, "sshbuf_putb");
- } else {
- if ((r = sshbuf_put_stringb(b,
- ssh->kex->session_id)) != 0)
- fatal(r, "sshbuf_put_stringb");
- }
- skip = sshbuf_len(b);
- if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
- (r = sshbuf_put_cstring(b, authctxt->server_user)) != 0 ||
- (r = sshbuf_put_cstring(b, authctxt->service)) != 0 ||
- (r = sshbuf_put_cstring(b, authctxt->method->name)) != 0 ||
- (r = sshbuf_put_u8(b, 1)) != 0 ||
- (r = sshbuf_put_cstring(b, alg)) != 0 ||
- (r = sshkey_puts(id->key, b)) != 0) {
- fatal("%s: assemble signed data: %s",
- __func__, ssh_err(r));
- }
- /* generate signature */
- r = identity_sign(sign_id, &signature, &slen,
- sshbuf_ptr(b), sshbuf_len(b), ssh->compat, alg);
- if (r == 0)
- break;
- else if (r == SSH_ERR_KEY_NOT_FOUND)
- goto out; /* soft failure */
- else if (r == SSH_ERR_SIGN_ALG_UNSUPPORTED &&
- !fallback_sigtype) {
- if (sign_id->agent_fd != -1)
- loc = "agent ";
- else if ((sign_id->key->flags & SSHKEY_FLAG_EXT) != 0)
- loc = "token ";
- logit("%skey %s %s returned incorrect signature type",
- loc, sshkey_type(id->key), fp);
- continue;
- }
- error("%s: signing failed for %s \"%s\"%s: %s", __func__,
- sshkey_type(sign_id->key), sign_id->filename,
- id->agent_fd != -1 ? " from agent" : "", ssh_err(r));
- goto out;
- }
- if (slen == 0 || signature == NULL) /* shouldn't happen */
- fatal("%s: no signature", __func__);
- /* append signature */
- if ((r = sshbuf_put_string(b, signature, slen)) != 0)
- fatal("%s: append signature: %s", __func__, ssh_err(r));
- #ifdef DEBUG_PK
- sshbuf_dump(b, stderr);
- #endif
- /* skip session id and packet type */
- if ((r = sshbuf_consume(b, skip + 1)) != 0)
- fatal("%s: consume: %s", __func__, ssh_err(r));
- /* put remaining data from buffer into packet */
- if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
- (r = sshpkt_putb(ssh, b)) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- fatal("%s: enqueue request: %s", __func__, ssh_err(r));
- /* success */
- sent = 1;
- out:
- free(fp);
- free(alg);
- sshbuf_free(b);
- freezero(signature, slen);
- return sent;
- }
- static int
- send_pubkey_test(struct ssh *ssh, Identity *id)
- {
- Authctxt *authctxt = (Authctxt *)ssh->authctxt;
- u_char *blob = NULL;
- char *alg = NULL;
- size_t bloblen;
- u_int have_sig = 0;
- int sent = 0, r;
- if ((alg = key_sig_algorithm(ssh, id->key)) == NULL) {
- debug("%s: no mutual signature algorithm", __func__);
- goto out;
- }
- if ((r = sshkey_to_blob(id->key, &blob, &bloblen)) != 0) {
- /* we cannot handle this key */
- debug3("%s: cannot handle key", __func__);
- goto out;
- }
- /* register callback for USERAUTH_PK_OK message */
- ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok);
- if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
- (r = sshpkt_put_u8(ssh, have_sig)) != 0 ||
- (r = sshpkt_put_cstring(ssh, alg)) != 0 ||
- (r = sshpkt_put_string(ssh, blob, bloblen)) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- fatal("%s: %s", __func__, ssh_err(r));
- sent = 1;
- out:
- free(alg);
- free(blob);
- return sent;
- }
- static struct sshkey *
- load_identity_file(Identity *id)
- {
- struct sshkey *private = NULL;
- char prompt[300], *passphrase, *comment;
- int r, quit = 0, i;
- struct stat st;
- if (stat(id->filename, &st) == -1) {
- do_log2(id->userprovided ?
- SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_DEBUG3,
- "no such identity: %s: %s", id->filename, strerror(errno));
- return NULL;
- }
- snprintf(prompt, sizeof prompt,
- "Enter passphrase for key '%.100s': ", id->filename);
- for (i = 0; i <= options.number_of_password_prompts; i++) {
- if (i == 0)
- passphrase = "";
- else {
- passphrase = read_passphrase(prompt, 0);
- if (*passphrase == '\0') {
- debug2("no passphrase given, try next key");
- free(passphrase);
- break;
- }
- }
- switch ((r = sshkey_load_private_type(KEY_UNSPEC, id->filename,
- passphrase, &private, &comment))) {
- case 0:
- break;
- case SSH_ERR_KEY_WRONG_PASSPHRASE:
- if (options.batch_mode) {
- quit = 1;
- break;
- }
- if (i != 0)
- debug2("bad passphrase given, try again...");
- break;
- case SSH_ERR_SYSTEM_ERROR:
- if (errno == ENOENT) {
- debug2("Load key \"%s\": %s",
- id->filename, ssh_err(r));
- quit = 1;
- break;
- }
- /* FALLTHROUGH */
- default:
- error("Load key \"%s\": %s", id->filename, ssh_err(r));
- quit = 1;
- break;
- }
- if (private != NULL && sshkey_is_sk(private) &&
- options.sk_provider == NULL) {
- debug("key \"%s\" is an authenticator-hosted key, "
- "but no provider specified", id->filename);
- sshkey_free(private);
- private = NULL;
- quit = 1;
- }
- if (!quit && private != NULL && id->agent_fd == -1 &&
- !(id->key && id->isprivate))
- maybe_add_key_to_agent(id->filename, private, comment,
- passphrase);
- if (i > 0)
- freezero(passphrase, strlen(passphrase));
- free(comment);
- if (private != NULL || quit)
- break;
- }
- return private;
- }
- static int
- key_type_allowed_by_config(struct sshkey *key)
- {
- if (match_pattern_list(sshkey_ssh_name(key),
- options.pubkey_accepted_algos, 0) == 1)
- return 1;
- /* RSA keys/certs might be allowed by alternate signature types */
- switch (key->type) {
- case KEY_RSA:
- if (match_pattern_list("rsa-sha2-512",
- options.pubkey_accepted_algos, 0) == 1)
- return 1;
- if (match_pattern_list("rsa-sha2-256",
- options.pubkey_accepted_algos, 0) == 1)
- return 1;
- break;
- case KEY_RSA_CERT:
- if (match_pattern_list("rsa-sha2-512-cert-v01@openssh.com",
- options.pubkey_accepted_algos, 0) == 1)
- return 1;
- if (match_pattern_list("rsa-sha2-256-cert-v01@openssh.com",
- options.pubkey_accepted_algos, 0) == 1)
- return 1;
- break;
- }
- return 0;
- }
- /*
- * try keys in the following order:
- * 1. certificates listed in the config file
- * 2. other input certificates
- * 3. agent keys that are found in the config file
- * 4. other agent keys
- * 5. keys that are only listed in the config file
- */
- static void
- pubkey_prepare(Authctxt *authctxt)
- {
- struct identity *id, *id2, *tmp;
- struct idlist agent, files, *preferred;
- struct sshkey *key;
- int agent_fd = -1, i, r, found;
- size_t j;
- struct ssh_identitylist *idlist;
- char *ident;
- TAILQ_INIT(&agent); /* keys from the agent */
- TAILQ_INIT(&files); /* keys from the config file */
- preferred = &authctxt->keys;
- TAILQ_INIT(preferred); /* preferred order of keys */
- /* list of keys stored in the filesystem and PKCS#11 */
- for (i = 0; i < options.num_identity_files; i++) {
- key = options.identity_keys[i];
- if (key && key->cert &&
- key->cert->type != SSH2_CERT_TYPE_USER) {
- debug("%s: ignoring certificate %s: not a user "
- "certificate", __func__,
- options.identity_files[i]);
- continue;
- }
- if (key && sshkey_is_sk(key) && options.sk_provider == NULL) {
- debug("%s: ignoring authenticator-hosted key %s as no "
- "SecurityKeyProvider has been specified",
- __func__, options.identity_files[i]);
- continue;
- }
- options.identity_keys[i] = NULL;
- id = xcalloc(1, sizeof(*id));
- id->agent_fd = -1;
- id->key = key;
- id->filename = xstrdup(options.identity_files[i]);
- id->userprovided = options.identity_file_userprovided[i];
- TAILQ_INSERT_TAIL(&files, id, next);
- }
- /* list of certificates specified by user */
- for (i = 0; i < options.num_certificate_files; i++) {
- key = options.certificates[i];
- if (!sshkey_is_cert(key) || key->cert == NULL ||
- key->cert->type != SSH2_CERT_TYPE_USER) {
- debug("%s: ignoring certificate %s: not a user "
- "certificate", __func__,
- options.identity_files[i]);
- continue;
- }
- if (key && sshkey_is_sk(key) && options.sk_provider == NULL) {
- debug("%s: ignoring authenticator-hosted key "
- "certificate %s as no "
- "SecurityKeyProvider has been specified",
- __func__, options.identity_files[i]);
- continue;
- }
- id = xcalloc(1, sizeof(*id));
- id->agent_fd = -1;
- id->key = key;
- id->filename = xstrdup(options.certificate_files[i]);
- id->userprovided = options.certificate_file_userprovided[i];
- TAILQ_INSERT_TAIL(preferred, id, next);
- }
- /* list of keys supported by the agent */
- if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) {
- if (r != SSH_ERR_AGENT_NOT_PRESENT)
- debug("%s: ssh_get_authentication_socket: %s",
- __func__, ssh_err(r));
- } else if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) {
- if (r != SSH_ERR_AGENT_NO_IDENTITIES)
- debug("%s: ssh_fetch_identitylist: %s",
- __func__, ssh_err(r));
- close(agent_fd);
- } else {
- for (j = 0; j < idlist->nkeys; j++) {
- found = 0;
- TAILQ_FOREACH(id, &files, next) {
- /*
- * agent keys from the config file are
- * preferred
- */
- if (sshkey_equal(idlist->keys[j], id->key)) {
- TAILQ_REMOVE(&files, id, next);
- TAILQ_INSERT_TAIL(preferred, id, next);
- id->agent_fd = agent_fd;
- found = 1;
- break;
- }
- }
- if (!found && !options.identities_only) {
- id = xcalloc(1, sizeof(*id));
- /* XXX "steals" key/comment from idlist */
- id->key = idlist->keys[j];
- id->filename = idlist->comments[j];
- idlist->keys[j] = NULL;
- idlist->comments[j] = NULL;
- id->agent_fd = agent_fd;
- TAILQ_INSERT_TAIL(&agent, id, next);
- }
- }
- ssh_free_identitylist(idlist);
- /* append remaining agent keys */
- TAILQ_CONCAT(preferred, &agent, next);
- authctxt->agent_fd = agent_fd;
- }
- /* Prefer PKCS11 keys that are explicitly listed */
- TAILQ_FOREACH_SAFE(id, &files, next, tmp) {
- if (id->key == NULL || (id->key->flags & SSHKEY_FLAG_EXT) == 0)
- continue;
- found = 0;
- TAILQ_FOREACH(id2, &files, next) {
- if (id2->key == NULL ||
- (id2->key->flags & SSHKEY_FLAG_EXT) != 0)
- continue;
- if (sshkey_equal(id->key, id2->key)) {
- TAILQ_REMOVE(&files, id, next);
- TAILQ_INSERT_TAIL(preferred, id, next);
- found = 1;
- break;
- }
- }
- /* If IdentitiesOnly set and key not found then don't use it */
- if (!found && options.identities_only) {
- TAILQ_REMOVE(&files, id, next);
- freezero(id, sizeof(*id));
- }
- }
- /* append remaining keys from the config file */
- TAILQ_CONCAT(preferred, &files, next);
- /* finally, filter by PubkeyAcceptedAlgorithms */
- TAILQ_FOREACH_SAFE(id, preferred, next, id2) {
- if (id->key != NULL && !key_type_allowed_by_config(id->key)) {
- debug("Skipping %s key %s - "
- "corresponding algo not in PubkeyAcceptedAlgorithms",
- sshkey_ssh_name(id->key), id->filename);
- TAILQ_REMOVE(preferred, id, next);
- sshkey_free(id->key);
- free(id->filename);
- memset(id, 0, sizeof(*id));
- continue;
- }
- }
- /* List the keys we plan on using */
- TAILQ_FOREACH_SAFE(id, preferred, next, id2) {
- ident = format_identity(id);
- debug("Will attempt key: %s", ident);
- free(ident);
- }
- debug2("%s: done", __func__);
- }
- static void
- pubkey_cleanup(struct ssh *ssh)
- {
- Authctxt *authctxt = (Authctxt *)ssh->authctxt;
- Identity *id;
- if (authctxt->agent_fd != -1) {
- ssh_close_authentication_socket(authctxt->agent_fd);
- authctxt->agent_fd = -1;
- }
- for (id = TAILQ_FIRST(&authctxt->keys); id;
- id = TAILQ_FIRST(&authctxt->keys)) {
- TAILQ_REMOVE(&authctxt->keys, id, next);
- sshkey_free(id->key);
- free(id->filename);
- free(id);
- }
- }
- static void
- pubkey_reset(Authctxt *authctxt)
- {
- Identity *id;
- TAILQ_FOREACH(id, &authctxt->keys, next)
- id->tried = 0;
- }
- static int
- try_identity(struct ssh *ssh, Identity *id)
- {
- if (!id->key)
- return (0);
- if (sshkey_type_plain(id->key->type) == KEY_RSA &&
- (ssh->compat & SSH_BUG_RSASIGMD5) != 0) {
- debug("Skipped %s key %s for RSA/MD5 server",
- sshkey_type(id->key), id->filename);
- return (0);
- }
- return 1;
- }
- static int
- userauth_pubkey(struct ssh *ssh)
- {
- Authctxt *authctxt = (Authctxt *)ssh->authctxt;
- Identity *id;
- int sent = 0;
- char *ident;
- while ((id = TAILQ_FIRST(&authctxt->keys))) {
- if (id->tried++)
- return (0);
- /* move key to the end of the queue */
- TAILQ_REMOVE(&authctxt->keys, id, next);
- TAILQ_INSERT_TAIL(&authctxt->keys, id, next);
- /*
- * send a test message if we have the public key. for
- * encrypted keys we cannot do this and have to load the
- * private key instead
- */
- if (id->key != NULL) {
- if (try_identity(ssh, id)) {
- ident = format_identity(id);
- debug("Offering public key: %s", ident);
- free(ident);
- sent = send_pubkey_test(ssh, id);
- }
- } else {
- debug("Trying private key: %s", id->filename);
- id->key = load_identity_file(id);
- if (id->key != NULL) {
- if (try_identity(ssh, id)) {
- id->isprivate = 1;
- sent = sign_and_send_pubkey(ssh, id);
- }
- sshkey_free(id->key);
- id->key = NULL;
- id->isprivate = 0;
- }
- }
- if (sent)
- return (sent);
- }
- return (0);
- }
- /*
- * Send userauth request message specifying keyboard-interactive method.
- */
- static int
- userauth_kbdint(struct ssh *ssh)
- {
- Authctxt *authctxt = (Authctxt *)ssh->authctxt;
- int r;
- if (authctxt->attempt_kbdint++ >= options.number_of_password_prompts)
- return 0;
- /* disable if no SSH2_MSG_USERAUTH_INFO_REQUEST has been seen */
- if (authctxt->attempt_kbdint > 1 && !authctxt->info_req_seen) {
- debug3("userauth_kbdint: disable: no info_req_seen");
- ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_INFO_REQUEST, NULL);
- return 0;
- }
- debug2("userauth_kbdint");
- if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
- (r = sshpkt_put_cstring(ssh, "")) != 0 || /* lang */
- (r = sshpkt_put_cstring(ssh, options.kbd_interactive_devices ?
- options.kbd_interactive_devices : "")) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- fatal("%s: %s", __func__, ssh_err(r));
- ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req);
- return 1;
- }
- /*
- * parse INFO_REQUEST, prompt user and send INFO_RESPONSE
- */
- static int
- input_userauth_info_req(int type, u_int32_t seq, struct ssh *ssh)
- {
- Authctxt *authctxt = ssh->authctxt;
- char *name = NULL, *inst = NULL, *lang = NULL, *prompt = NULL;
- char *display_prompt = NULL, *response = NULL;
- u_char echo = 0;
- u_int num_prompts, i;
- int r;
- debug2("entering");
- if (authctxt == NULL)
- fatal("no authentication context");
- authctxt->info_req_seen = 1;
- if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0 ||
- (r = sshpkt_get_cstring(ssh, &inst, NULL)) != 0 ||
- (r = sshpkt_get_cstring(ssh, &lang, NULL)) != 0)
- goto out;
- if (strlen(name) > 0)
- logit("%s", name);
- if (strlen(inst) > 0)
- logit("%s", inst);
- if ((r = sshpkt_get_u32(ssh, &num_prompts)) != 0)
- goto out;
- /*
- * Begin to build info response packet based on prompts requested.
- * We commit to providing the correct number of responses, so if
- * further on we run into a problem that prevents this, we have to
- * be sure and clean this up and send a correct error response.
- */
- if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_INFO_RESPONSE)) != 0 ||
- (r = sshpkt_put_u32(ssh, num_prompts)) != 0)
- goto out;
- debug2("num_prompts %d", num_prompts);
- for (i = 0; i < num_prompts; i++) {
- if ((r = sshpkt_get_cstring(ssh, &prompt, NULL)) != 0 ||
- (r = sshpkt_get_u8(ssh, &echo)) != 0)
- goto out;
- if (asmprintf(&display_prompt, INT_MAX, NULL, "(%s@%s) %s",
- authctxt->server_user, options.host_key_alias ?
- options.host_key_alias : authctxt->host, prompt) == -1)
- fatal("asmprintf failed");
- response = read_passphrase(display_prompt, echo ? RP_ECHO : 0);
- if ((r = sshpkt_put_cstring(ssh, response)) != 0)
- goto out;
- freezero(response, strlen(response));
- free(prompt);
- free(display_prompt);
- display_prompt = response = prompt = NULL;
- }
- /* done with parsing incoming message. */
- if ((r = sshpkt_get_end(ssh)) != 0 ||
- (r = sshpkt_add_padding(ssh, 64)) != 0)
- goto out;
- r = sshpkt_send(ssh);
- out:
- if (response)
- freezero(response, strlen(response));
- free(prompt);
- free(display_prompt);
- free(name);
- free(inst);
- free(lang);
- return r;
- }
- static int
- ssh_keysign(struct ssh *ssh, struct sshkey *key, u_char **sigp, size_t *lenp,
- const u_char *data, size_t datalen)
- {
- struct sshbuf *b;
- struct stat st;
- pid_t pid;
- int r, to[2], from[2], status;
- int sock = ssh_packet_get_connection_in(ssh);
- u_char rversion = 0, version = 2;
- void (*osigchld)(int);
- *sigp = NULL;
- *lenp = 0;
- if (stat(_PATH_SSH_KEY_SIGN, &st) == -1) {
- error("%s: not installed: %s", __func__, strerror(errno));
- return -1;
- }
- if (fflush(stdout) != 0) {
- error("%s: fflush: %s", __func__, strerror(errno));
- return -1;
- }
- if (pipe(to) == -1) {
- error("%s: pipe: %s", __func__, strerror(errno));
- return -1;
- }
- if (pipe(from) == -1) {
- error("%s: pipe: %s", __func__, strerror(errno));
- return -1;
- }
- if ((pid = fork()) == -1) {
- error("%s: fork: %s", __func__, strerror(errno));
- return -1;
- }
- osigchld = ssh_signal(SIGCHLD, SIG_DFL);
- if (pid == 0) {
- close(from[0]);
- if (dup2(from[1], STDOUT_FILENO) == -1)
- fatal("%s: dup2: %s", __func__, strerror(errno));
- close(to[1]);
- if (dup2(to[0], STDIN_FILENO) == -1)
- fatal("%s: dup2: %s", __func__, strerror(errno));
- close(from[1]);
- close(to[0]);
- if (dup2(sock, STDERR_FILENO + 1) == -1)
- fatal("%s: dup2: %s", __func__, strerror(errno));
- sock = STDERR_FILENO + 1;
- fcntl(sock, F_SETFD, 0); /* keep the socket on exec */
- closefrom(sock + 1);
- debug3("%s: [child] pid=%ld, exec %s",
- __func__, (long)getpid(), _PATH_SSH_KEY_SIGN);
- execl(_PATH_SSH_KEY_SIGN, _PATH_SSH_KEY_SIGN, (char *)NULL);
- fatal("%s: exec(%s): %s", __func__, _PATH_SSH_KEY_SIGN,
- strerror(errno));
- }
- close(from[1]);
- close(to[0]);
- sock = STDERR_FILENO + 1;
- if ((b = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
- /* send # of sock, data to be signed */
- if ((r = sshbuf_put_u32(b, sock)) != 0 ||
- (r = sshbuf_put_string(b, data, datalen)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
- if (ssh_msg_send(to[1], version, b) == -1)
- fatal("%s: couldn't send request", __func__);
- sshbuf_reset(b);
- r = ssh_msg_recv(from[0], b);
- close(from[0]);
- close(to[1]);
- if (r < 0) {
- error("%s: no reply", __func__);
- goto fail;
- }
- errno = 0;
- while (waitpid(pid, &status, 0) == -1) {
- if (errno != EINTR) {
- error("%s: waitpid %ld: %s",
- __func__, (long)pid, strerror(errno));
- goto fail;
- }
- }
- if (!WIFEXITED(status)) {
- error("%s: exited abnormally", __func__);
- goto fail;
- }
- if (WEXITSTATUS(status) != 0) {
- error("%s: exited with status %d",
- __func__, WEXITSTATUS(status));
- goto fail;
- }
- if ((r = sshbuf_get_u8(b, &rversion)) != 0) {
- error("%s: buffer error: %s", __func__, ssh_err(r));
- goto fail;
- }
- if (rversion != version) {
- error("%s: bad version", __func__);
- goto fail;
- }
- if ((r = sshbuf_get_string(b, sigp, lenp)) != 0) {
- error("%s: buffer error: %s", __func__, ssh_err(r));
- fail:
- ssh_signal(SIGCHLD, osigchld);
- sshbuf_free(b);
- return -1;
- }
- ssh_signal(SIGCHLD, osigchld);
- sshbuf_free(b);
- return 0;
- }
- static int
- userauth_hostbased(struct ssh *ssh)
- {
- Authctxt *authctxt = (Authctxt *)ssh->authctxt;
- struct sshkey *private = NULL;
- struct sshbuf *b = NULL;
- u_char *sig = NULL, *keyblob = NULL;
- char *fp = NULL, *chost = NULL, *lname = NULL;
- size_t siglen = 0, keylen = 0;
- int i, r, success = 0;
- if (authctxt->ktypes == NULL) {
- authctxt->oktypes = xstrdup(options.hostbased_accepted_algos);
- authctxt->ktypes = authctxt->oktypes;
- }
- /*
- * Work through each listed type pattern in HostbasedAcceptedAlgorithms,
- * trying each hostkey that matches the type in turn.
- */
- for (;;) {
- if (authctxt->active_ktype == NULL)
- authctxt->active_ktype = strsep(&authctxt->ktypes, ",");
- if (authctxt->active_ktype == NULL ||
- *authctxt->active_ktype == '\0')
- break;
- debug3("%s: trying key type %s", __func__,
- authctxt->active_ktype);
- /* check for a useful key */
- private = NULL;
- for (i = 0; i < authctxt->sensitive->nkeys; i++) {
- if (authctxt->sensitive->keys[i] == NULL ||
- authctxt->sensitive->keys[i]->type == KEY_UNSPEC)
- continue;
- if (match_pattern_list(
- sshkey_ssh_name(authctxt->sensitive->keys[i]),
- authctxt->active_ktype, 0) != 1)
- continue;
- /* we take and free the key */
- private = authctxt->sensitive->keys[i];
- authctxt->sensitive->keys[i] = NULL;
- break;
- }
- /* Found one */
- if (private != NULL)
- break;
- /* No more keys of this type; advance */
- authctxt->active_ktype = NULL;
- }
- if (private == NULL) {
- free(authctxt->oktypes);
- authctxt->oktypes = authctxt->ktypes = NULL;
- authctxt->active_ktype = NULL;
- debug("No more client hostkeys for hostbased authentication.");
- goto out;
- }
- if ((fp = sshkey_fingerprint(private, options.fingerprint_hash,
- SSH_FP_DEFAULT)) == NULL) {
- error("%s: sshkey_fingerprint failed", __func__);
- goto out;
- }
- debug("%s: trying hostkey %s %s",
- __func__, sshkey_ssh_name(private), fp);
- /* figure out a name for the client host */
- lname = get_local_name(ssh_packet_get_connection_in(ssh));
- if (lname == NULL) {
- error("%s: cannot get local ipaddr/name", __func__);
- goto out;
- }
- /* XXX sshbuf_put_stringf? */
- xasprintf(&chost, "%s.", lname);
- debug2("%s: chost %s", __func__, chost);
- /* construct data */
- if ((b = sshbuf_new()) == NULL) {
- error("%s: sshbuf_new failed", __func__);
- goto out;
- }
- if ((r = sshkey_to_blob(private, &keyblob, &keylen)) != 0) {
- error("%s: sshkey_to_blob: %s", __func__, ssh_err(r));
- goto out;
- }
- if ((r = sshbuf_put_stringb(b, ssh->kex->session_id)) != 0 ||
- (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
- (r = sshbuf_put_cstring(b, authctxt->server_user)) != 0 ||
- (r = sshbuf_put_cstring(b, authctxt->service)) != 0 ||
- (r = sshbuf_put_cstring(b, authctxt->method->name)) != 0 ||
- (r = sshbuf_put_cstring(b, authctxt->active_ktype)) != 0 ||
- (r = sshbuf_put_string(b, keyblob, keylen)) != 0 ||
- (r = sshbuf_put_cstring(b, chost)) != 0 ||
- (r = sshbuf_put_cstring(b, authctxt->local_user)) != 0) {
- error("%s: buffer error: %s", __func__, ssh_err(r));
- goto out;
- }
- #ifdef DEBUG_PK
- sshbuf_dump(b, stderr);
- #endif
- if ((r = ssh_keysign(ssh, private, &sig, &siglen,
- sshbuf_ptr(b), sshbuf_len(b))) != 0) {
- error("sign using hostkey %s %s failed",
- sshkey_ssh_name(private), fp);
- goto out;
- }
- if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->active_ktype)) != 0 ||
- (r = sshpkt_put_string(ssh, keyblob, keylen)) != 0 ||
- (r = sshpkt_put_cstring(ssh, chost)) != 0 ||
- (r = sshpkt_put_cstring(ssh, authctxt->local_user)) != 0 ||
- (r = sshpkt_put_string(ssh, sig, siglen)) != 0 ||
- (r = sshpkt_send(ssh)) != 0) {
- error("%s: packet error: %s", __func__, ssh_err(r));
- goto out;
- }
- success = 1;
- out:
- if (sig != NULL)
- freezero(sig, siglen);
- free(keyblob);
- free(lname);
- free(fp);
- free(chost);
- sshkey_free(private);
- sshbuf_free(b);
- return success;
- }
- /* find auth method */
- /*
- * given auth method name, if configurable options permit this method fill
- * in auth_ident field and return true, otherwise return false.
- */
- static int
- authmethod_is_enabled(Authmethod *method)
- {
- if (method == NULL)
- return 0;
- /* return false if options indicate this method is disabled */
- if (method->enabled == NULL || *method->enabled == 0)
- return 0;
- /* return false if batch mode is enabled but method needs interactive mode */
- if (method->batch_flag != NULL && *method->batch_flag != 0)
- return 0;
- return 1;
- }
- static Authmethod *
- authmethod_lookup(const char *name)
- {
- Authmethod *method = NULL;
- if (name != NULL)
- for (method = authmethods; method->name != NULL; method++)
- if (strcmp(name, method->name) == 0)
- return method;
- debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
- return NULL;
- }
- /* XXX internal state */
- static Authmethod *current = NULL;
- static char *supported = NULL;
- static char *preferred = NULL;
- /*
- * Given the authentication method list sent by the server, return the
- * next method we should try. If the server initially sends a nil list,
- * use a built-in default list.
- */
- static Authmethod *
- authmethod_get(char *authlist)
- {
- char *name = NULL;
- u_int next;
- /* Use a suitable default if we're passed a nil list. */
- if (authlist == NULL || strlen(authlist) == 0)
- authlist = options.preferred_authentications;
- if (supported == NULL || strcmp(authlist, supported) != 0) {
- debug3("start over, passed a different list %s", authlist);
- free(supported);
- supported = xstrdup(authlist);
- preferred = options.preferred_authentications;
- debug3("preferred %s", preferred);
- current = NULL;
- } else if (current != NULL && authmethod_is_enabled(current))
- return current;
- for (;;) {
- if ((name = match_list(preferred, supported, &next)) == NULL) {
- debug("No more authentication methods to try.");
- current = NULL;
- return NULL;
- }
- preferred += next;
- debug3("authmethod_lookup %s", name);
- debug3("remaining preferred: %s", preferred);
- if ((current = authmethod_lookup(name)) != NULL &&
- authmethod_is_enabled(current)) {
- debug3("authmethod_is_enabled %s", name);
- debug("Next authentication method: %s", name);
- free(name);
- return current;
- }
- free(name);
- }
- }
- static char *
- authmethods_get(void)
- {
- Authmethod *method = NULL;
- struct sshbuf *b;
- char *list;
- int r;
- if ((b = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
- for (method = authmethods; method->name != NULL; method++) {
- if (authmethod_is_enabled(method)) {
- if ((r = sshbuf_putf(b, "%s%s",
- sshbuf_len(b) ? "," : "", method->name)) != 0)
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
- }
- }
- if ((list = sshbuf_dup_string(b)) == NULL)
- fatal("%s: sshbuf_dup_string failed", __func__);
- sshbuf_free(b);
- return list;
- }
|