[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.52

1.52    ! dtucker     1: /* $OpenBSD: auth2-hostbased.c,v 1.51 2023/02/17 04:22:50 dtucker 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"
1.29      markus     42: #include "sshkey.h"
1.11      deraadt    43: #include "hostfile.h"
1.1       markus     44: #include "auth.h"
                     45: #include "canohost.h"
1.11      deraadt    46: #ifdef GSSAPI
                     47: #include "ssh-gss.h"
                     48: #endif
1.1       markus     49: #include "monitor_wrap.h"
                     50: #include "pathnames.h"
1.29      markus     51: #include "ssherr.h"
1.22      djm        52: #include "match.h"
1.39      djm        53:
1.1       markus     54: /* import */
                     55: extern ServerOptions options;
                     56:
1.2       markus     57: static int
1.48      djm        58: userauth_hostbased(struct ssh *ssh, const char *method)
1.1       markus     59: {
1.30      markus     60:        Authctxt *authctxt = ssh->authctxt;
1.29      markus     61:        struct sshbuf *b;
1.27      markus     62:        struct sshkey *key = NULL;
1.33      djm        63:        char *pkalg, *cuser, *chost;
1.1       markus     64:        u_char *pkblob, *sig;
1.29      markus     65:        size_t alen, blen, slen;
                     66:        int r, pktype, authenticated = 0;
1.1       markus     67:
1.29      markus     68:        /* XXX use sshkey_froms() */
                     69:        if ((r = sshpkt_get_cstring(ssh, &pkalg, &alen)) != 0 ||
                     70:            (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0 ||
                     71:            (r = sshpkt_get_cstring(ssh, &chost, NULL)) != 0 ||
                     72:            (r = sshpkt_get_cstring(ssh, &cuser, NULL)) != 0 ||
                     73:            (r = sshpkt_get_string(ssh, &sig, &slen)) != 0)
1.43      djm        74:                fatal_fr(r, "parse packet");
1.1       markus     75:
1.43      djm        76:        debug_f("cuser %s chost %s pkalg %s slen %zu",
1.1       markus     77:            cuser, chost, pkalg, slen);
                     78: #ifdef DEBUG_PK
                     79:        debug("signature:");
1.37      mestre     80:        sshbuf_dump_data(sig, slen, stderr);
1.1       markus     81: #endif
1.29      markus     82:        pktype = sshkey_type_from_name(pkalg);
1.1       markus     83:        if (pktype == KEY_UNSPEC) {
                     84:                /* this is perfectly legal */
1.43      djm        85:                logit_f("unsupported public key algorithm: %s",
                     86:                    pkalg);
1.29      markus     87:                goto done;
                     88:        }
                     89:        if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
1.43      djm        90:                error_fr(r, "key_from_blob");
1.1       markus     91:                goto done;
                     92:        }
                     93:        if (key == NULL) {
1.43      djm        94:                error_f("cannot decode key: %s", pkalg);
1.1       markus     95:                goto done;
                     96:        }
                     97:        if (key->type != pktype) {
1.43      djm        98:                error_f("type mismatch for decoded key "
                     99:                    "(received %d, expected %d)", key->type, pktype);
1.1       markus    100:                goto done;
                    101:        }
1.45      dtucker   102:        if (match_pattern_list(pkalg, options.hostbased_accepted_algos, 0) != 1) {
1.49      djm       103:                logit_f("signature algorithm %s not in "
                    104:                    "HostbasedAcceptedAlgorithms", pkalg);
1.38      djm       105:                goto done;
                    106:        }
                    107:        if ((r = sshkey_check_cert_sigtype(key,
                    108:            options.ca_sign_algorithms)) != 0) {
1.43      djm       109:                logit_fr(r, "certificate signature algorithm %s",
1.38      djm       110:                    (key->cert == NULL || key->cert->signature_type == NULL) ?
1.43      djm       111:                    "(null)" : key->cert->signature_type);
1.50      djm       112:                goto done;
                    113:        }
                    114:        if ((r = sshkey_check_rsa_length(key,
                    115:            options.required_rsa_size)) != 0) {
                    116:                logit_r(r, "refusing %s key", sshkey_type(key));
1.36      djm       117:                goto done;
                    118:        }
                    119:
                    120:        if (!authctxt->valid || authctxt->user == NULL) {
1.43      djm       121:                debug2_f("disabled because of invalid user");
1.22      djm       122:                goto done;
                    123:        }
                    124:
1.29      markus    125:        if ((b = sshbuf_new()) == NULL)
1.43      djm       126:                fatal_f("sshbuf_new failed");
1.1       markus    127:        /* reconstruct packet */
1.46      djm       128:        if ((r = sshbuf_put_stringb(b, ssh->kex->session_id)) != 0 ||
1.29      markus    129:            (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
                    130:            (r = sshbuf_put_cstring(b, authctxt->user)) != 0 ||
1.33      djm       131:            (r = sshbuf_put_cstring(b, authctxt->service)) != 0 ||
1.48      djm       132:            (r = sshbuf_put_cstring(b, method)) != 0 ||
1.29      markus    133:            (r = sshbuf_put_string(b, pkalg, alen)) != 0 ||
                    134:            (r = sshbuf_put_string(b, pkblob, blen)) != 0 ||
                    135:            (r = sshbuf_put_cstring(b, chost)) != 0 ||
                    136:            (r = sshbuf_put_cstring(b, cuser)) != 0)
1.43      djm       137:                fatal_fr(r, "reconstruct packet");
1.1       markus    138: #ifdef DEBUG_PK
1.29      markus    139:        sshbuf_dump(b, stderr);
1.1       markus    140: #endif
1.16      djm       141:
1.31      djm       142:        auth2_record_info(authctxt,
1.16      djm       143:            "client user \"%.100s\", client host \"%.100s\"", cuser, chost);
                    144:
1.1       markus    145:        /* test for allowed key and correct signature */
                    146:        authenticated = 0;
1.40      djm       147:        if (PRIVSEP(hostbased_key_allowed(ssh, authctxt->pw, cuser,
                    148:            chost, key)) &&
1.29      markus    149:            PRIVSEP(sshkey_verify(key, sig, slen,
1.42      djm       150:            sshbuf_ptr(b), sshbuf_len(b), pkalg, ssh->compat, NULL)) == 0)
1.1       markus    151:                authenticated = 1;
                    152:
1.31      djm       153:        auth2_record_key(authctxt, authenticated, key);
1.29      markus    154:        sshbuf_free(b);
1.1       markus    155: done:
1.43      djm       156:        debug2_f("authenticated %d", authenticated);
1.31      djm       157:        sshkey_free(key);
1.15      djm       158:        free(pkalg);
                    159:        free(pkblob);
                    160:        free(cuser);
                    161:        free(chost);
                    162:        free(sig);
1.1       markus    163:        return authenticated;
                    164: }
                    165:
                    166: /* return 1 if given hostkey is allowed */
                    167: int
1.40      djm       168: hostbased_key_allowed(struct ssh *ssh, struct passwd *pw,
                    169:     const char *cuser, char *chost, struct sshkey *key)
1.1       markus    170: {
1.14      djm       171:        const char *resolvedname, *ipaddr, *lookup, *reason;
1.1       markus    172:        HostStatus host_status;
                    173:        int len;
1.14      djm       174:        char *fp;
1.13      djm       175:
                    176:        if (auth_key_is_revoked(key))
                    177:                return 0;
1.1       markus    178:
1.26      djm       179:        resolvedname = auth_get_canonical_hostname(ssh, options.use_dns);
                    180:        ipaddr = ssh_remote_ipaddr(ssh);
1.1       markus    181:
1.43      djm       182:        debug2_f("chost %s resolvedname %s ipaddr %s",
1.1       markus    183:            chost, resolvedname, ipaddr);
                    184:
1.12      djm       185:        if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') {
                    186:                debug2("stripping trailing dot from chost %s", chost);
                    187:                chost[len - 1] = '\0';
                    188:        }
                    189:
1.1       markus    190:        if (options.hostbased_uses_name_from_packet_only) {
1.20      djm       191:                if (auth_rhosts2(pw, cuser, chost, chost) == 0) {
1.43      djm       192:                        debug2_f("auth_rhosts2 refused user \"%.100s\" "
                    193:                            "host \"%.100s\" (from packet)", cuser, chost);
1.1       markus    194:                        return 0;
1.20      djm       195:                }
1.1       markus    196:                lookup = chost;
                    197:        } else {
                    198:                if (strcasecmp(resolvedname, chost) != 0)
1.3       itojun    199:                        logit("userauth_hostbased mismatch: "
1.1       markus    200:                            "client sends %s, but we resolve %s to %s",
                    201:                            chost, ipaddr, resolvedname);
1.20      djm       202:                if (auth_rhosts2(pw, cuser, resolvedname, ipaddr) == 0) {
1.43      djm       203:                        debug2_f("auth_rhosts2 refused "
1.20      djm       204:                            "user \"%.100s\" host \"%.100s\" addr \"%.100s\"",
1.43      djm       205:                            cuser, resolvedname, ipaddr);
1.1       markus    206:                        return 0;
1.20      djm       207:                }
1.1       markus    208:                lookup = resolvedname;
                    209:        }
1.43      djm       210:        debug2_f("access allowed by auth_rhosts2");
1.1       markus    211:
1.29      markus    212:        if (sshkey_is_cert(key) &&
1.47      djm       213:            sshkey_cert_check_authority_now(key, 1, 0, 0, lookup, &reason)) {
1.14      djm       214:                error("%s", reason);
                    215:                auth_debug_add("%s", reason);
                    216:                return 0;
                    217:        }
                    218:
1.1       markus    219:        host_status = check_key_in_hostfiles(pw, key, lookup,
                    220:            _PATH_SSH_SYSTEM_HOSTFILE,
                    221:            options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE);
                    222:
                    223:        /* backward compat if no key has been found. */
1.14      djm       224:        if (host_status == HOST_NEW) {
1.1       markus    225:                host_status = check_key_in_hostfiles(pw, key, lookup,
                    226:                    _PATH_SSH_SYSTEM_HOSTFILE2,
                    227:                    options.ignore_user_known_hosts ? NULL :
                    228:                    _PATH_SSH_USER_HOSTFILE2);
1.14      djm       229:        }
                    230:
                    231:        if (host_status == HOST_OK) {
1.29      markus    232:                if (sshkey_is_cert(key)) {
1.24      djm       233:                        if ((fp = sshkey_fingerprint(key->cert->signature_key,
                    234:                            options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
1.43      djm       235:                                fatal_f("sshkey_fingerprint fail");
1.14      djm       236:                        verbose("Accepted certificate ID \"%s\" signed by "
                    237:                            "%s CA %s from %s@%s", key->cert->key_id,
1.29      markus    238:                            sshkey_type(key->cert->signature_key), fp,
1.14      djm       239:                            cuser, lookup);
                    240:                } else {
1.24      djm       241:                        if ((fp = sshkey_fingerprint(key,
                    242:                            options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
1.43      djm       243:                                fatal_f("sshkey_fingerprint fail");
1.14      djm       244:                        verbose("Accepted %s public key %s from %s@%s",
1.29      markus    245:                            sshkey_type(key), fp, cuser, lookup);
1.14      djm       246:                }
1.15      djm       247:                free(fp);
1.14      djm       248:        }
1.1       markus    249:
                    250:        return (host_status == HOST_OK);
                    251: }
1.2       markus    252:
                    253: Authmethod method_hostbased = {
                    254:        "hostbased",
1.48      djm       255:        NULL,
1.2       markus    256:        userauth_hostbased,
                    257:        &options.hostbased_authentication
                    258: };