10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * Copyright (c) 2000 Markus Friedl. All rights reserved.
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without
50Sstevel@tonic-gate * modification, are permitted provided that the following conditions
60Sstevel@tonic-gate * are met:
70Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright
80Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer.
90Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright
100Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the
110Sstevel@tonic-gate * documentation and/or other materials provided with the distribution.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
140Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
150Sstevel@tonic-gate * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
160Sstevel@tonic-gate * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
170Sstevel@tonic-gate * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
180Sstevel@tonic-gate * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
190Sstevel@tonic-gate * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
200Sstevel@tonic-gate * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
210Sstevel@tonic-gate * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
220Sstevel@tonic-gate * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
230Sstevel@tonic-gate */
240Sstevel@tonic-gate /*
253908Sjp161948 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
260Sstevel@tonic-gate * Use is subject to license terms.
270Sstevel@tonic-gate */
280Sstevel@tonic-gate
290Sstevel@tonic-gate #include "includes.h"
300Sstevel@tonic-gate RCSID("$OpenBSD: auth2-hostbased.c,v 1.2 2002/05/31 11:35:15 markus Exp $");
310Sstevel@tonic-gate
320Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
330Sstevel@tonic-gate
340Sstevel@tonic-gate #include "ssh2.h"
350Sstevel@tonic-gate #include "xmalloc.h"
360Sstevel@tonic-gate #include "packet.h"
370Sstevel@tonic-gate #include "buffer.h"
380Sstevel@tonic-gate #include "log.h"
390Sstevel@tonic-gate #include "servconf.h"
400Sstevel@tonic-gate #include "compat.h"
410Sstevel@tonic-gate #include "bufaux.h"
420Sstevel@tonic-gate #include "auth.h"
430Sstevel@tonic-gate
440Sstevel@tonic-gate #ifdef USE_PAM
450Sstevel@tonic-gate #include "auth-pam.h"
460Sstevel@tonic-gate #endif /* USE_PAM */
470Sstevel@tonic-gate
480Sstevel@tonic-gate #include "key.h"
490Sstevel@tonic-gate #include "canohost.h"
500Sstevel@tonic-gate #include "pathnames.h"
510Sstevel@tonic-gate
520Sstevel@tonic-gate /* import */
530Sstevel@tonic-gate extern ServerOptions options;
540Sstevel@tonic-gate extern u_char *session_id2;
550Sstevel@tonic-gate extern int session_id2_len;
560Sstevel@tonic-gate
570Sstevel@tonic-gate static void
userauth_hostbased(Authctxt * authctxt)580Sstevel@tonic-gate userauth_hostbased(Authctxt *authctxt)
590Sstevel@tonic-gate {
600Sstevel@tonic-gate Buffer b;
610Sstevel@tonic-gate Key *key = NULL;
620Sstevel@tonic-gate char *pkalg, *cuser, *chost, *service;
630Sstevel@tonic-gate u_char *pkblob, *sig;
640Sstevel@tonic-gate u_int alen, blen, slen;
650Sstevel@tonic-gate int pktype;
660Sstevel@tonic-gate int authenticated = 0;
670Sstevel@tonic-gate
680Sstevel@tonic-gate if (!authctxt || !authctxt->method)
690Sstevel@tonic-gate fatal("%s: missing context", __func__);
700Sstevel@tonic-gate
710Sstevel@tonic-gate pkalg = packet_get_string(&alen);
720Sstevel@tonic-gate pkblob = packet_get_string(&blen);
730Sstevel@tonic-gate chost = packet_get_string(NULL);
740Sstevel@tonic-gate cuser = packet_get_string(NULL);
750Sstevel@tonic-gate sig = packet_get_string(&slen);
760Sstevel@tonic-gate
770Sstevel@tonic-gate debug("userauth_hostbased: cuser %s chost %s pkalg %s slen %d",
780Sstevel@tonic-gate cuser, chost, pkalg, slen);
790Sstevel@tonic-gate #ifdef DEBUG_PK
800Sstevel@tonic-gate debug("signature:");
810Sstevel@tonic-gate buffer_init(&b);
820Sstevel@tonic-gate buffer_append(&b, sig, slen);
830Sstevel@tonic-gate buffer_dump(&b);
840Sstevel@tonic-gate buffer_free(&b);
850Sstevel@tonic-gate #endif
860Sstevel@tonic-gate pktype = key_type_from_name(pkalg);
870Sstevel@tonic-gate if (pktype == KEY_UNSPEC) {
880Sstevel@tonic-gate /* this is perfectly legal */
890Sstevel@tonic-gate log("userauth_hostbased: unsupported "
900Sstevel@tonic-gate "public key algorithm: %s", pkalg);
910Sstevel@tonic-gate goto done;
920Sstevel@tonic-gate }
930Sstevel@tonic-gate key = key_from_blob(pkblob, blen);
940Sstevel@tonic-gate if (key == NULL) {
950Sstevel@tonic-gate error("userauth_hostbased: cannot decode key: %s", pkalg);
960Sstevel@tonic-gate goto done;
970Sstevel@tonic-gate }
980Sstevel@tonic-gate if (key->type != pktype) {
990Sstevel@tonic-gate error("userauth_hostbased: type mismatch for decoded key "
1000Sstevel@tonic-gate "(received %d, expected %d)", key->type, pktype);
1010Sstevel@tonic-gate goto done;
1020Sstevel@tonic-gate }
1030Sstevel@tonic-gate service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" :
1040Sstevel@tonic-gate authctxt->service;
1050Sstevel@tonic-gate buffer_init(&b);
1060Sstevel@tonic-gate buffer_put_string(&b, session_id2, session_id2_len);
1070Sstevel@tonic-gate /* reconstruct packet */
1080Sstevel@tonic-gate buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
1090Sstevel@tonic-gate buffer_put_cstring(&b, authctxt->user);
1100Sstevel@tonic-gate buffer_put_cstring(&b, service);
1110Sstevel@tonic-gate buffer_put_cstring(&b, "hostbased");
1120Sstevel@tonic-gate buffer_put_string(&b, pkalg, alen);
1130Sstevel@tonic-gate buffer_put_string(&b, pkblob, blen);
1140Sstevel@tonic-gate buffer_put_cstring(&b, chost);
1150Sstevel@tonic-gate buffer_put_cstring(&b, cuser);
1160Sstevel@tonic-gate #ifdef DEBUG_PK
1170Sstevel@tonic-gate buffer_dump(&b);
1180Sstevel@tonic-gate #endif
1190Sstevel@tonic-gate /* test for allowed key and correct signature */
1200Sstevel@tonic-gate authenticated = 0;
121*5562Sjp161948 if (hostbased_key_allowed(authctxt->pw, cuser, chost, key) &&
122*5562Sjp161948 key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
1230Sstevel@tonic-gate authenticated = 1;
1240Sstevel@tonic-gate
1250Sstevel@tonic-gate buffer_clear(&b);
1260Sstevel@tonic-gate done:
1270Sstevel@tonic-gate /*
1280Sstevel@tonic-gate * XXX TODO: Add config options for specifying users for whom
1290Sstevel@tonic-gate * this userauth is insufficient and what userauths
1300Sstevel@tonic-gate * may continue.
1310Sstevel@tonic-gate *
1320Sstevel@tonic-gate * NOTE: do_pam_non_initial_userauth() does this for
1330Sstevel@tonic-gate * users with expired passwords.
1340Sstevel@tonic-gate */
1350Sstevel@tonic-gate #ifdef USE_PAM
1360Sstevel@tonic-gate if (authenticated) {
1373908Sjp161948 authctxt->cuser = cuser;
1380Sstevel@tonic-gate if (!do_pam_non_initial_userauth(authctxt))
1390Sstevel@tonic-gate authenticated = 0;
1403908Sjp161948 /* Make sure nobody else will use this pointer since we are
1413908Sjp161948 * going to free that string. */
1423908Sjp161948 authctxt->cuser = NULL;
1430Sstevel@tonic-gate }
1440Sstevel@tonic-gate #endif /* USE_PAM */
1450Sstevel@tonic-gate
1460Sstevel@tonic-gate if (authenticated)
1470Sstevel@tonic-gate authctxt->method->authenticated = 1;
1480Sstevel@tonic-gate
1490Sstevel@tonic-gate debug2("userauth_hostbased: authenticated %d", authenticated);
1500Sstevel@tonic-gate if (key != NULL)
1510Sstevel@tonic-gate key_free(key);
1520Sstevel@tonic-gate xfree(pkalg);
1530Sstevel@tonic-gate xfree(pkblob);
1540Sstevel@tonic-gate xfree(cuser);
1550Sstevel@tonic-gate xfree(chost);
1560Sstevel@tonic-gate xfree(sig);
1570Sstevel@tonic-gate return;
1580Sstevel@tonic-gate }
1590Sstevel@tonic-gate
1600Sstevel@tonic-gate /* return 1 if given hostkey is allowed */
1610Sstevel@tonic-gate int
hostbased_key_allowed(struct passwd * pw,const char * cuser,char * chost,Key * key)1620Sstevel@tonic-gate hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost,
1630Sstevel@tonic-gate Key *key)
1640Sstevel@tonic-gate {
1650Sstevel@tonic-gate const char *resolvedname, *ipaddr, *lookup;
1660Sstevel@tonic-gate HostStatus host_status;
1670Sstevel@tonic-gate int len;
1680Sstevel@tonic-gate
1690Sstevel@tonic-gate resolvedname = get_canonical_hostname(options.verify_reverse_mapping);
1700Sstevel@tonic-gate ipaddr = get_remote_ipaddr();
1710Sstevel@tonic-gate
1720Sstevel@tonic-gate debug2("userauth_hostbased: chost %s resolvedname %s ipaddr %s",
1730Sstevel@tonic-gate chost, resolvedname, ipaddr);
1740Sstevel@tonic-gate
1750Sstevel@tonic-gate if (pw == NULL)
1760Sstevel@tonic-gate return 0;
1770Sstevel@tonic-gate
1780Sstevel@tonic-gate if (options.hostbased_uses_name_from_packet_only) {
1790Sstevel@tonic-gate if (auth_rhosts2(pw, cuser, chost, chost) == 0)
1800Sstevel@tonic-gate return 0;
1810Sstevel@tonic-gate lookup = chost;
1820Sstevel@tonic-gate } else {
1830Sstevel@tonic-gate if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') {
1840Sstevel@tonic-gate debug2("stripping trailing dot from chost %s", chost);
1850Sstevel@tonic-gate chost[len - 1] = '\0';
1860Sstevel@tonic-gate }
1870Sstevel@tonic-gate if (strcasecmp(resolvedname, chost) != 0)
1880Sstevel@tonic-gate log("userauth_hostbased mismatch: "
1890Sstevel@tonic-gate "client sends %s, but we resolve %s to %s",
1900Sstevel@tonic-gate chost, ipaddr, resolvedname);
1910Sstevel@tonic-gate if (auth_rhosts2(pw, cuser, resolvedname, ipaddr) == 0)
1920Sstevel@tonic-gate return 0;
1930Sstevel@tonic-gate lookup = resolvedname;
1940Sstevel@tonic-gate }
1950Sstevel@tonic-gate debug2("userauth_hostbased: access allowed by auth_rhosts2");
1960Sstevel@tonic-gate
1970Sstevel@tonic-gate host_status = check_key_in_hostfiles(pw, key, lookup,
1980Sstevel@tonic-gate _PATH_SSH_SYSTEM_HOSTFILE,
1990Sstevel@tonic-gate options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE);
2000Sstevel@tonic-gate
2010Sstevel@tonic-gate /* backward compat if no key has been found. */
2020Sstevel@tonic-gate if (host_status == HOST_NEW)
2030Sstevel@tonic-gate host_status = check_key_in_hostfiles(pw, key, lookup,
2040Sstevel@tonic-gate _PATH_SSH_SYSTEM_HOSTFILE2,
2050Sstevel@tonic-gate options.ignore_user_known_hosts ? NULL :
2060Sstevel@tonic-gate _PATH_SSH_USER_HOSTFILE2);
2070Sstevel@tonic-gate
2080Sstevel@tonic-gate return (host_status == HOST_OK);
2090Sstevel@tonic-gate }
2100Sstevel@tonic-gate
2110Sstevel@tonic-gate Authmethod method_hostbased = {
2120Sstevel@tonic-gate "hostbased",
2130Sstevel@tonic-gate &options.hostbased_authentication,
2140Sstevel@tonic-gate userauth_hostbased,
2150Sstevel@tonic-gate NULL, /* no abandon function */
2160Sstevel@tonic-gate NULL, NULL, /* method data and hist data */
2170Sstevel@tonic-gate 0, /* is not initial userauth */
2180Sstevel@tonic-gate 0, 0, 0, /* counters */
2190Sstevel@tonic-gate 0, 0, 0, 0, 0, 0 /* state */
2200Sstevel@tonic-gate };
221