auth2.c revision 92559
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 */ 2465674Skris 2560573Skris#include "includes.h" 2692559SdesRCSID("$OpenBSD: auth2.c,v 1.85 2002/02/24 19:14:59 markus Exp $"); 2765674SkrisRCSID("$FreeBSD: head/crypto/openssh/auth2.c 92559 2002-03-18 10:09:43Z des $"); 2860573Skris 2960573Skris#include <openssl/evp.h> 3060573Skris 3176262Sgreen#include "ssh2.h" 3260573Skris#include "xmalloc.h" 3360573Skris#include "rsa.h" 3476262Sgreen#include "sshpty.h" 3560573Skris#include "packet.h" 3660573Skris#include "buffer.h" 3776262Sgreen#include "log.h" 3860573Skris#include "servconf.h" 3960573Skris#include "compat.h" 4060573Skris#include "channels.h" 4160573Skris#include "bufaux.h" 4260573Skris#include "auth.h" 4360573Skris#include "session.h" 4460573Skris#include "dispatch.h" 4560573Skris#include "key.h" 4676262Sgreen#include "cipher.h" 4760573Skris#include "kex.h" 4876262Sgreen#include "pathnames.h" 4960573Skris#include "uidswap.h" 5065674Skris#include "auth-options.h" 5176262Sgreen#include "misc.h" 5276262Sgreen#include "hostfile.h" 5376262Sgreen#include "canohost.h" 5492559Sdes#include "match.h" 5560573Skris 5668704Sgreen#ifdef HAVE_LOGIN_CAP 5768704Sgreen#include <login_cap.h> 5868704Sgreen#endif /* HAVE_LOGIN_CAP */ 5968704Sgreen 6060573Skris/* import */ 6160573Skrisextern ServerOptions options; 6276262Sgreenextern u_char *session_id2; 6360573Skrisextern int session_id2_len; 6460573Skris 6569591Sgreenstatic Authctxt *x_authctxt = NULL; 6669591Sgreenstatic int one = 1; 6769591Sgreen 6869591Sgreentypedef struct Authmethod Authmethod; 6969591Sgreenstruct Authmethod { 7069591Sgreen char *name; 7169591Sgreen int (*userauth)(Authctxt *authctxt); 7269591Sgreen int *enabled; 7369591Sgreen}; 7469591Sgreen 7560573Skris/* protocol */ 7660573Skris 7792559Sdesstatic void input_service_request(int, u_int32_t, void *); 7892559Sdesstatic void input_userauth_request(int, u_int32_t, void *); 7960573Skris 8060573Skris/* helper */ 8192559Sdesstatic Authmethod *authmethod_lookup(const char *); 8292559Sdesstatic char *authmethods_get(void); 8392559Sdesstatic int user_key_allowed(struct passwd *, Key *); 8492559Sdesstatic int hostbased_key_allowed(struct passwd *, const char *, char *, Key *); 8560573Skris 8669591Sgreen/* auth */ 8792559Sdesstatic void userauth_banner(void); 8892559Sdesstatic int userauth_none(Authctxt *); 8992559Sdesstatic int userauth_passwd(Authctxt *); 9092559Sdesstatic int userauth_pubkey(Authctxt *); 9192559Sdesstatic int userauth_hostbased(Authctxt *); 9292559Sdesstatic int userauth_kbdint(Authctxt *); 9369591Sgreen 9469591SgreenAuthmethod authmethods[] = { 9569591Sgreen {"none", 9669591Sgreen userauth_none, 9769591Sgreen &one}, 9869591Sgreen {"publickey", 9969591Sgreen userauth_pubkey, 10076262Sgreen &options.pubkey_authentication}, 10176262Sgreen {"password", 10276262Sgreen userauth_passwd, 10376262Sgreen &options.password_authentication}, 10469591Sgreen {"keyboard-interactive", 10569591Sgreen userauth_kbdint, 10669591Sgreen &options.kbd_interactive_authentication}, 10776262Sgreen {"hostbased", 10876262Sgreen userauth_hostbased, 10976262Sgreen &options.hostbased_authentication}, 11069591Sgreen {NULL, NULL, NULL} 11160573Skris}; 11260573Skris 11360573Skris/* 11469591Sgreen * loop until authctxt->success == TRUE 11560573Skris */ 11660573Skris 11760573Skrisvoid 11892559Sdesdo_authentication2(void) 11960573Skris{ 12076262Sgreen Authctxt *authctxt = authctxt_new(); 12176262Sgreen 12269591Sgreen x_authctxt = authctxt; /*XXX*/ 12369591Sgreen 12492559Sdes /* challenge-response is implemented via keyboard interactive */ 12592559Sdes if (options.challenge_response_authentication) 12676262Sgreen options.kbd_interactive_authentication = 1; 12776262Sgreen 12892559Sdes dispatch_init(&dispatch_protocol_error); 12960573Skris dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); 13069591Sgreen dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt); 13176262Sgreen do_authenticated(authctxt); 13260573Skris} 13360573Skris 13492559Sdesstatic void 13592559Sdesinput_service_request(int type, u_int32_t seq, void *ctxt) 13660573Skris{ 13769591Sgreen Authctxt *authctxt = ctxt; 13876262Sgreen u_int len; 13960573Skris int accept = 0; 14060573Skris char *service = packet_get_string(&len); 14192559Sdes packet_check_eom(); 14260573Skris 14369591Sgreen if (authctxt == NULL) 14469591Sgreen fatal("input_service_request: no authctxt"); 14569591Sgreen 14660573Skris if (strcmp(service, "ssh-userauth") == 0) { 14769591Sgreen if (!authctxt->success) { 14860573Skris accept = 1; 14960573Skris /* now we can handle user-auth requests */ 15060573Skris dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); 15160573Skris } 15260573Skris } 15360573Skris /* XXX all other service requests are denied */ 15460573Skris 15560573Skris if (accept) { 15660573Skris packet_start(SSH2_MSG_SERVICE_ACCEPT); 15760573Skris packet_put_cstring(service); 15860573Skris packet_send(); 15960573Skris packet_write_wait(); 16060573Skris } else { 16160573Skris debug("bad service request %s", service); 16260573Skris packet_disconnect("bad service request %s", service); 16360573Skris } 16460573Skris xfree(service); 16560573Skris} 16660573Skris 16792559Sdesstatic void 16892559Sdesinput_userauth_request(int type, u_int32_t seq, void *ctxt) 16960573Skris{ 17069591Sgreen Authctxt *authctxt = ctxt; 17169591Sgreen Authmethod *m = NULL; 17276262Sgreen char *user, *service, *method, *style = NULL; 17360573Skris int authenticated = 0; 17468704Sgreen#ifdef HAVE_LOGIN_CAP 17568704Sgreen login_cap_t *lc; 17668704Sgreen#endif /* HAVE_LOGIN_CAP */ 17768704Sgreen#if defined(HAVE_LOGIN_CAP) || defined(LOGIN_ACCESS) 17868704Sgreen const char *from_host, *from_ip; 17960573Skris 18092559Sdes from_host = get_canonical_hostname(options.verify_reverse_mapping); 18168704Sgreen from_ip = get_remote_ipaddr(); 18268704Sgreen#endif /* HAVE_LOGIN_CAP || LOGIN_ACCESS */ 18368704Sgreen 18469591Sgreen if (authctxt == NULL) 18569591Sgreen fatal("input_userauth_request: no authctxt"); 18660573Skris 18769591Sgreen user = packet_get_string(NULL); 18869591Sgreen service = packet_get_string(NULL); 18969591Sgreen method = packet_get_string(NULL); 19060573Skris debug("userauth-request for user %s service %s method %s", user, service, method); 19176262Sgreen debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); 19260573Skris 19376262Sgreen if ((style = strchr(user, ':')) != NULL) 19476262Sgreen *style++ = 0; 19576262Sgreen 19676262Sgreen if (authctxt->attempt++ == 0) { 19769591Sgreen /* setup auth context */ 19869591Sgreen struct passwd *pw = NULL; 19969591Sgreen pw = getpwnam(user); 20069591Sgreen if (pw && allowed_user(pw) && strcmp(service, "ssh-connection")==0) { 20169591Sgreen authctxt->pw = pwcopy(pw); 20269591Sgreen authctxt->valid = 1; 20369591Sgreen debug2("input_userauth_request: setting up authctxt for %s", user); 20469591Sgreen#ifdef USE_PAM 20569591Sgreen start_pam(pw); 20669591Sgreen#endif 20769591Sgreen } else { 20869591Sgreen log("input_userauth_request: illegal user %s", user); 20974278Sgreen authctxt->pw = NULL; 21060573Skris } 21176262Sgreen setproctitle("%s", pw ? user : "unknown"); 21269591Sgreen authctxt->user = xstrdup(user); 21369591Sgreen authctxt->service = xstrdup(service); 21492559Sdes authctxt->style = style ? xstrdup(style) : NULL; 21592559Sdes } else if (strcmp(user, authctxt->user) != 0 || 21692559Sdes strcmp(service, authctxt->service) != 0) { 21792559Sdes packet_disconnect("Change of username or service not allowed: " 21892559Sdes "(%s,%s) -> (%s,%s)", 21992559Sdes authctxt->user, authctxt->service, user, service); 22060573Skris } 22160573Skris 22268704Sgreen#ifdef HAVE_LOGIN_CAP 22369591Sgreen if (authctxt->pw != NULL) { 22469591Sgreen lc = login_getpwclass(authctxt->pw); 22569591Sgreen if (lc == NULL) 22669591Sgreen lc = login_getclassbyname(NULL, authctxt->pw); 22769591Sgreen if (!auth_hostok(lc, from_host, from_ip)) { 22869591Sgreen log("Denied connection for %.200s from %.200s [%.200s].", 22969591Sgreen authctxt->pw->pw_name, from_host, from_ip); 23069591Sgreen packet_disconnect("Sorry, you are not allowed to connect."); 23169591Sgreen } 23269591Sgreen if (!auth_timeok(lc, time(NULL))) { 23369591Sgreen log("LOGIN %.200s REFUSED (TIME) FROM %.200s", 23469591Sgreen authctxt->pw->pw_name, from_host); 23569591Sgreen packet_disconnect("Logins not available right now."); 23669591Sgreen } 23769591Sgreen login_close(lc); 23869591Sgreen lc = NULL; 23968704Sgreen } 24068704Sgreen#endif /* HAVE_LOGIN_CAP */ 24168704Sgreen#ifdef LOGIN_ACCESS 24269591Sgreen if (authctxt->pw != NULL && 24369591Sgreen !login_access(authctxt->pw->pw_name, from_host)) { 24468704Sgreen log("Denied connection for %.200s from %.200s [%.200s].", 24569591Sgreen authctxt->pw->pw_name, from_host, from_ip); 24668704Sgreen packet_disconnect("Sorry, you are not allowed to connect."); 24768704Sgreen } 24868704Sgreen#endif /* LOGIN_ACCESS */ 24976262Sgreen /* reset state */ 25092559Sdes auth2_challenge_stop(authctxt); 25176262Sgreen authctxt->postponed = 0; 25268704Sgreen 25376262Sgreen /* try to authenticate user */ 25469591Sgreen m = authmethod_lookup(method); 25569591Sgreen if (m != NULL) { 25669591Sgreen debug2("input_userauth_request: try method %s", method); 25769591Sgreen authenticated = m->userauth(authctxt); 25869591Sgreen } 25969591Sgreen#ifdef USE_PAM 26069591Sgreen if (authenticated && authctxt->user && !do_pam_account(authctxt->user, NULL)) 26169591Sgreen authenticated = 0; 26269591Sgreen#endif /* USE_PAM */ 26376262Sgreen userauth_finish(authctxt, authenticated, method); 26469591Sgreen 26569591Sgreen xfree(service); 26669591Sgreen xfree(user); 26769591Sgreen xfree(method); 26869591Sgreen} 26969591Sgreen 27069591Sgreenvoid 27176262Sgreenuserauth_finish(Authctxt *authctxt, int authenticated, char *method) 27269591Sgreen{ 27392559Sdes char *methods; 27492559Sdes 27576262Sgreen if (!authctxt->valid && authenticated) 27676262Sgreen fatal("INTERNAL ERROR: authenticated invalid user %s", 27776262Sgreen authctxt->user); 27869591Sgreen 27976262Sgreen /* Special handling for root */ 28076262Sgreen if (authenticated && authctxt->pw->pw_uid == 0 && 28176262Sgreen !auth_root_allowed(method)) 28276262Sgreen authenticated = 0; 28360573Skris 28476262Sgreen /* Log before sending the reply */ 28576262Sgreen auth_log(authctxt, authenticated, method, " ssh2"); 28669591Sgreen 28792559Sdes if (authctxt->postponed) 28892559Sdes return; 28992559Sdes 29092559Sdes /* XXX todo: check if multiple auth methods are needed */ 29192559Sdes if (authenticated == 1) { 29292559Sdes /* turn off userauth */ 29392559Sdes dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); 29492559Sdes packet_start(SSH2_MSG_USERAUTH_SUCCESS); 29592559Sdes packet_send(); 29692559Sdes packet_write_wait(); 29792559Sdes /* now we can break out */ 29892559Sdes authctxt->success = 1; 29992559Sdes } else { 30092559Sdes if (authctxt->failures++ > AUTH_FAIL_MAX) 30192559Sdes packet_disconnect(AUTH_FAIL_MSG, authctxt->user); 30292559Sdes methods = authmethods_get(); 30392559Sdes packet_start(SSH2_MSG_USERAUTH_FAILURE); 30492559Sdes packet_put_cstring(methods); 30592559Sdes packet_put_char(0); /* XXX partial success, unused */ 30692559Sdes packet_send(); 30792559Sdes packet_write_wait(); 30892559Sdes xfree(methods); 30992559Sdes } 31076262Sgreen} 31169591Sgreen 31292559Sdesstatic void 31376262Sgreenuserauth_banner(void) 31476262Sgreen{ 31576262Sgreen struct stat st; 31676262Sgreen char *banner = NULL; 31776262Sgreen off_t len, n; 31876262Sgreen int fd; 31976262Sgreen 32076262Sgreen if (options.banner == NULL || (datafellows & SSH_BUG_BANNER)) 32176262Sgreen return; 32276262Sgreen if ((fd = open(options.banner, O_RDONLY)) < 0) 32376262Sgreen return; 32476262Sgreen if (fstat(fd, &st) < 0) 32576262Sgreen goto done; 32676262Sgreen len = st.st_size; 32776262Sgreen banner = xmalloc(len + 1); 32876262Sgreen if ((n = read(fd, banner, len)) < 0) 32976262Sgreen goto done; 33076262Sgreen banner[n] = '\0'; 33176262Sgreen packet_start(SSH2_MSG_USERAUTH_BANNER); 33276262Sgreen packet_put_cstring(banner); 33376262Sgreen packet_put_cstring(""); /* language, unused */ 33476262Sgreen packet_send(); 33576262Sgreen debug("userauth_banner: sent"); 33676262Sgreendone: 33776262Sgreen if (banner) 33876262Sgreen xfree(banner); 33976262Sgreen close(fd); 34076262Sgreen return; 34169591Sgreen} 34260573Skris 34392559Sdesstatic int 34469591Sgreenuserauth_none(Authctxt *authctxt) 34560573Skris{ 34669591Sgreen /* disable method "none", only allowed one time */ 34769591Sgreen Authmethod *m = authmethod_lookup("none"); 34869591Sgreen if (m != NULL) 34969591Sgreen m->enabled = NULL; 35092559Sdes packet_check_eom(); 35176262Sgreen userauth_banner(); 35269591Sgreen#ifdef USE_PAM 35376262Sgreen return authctxt->valid ? auth_pam_password(authctxt, "") : 0; 35469591Sgreen#else /* !USE_PAM */ 35576262Sgreen return authctxt->valid ? auth_password(authctxt, "") : 0; 35669591Sgreen#endif /* USE_PAM */ 35760573Skris} 35869591Sgreen 35992559Sdesstatic int 36069591Sgreenuserauth_passwd(Authctxt *authctxt) 36160573Skris{ 36260573Skris char *password; 36360573Skris int authenticated = 0; 36460573Skris int change; 36576262Sgreen u_int len; 36660573Skris change = packet_get_char(); 36760573Skris if (change) 36860573Skris log("password change not supported"); 36960573Skris password = packet_get_string(&len); 37092559Sdes packet_check_eom(); 37169591Sgreen if (authctxt->valid && 37269591Sgreen#ifdef USE_PAM 37392402Sdes auth_password(authctxt, password) == 1) 37469591Sgreen#else 37592402Sdes auth_password(authctxt, password) == 1) 37669591Sgreen#endif 37760573Skris authenticated = 1; 37860573Skris memset(password, 0, len); 37960573Skris xfree(password); 38060573Skris return authenticated; 38160573Skris} 38269591Sgreen 38392559Sdesstatic int 38469591Sgreenuserauth_kbdint(Authctxt *authctxt) 38560573Skris{ 38669591Sgreen int authenticated = 0; 38792559Sdes char *lang, *devs; 38869591Sgreen 38969591Sgreen lang = packet_get_string(NULL); 39069591Sgreen devs = packet_get_string(NULL); 39192559Sdes packet_check_eom(); 39269591Sgreen 39392559Sdes debug("keyboard-interactive devs %s", devs); 39476262Sgreen 39592559Sdes if (options.challenge_response_authentication) 39676262Sgreen authenticated = auth2_challenge(authctxt, devs); 39776262Sgreen 39892559Sdes xfree(devs); 39969591Sgreen xfree(lang); 40069591Sgreen return authenticated; 40169591Sgreen} 40269591Sgreen 40392559Sdesstatic int 40469591Sgreenuserauth_pubkey(Authctxt *authctxt) 40569591Sgreen{ 40660573Skris Buffer b; 40792559Sdes Key *key = NULL; 40892559Sdes char *pkalg; 40992559Sdes u_char *pkblob, *sig; 41076262Sgreen u_int alen, blen, slen; 41176262Sgreen int have_sig, pktype; 41260573Skris int authenticated = 0; 41360573Skris 41469591Sgreen if (!authctxt->valid) { 41569591Sgreen debug2("userauth_pubkey: disabled because of invalid user"); 41660573Skris return 0; 41760573Skris } 41860573Skris have_sig = packet_get_char(); 41976262Sgreen if (datafellows & SSH_BUG_PKAUTH) { 42076262Sgreen debug2("userauth_pubkey: SSH_BUG_PKAUTH"); 42176262Sgreen /* no explicit pkalg given */ 42276262Sgreen pkblob = packet_get_string(&blen); 42376262Sgreen buffer_init(&b); 42476262Sgreen buffer_append(&b, pkblob, blen); 42576262Sgreen /* so we have to extract the pkalg from the pkblob */ 42676262Sgreen pkalg = buffer_get_string(&b, &alen); 42776262Sgreen buffer_free(&b); 42876262Sgreen } else { 42976262Sgreen pkalg = packet_get_string(&alen); 43076262Sgreen pkblob = packet_get_string(&blen); 43176262Sgreen } 43276262Sgreen pktype = key_type_from_name(pkalg); 43376262Sgreen if (pktype == KEY_UNSPEC) { 43476262Sgreen /* this is perfectly legal */ 43592559Sdes log("userauth_pubkey: unsupported public key algorithm: %s", 43692559Sdes pkalg); 43792559Sdes goto done; 43860573Skris } 43976262Sgreen key = key_from_blob(pkblob, blen); 44092559Sdes if (key == NULL) { 44192559Sdes error("userauth_pubkey: cannot decode key: %s", pkalg); 44292559Sdes goto done; 44392559Sdes } 44492559Sdes if (key->type != pktype) { 44592559Sdes error("userauth_pubkey: type mismatch for decoded key " 44692559Sdes "(received %d, expected %d)", key->type, pktype); 44792559Sdes goto done; 44892559Sdes } 44992559Sdes if (have_sig) { 45092559Sdes sig = packet_get_string(&slen); 45192559Sdes packet_check_eom(); 45292559Sdes buffer_init(&b); 45392559Sdes if (datafellows & SSH_OLD_SESSIONID) { 45492559Sdes buffer_append(&b, session_id2, session_id2_len); 45592559Sdes } else { 45692559Sdes buffer_put_string(&b, session_id2, session_id2_len); 45792559Sdes } 45892559Sdes /* reconstruct packet */ 45992559Sdes buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); 46092559Sdes buffer_put_cstring(&b, authctxt->user); 46192559Sdes buffer_put_cstring(&b, 46292559Sdes datafellows & SSH_BUG_PKSERVICE ? 46392559Sdes "ssh-userauth" : 46492559Sdes authctxt->service); 46592559Sdes if (datafellows & SSH_BUG_PKAUTH) { 46692559Sdes buffer_put_char(&b, have_sig); 46792559Sdes } else { 46892559Sdes buffer_put_cstring(&b, "publickey"); 46992559Sdes buffer_put_char(&b, have_sig); 47092559Sdes buffer_put_cstring(&b, pkalg); 47192559Sdes } 47292559Sdes buffer_put_string(&b, pkblob, blen); 47376262Sgreen#ifdef DEBUG_PK 47492559Sdes buffer_dump(&b); 47560573Skris#endif 47692559Sdes /* test for correct signature */ 47792559Sdes if (user_key_allowed(authctxt->pw, key) && 47892559Sdes key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1) 47992559Sdes authenticated = 1; 48092559Sdes buffer_clear(&b); 48192559Sdes xfree(sig); 48292559Sdes } else { 48392559Sdes debug("test whether pkalg/pkblob are acceptable"); 48492559Sdes packet_check_eom(); 48569591Sgreen 48692559Sdes /* XXX fake reply and always send PK_OK ? */ 48792559Sdes /* 48892559Sdes * XXX this allows testing whether a user is allowed 48992559Sdes * to login: if you happen to have a valid pubkey this 49092559Sdes * message is sent. the message is NEVER sent at all 49192559Sdes * if a user is not allowed to login. is this an 49292559Sdes * issue? -markus 49392559Sdes */ 49492559Sdes if (user_key_allowed(authctxt->pw, key)) { 49592559Sdes packet_start(SSH2_MSG_USERAUTH_PK_OK); 49692559Sdes packet_put_string(pkalg, alen); 49792559Sdes packet_put_string(pkblob, blen); 49892559Sdes packet_send(); 49992559Sdes packet_write_wait(); 50092559Sdes authctxt->postponed = 1; 50160573Skris } 50260573Skris } 50392559Sdes if (authenticated != 1) 50492559Sdes auth_clear_options(); 50592559Sdesdone: 50676262Sgreen debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg); 50792559Sdes if (key != NULL) 50892559Sdes key_free(key); 50960573Skris xfree(pkalg); 51060573Skris xfree(pkblob); 51160573Skris return authenticated; 51260573Skris} 51360573Skris 51492559Sdesstatic int 51576262Sgreenuserauth_hostbased(Authctxt *authctxt) 51676262Sgreen{ 51776262Sgreen Buffer b; 51892559Sdes Key *key = NULL; 51992559Sdes char *pkalg, *cuser, *chost, *service; 52092559Sdes u_char *pkblob, *sig; 52176262Sgreen u_int alen, blen, slen; 52276262Sgreen int pktype; 52376262Sgreen int authenticated = 0; 52476262Sgreen 52576262Sgreen if (!authctxt->valid) { 52676262Sgreen debug2("userauth_hostbased: disabled because of invalid user"); 52776262Sgreen return 0; 52876262Sgreen } 52976262Sgreen pkalg = packet_get_string(&alen); 53076262Sgreen pkblob = packet_get_string(&blen); 53176262Sgreen chost = packet_get_string(NULL); 53276262Sgreen cuser = packet_get_string(NULL); 53376262Sgreen sig = packet_get_string(&slen); 53476262Sgreen 53576262Sgreen debug("userauth_hostbased: cuser %s chost %s pkalg %s slen %d", 53676262Sgreen cuser, chost, pkalg, slen); 53776262Sgreen#ifdef DEBUG_PK 53876262Sgreen debug("signature:"); 53976262Sgreen buffer_init(&b); 54076262Sgreen buffer_append(&b, sig, slen); 54176262Sgreen buffer_dump(&b); 54276262Sgreen buffer_free(&b); 54376262Sgreen#endif 54476262Sgreen pktype = key_type_from_name(pkalg); 54576262Sgreen if (pktype == KEY_UNSPEC) { 54676262Sgreen /* this is perfectly legal */ 54776262Sgreen log("userauth_hostbased: unsupported " 54876262Sgreen "public key algorithm: %s", pkalg); 54976262Sgreen goto done; 55076262Sgreen } 55176262Sgreen key = key_from_blob(pkblob, blen); 55276262Sgreen if (key == NULL) { 55392559Sdes error("userauth_hostbased: cannot decode key: %s", pkalg); 55476262Sgreen goto done; 55576262Sgreen } 55692559Sdes if (key->type != pktype) { 55792559Sdes error("userauth_hostbased: type mismatch for decoded key " 55892559Sdes "(received %d, expected %d)", key->type, pktype); 55992559Sdes goto done; 56092559Sdes } 56176262Sgreen service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" : 56276262Sgreen authctxt->service; 56376262Sgreen buffer_init(&b); 56476262Sgreen buffer_put_string(&b, session_id2, session_id2_len); 56576262Sgreen /* reconstruct packet */ 56676262Sgreen buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); 56776262Sgreen buffer_put_cstring(&b, authctxt->user); 56876262Sgreen buffer_put_cstring(&b, service); 56976262Sgreen buffer_put_cstring(&b, "hostbased"); 57076262Sgreen buffer_put_string(&b, pkalg, alen); 57176262Sgreen buffer_put_string(&b, pkblob, blen); 57276262Sgreen buffer_put_cstring(&b, chost); 57376262Sgreen buffer_put_cstring(&b, cuser); 57476262Sgreen#ifdef DEBUG_PK 57576262Sgreen buffer_dump(&b); 57676262Sgreen#endif 57776262Sgreen /* test for allowed key and correct signature */ 57876262Sgreen if (hostbased_key_allowed(authctxt->pw, cuser, chost, key) && 57976262Sgreen key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1) 58076262Sgreen authenticated = 1; 58176262Sgreen 58276262Sgreen buffer_clear(&b); 58376262Sgreendone: 58476262Sgreen debug2("userauth_hostbased: authenticated %d", authenticated); 58592559Sdes if (key != NULL) 58692559Sdes key_free(key); 58776262Sgreen xfree(pkalg); 58876262Sgreen xfree(pkblob); 58976262Sgreen xfree(cuser); 59076262Sgreen xfree(chost); 59176262Sgreen xfree(sig); 59276262Sgreen return authenticated; 59376262Sgreen} 59476262Sgreen 59569591Sgreen/* get current user */ 59660573Skris 59760573Skrisstruct passwd* 59860573Skrisauth_get_user(void) 59960573Skris{ 60069591Sgreen return (x_authctxt != NULL && x_authctxt->valid) ? x_authctxt->pw : NULL; 60160573Skris} 60260573Skris 60369591Sgreen#define DELIM "," 60469591Sgreen 60592559Sdesstatic char * 60669591Sgreenauthmethods_get(void) 60760573Skris{ 60869591Sgreen Authmethod *method = NULL; 60992559Sdes Buffer b; 61069591Sgreen char *list; 61160573Skris 61292559Sdes buffer_init(&b); 61369591Sgreen for (method = authmethods; method->name != NULL; method++) { 61469591Sgreen if (strcmp(method->name, "none") == 0) 61569591Sgreen continue; 61669591Sgreen if (method->enabled != NULL && *(method->enabled) != 0) { 61792559Sdes if (buffer_len(&b) > 0) 61892559Sdes buffer_append(&b, ",", 1); 61992559Sdes buffer_append(&b, method->name, strlen(method->name)); 62060573Skris } 62169591Sgreen } 62292559Sdes buffer_append(&b, "\0", 1); 62392559Sdes list = xstrdup(buffer_ptr(&b)); 62492559Sdes buffer_free(&b); 62569591Sgreen return list; 62660573Skris} 62760573Skris 62892559Sdesstatic Authmethod * 62969591Sgreenauthmethod_lookup(const char *name) 63069591Sgreen{ 63169591Sgreen Authmethod *method = NULL; 63269591Sgreen if (name != NULL) 63369591Sgreen for (method = authmethods; method->name != NULL; method++) 63469591Sgreen if (method->enabled != NULL && 63569591Sgreen *(method->enabled) != 0 && 63669591Sgreen strcmp(name, method->name) == 0) 63769591Sgreen return method; 63869591Sgreen debug2("Unrecognized authentication method name: %s", name ? name : "NULL"); 63969591Sgreen return NULL; 64069591Sgreen} 64169591Sgreen 64260573Skris/* return 1 if user allows given key */ 64392559Sdesstatic int 64492559Sdesuser_key_allowed2(struct passwd *pw, Key *key, char *file) 64560573Skris{ 64692559Sdes char line[8192]; 64760573Skris int found_key = 0; 64860573Skris FILE *f; 64976262Sgreen u_long linenum = 0; 65060573Skris struct stat st; 65160573Skris Key *found; 65292559Sdes char *fp; 65360573Skris 65469591Sgreen if (pw == NULL) 65569591Sgreen return 0; 65669591Sgreen 65760573Skris /* Temporarily use the user's uid. */ 65876262Sgreen temporarily_use_uid(pw); 65960573Skris 66092559Sdes debug("trying public key file %s", file); 66160573Skris 66260573Skris /* Fail quietly if file does not exist */ 66360573Skris if (stat(file, &st) < 0) { 66460573Skris /* Restore the privileged uid. */ 66560573Skris restore_uid(); 66660573Skris return 0; 66760573Skris } 66860573Skris /* Open the file containing the authorized keys. */ 66960573Skris f = fopen(file, "r"); 67060573Skris if (!f) { 67160573Skris /* Restore the privileged uid. */ 67260573Skris restore_uid(); 67360573Skris return 0; 67460573Skris } 67592559Sdes if (options.strict_modes && 67692559Sdes secure_filename(f, file, pw, line, sizeof(line)) != 0) { 67792559Sdes fclose(f); 67892559Sdes log("Authentication refused: %s", line); 67992559Sdes restore_uid(); 68092559Sdes return 0; 68160573Skris } 68292559Sdes 68360573Skris found_key = 0; 68469591Sgreen found = key_new(key->type); 68560573Skris 68660573Skris while (fgets(line, sizeof(line), f)) { 68765674Skris char *cp, *options = NULL; 68860573Skris linenum++; 68960573Skris /* Skip leading whitespace, empty and comment lines. */ 69060573Skris for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 69160573Skris ; 69260573Skris if (!*cp || *cp == '\n' || *cp == '#') 69360573Skris continue; 69465674Skris 69592559Sdes if (key_read(found, &cp) != 1) { 69665674Skris /* no key? check if there are options for this key */ 69765674Skris int quoted = 0; 69876262Sgreen debug2("user_key_allowed: check options: '%s'", cp); 69965674Skris options = cp; 70065674Skris for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { 70165674Skris if (*cp == '\\' && cp[1] == '"') 70265674Skris cp++; /* Skip both */ 70365674Skris else if (*cp == '"') 70465674Skris quoted = !quoted; 70565674Skris } 70665674Skris /* Skip remaining whitespace. */ 70765674Skris for (; *cp == ' ' || *cp == '\t'; cp++) 70865674Skris ; 70992559Sdes if (key_read(found, &cp) != 1) { 71076262Sgreen debug2("user_key_allowed: advance: '%s'", cp); 71165674Skris /* still no key? advance to next line*/ 71265674Skris continue; 71365674Skris } 71465674Skris } 71565674Skris if (key_equal(found, key) && 71676262Sgreen auth_parse_options(pw, options, file, linenum) == 1) { 71760573Skris found_key = 1; 71892559Sdes debug("matching key found: file %s, line %lu", 71960573Skris file, linenum); 72092559Sdes fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); 72192559Sdes verbose("Found matching %s key: %s", 72292559Sdes key_type(found), fp); 72392559Sdes xfree(fp); 72460573Skris break; 72560573Skris } 72660573Skris } 72760573Skris restore_uid(); 72860573Skris fclose(f); 72960573Skris key_free(found); 73076262Sgreen if (!found_key) 73176262Sgreen debug2("key not found"); 73260573Skris return found_key; 73360573Skris} 73469591Sgreen 73592559Sdes/* check whether given key is in .ssh/authorized_keys* */ 73692559Sdesstatic int 73792559Sdesuser_key_allowed(struct passwd *pw, Key *key) 73892559Sdes{ 73992559Sdes int success; 74092559Sdes char *file; 74192559Sdes 74292559Sdes file = authorized_keys_file(pw); 74392559Sdes success = user_key_allowed2(pw, key, file); 74492559Sdes xfree(file); 74592559Sdes if (success) 74692559Sdes return success; 74792559Sdes 74892559Sdes /* try suffix "2" for backward compat, too */ 74992559Sdes file = authorized_keys_file2(pw); 75092559Sdes success = user_key_allowed2(pw, key, file); 75192559Sdes xfree(file); 75292559Sdes return success; 75392559Sdes} 75492559Sdes 75576262Sgreen/* return 1 if given hostkey is allowed */ 75692559Sdesstatic int 75776262Sgreenhostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, 75876262Sgreen Key *key) 75969591Sgreen{ 76076262Sgreen const char *resolvedname, *ipaddr, *lookup; 76192559Sdes HostStatus host_status; 76292559Sdes int len; 76376262Sgreen 76492559Sdes resolvedname = get_canonical_hostname(options.verify_reverse_mapping); 76576262Sgreen ipaddr = get_remote_ipaddr(); 76676262Sgreen 76776262Sgreen debug2("userauth_hostbased: chost %s resolvedname %s ipaddr %s", 76876262Sgreen chost, resolvedname, ipaddr); 76976262Sgreen 77076262Sgreen if (options.hostbased_uses_name_from_packet_only) { 77176262Sgreen if (auth_rhosts2(pw, cuser, chost, chost) == 0) 77276262Sgreen return 0; 77376262Sgreen lookup = chost; 77476262Sgreen } else { 77576262Sgreen if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') { 77676262Sgreen debug2("stripping trailing dot from chost %s", chost); 77776262Sgreen chost[len - 1] = '\0'; 77876262Sgreen } 77976262Sgreen if (strcasecmp(resolvedname, chost) != 0) 78076262Sgreen log("userauth_hostbased mismatch: " 78176262Sgreen "client sends %s, but we resolve %s to %s", 78276262Sgreen chost, ipaddr, resolvedname); 78376262Sgreen if (auth_rhosts2(pw, cuser, resolvedname, ipaddr) == 0) 78476262Sgreen return 0; 78576262Sgreen lookup = resolvedname; 78676262Sgreen } 78776262Sgreen debug2("userauth_hostbased: access allowed by auth_rhosts2"); 78876262Sgreen 78992559Sdes host_status = check_key_in_hostfiles(pw, key, lookup, 79092559Sdes _PATH_SSH_SYSTEM_HOSTFILE, 79192559Sdes options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE); 79276262Sgreen 79392559Sdes /* backward compat if no key has been found. */ 79492559Sdes if (host_status == HOST_NEW) 79592559Sdes host_status = check_key_in_hostfiles(pw, key, lookup, 79692559Sdes _PATH_SSH_SYSTEM_HOSTFILE2, 79792559Sdes options.ignore_user_known_hosts ? NULL : 79892559Sdes _PATH_SSH_USER_HOSTFILE2); 79976262Sgreen 80076262Sgreen return (host_status == HOST_OK); 80169591Sgreen} 802