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

Annotation of src/usr.bin/ssh/gss-serv.c, Revision 1.32

1.32    ! djm         1: /* $OpenBSD: gss-serv.c,v 1.31 2018/07/09 21:37:55 markus Exp $ */
1.1       markus      2:
                      3: /*
                      4:  * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
                      5:  *
                      6:  * Redistribution and use in source and binary forms, with or without
                      7:  * modification, are permitted provided that the following conditions
                      8:  * are met:
                      9:  * 1. Redistributions of source code must retain the above copyright
                     10:  *    notice, this list of conditions and the following disclaimer.
                     11:  * 2. Redistributions in binary form must reproduce the above copyright
                     12:  *    notice, this list of conditions and the following disclaimer in the
                     13:  *    documentation and/or other materials provided with the distribution.
                     14:  *
                     15:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
                     16:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
                     17:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
                     18:  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
                     19:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
                     20:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
                     21:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
                     22:  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     23:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
                     24:  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     25:  */
                     26:
1.20      deraadt    27: #include <sys/types.h>
1.22      djm        28: #include <sys/queue.h>
1.1       markus     29:
                     30: #ifdef GSSAPI
1.19      stevesk    31:
                     32: #include <string.h>
1.1       markus     33:
1.20      deraadt    34: #include "xmalloc.h"
1.31      markus     35: #include "sshkey.h"
1.20      deraadt    36: #include "hostfile.h"
1.1       markus     37: #include "auth.h"
                     38: #include "log.h"
                     39: #include "channels.h"
                     40: #include "session.h"
1.17      djm        41: #include "misc.h"
1.29      djm        42: #include "servconf.h"
1.1       markus     43:
                     44: #include "ssh-gss.h"
                     45:
1.29      djm        46: extern ServerOptions options;
                     47:
1.1       markus     48: static ssh_gssapi_client gssapi_client =
                     49:     { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
1.24      djm        50:     GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}};
1.1       markus     51:
                     52: ssh_gssapi_mech gssapi_null_mech =
                     53:     { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL};
                     54:
                     55: #ifdef KRB5
                     56: extern ssh_gssapi_mech gssapi_kerberos_mech;
                     57: #endif
                     58:
                     59: ssh_gssapi_mech* supported_mechs[]= {
                     60: #ifdef KRB5
                     61:        &gssapi_kerberos_mech,
                     62: #endif
                     63:        &gssapi_null_mech,
                     64: };
1.21      djm        65:
1.26      djm        66: /*
                     67:  * ssh_gssapi_supported_oids() can cause sandbox violations, so prepare the
                     68:  * list of supported mechanisms before privsep is set up.
                     69:  */
                     70: static gss_OID_set supported_oids;
                     71:
                     72: void
                     73: ssh_gssapi_prepare_supported_oids(void)
                     74: {
                     75:        ssh_gssapi_supported_oids(&supported_oids);
                     76: }
                     77:
                     78: OM_uint32
                     79: ssh_gssapi_test_oid_supported(OM_uint32 *ms, gss_OID member, int *present)
                     80: {
                     81:        if (supported_oids == NULL)
                     82:                ssh_gssapi_prepare_supported_oids();
                     83:        return gss_test_oid_set_member(ms, member, supported_oids, present);
                     84: }
