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