1294336Sdes/* $OpenBSD: gss-serv.c,v 1.29 2015/05/22 03:50:02 djm Exp $ */
2124208Sdes
3124208Sdes/*
4124208Sdes * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
5124208Sdes *
6124208Sdes * Redistribution and use in source and binary forms, with or without
7124208Sdes * modification, are permitted provided that the following conditions
8124208Sdes * are met:
9124208Sdes * 1. Redistributions of source code must retain the above copyright
10124208Sdes *    notice, this list of conditions and the following disclaimer.
11124208Sdes * 2. Redistributions in binary form must reproduce the above copyright
12124208Sdes *    notice, this list of conditions and the following disclaimer in the
13124208Sdes *    documentation and/or other materials provided with the distribution.
14124208Sdes *
15124208Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
16124208Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17124208Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18124208Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19124208Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20124208Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21124208Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22124208Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23124208Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24124208Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25124208Sdes */
26124208Sdes
27124208Sdes#include "includes.h"
28124208Sdes
29124208Sdes#ifdef GSSAPI
30124208Sdes
31162852Sdes#include <sys/types.h>
32162852Sdes
33162852Sdes#include <stdarg.h>
34162852Sdes#include <string.h>
35162852Sdes#include <unistd.h>
36162852Sdes
37181111Sdes#include "openbsd-compat/sys-queue.h"
38162852Sdes#include "xmalloc.h"
39162852Sdes#include "buffer.h"
40162852Sdes#include "key.h"
41162852Sdes#include "hostfile.h"
42124208Sdes#include "auth.h"
43124208Sdes#include "log.h"
44124208Sdes#include "channels.h"
45124208Sdes#include "session.h"
46162852Sdes#include "misc.h"
47294336Sdes#include "servconf.h"
48124208Sdes
49124208Sdes#include "ssh-gss.h"
50124208Sdes
51294336Sdesextern ServerOptions options;
52294336Sdes
53124208Sdesstatic ssh_gssapi_client gssapi_client =
54124208Sdes    { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
55255767Sdes    GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}};
56124208Sdes
57124208Sdesssh_gssapi_mech gssapi_null_mech =
58124208Sdes    { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL};
59124208Sdes
60124208Sdes#ifdef KRB5
61124208Sdesextern ssh_gssapi_mech gssapi_kerberos_mech;
62124208Sdes#endif
63124208Sdes
64124208Sdesssh_gssapi_mech* supported_mechs[]= {
65124208Sdes#ifdef KRB5
66124208Sdes	&gssapi_kerberos_mech,
67124208Sdes#endif
68124208Sdes	&gssapi_null_mech,
69124208Sdes};
70124208Sdes
71263712Sdes/*
72263712Sdes * ssh_gssapi_supported_oids() can cause sandbox violations, so prepare the
73263712Sdes * list of supported mechanisms before privsep is set up.
74263712Sdes */
75263712Sdesstatic gss_OID_set supported_oids;
76181111Sdes
77263712Sdesvoid
78263712Sdesssh_gssapi_prepare_supported_oids(void)
79263712Sdes{
80263712Sdes	ssh_gssapi_supported_oids(&supported_oids);
81263712Sdes}
82263712Sdes
83263712SdesOM_uint32
84263712Sdesssh_gssapi_test_oid_supported(OM_uint32 *ms, gss_OID member, int *present)
85263712Sdes{
86263712Sdes	if (supported_oids == NULL)
87263712Sdes		ssh_gssapi_prepare_supported_oids();
88263712Sdes	return gss_test_oid_set_member(ms, member, supported_oids, present);
89263712Sdes}
90263712Sdes
91181111Sdes/*
92181111Sdes * Acquire credentials for a server running on the current host.
93181111Sdes * Requires that the context structure contains a valid OID
94181111Sdes */
95181111Sdes
96181111Sdes/* Returns a GSSAPI error code */
97181111Sdes/* Privileged (called from ssh_gssapi_server_ctx) */
98181111Sdesstatic OM_uint32
99181111Sdesssh_gssapi_acquire_cred(Gssctxt *ctx)
100181111Sdes{
101181111Sdes	OM_uint32 status;
102294328Sdes	char lname[NI_MAXHOST];
103181111Sdes	gss_OID_set oidset;
104181111Sdes
105294336Sdes	if (options.gss_strict_acceptor) {
106294336Sdes		gss_create_empty_oid_set(&status, &oidset);
107294336Sdes		gss_add_oid_set_member(&status, ctx->oid, &oidset);
108181111Sdes
109294336Sdes		if (gethostname(lname, MAXHOSTNAMELEN)) {
110294336Sdes			gss_release_oid_set(&status, &oidset);
111294336Sdes			return (-1);
112294336Sdes		}
113181111Sdes
114294336Sdes		if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) {
115294336Sdes			gss_release_oid_set(&status, &oidset);
116294336Sdes			return (ctx->major);
117294336Sdes		}
118294336Sdes
119294336Sdes		if ((ctx->major = gss_acquire_cred(&ctx->minor,
120294336Sdes		    ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds,
121294336Sdes		    NULL, NULL)))
122294336Sdes			ssh_gssapi_error(ctx);
123294336Sdes
124181111Sdes		gss_release_oid_set(&status, &oidset);
125181111Sdes		return (ctx->major);
126294336Sdes	} else {
127294336Sdes		ctx->name = GSS_C_NO_NAME;
128294336Sdes		ctx->creds = GSS_C_NO_CREDENTIAL;
129181111Sdes	}
130294336Sdes	return GSS_S_COMPLETE;
131181111Sdes}
132181111Sdes
133181111Sdes/* Privileged */
134181111SdesOM_uint32
135181111Sdesssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
136181111Sdes{
137181111Sdes	if (*ctx)
138181111Sdes		ssh_gssapi_delete_ctx(ctx);
139181111Sdes	ssh_gssapi_build_ctx(ctx);
140181111Sdes	ssh_gssapi_set_oid(*ctx, oid);
141181111Sdes	return (ssh_gssapi_acquire_cred(*ctx));
142181111Sdes}
143181111Sdes
144157016Sdes/* Unprivileged */
145124208Sdesvoid
146124208Sdesssh_gssapi_supported_oids(gss_OID_set *oidset)
147124208Sdes{
148124208Sdes	int i = 0;
149124208Sdes	OM_uint32 min_status;
150124208Sdes	int present;
151124208Sdes	gss_OID_set supported;
152124208Sdes
153124208Sdes	gss_create_empty_oid_set(&min_status, oidset);
154124208Sdes	gss_indicate_mechs(&min_status, &supported);
155124208Sdes
156124208Sdes	while (supported_mechs[i]->name != NULL) {
157124208Sdes		if (GSS_ERROR(gss_test_oid_set_member(&min_status,
158124208Sdes		    &supported_mechs[i]->oid, supported, &present)))
159124208Sdes			present = 0;
160124208Sdes		if (present)
161124208Sdes			gss_add_oid_set_member(&min_status,
162124208Sdes			    &supported_mechs[i]->oid, oidset);
163124208Sdes		i++;
164124208Sdes	}
165162852Sdes
166162852Sdes	gss_release_oid_set(&min_status, &supported);
167124208Sdes}
168124208Sdes
169124208Sdes
170124208Sdes/* Wrapper around accept_sec_context
171124208Sdes * Requires that the context contains:
172124208Sdes *    oid
173124208Sdes *    credentials	(from ssh_gssapi_acquire_cred)
174124208Sdes */
175157016Sdes/* Privileged */
176124208SdesOM_uint32
177124208Sdesssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *recv_tok,
178124208Sdes    gss_buffer_desc *send_tok, OM_uint32 *flags)
179124208Sdes{
180124208Sdes	OM_uint32 status;
181124208Sdes	gss_OID mech;
182124208Sdes
183124208Sdes	ctx->major = gss_accept_sec_context(&ctx->minor,
184124208Sdes	    &ctx->context, ctx->creds, recv_tok,
185124208Sdes	    GSS_C_NO_CHANNEL_BINDINGS, &ctx->client, &mech,
186124208Sdes	    send_tok, flags, NULL, &ctx->client_creds);
187124208Sdes
188124208Sdes	if (GSS_ERROR(ctx->major))
189124208Sdes		ssh_gssapi_error(ctx);
190124208Sdes
191124208Sdes	if (ctx->client_creds)
192124208Sdes		debug("Received some client credentials");
193124208Sdes	else
194124208Sdes		debug("Got no client credentials");
195124208Sdes
196124208Sdes	status = ctx->major;
197124208Sdes
198124208Sdes	/* Now, if we're complete and we have the right flags, then
199124208Sdes	 * we flag the user as also having been authenticated
200124208Sdes	 */
201124208Sdes
202124208Sdes	if (((flags == NULL) || ((*flags & GSS_C_MUTUAL_FLAG) &&
203124208Sdes	    (*flags & GSS_C_INTEG_FLAG))) && (ctx->major == GSS_S_COMPLETE)) {
204124208Sdes		if (ssh_gssapi_getclient(ctx, &gssapi_client))
205124208Sdes			fatal("Couldn't convert client name");
206124208Sdes	}
207124208Sdes
208124208Sdes	return (status);
209124208Sdes}
210124208Sdes
211124208Sdes/*
212124208Sdes * This parses an exported name, extracting the mechanism specific portion
213124208Sdes * to use for ACL checking. It verifies that the name belongs the mechanism
214124208Sdes * originally selected.
215124208Sdes */
216124208Sdesstatic OM_uint32
217124208Sdesssh_gssapi_parse_ename(Gssctxt *ctx, gss_buffer_t ename, gss_buffer_t name)
218124208Sdes{
219149749Sdes	u_char *tok;
220124208Sdes	OM_uint32 offset;
221124208Sdes	OM_uint32 oidl;
222124208Sdes
223157016Sdes	tok = ename->value;
224124208Sdes
225124208Sdes	/*
226124208Sdes	 * Check that ename is long enough for all of the fixed length
227124208Sdes	 * header, and that the initial ID bytes are correct
228124208Sdes	 */
229124208Sdes
230157016Sdes	if (ename->length < 6 || memcmp(tok, "\x04\x01", 2) != 0)
231124208Sdes		return GSS_S_FAILURE;
232124208Sdes
233124208Sdes	/*
234124208Sdes	 * Extract the OID, and check it. Here GSSAPI breaks with tradition
235124208Sdes	 * and does use the OID type and length bytes. To confuse things
236124208Sdes	 * there are two lengths - the first including these, and the
237124208Sdes	 * second without.
238124208Sdes	 */
239124208Sdes
240162852Sdes	oidl = get_u16(tok+2); /* length including next two bytes */
241124208Sdes	oidl = oidl-2; /* turn it into the _real_ length of the variable OID */
242124208Sdes
243124208Sdes	/*
244124208Sdes	 * Check the BER encoding for correct type and length, that the
245124208Sdes	 * string is long enough and that the OID matches that in our context
246124208Sdes	 */
247124208Sdes	if (tok[4] != 0x06 || tok[5] != oidl ||
248124208Sdes	    ename->length < oidl+6 ||
249157016Sdes	    !ssh_gssapi_check_oid(ctx, tok+6, oidl))
250124208Sdes		return GSS_S_FAILURE;
251124208Sdes
252124208Sdes	offset = oidl+6;
253124208Sdes
254124208Sdes	if (ename->length < offset+4)
255124208Sdes		return GSS_S_FAILURE;
256124208Sdes
257162852Sdes	name->length = get_u32(tok+offset);
258124208Sdes	offset += 4;
259124208Sdes
260226046Sdes	if (UINT_MAX - offset < name->length)
261226046Sdes		return GSS_S_FAILURE;
262124208Sdes	if (ename->length < offset+name->length)
263124208Sdes		return GSS_S_FAILURE;
264124208Sdes
265124208Sdes	name->value = xmalloc(name->length+1);
266162852Sdes	memcpy(name->value, tok+offset, name->length);
267124208Sdes	((char *)name->value)[name->length] = 0;
268124208Sdes
269124208Sdes	return GSS_S_COMPLETE;
270124208Sdes}
271124208Sdes
272124208Sdes/* Extract the client details from a given context. This can only reliably
273124208Sdes * be called once for a context */
274124208Sdes
275157016Sdes/* Privileged (called from accept_secure_ctx) */
276124208SdesOM_uint32
277124208Sdesssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
278124208Sdes{
279124208Sdes	int i = 0;
280124208Sdes
281124208Sdes	gss_buffer_desc ename;
282124208Sdes
283124208Sdes	client->mech = NULL;
284124208Sdes
285124208Sdes	while (supported_mechs[i]->name != NULL) {
286124208Sdes		if (supported_mechs[i]->oid.length == ctx->oid->length &&
287124208Sdes		    (memcmp(supported_mechs[i]->oid.elements,
288124208Sdes		    ctx->oid->elements, ctx->oid->length) == 0))
289124208Sdes			client->mech = supported_mechs[i];
290124208Sdes		i++;
291124208Sdes	}
292124208Sdes
293124208Sdes	if (client->mech == NULL)
294124208Sdes		return GSS_S_FAILURE;
295124208Sdes
296124208Sdes	if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
297124208Sdes	    &client->displayname, NULL))) {
298124208Sdes		ssh_gssapi_error(ctx);
299124208Sdes		return (ctx->major);
300124208Sdes	}
301124208Sdes
302124208Sdes	if ((ctx->major = gss_export_name(&ctx->minor, ctx->client,
303124208Sdes	    &ename))) {
304124208Sdes		ssh_gssapi_error(ctx);
305124208Sdes		return (ctx->major);
306124208Sdes	}
307124208Sdes
308124208Sdes	if ((ctx->major = ssh_gssapi_parse_ename(ctx,&ename,
309124208Sdes	    &client->exportedname))) {
310124208Sdes		return (ctx->major);
311124208Sdes	}
312124208Sdes
313124208Sdes	/* We can't copy this structure, so we just move the pointer to it */
314124208Sdes	client->creds = ctx->client_creds;
315124208Sdes	ctx->client_creds = GSS_C_NO_CREDENTIAL;
316124208Sdes	return (ctx->major);
317124208Sdes}
318124208Sdes
319126274Sdes/* As user - called on fatal/exit */
320124208Sdesvoid
321126274Sdesssh_gssapi_cleanup_creds(void)
322124208Sdes{
323124208Sdes	if (gssapi_client.store.filename != NULL) {
324124208Sdes		/* Unlink probably isn't sufficient */
325162852Sdes		debug("removing gssapi cred file\"%s\"",
326162852Sdes		    gssapi_client.store.filename);
327124208Sdes		unlink(gssapi_client.store.filename);
328124208Sdes	}
329124208Sdes}
330124208Sdes
331124208Sdes/* As user */
332124208Sdesvoid
333124208Sdesssh_gssapi_storecreds(void)
334124208Sdes{
335124208Sdes	if (gssapi_client.mech && gssapi_client.mech->storecreds) {
336124208Sdes		(*gssapi_client.mech->storecreds)(&gssapi_client);
337124208Sdes	} else
338124208Sdes		debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism");
339124208Sdes}
340124208Sdes
341124208Sdes/* This allows GSSAPI methods to do things to the childs environment based
342124208Sdes * on the passed authentication process and credentials.
343124208Sdes */
344124208Sdes/* As user */
345124208Sdesvoid
346124208Sdesssh_gssapi_do_child(char ***envp, u_int *envsizep)
347124208Sdes{
348124208Sdes
349124208Sdes	if (gssapi_client.store.envvar != NULL &&
350124208Sdes	    gssapi_client.store.envval != NULL) {
351124208Sdes		debug("Setting %s to %s", gssapi_client.store.envvar,
352157016Sdes		    gssapi_client.store.envval);
353124208Sdes		child_set_env(envp, envsizep, gssapi_client.store.envvar,
354149749Sdes		    gssapi_client.store.envval);
355124208Sdes	}
356124208Sdes}
357124208Sdes
358157016Sdes/* Privileged */
359124208Sdesint
360124208Sdesssh_gssapi_userok(char *user)
361124208Sdes{
362149749Sdes	OM_uint32 lmin;
363149749Sdes
364124208Sdes	if (gssapi_client.exportedname.length == 0 ||
365124208Sdes	    gssapi_client.exportedname.value == NULL) {
366124208Sdes		debug("No suitable client data");
367124208Sdes		return 0;
368124208Sdes	}
369124208Sdes	if (gssapi_client.mech && gssapi_client.mech->userok)
370149749Sdes		if ((*gssapi_client.mech->userok)(&gssapi_client, user))
371149749Sdes			return 1;
372149749Sdes		else {
373149749Sdes			/* Destroy delegated credentials if userok fails */
374149749Sdes			gss_release_buffer(&lmin, &gssapi_client.displayname);
375149749Sdes			gss_release_buffer(&lmin, &gssapi_client.exportedname);
376149749Sdes			gss_release_cred(&lmin, &gssapi_client.creds);
377263712Sdes			explicit_bzero(&gssapi_client,
378263712Sdes			    sizeof(ssh_gssapi_client));
379149749Sdes			return 0;
380149749Sdes		}
381124208Sdes	else
382124208Sdes		debug("ssh_gssapi_userok: Unknown GSSAPI mechanism");
383124208Sdes	return (0);
384124208Sdes}
385124208Sdes
386157016Sdes/* Privileged */
387126274SdesOM_uint32
388126274Sdesssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
389126274Sdes{
390126274Sdes	ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
391126274Sdes	    gssbuf, gssmic, NULL);
392126274Sdes
393126274Sdes	return (ctx->major);
394126274Sdes}
395126274Sdes
396124208Sdes#endif
397