1.21      djm        85:
                     86: /*
                     87:  * Acquire credentials for a server running on the current host.
                     88:  * Requires that the context structure contains a valid OID
                     89:  */
                     90:
                     91: /* Returns a GSSAPI error code */
                     92: /* Privileged (called from ssh_gssapi_server_ctx) */
                     93: static OM_uint32
                     94: ssh_gssapi_acquire_cred(Gssctxt *ctx)
                     95: {
                     96:        OM_uint32 status;
1.27      djm        97:        char lname[NI_MAXHOST];
1.21      djm        98:        gss_OID_set oidset;
                     99:
1.29      djm       100:        if (options.gss_strict_acceptor) {
                    101:                gss_create_empty_oid_set(&status, &oidset);
                    102:                gss_add_oid_set_member(&status, ctx->oid, &oidset);
                    103:
                    104:                if (gethostname(lname, MAXHOSTNAMELEN)) {
                    105:                        gss_release_oid_set(&status, &oidset);
                    106:                        return (-1);
                    107:                }
                    108:
                    109:                if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) {
                    110:                        gss_release_oid_set(&status, &oidset);
                    111:                        return (ctx->major);
                    112:                }
                    113:
                    114:                if ((ctx->major = gss_acquire_cred(&ctx->minor,
                    115:                    ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds,
                    116:                    NULL, NULL)))
                    117:                        ssh_gssapi_error(ctx);
1.21      djm       118:
                    119:                gss_release_oid_set(&status, &oidset);
                    120:                return (ctx->major);
1.29      djm       121:        } else {
                    122:                ctx->name = GSS_C_NO_NAME;
                    123:                ctx->creds = GSS_C_NO_CREDENTIAL;
1.21      djm       124:        }
1.29      djm       125:        return GSS_S_COMPLETE;
1.21      djm       126: }
                    127:
                    128: /* Privileged */
                    129: OM_uint32
                    130: ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
                    131: {
                    132:        if (*ctx)
                    133:                ssh_gssapi_delete_ctx(ctx);
                    134:        ssh_gssapi_build_ctx(ctx);
                    135:        ssh_gssapi_set_oid(*ctx, oid);
                    136:        return (ssh_gssapi_acquire_cred(*ctx));
                    137: }
1.1       markus    138:
1.11      stevesk   139: /* Unprivileged */
1.1       markus    140: void
                    141: ssh_gssapi_supported_oids(gss_OID_set *oidset)
                    142: {
                    143:        int i = 0;
                    144:        OM_uint32 min_status;
                    145:        int present;
                    146:        gss_OID_set supported;
                    147:
                    148:        gss_create_empty_oid_set(&min_status, oidset);
                    149:        gss_indicate_mechs(&min_status, &supported);
                    150:
                    151:        while (supported_mechs[i]->name != NULL) {
                    152:                if (GSS_ERROR(gss_test_oid_set_member(&min_status,
                    153:                    &supported_mechs[i]->oid, supported, &present)))
                    154:                        present = 0;
                    155:                if (present)
                    156:                        gss_add_oid_set_member(&min_status,
                    157:                            &supported_mechs[i]->oid, oidset);
                    158:                i++;
                    159:        }
1.15      djm       160:
                    161:        gss_release_oid_set(&min_status, &supported);
1.1       markus    162: }
                    163:
                    164:
                    165: /* Wrapper around accept_sec_context
                    166:  * Requires that the context contains:
                    167:  *    oid
                    168:  *    credentials      (from ssh_gssapi_acquire_cred)
                    169:  */
