[BACK]Return to auth2-hostbased.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Annotation of src/usr.bin/ssh/auth2-hostbased.c, Revision 1.49

1.49    ! djm         1: /* $OpenBSD: auth2-hostbased.c,v 1.48 2021/12/19 22:12:07 djm Exp $ */
1.1       markus      2: /*
                      3:  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
                      4:  *
                      5:  * Redistribution and use in source and binary forms, with or without
                      6:  * modification, are permitted provided that the following conditions
                      7:  * are met:
                      8:  * 1. Redistributions of source code must retain the above copyright
                      9:  *    notice, this list of conditions and the following disclaimer.
                     10:  * 2. Redistributions in binary form must reproduce the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer in the
                     12:  *    documentation and/or other materials provided with the distribution.
                     13:  *
                     14:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
                     15:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
                     16:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
                     17:  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
                     18:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
                     19:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
                     20:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
                     21:  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     22:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
                     23:  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     24:  */
                     25:
1.9       stevesk    26:
                     27: #include <sys/types.h>
                     28:
1.41      djm        29: #include <stdlib.h>
1.9       stevesk    30: #include <pwd.h>
1.10      stevesk    31: #include <string.h>
1.11      deraadt    32: #include <stdarg.h>
1.1       markus     33:
1.11      deraadt    34: #include "xmalloc.h"
1.1       markus     35: #include "ssh2.h"
                     36: #include "packet.h"
1.46      djm        37: #include "kex.h"
1.35      markus     38: #include "sshbuf.h"
1.1       markus     39: #include "log.h"
1.18      millert    40: #include "misc.h"
1.1       markus     41: #include "servconf.h"
                     42: #include "compat.h"
1.29      markus     43: #include "sshkey.h"
1.11      deraadt    44: #include "hostfile.h"
1.1       markus     45: #include "auth.h"
                     46: #include "canohost.h"
1.11      deraadt    47: #ifdef GSSAPI
                     48: #include "ssh-gss.h"
                     49: #endif
1.1       markus     50: #include "monitor_wrap.h"
                     51: #include "pathnames.h"
1.29      markus     52: #include "ssherr.h"
1.22      djm        53: #include "match.h"
1.39      djm        54:
1.1       markus     55: /* import */
                     56: extern ServerOptions options;
                     57:
