Home | History | Annotate | Download | only in openssh
      1 /* $OpenBSD: gss-serv.c,v 1.23 2011/08/01 19:18:15 markus Exp $ */
      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 
     27 #include "includes.h"
     28 
     29 #ifdef GSSAPI
     30 
     31 #include <sys/types.h>
     32 #include <sys/param.h>
     33 
     34 #include <stdarg.h>
     35 #include <string.h>
     36 #include <unistd.h>
     37 
     38 #include "openbsd-compat/sys-queue.h"
     39 #include "xmalloc.h"
     40 #include "buffer.h"
     41 #include "key.h"
     42 #include "hostfile.h"
     43 #include "auth.h"
     44 #include "log.h"
     45 #include "channels.h"
     46 #include "session.h"
     47 #include "misc.h"
     48 
     49 #include "ssh-gss.h"
     50 
     51 static ssh_gssapi_client gssapi_client =
     52     { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
     53     GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL}};
     54 
     55 ssh_gssapi_mech gssapi_null_mech =
     56     { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL};
     57 
     58 #ifdef KRB5
     59 extern ssh_gssapi_mech gssapi_kerberos_mech;
     60 #endif
     61 
     62 ssh_gssapi_mech* supported_mechs[]= {
     63 #ifdef KRB5
     64 	&gssapi_kerberos_mech,
     65 #endif
     66 	&gssapi_null_mech,
     67 };
     68 
     69 
     70 /*
     71  * Acquire credentials for a server running on the current host.
     72  * Requires that the context structure contains a valid OID
     73  */
     74 
     75 /* Returns a GSSAPI error code */
     76 /* Privileged (called from ssh_gssapi_server_ctx) */
     77 static OM_uint32
     78 ssh_gssapi_acquire_cred(Gssctxt *ctx)
     79 {
     80 	OM_uint32 status;
     81 	char lname[MAXHOSTNAMELEN];
     82 	gss_OID_set oidset;
     83 
     84 	gss_create_empty_oid_set(&status, &oidset);
     85 	gss_add_oid_set_member(&status, ctx->oid, &oidset);
     86 
     87 	if (gethostname(lname, MAXHOSTNAMELEN)) {
     88 		gss_release_oid_set(&status, &oidset);
     89 		return (-1);
     90 	}
     91 
     92 	if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) {
     93 		gss_release_oid_set(&status, &oidset);
     94 		return (ctx->major);
     95 	}
     96 
     97 	if ((ctx->major = gss_acquire_cred(&ctx->minor,
     98 	    ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, NULL, NULL)))
     99 		ssh_gssapi_error(ctx);
    100 
    101 	gss_release_oid_set(&status, &oidset);
    102 	return (ctx->major);
    103 }
    104 
    105 /* Privileged */
    106 OM_uint32
    107 ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
    108 {
    109 	if (*ctx)
    110 		ssh_gssapi_delete_ctx(ctx);
    111 	ssh_gssapi_build_ctx(ctx);
    112 	ssh_gssapi_set_oid(*ctx, oid);
    113 	return (ssh_gssapi_acquire_cred(*ctx));
    114 }
    115 
    116 /* Unprivileged */
    117 void
    118 ssh_gssapi_supported_oids(gss_OID_set *oidset)
    119 {
    120 	int i = 0;
    121 	OM_uint32 min_status;
    122 	int present;
    123 	gss_OID_set supported;
    124 
    125 	gss_create_empty_oid_set(&min_status, oidset);
    126 	gss_indicate_mechs(&min_status, &supported);
    127 
    128 	while (supported_mechs[i]->name != NULL) {
    129 		if (GSS_ERROR(gss_test_oid_set_member(&min_status,
    130 		    &supported_mechs[i]->oid, supported, &present)))
    131 			present = 0;
    132 		if (present)
    133 			gss_add_oid_set_member(&min_status,
    134 			    &supported_mechs[i]->oid, oidset);
    135 		i++;
    136 	}
    137 
    138 	gss_release_oid_set(&min_status, &supported);
    139 }
    140 
    141 
    142 /* Wrapper around accept_sec_context
    143  * Requires that the context contains:
    144  *    oid
    145  *    credentials	(from ssh_gssapi_acquire_cred)
    146  */
    147 /* Privileged */
    148 OM_uint32
    149 ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *recv_tok,
    150     gss_buffer_desc *send_tok, OM_uint32 *flags)
    151 {
    152 	OM_uint32 status;
    153 	gss_OID mech;
    154 
    155 	ctx->major = gss_accept_sec_context(&ctx->minor,
    156 	    &ctx->context, ctx->creds, recv_tok,
    157 	    GSS_C_NO_CHANNEL_BINDINGS, &ctx->client, &mech,
    158 	    send_tok, flags, NULL, &ctx->client_creds);
    159 
    160 	if (GSS_ERROR(ctx->major))
    161 		ssh_gssapi_error(ctx);
    162 
    163 	if (ctx->client_creds)
    164 		debug("Received some client credentials");
    165 	else
    166 		debug("Got no client credentials");
    167 
    168 	status = ctx->major;
    169 
    170 	/* Now, if we're complete and we have the right flags, then
    171 	 * we flag the user as also having been authenticated
    172 	 */
    173 
    174 	if (((flags == NULL) || ((*flags & GSS_C_MUTUAL_FLAG) &&
    175 	    (*flags & GSS_C_INTEG_FLAG))) && (ctx->major == GSS_S_COMPLETE)) {
    176 		if (ssh_gssapi_getclient(ctx, &gssapi_client))
    177 			fatal("Couldn't convert client name");
    178 	}
    179 
    180 	return (status);
    181 }
    182 
    183 /*
    184  * This parses an exported name, extracting the mechanism specific portion
    185  * to use for ACL checking. It verifies that the name belongs the mechanism
    186  * originally selected.
    187  */
    188 static OM_uint32
    189 ssh_gssapi_parse_ename(Gssctxt *ctx, gss_buffer_t ename, gss_buffer_t name)
    190 {
    191 	u_char *tok;
    192 	OM_uint32 offset;
    193 	OM_uint32 oidl;
    194 
    195 	tok = ename->value;
    196 
    197 	/*
    198 	 * Check that ename is long enough for all of the fixed length
    199 	 * header, and that the initial ID bytes are correct
    200 	 */
    201 
    202 	if (ename->length < 6 || memcmp(tok, "\x04\x01", 2) != 0)
    203 		return GSS_S_FAILURE;
    204 
    205 	/*
    206 	 * Extract the OID, and check it. Here GSSAPI breaks with tradition
    207 	 * and does use the OID type and length bytes. To confuse things
    208 	 * there are two lengths - the first including these, and the
    209 	 * second without.
    210 	 */
    211 
    212 	oidl = get_u16(tok+2); /* length including next two bytes */
    213 	oidl = oidl-2; /* turn it into the _real_ length of the variable OID */
    214 
    215 	/*
    216 	 * Check the BER encoding for correct type and length, that the
    217 	 * string is long enough and that the OID matches that in our context
    218 	 */
    219 	if (tok[4] != 0x06 || tok[5] != oidl ||
    220 	    ename->length < oidl+6 ||
    221 	    !ssh_gssapi_check_oid(ctx, tok+6, oidl))
    222 		return GSS_S_FAILURE;
    223 
    224 	offset = oidl+6;
    225 
    226 	if (ename->length < offset+4)
    227 		return GSS_S_FAILURE;
    228 
    229 	name->length = get_u32(tok+offset);
    230 	offset += 4;
    231 
    232 	if (UINT_MAX - offset < name->length)
    233 		return GSS_S_FAILURE;
    234 	if (ename->length < offset+name->length)
    235 		return GSS_S_FAILURE;
    236 
    237 	name->value = xmalloc(name->length+1);
    238 	memcpy(name->value, tok+offset, name->length);
    239 	((char *)name->value)[name->length] = 0;
    240 
    241 	return GSS_S_COMPLETE;
    242 }
    243 
    244 /* Extract the client details from a given context. This can only reliably
    245  * be called once for a context */
    246 
    247 /* Privileged (called from accept_secure_ctx) */
    248 OM_uint32
    249 ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
    250 {
    251 	int i = 0;
    252 
    253 	gss_buffer_desc ename;
    254 
    255 	client->mech = NULL;
    256 
    257 	while (supported_mechs[i]->name != NULL) {
    258 		if (supported_mechs[i]->oid.length == ctx->oid->length &&
    259 		    (memcmp(supported_mechs[i]->oid.elements,
    260 		    ctx->oid->elements, ctx->oid->length) == 0))
    261 			client->mech = supported_mechs[i];
    262 		i++;
    263 	}
    264 
    265 	if (client->mech == NULL)
    266 		return GSS_S_FAILURE;
    267 
    268 	if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
    269 	    &client->displayname, NULL))) {
    270 		ssh_gssapi_error(ctx);
    271 		return (ctx->major);
    272 	}
    273 
    274 	if ((ctx->major = gss_export_name(&ctx->minor, ctx->client,
    275 	    &ename))) {
    276 		ssh_gssapi_error(ctx);
    277 		return (ctx->major);
    278 	}
    279 
    280 	if ((ctx->major = ssh_gssapi_parse_ename(ctx,&ename,
    281 	    &client->exportedname))) {
    282 		return (ctx->major);
    283 	}
    284 
    285 	/* We can't copy this structure, so we just move the pointer to it */
    286 	client->creds = ctx->client_creds;
    287 	ctx->client_creds = GSS_C_NO_CREDENTIAL;
    288 	return (ctx->major);
    289 }
    290 
    291 /* As user - called on fatal/exit */
    292 void
    293 ssh_gssapi_cleanup_creds(void)
    294 {
    295 	if (gssapi_client.store.filename != NULL) {
    296 		/* Unlink probably isn't sufficient */
    297 		debug("removing gssapi cred file\"%s\"",
    298 		    gssapi_client.store.filename);
    299 		unlink(gssapi_client.store.filename);
    300 	}
    301 }
    302 
    303 /* As user */
    304 void
    305 ssh_gssapi_storecreds(void)
    306 {
    307 	if (gssapi_client.mech && gssapi_client.mech->storecreds) {
    308 		(*gssapi_client.mech->storecreds)(&gssapi_client);
    309 	} else
    310 		debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism");
    311 }
    312 
    313 /* This allows GSSAPI methods to do things to the childs environment based
    314  * on the passed authentication process and credentials.
    315  */
    316 /* As user */
    317 void
    318 ssh_gssapi_do_child(char ***envp, u_int *envsizep)
    319 {
    320 
    321 	if (gssapi_client.store.envvar != NULL &&
    322 	    gssapi_client.store.envval != NULL) {
    323 		debug("Setting %s to %s", gssapi_client.store.envvar,
    324 		    gssapi_client.store.envval);
    325 		child_set_env(envp, envsizep, gssapi_client.store.envvar,
    326 		    gssapi_client.store.envval);
    327 	}
    328 }
    329 
    330 /* Privileged */
    331 int
    332 ssh_gssapi_userok(char *user)
    333 {
    334 	OM_uint32 lmin;
    335 
    336 	if (gssapi_client.exportedname.length == 0 ||
    337 	    gssapi_client.exportedname.value == NULL) {
    338 		debug("No suitable client data");
    339 		return 0;
    340 	}
    341 	if (gssapi_client.mech && gssapi_client.mech->userok)
    342 		if ((*gssapi_client.mech->userok)(&gssapi_client, user))
    343 			return 1;
    344 		else {
    345 			/* Destroy delegated credentials if userok fails */
    346 			gss_release_buffer(&lmin, &gssapi_client.displayname);
    347 			gss_release_buffer(&lmin, &gssapi_client.exportedname);
    348 			gss_release_cred(&lmin, &gssapi_client.creds);
    349 			memset(&gssapi_client, 0, sizeof(ssh_gssapi_client));
    350 			return 0;
    351 		}
    352 	else
    353 		debug("ssh_gssapi_userok: Unknown GSSAPI mechanism");
    354 	return (0);
    355 }
    356 
    357 /* Privileged */
    358 OM_uint32
    359 ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
    360 {
    361 	ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
    362 	    gssbuf, gssmic, NULL);
    363 
    364 	return (ctx->major);
    365 }
    366 
    367 #endif
    368