1.11      stevesk   170: /* Privileged */
1.1       markus    171: OM_uint32
                    172: ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *recv_tok,
                    173:     gss_buffer_desc *send_tok, OM_uint32 *flags)
                    174: {
                    175:        OM_uint32 status;
                    176:        gss_OID mech;
                    177:
                    178:        ctx->major = gss_accept_sec_context(&ctx->minor,
                    179:            &ctx->context, ctx->creds, recv_tok,
                    180:            GSS_C_NO_CHANNEL_BINDINGS, &ctx->client, &mech,
                    181:            send_tok, flags, NULL, &ctx->client_creds);
                    182:
                    183:        if (GSS_ERROR(ctx->major))
                    184:                ssh_gssapi_error(ctx);
                    185:
                    186:        if (ctx->client_creds)
                    187:                debug("Received some client credentials");
                    188:        else
                    189:                debug("Got no client credentials");
                    190:
                    191:        status = ctx->major;
                    192:
                    193:        /* Now, if we're complete and we have the right flags, then
                    194:         * we flag the user as also having been authenticated
                    195:         */
                    196:
                    197:        if (((flags == NULL) || ((*flags & GSS_C_MUTUAL_FLAG) &&
                    198:            (*flags & GSS_C_INTEG_FLAG))) && (ctx->major == GSS_S_COMPLETE)) {
                    199:                if (ssh_gssapi_getclient(ctx, &gssapi_client))
                    200:                        fatal("Couldn't convert client name");
                    201:        }
                    202:
                    203:        return (status);
                    204: }
                    205:
                    206: /*
                    207:  * This parses an exported name, extracting the mechanism specific portion
                    208:  * to use for ACL checking. It verifies that the name belongs the mechanism
                    209:  * originally selected.
                    210:  */
                    211: static OM_uint32
                    212: ssh_gssapi_parse_ename(Gssctxt *ctx, gss_buffer_t ename, gss_buffer_t name)
                    213: {
1.6       djm       214:        u_char *tok;
1.1       markus    215:        OM_uint32 offset;
                    216:        OM_uint32 oidl;
1.3       markus    217:
1.13      stevesk   218:        tok = ename->value;
1.3       markus    219:
                    220:        /*
                    221:         * Check that ename is long enough for all of the fixed length
1.1       markus    222:         * header, and that the initial ID bytes are correct
                    223:         */
                    224:
1.13      stevesk   225:        if (ename->length < 6 || memcmp(tok, "\x04\x01", 2) != 0)
1.1       markus    226:                return GSS_S_FAILURE;
                    227:
                    228:        /*
                    229:         * Extract the OID, and check it. Here GSSAPI breaks with tradition
                    230:         * and does use the OID type and length bytes. To confuse things
                    231:         * there are two lengths - the first including these, and the
                    232:         * second without.
                    233:         */
                    234:
1.17      djm       235:        oidl = get_u16(tok+2); /* length including next two bytes */
1.1       markus    236:        oidl = oidl-2; /* turn it into the _real_ length of the variable OID */
                    237:
                    238:        /*
                    239:         * Check the BER encoding for correct type and length, that the
                    240:         * string is long enough and that the OID matches that in our context
                    241:         */
                    242:        if (tok[4] != 0x06 || tok[5] != oidl ||
1.3       markus    243:            ename->length < oidl+6 ||
1.13      stevesk   244:            !ssh_gssapi_check_oid(ctx, tok+6, oidl))
1.1       markus    245:                return GSS_S_FAILURE;
                    246:
                    247:        offset = oidl+6;
1.3       markus    248:
1.1       markus    249:        if (ename->length < offset+4)
                    250:                return GSS_S_FAILURE;
1.3       markus    251:
1.17      djm       252:        name->length = get_u32(tok+offset);
1.1       markus    253:        offset += 4;
1.3       markus    254:
1.23      markus    255:        if (UINT_MAX - offset < name->length)
                    256:                return GSS_S_FAILURE;
1.1       markus    257:        if (ename->length < offset+name->length)
1.3       markus    258:                return GSS_S_FAILURE;
                    259:
1.2       markus    260:        name->value = xmalloc(name->length+1);
1.14      stevesk   261:        memcpy(name->value, tok+offset, name->length);
1.2       markus    262:        ((char *)name->value)[name->length] = 0;
                    263:
1.1       markus    264:        return GSS_S_COMPLETE;
1.3       markus    265: }
1.1       markus    266:
                    267: /* Extract the client details from a given context. This can only reliably
                    268:  * be called once for a context */
                    269:
1.11      stevesk   270: /* Privileged (called from accept_secure_ctx) */
1.1       markus    271: OM_uint32
                    272: ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
                    273: {
                    274:        int i = 0;
                    275:
                    276:        gss_buffer_desc ename;
1.3       markus    277:
1.1       markus    278:        client->mech = NULL;
                    279:
                    280:        while (supported_mechs[i]->name != NULL) {
                    281:                if (supported_mechs[i]->oid.length == ctx->oid->length &&
                    282:                    (memcmp(supported_mechs[i]->oid.elements,
                    283:                    ctx->oid->elements, ctx->oid->length) == 0))
                    284:                        client->mech = supported_mechs[i];
                    285:                i++;
                    286:        }
                    287:
1.3       markus    288:        if (client->mech == NULL)
1.1       markus    289:                return GSS_S_FAILURE;
1.3       markus    290:
                    291:        if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
1.1       markus    292:            &client->displayname, NULL))) {
1.3       markus    293:                ssh_gssapi_error(ctx);
                    294:                return (ctx->major);
1.1       markus    295:        }
1.3       markus    296:
                    297:        if ((ctx->major = gss_export_name(&ctx->minor, ctx->client,
1.1       markus    298:            &ename))) {
                    299:                ssh_gssapi_error(ctx);
                    300:                return (ctx->major);
                    301:        }