1.2       markus     58: static int
1.48      djm        59: userauth_hostbased(struct ssh *ssh, const char *method)
1.1       markus     60: {
1.30      markus     61:        Authctxt *authctxt = ssh->authctxt;
1.29      markus     62:        struct sshbuf *b;
1.27      markus     63:        struct sshkey *key = NULL;
1.33      djm        64:        char *pkalg, *cuser, *chost;
1.1       markus     65:        u_char *pkblob, *sig;
1.29      markus     66:        size_t alen, blen, slen;
                     67:        int r, pktype, authenticated = 0;
1.1       markus     68:
1.29      markus     69:        /* XXX use sshkey_froms() */
                     70:        if ((r = sshpkt_get_cstring(ssh, &pkalg, &alen)) != 0 ||
                     71:            (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0 ||
                     72:            (r = sshpkt_get_cstring(ssh, &chost, NULL)) != 0 ||
                     73:            (r = sshpkt_get_cstring(ssh, &cuser, NULL)) != 0 ||
                     74:            (r = sshpkt_get_string(ssh, &sig, &slen)) != 0)
1.43      djm        75:                fatal_fr(r, "parse packet");
1.1       markus     76:
1.43      djm        77:        debug_f("cuser %s chost %s pkalg %s slen %zu",
1.1       markus     78:            cuser, chost, pkalg, slen);
                     79: #ifdef DEBUG_PK
                     80:        debug("signature:");
1.37      mestre     81:        sshbuf_dump_data(sig, slen, stderr);
1.1       markus     82: #endif
1.29      markus     83:        pktype = sshkey_type_from_name(pkalg);
1.1       markus     84:        if (pktype == KEY_UNSPEC) {
                     85:                /* this is perfectly legal */
1.43      djm        86:                logit_f("unsupported public key algorithm: %s",
                     87:                    pkalg);
1.29      markus     88:                goto done;
                     89:        }
                     90:        if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
1.43      djm        91:                error_fr(r, "key_from_blob");
1.1       markus     92:                goto done;
                     93:        }
                     94:        if (key == NULL) {
1.43      djm        95:                error_f("cannot decode key: %s", pkalg);
1.1       markus     96:                goto done;
                     97:        }
                     98:        if (key->type != pktype) {
1.43      djm        99:                error_f("type mismatch for decoded key "
                    100:                    "(received %d, expected %d)", key->type, pktype);
1.17      djm       101:                goto done;
                    102:        }
1.29      markus    103:        if (sshkey_type_plain(key->type) == KEY_RSA &&
                    104:            (ssh->compat & SSH_BUG_RSASIGMD5) != 0) {
1.17      djm       105:                error("Refusing RSA key because peer uses unsafe "
                    106:                    "signature format");
1.1       markus    107:                goto done;
                    108:        }
1.45      dtucker   109:        if (match_pattern_list(pkalg, options.hostbased_accepted_algos, 0) != 1) {
1.49    ! djm       110:                logit_f("signature algorithm %s not in "
        !           111:                    "HostbasedAcceptedAlgorithms", pkalg);
1.38      djm       112:                goto done;
                    113:        }
                    114:        if ((r = sshkey_check_cert_sigtype(key,
                    115:            options.ca_sign_algorithms)) != 0) {
1.43      djm       116:                logit_fr(r, "certificate signature algorithm %s",
1.38      djm       117:                    (key->cert == NULL || key->cert->signature_type == NULL) ?
1.43      djm       118:                    "(null)" : key->cert->signature_type);
1.36      djm       119:                goto done;
                    120:        }
                    121:
                    122:        if (!authctxt->valid || authctxt->user == NULL) {
1.43      djm       123:                debug2_f("disabled because of invalid user");
1.22      djm       124:                goto done;
                    125:        }
                    126:
1.29      markus    127:        if ((b = sshbuf_new()) == NULL)
1.43      djm       128:                fatal_f("sshbuf_new failed");
1.1       markus    129:        /* reconstruct packet */
1.46      djm       130:        if ((r = sshbuf_put_stringb(b, ssh->kex->session_id)) != 0 ||
1.29      markus    131:            (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
                    132:            (r = sshbuf_put_cstring(b, authctxt->user)) != 0 ||
1.33      djm       133:            (r = sshbuf_put_cstring(b, authctxt->service)) != 0 ||
1.48      djm       134:            (r = sshbuf_put_cstring(b, method)) != 0 ||
1.29      markus    135:            (r = sshbuf_put_string(b, pkalg, alen)) != 0 ||
                    136:            (r = sshbuf_put_string(b, pkblob, blen)) != 0 ||
                    137:            (r = sshbuf_put_cstring(b, chost)) != 0 ||
                    138:            (r = sshbuf_put_cstring(b, cuser)) != 0)
1.43      djm       139:                fatal_fr(r, "reconstruct packet");
1.1       markus    140: #ifdef DEBUG_PK
1.29      markus    141:        sshbuf_dump(b, stderr);
1.1       markus    142: #endif
1.16      djm       143:
1.31      djm       144:        auth2_record_info(authctxt,
1.16      djm       145:            "client user \"%.100s\", client host \"%.100s\"", cuser, chost);
                    146:
1.1       markus    147:        /* test for allowed key and correct signature */
                    148:        authenticated = 0;
1.40      djm       149:        if (PRIVSEP(hostbased_key_allowed(ssh, authctxt->pw, cuser,
                    150:            chost, key)) &&
1.29      markus    151:            PRIVSEP(sshkey_verify(key, sig, slen,
1.42      djm       152:            sshbuf_ptr(b), sshbuf_len(b), pkalg, ssh->compat, NULL)) == 0)
1.1       markus    153:                authenticated = 1;
                    154:
1.31      djm       155:        auth2_record_key(authctxt, authenticated, key);
1.29      markus    156:        sshbuf_free(b);
1.1       markus    157: done:
1.43      djm       158:        debug2_f("authenticated %d", authenticated);
1.31      djm       159:        sshkey_free(key);
1.15      djm       160:        free(pkalg);
                    161:        free(pkblob);
                    162:        free(cuser);
                    163:        free(chost);
                    164:        free(sig);
1.1       markus    165:        return authenticated;
                    166: }
                    167:
                    168: /* return 1 if given hostkey is allowed */
                    169: int
1.40      djm       170: hostbased_key_allowed(struct ssh *ssh, struct passwd *pw,
                    171:     const char *cuser, char *chost, struct sshkey *key)
1.1       markus    172: {
1.14      djm       173:        const char *resolvedname, *ipaddr, *lookup, *reason;
1.1       markus    174:        HostStatus host_status;
                    175:        int len;
1.14      djm       176:        char *fp;
1.13      djm       177:
                    178:        if (auth_key_is_revoked(key))
                    179:                return 0;
1.1       markus    180:
1.26      djm       181:        resolvedname = auth_get_canonical_hostname(ssh, options.use_dns);
                    182:        ipaddr = ssh_remote_ipaddr(ssh);
1.1       markus    183:
1.43      djm       184:        debug2_f("chost %s resolvedname %s ipaddr %s",
1.1       markus    185:            chost, resolvedname, ipaddr);
                    186:
1.12      djm       187:        if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') {
                    188:                debug2("stripping trailing dot from chost %s", chost);
                    189:                chost[len - 1] = '\0';
                    190:        }
                    191:
1.1       markus    192:        if (options.hostbased_uses_name_from_packet_only) {
1.20      djm       193:                if (auth_rhosts2(pw, cuser, chost, chost) == 0) {
1.43      djm       194:                        debug2_f("auth_rhosts2 refused user \"%.100s\" "
                    195:                            "host \"%.100s\" (from packet)", cuser, chost);
1.1       markus    196:                        return 0;
1.20      djm       197:                }
1.1       markus    198:                lookup = chost;
                    199:        } else {
                    200:                if (strcasecmp(resolvedname, chost) != 0)
1.3       itojun    201:                        logit("userauth_hostbased mismatch: "
1.1       markus    202:                            "client sends %s, but we resolve %s to %s",
                    203:                            chost, ipaddr, resolvedname);
1.20      djm       204:                if (auth_rhosts2(pw, cuser, resolvedname, ipaddr) == 0) {
1.43      djm       205:                        debug2_f("auth_rhosts2 refused "
1.20      djm       206:                            "user \"%.100s\" host \"%.100s\" addr \"%.100s\"",
1.43      djm       207:                            cuser, resolvedname, ipaddr);
1.1       markus    208:                        return 0;
1.20      djm       209:                }
1.1       markus    210:                lookup = resolvedname;
                    211:        }
1.43      djm       212:        debug2_f("access allowed by auth_rhosts2");
1.1       markus    213:
1.29      markus    214:        if (sshkey_is_cert(key) &&
1.47      djm       215:            sshkey_cert_check_authority_now(key, 1, 0, 0, lookup, &reason)) {
1.14      djm       216:                error("%s", reason);
                    217:                auth_debug_add("%s", reason);
                    218:                return 0;
                    219:        }
                    220:
1.1       markus    221:        host_status = check_key_in_hostfiles(pw, key, lookup,
                    222:            _PATH_SSH_SYSTEM_HOSTFILE,
                    223:            options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE);
                    224:
                    225:        /* backward compat if no key has been found. */
1.14      djm       226:        if (host_status == HOST_NEW) {
1.1       markus    227:                host_status = check_key_in_hostfiles(pw, key, lookup,
                    228:                    _PATH_SSH_SYSTEM_HOSTFILE2,
                    229:                    options.ignore_user_known_hosts ? NULL :
                    230:                    _PATH_SSH_USER_HOSTFILE2);
1.14      djm       231:        }
                    232:
                    233:        if (host_status == HOST_OK) {
1.29      markus    234:                if (sshkey_is_cert(key)) {
1.24      djm       235:                        if ((fp = sshkey_fingerprint(key->cert->signature_key,
                    236:                            options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
1.43      djm       237:                                fatal_f("sshkey_fingerprint fail");
1.14      djm       238:                        verbose("Accepted certificate ID \"%s\" signed by "
                    239:                            "%s CA %s from %s@%s", key->cert->key_id,
1.29      markus    240:                            sshkey_type(key->cert->signature_key), fp,
1.14      djm       241:                            cuser, lookup);
                    242:                } else {
1.24      djm       243:                        if ((fp = sshkey_fingerprint(key,
                    244:                            options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
1.43      djm       245:                                fatal_f("sshkey_fingerprint fail");
1.14      djm       246:                        verbose("Accepted %s public key %s from %s@%s",
1.29      markus    247:                            sshkey_type(key), fp, cuser, lookup);
1.14      djm       248:                }
1.15      djm       249:                free(fp);
1.14      djm       250:        }
1.1       markus    251:
                    252:        return (host_status == HOST_OK);
                    253: }
1.2       markus    254:
                    255: Authmethod method_hostbased = {
                    256:        "hostbased",
1.48      djm       257:        NULL,
1.2       markus    258:        userauth_hostbased,
                    259:        &options.hostbased_authentication
                    260: };