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