1.3       markus    302:
1.1       markus    303:        if ((ctx->major = ssh_gssapi_parse_ename(ctx,&ename,
                    304:            &client->exportedname))) {
                    305:                return (ctx->major);
                    306:        }
                    307:
                    308:        /* We can't copy this structure, so we just move the pointer to it */
                    309:        client->creds = ctx->client_creds;
                    310:        ctx->client_creds = GSS_C_NO_CREDENTIAL;
                    311:        return (ctx->major);
                    312: }
                    313:
1.4       markus    314: /* As user - called on fatal/exit */
1.1       markus    315: void
1.4       markus    316: ssh_gssapi_cleanup_creds(void)
1.1       markus    317: {
                    318:        if (gssapi_client.store.filename != NULL) {
                    319:                /* Unlink probably isn't sufficient */
1.14      stevesk   320:                debug("removing gssapi cred file\"%s\"",
                    321:                    gssapi_client.store.filename);
1.1       markus    322:                unlink(gssapi_client.store.filename);
                    323:        }
                    324: }
                    325:
                    326: /* As user */
                    327: void
                    328: ssh_gssapi_storecreds(void)
                    329: {
                    330:        if (gssapi_client.mech && gssapi_client.mech->storecreds) {
                    331:                (*gssapi_client.mech->storecreds)(&gssapi_client);
                    332:        } else
                    333:                debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism");
                    334: }
                    335:
1.32    ! djm       336: /* This allows GSSAPI methods to do things to the child's environment based
1.1       markus    337:  * on the passed authentication process and credentials.
                    338:  */
                    339: /* As user */
                    340: void
                    341: ssh_gssapi_do_child(char ***envp, u_int *envsizep)
                    342: {
                    343:
                    344:        if (gssapi_client.store.envvar != NULL &&
                    345:            gssapi_client.store.envval != NULL) {
                    346:                debug("Setting %s to %s", gssapi_client.store.envvar,
1.13      stevesk   347:                    gssapi_client.store.envval);
1.1       markus    348:                child_set_env(envp, envsizep, gssapi_client.store.envvar,
1.7       djm       349:                    gssapi_client.store.envval);
1.1       markus    350:        }
                    351: }
                    352:
1.9       djm       353: /* Privileged */
1.1       markus    354: int
                    355: ssh_gssapi_userok(char *user)
                    356: {
1.8       djm       357:        OM_uint32 lmin;
                    358:
1.1       markus    359:        if (gssapi_client.exportedname.length == 0 ||
                    360:            gssapi_client.exportedname.value == NULL) {
                    361:                debug("No suitable client data");
                    362:                return 0;
                    363:        }
                    364:        if (gssapi_client.mech && gssapi_client.mech->userok)
1.8       djm       365:                if ((*gssapi_client.mech->userok)(&gssapi_client, user))
                    366:                        return 1;
                    367:                else {
                    368:                        /* Destroy delegated credentials if userok fails */
                    369:                        gss_release_buffer(&lmin, &gssapi_client.displayname);
                    370:                        gss_release_buffer(&lmin, &gssapi_client.exportedname);
                    371:                        gss_release_cred(&lmin, &gssapi_client.creds);
1.25      djm       372:                        explicit_bzero(&gssapi_client,
                    373:                            sizeof(ssh_gssapi_client));
1.8       djm       374:                        return 0;
                    375:                }
1.1       markus    376:        else
                    377:                debug("ssh_gssapi_userok: Unknown GSSAPI mechanism");
                    378:        return (0);
1.5       markus    379: }
                    380:
1.11      stevesk   381: /* Privileged */
1.5       markus    382: OM_uint32
                    383: ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
                    384: {
                    385:        ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
                    386:            gssbuf, gssmic, NULL);
                    387:
                    388:        return (ctx->major);
1.30      djm       389: }
                    390:
                    391: /* Privileged */
                    392: const char *ssh_gssapi_displayname(void)
                    393: {
                    394:        if (gssapi_client.displayname.length == 0 ||
                    395:            gssapi_client.displayname.value == NULL)
                    396:                return NULL;
                    397:        return (char *)gssapi_client.displayname.value;
1.1       markus    398: }
                    399:
                    400: #endif