]> andersk Git - gssapi-openssh.git/blame - openssh/gss-genr.c
- fix up a merge error
[gssapi-openssh.git] / openssh / gss-genr.c
CommitLineData
5598e598 1/*
2 * Copyright (c) 2001 Simon Wilkinson. All rights reserved. *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions
5 * are met:
6 * 1. Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * 2. Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
11 *
12 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
13 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
14 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
15 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
16 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
17 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
18 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
19 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
21 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22 */
23
24#include "includes.h"
25
26#ifdef GSSAPI
27
28#include "ssh.h"
29#include "ssh2.h"
30#include "xmalloc.h"
31#include "buffer.h"
32#include "bufaux.h"
33#include "packet.h"
34#include "compat.h"
35#include <openssl/evp.h>
36#include "cipher.h"
37#include "kex.h"
38#include "log.h"
39
40#include <netdb.h>
41
42#include "ssh-gss.h"
43
44/* Assorted globals for tracking the clients identity once they've
45 * authenticated */
46
47gss_buffer_desc gssapi_client_name = {0,NULL}; /* Name of our client */
48gss_cred_id_t gssapi_client_creds = GSS_C_NO_CREDENTIAL; /* Their credentials */
49enum ssh_gss_id gssapi_client_type = GSS_LAST_ENTRY;
50
51/* The mechanism name used in the list below is defined in the internet
52 * draft as the Base 64 encoding of the MD5 hash of the ASN.1 DER encoding
53 * of the underlying GSSAPI mechanism's OID.
54 *
55 * Also from the draft, before considering adding SPNEGO, bear in mind that
56 * "mechanisms ... MUST NOT use SPNEGO as the underlying GSSAPI mechanism"
57 */
58
59/* These must be in the same order as ssh_gss_id, in ssh-gss.h */
60
61ssh_gssapi_mech supported_mechs[]= {
62#ifdef KRB5
63 /* Official OID - 1.2.850.113554.1.2.2 */
64 {"Se3H81ismmOC3OE+FwYCiQ==","Kerberos",
65 {9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"}},
66#endif
67#ifdef GSI
68 /* gssapi_ssleay 1.3.6.1.4.1.3536.1.1 */
69 {"N3+k7/4wGxHyuP8Yxi4RhA==",
70 "GSI",
71 {9, "\x2B\x06\x01\x04\x01\x9B\x50\x01\x01"}
72 },
73#endif /* GSI */
74 {NULL,NULL,{0,0}}
75};
76
77char gssprefix[]=KEX_GSS_SHA1;
78
79/* Return a list of the gss-group1-sha1-x mechanisms supported by this
80 * program.
81 *
82 * We only support the mechanisms that we've indicated in the list above,
83 * but we check that they're supported by the GSSAPI mechanism on the
84 * machine. We also check, before including them in the list, that
85 * we have the necesary information in order to carry out the key exchange
86 * (that is, that the user has credentials, the server's creds are accessible,
87 * etc)
88 *
89 * The way that this is done is fairly nasty, as we do a lot of work that
90 * is then thrown away. This should possibly be implemented with a cache
91 * that stores the results (in an expanded Gssctxt structure), which are
92 * then used by the first calls if that key exchange mechanism is chosen.
93 */
94
95char *
96ssh_gssapi_mechanisms(int server,char *host) {
97 gss_OID_set supported;
98 OM_uint32 maj_status, min_status;
99 Buffer buf;
100 int i = 0;
101 int present;
102 char * mechs;
103 Gssctxt ctx;
104 gss_buffer_desc token;
105
106
107 gss_indicate_mechs(&min_status, &supported);
108
109 buffer_init(&buf);
110
111 do {
112 if ((maj_status=gss_test_oid_set_member(&min_status,
113 &supported_mechs[i].oid,
114 supported,
115 &present))) {
116 present=0;
117 }
118 if (present) {
119 ssh_gssapi_build_ctx(&ctx);
120 ssh_gssapi_set_oid(&ctx,&supported_mechs[i].oid);
121 if (server) {
122 if (ssh_gssapi_acquire_cred(&ctx)) {
123 ssh_gssapi_delete_ctx(&ctx);
124 continue;
125 }
126 }
127 else { /* client */
128 if (ssh_gssapi_import_name(&ctx,host))
129 continue;
130 maj_status=ssh_gssapi_init_ctx(&ctx, 0,
131 GSS_C_NO_BUFFER,
132 &token,
133 NULL);
134 ssh_gssapi_delete_ctx(&ctx);
135 if (GSS_ERROR(maj_status)) {
136 continue;
137 }
138 }
139
140 /* Append gss_group1_sha1_x to our list */
141 buffer_append(&buf, gssprefix,
142 strlen(gssprefix));
143 buffer_append(&buf, supported_mechs[i].enc_name,
144 strlen(supported_mechs[i].enc_name));
145 }
146 } while (supported_mechs[++i].name != NULL);
147
148 buffer_put_char(&buf,'\0');
149
150 mechs=xmalloc(buffer_len(&buf));
151 buffer_get(&buf,mechs,buffer_len(&buf));
152 buffer_free(&buf);
153 if (strlen(mechs)==0)
154 return(NULL);
155 else
156 return(mechs);
157}
158
159void ssh_gssapi_supported_oids(gss_OID_set *oidset) {
160 enum ssh_gss_id i =0;
161 OM_uint32 maj_status,min_status;
162 int present;
163 gss_OID_set supported;
164
165 gss_create_empty_oid_set(&min_status,oidset);
166 gss_indicate_mechs(&min_status, &supported);
167
168 while (supported_mechs[i].name!=NULL) {
169 if ((maj_status=gss_test_oid_set_member(&min_status,
170 &supported_mechs[i].oid,
171 supported,
172 &present))) {
173 present=0;
174 }
175 if (present) {
176 gss_add_oid_set_member(&min_status,
177 &supported_mechs[i].oid,
178 oidset);
179 }
180 i++;
181 }
182}
183
184/* Set the contexts OID from a data stream */
185void ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len) {
186 if (ctx->oid != GSS_C_NO_OID) {
187 xfree(ctx->oid->elements);
188 xfree(ctx->oid);
189 }
190 ctx->oid=xmalloc(sizeof(gss_OID_desc));
191 ctx->oid->length=len;
192 ctx->oid->elements=xmalloc(len);
193 memcpy(ctx->oid->elements,data,len);
194}
195
196/* Set the contexts OID */
197void ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid) {
198 ssh_gssapi_set_oid_data(ctx,oid->elements,oid->length);
199}
200
201/* Find out which GSS type (out of the list we define in ssh-gss.h) a
202 * particular connection is using
203 */
204enum ssh_gss_id ssh_gssapi_get_ctype(Gssctxt *ctxt) {
205 enum ssh_gss_id i=0;
206
207 while(supported_mechs[i].name!=NULL &&
208 supported_mechs[i].oid.length != ctxt->oid->length &&
209 (memcmp(supported_mechs[i].oid.elements,
210 ctxt->oid->elements,ctxt->oid->length) !=0)) {
211 i++;
212 }
213 return(i);
214}
215
216/* Set the GSS context's OID to the oid indicated by the given key exchange
217 * name. */
218int ssh_gssapi_id_kex(Gssctxt *ctx, char *name) {
219 enum ssh_gss_id i=0;
220
221 if (strncmp(name, gssprefix, strlen(gssprefix)-1) !=0) {
222 return(1);
223 }
224
225 name+=strlen(gssprefix); /* Move to the start of the MIME string */
226
227 while (supported_mechs[i].name!=NULL &&
228 strcmp(name,supported_mechs[i].enc_name)!=0) {
229 i++;
230 }
231
232 if (supported_mechs[i].name==NULL)
233 return (1);
234
235 ssh_gssapi_set_oid(ctx,&supported_mechs[i].oid);
236
237 return 0;
238}
239
240
241/* All this effort to report an error ... */
242void
243ssh_gssapi_error(OM_uint32 major_status,OM_uint32 minor_status) {
244 OM_uint32 lmaj, lmin;
245 gss_buffer_desc msg;
246 OM_uint32 ctx;
247
248 ctx = 0;
249 /* The GSSAPI error */
250 do {
251 lmaj = gss_display_status(&lmin, major_status,
252 GSS_C_GSS_CODE,
253 GSS_C_NULL_OID,
254 &ctx, &msg);
255 if (lmaj == GSS_S_COMPLETE) {
256 debug((char *)msg.value);
257 (void) gss_release_buffer(&lmin, &msg);
258 }
259 } while (ctx!=0);
260
261 /* The mechanism specific error */
262 do {
263 lmaj = gss_display_status(&lmin, minor_status,
264 GSS_C_MECH_CODE,
265 GSS_C_NULL_OID,
266 &ctx, &msg);
267 if (lmaj == GSS_S_COMPLETE) {
268 debug((char *)msg.value);
269 (void) gss_release_buffer(&lmin, &msg);
270 }
271 } while (ctx!=0);
272}
273
274/* Initialise our GSSAPI context. We use this opaque structure to contain all
275 * of the data which both the client and server need to persist across
276 * {accept,init}_sec_context calls, so that when we do it from the userauth
277 * stuff life is a little easier
278 */
279void
280ssh_gssapi_build_ctx(Gssctxt *ctx)
281{
282 ctx->context=GSS_C_NO_CONTEXT;
283 ctx->name=GSS_C_NO_NAME;
284 ctx->oid=GSS_C_NO_OID;
285 ctx->creds=GSS_C_NO_CREDENTIAL;
286 ctx->client=GSS_C_NO_NAME;
287 ctx->client_creds=GSS_C_NO_CREDENTIAL;
288}
289
290/* Delete our context, providing it has been built correctly */
291void
292ssh_gssapi_delete_ctx(Gssctxt *ctx)
293{
294 OM_uint32 ms;
295
296 if (ctx->context != GSS_C_NO_CONTEXT)
297 gss_delete_sec_context(&ms,&ctx->context,GSS_C_NO_BUFFER);
298 if (ctx->name != GSS_C_NO_NAME)
299 gss_release_name(&ms,&ctx->name);
300 if (ctx->oid != GSS_C_NO_OID) {
301 xfree(ctx->oid->elements);
302 xfree(ctx->oid);
303 ctx->oid = GSS_C_NO_OID;
304 }
305 if (ctx->creds != GSS_C_NO_CREDENTIAL)
306 gss_release_cred(&ms,&ctx->creds);
307 if (ctx->client != GSS_C_NO_NAME)
308 gss_release_name(&ms,&ctx->client);
309 if (ctx->client_creds != GSS_C_NO_CREDENTIAL)
310 gss_release_cred(&ms,&ctx->client_creds);
311}
312
313/* Wrapper to init_sec_context
314 * Requires that the context contains:
315 * oid
316 * server name (from ssh_gssapi_import_name)
317 */
318OM_uint32
319ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
320 gss_buffer_desc* send_tok, OM_uint32 *flags)
321{
322 OM_uint32 maj_status, min_status;
323 int deleg_flag = 0;
324
325 if (deleg_creds) {
326 deleg_flag=GSS_C_DELEG_FLAG;
327 debug("Delegating credentials");
328 }
329
330 maj_status=gss_init_sec_context(&min_status,
331 GSS_C_NO_CREDENTIAL, /* def. cred */
332 &ctx->context,
333 ctx->name,
334 ctx->oid,
335 GSS_C_MUTUAL_FLAG |
336 GSS_C_INTEG_FLAG |
337 deleg_flag,
338 0, /* default lifetime */
339 NULL, /* no channel bindings */
340 recv_tok,
341 NULL,
342 send_tok,
343 flags,
344 NULL);
345 ctx->status=maj_status;
346 if (GSS_ERROR(maj_status)) {
347 ssh_gssapi_error(maj_status,min_status);
348 }
349 return(maj_status);
350}
351
352/* Wrapper arround accept_sec_context
353 * Requires that the context contains:
354 * oid
355 * credentials (from ssh_gssapi_acquire_cred)
356 */
357OM_uint32 ssh_gssapi_accept_ctx(Gssctxt *ctx,gss_buffer_desc *recv_tok,
358 gss_buffer_desc *send_tok, OM_uint32 *flags)
359{
360 OM_uint32 maj_status, min_status;
361 gss_OID mech;
362
363 maj_status=gss_accept_sec_context(&min_status,
364 &ctx->context,
365 ctx->creds,
366 recv_tok,
367 GSS_C_NO_CHANNEL_BINDINGS,
368 &ctx->client,
369 &mech,
370 send_tok,
371 flags,
372 NULL,
373 &ctx->client_creds);
374 if (GSS_ERROR(maj_status)) {
375 ssh_gssapi_error(maj_status,min_status);
376 }
377
378 if (ctx->client_creds) {
379 debug("Received some client credentials");
380 } else {
381 debug("Got no client credentials");
382 }
383
384 /* FIXME: We should check that the mechanism thats being used is
385 * the one that we asked for (in ctx->oid) */
386
387 ctx->status=maj_status;
388
389 return(maj_status);
390}
391
392/* Create a service name for the given host */
393OM_uint32
394ssh_gssapi_import_name(Gssctxt *ctx,char *host) {
395 gss_buffer_desc gssbuf;
396 OM_uint32 maj_status, min_status;
397 struct hostent *hostinfo = NULL;
398 char *xhost;
399
400 /* Make a copy of the host name, in case it was returned by a
401 * previous call to gethostbyname(). */
402 xhost = xstrdup(host);
403
404 /* Make sure we have the FQDN. Some GSSAPI implementations don't do
405 * this for us themselves */
406
407 hostinfo = gethostbyname(xhost);
408
409 if ((hostinfo == NULL) || (hostinfo->h_name == NULL)) {
410 debug("Unable to get FQDN for \"%s\"", xhost);
411 } else {
412 host = hostinfo->h_name;
413 }
414 xfree(xhost);
415
416 gssbuf.length = sizeof("host@")+strlen(host);
417
418 gssbuf.value = xmalloc(gssbuf.length);
419 if (gssbuf.value == NULL) {
420 return(-1);
421 }
422 snprintf(gssbuf.value,gssbuf.length,"host@%s",host);
423 if ((maj_status=gss_import_name(&min_status,
424 &gssbuf,
425 GSS_C_NT_HOSTBASED_SERVICE,
426 &ctx->name))) {
427 ssh_gssapi_error(maj_status,min_status);
428 }
429
430 xfree(gssbuf.value);
431 return(maj_status);
432}
433
434/* Acquire credentials for a server running on the current host.
435 * Requires that the context structure contains a valid OID
436 */
437OM_uint32
438ssh_gssapi_acquire_cred(Gssctxt *ctx) {
439 OM_uint32 maj_status, min_status;
440 char lname[MAXHOSTNAMELEN];
441 gss_OID_set oidset;
442
443 gss_create_empty_oid_set(&min_status,&oidset);
444 gss_add_oid_set_member(&min_status,ctx->oid,&oidset);
445
446 if (gethostname(lname, MAXHOSTNAMELEN)) {
447 return(-1);
448 }
449
450 if ((maj_status=ssh_gssapi_import_name(ctx,lname))) {
451 return(maj_status);
452 }
453 if ((maj_status=gss_acquire_cred(&min_status,
454 ctx->name,
455 0,
456 oidset,
457 GSS_C_ACCEPT,
458 &ctx->creds,
459 NULL,
460 NULL))) {
461 ssh_gssapi_error(maj_status,min_status);
462 }
463
464 gss_release_oid_set(&min_status, &oidset);
465 return(maj_status);
466}
467
468/* Extract the client details from a given context. This can only reliably
469 * be called once for a context */
470
471OM_uint32
472ssh_gssapi_getclient(Gssctxt *ctx, enum ssh_gss_id *type,
473 gss_buffer_desc *name, gss_cred_id_t *creds) {
474
475 OM_uint32 maj_status,min_status;
476
477 *type=ssh_gssapi_get_ctype(ctx);
478 if ((maj_status=gss_display_name(&min_status,ctx->client,name,NULL))) {
479 ssh_gssapi_error(maj_status,min_status);
480 }
481
482 /* This is icky. There appears to be no way to copy this structure,
483 * rather than the pointer to it, so we simply copy the pointer and
484 * mark the originator as empty so we don't destroy it.
485 */
486 *creds=ctx->client_creds;
487 ctx->client_creds=GSS_C_NO_CREDENTIAL;
488 return(maj_status);
489}
490
491#endif /* GSSAPI */
This page took 0.198167 seconds and 5 git commands to generate.