]> andersk Git - gssapi-openssh.git/blob - openssh/gss-genr.c
on gssapi authentication errors, send error information back to the client
[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 static void
245 ssh_gssapi_error_ex(OM_uint32 major_status,OM_uint32 minor_status,
246                     int send_packet) {
247         OM_uint32 lmaj, lmin;
248         gss_buffer_desc msg;
249         OM_uint32 ctx;
250         
251         ctx = 0;
252         /* The GSSAPI error */
253         do {
254                 lmaj = gss_display_status(&lmin, major_status,
255                                           GSS_C_GSS_CODE,
256                                           GSS_C_NULL_OID,
257                                           &ctx, &msg);
258                 if (lmaj == GSS_S_COMPLETE) {
259                         debug((char *)msg.value);
260                         if (send_packet) packet_send_debug((char *)msg.value);
261                         (void) gss_release_buffer(&lmin, &msg);
262                 }
263         } while (ctx!=0);          
264
265         /* The mechanism specific error */
266         do {
267                 lmaj = gss_display_status(&lmin, minor_status,
268                                           GSS_C_MECH_CODE,
269                                           GSS_C_NULL_OID,
270                                           &ctx, &msg);
271                 if (lmaj == GSS_S_COMPLETE) {
272                         debug((char *)msg.value);
273                         if (send_packet) packet_send_debug((char *)msg.value);
274                         (void) gss_release_buffer(&lmin, &msg);
275                 }
276         } while (ctx!=0);
277 }
278
279 void
280 ssh_gssapi_error(OM_uint32 major_status,OM_uint32 minor_status) {
281     ssh_gssapi_error_ex(major_status, minor_status, 0);
282 }
283
284 void
285 ssh_gssapi_send_error(OM_uint32 major_status,OM_uint32 minor_status) {
286     ssh_gssapi_error_ex(major_status, minor_status, 1);
287 }
288
289
290
291
292 /* Initialise our GSSAPI context. We use this opaque structure to contain all
293  * of the data which both the client and server need to persist across
294  * {accept,init}_sec_context calls, so that when we do it from the userauth
295  * stuff life is a little easier
296  */
297 void
298 ssh_gssapi_build_ctx(Gssctxt *ctx)
299 {
300         ctx->context=GSS_C_NO_CONTEXT;
301         ctx->name=GSS_C_NO_NAME;
302         ctx->oid=GSS_C_NO_OID;
303         ctx->creds=GSS_C_NO_CREDENTIAL;
304         ctx->client=GSS_C_NO_NAME;
305         ctx->client_creds=GSS_C_NO_CREDENTIAL;
306 }
307
308 /* Delete our context, providing it has been built correctly */
309 void
310 ssh_gssapi_delete_ctx(Gssctxt *ctx)
311 {
312         OM_uint32 ms;
313         
314         if (ctx->context != GSS_C_NO_CONTEXT) 
315                 gss_delete_sec_context(&ms,&ctx->context,GSS_C_NO_BUFFER);
316         if (ctx->name != GSS_C_NO_NAME)
317                 gss_release_name(&ms,&ctx->name);
318         if (ctx->oid != GSS_C_NO_OID) {
319                 xfree(ctx->oid->elements);
320                 xfree(ctx->oid);
321                 ctx->oid = GSS_C_NO_OID;
322         }
323         if (ctx->creds != GSS_C_NO_CREDENTIAL)
324                 gss_release_cred(&ms,&ctx->creds);
325         if (ctx->client != GSS_C_NO_NAME)
326                 gss_release_name(&ms,&ctx->client);     
327         if (ctx->client_creds != GSS_C_NO_CREDENTIAL)
328                 gss_release_cred(&ms,&ctx->client_creds); 
329 }
330
331 /* Wrapper to init_sec_context 
332  * Requires that the context contains:
333  *      oid
334  *      server name (from ssh_gssapi_import_name)
335  */
336 OM_uint32 
337 ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
338                     gss_buffer_desc* send_tok, OM_uint32 *flags) 
339 {
340         OM_uint32 maj_status, min_status;
341         int deleg_flag = 0;
342         
343         if (deleg_creds) {
344                 deleg_flag=GSS_C_DELEG_FLAG;
345                 debug("Delegating credentials");
346         }
347                 
348         maj_status=gss_init_sec_context(&min_status,
349                                         GSS_C_NO_CREDENTIAL, /* def. cred */
350                                         &ctx->context,
351                                         ctx->name,
352                                         ctx->oid,
353                                         GSS_C_MUTUAL_FLAG |
354                                         GSS_C_INTEG_FLAG |
355                                         deleg_flag,
356                                         0, /* default lifetime */
357                                         NULL, /* no channel bindings */
358                                         recv_tok,
359                                         NULL,
360                                         send_tok,
361                                         flags,
362                                         NULL);
363         ctx->status=maj_status;
364         if (GSS_ERROR(maj_status)) {
365                 ssh_gssapi_error(maj_status,min_status);
366         }
367         return(maj_status);
368 }
369
370 /* Wrapper arround accept_sec_context
371  * Requires that the context contains:
372  *    oid               
373  *    credentials       (from ssh_gssapi_acquire_cred)
374  */
375 OM_uint32 ssh_gssapi_accept_ctx(Gssctxt *ctx,gss_buffer_desc *recv_tok,
376                                 gss_buffer_desc *send_tok, OM_uint32 *flags) 
377 {
378         OM_uint32 maj_status, min_status;
379         gss_OID mech;
380         
381         maj_status=gss_accept_sec_context(&min_status,
382                                           &ctx->context,
383                                           ctx->creds,
384                                           recv_tok,
385                                           GSS_C_NO_CHANNEL_BINDINGS,
386                                           &ctx->client,
387                                           &mech,
388                                           send_tok,
389                                           flags,
390                                           NULL,
391                                           &ctx->client_creds);
392         if (GSS_ERROR(maj_status)) {
393                 ssh_gssapi_send_error(maj_status,min_status);
394         }
395         
396         if (ctx->client_creds) {
397                 debug("Received some client credentials");
398         } else {
399                 debug("Got no client credentials");
400         }
401
402         /* FIXME: We should check that the mechanism thats being used is
403          * the one that we asked for (in ctx->oid) */
404
405         ctx->status=maj_status;
406         
407         return(maj_status);
408 }
409
410 /* Create a service name for the given host */
411 OM_uint32
412 ssh_gssapi_import_name(Gssctxt *ctx, const char *host) {
413         gss_buffer_desc gssbuf;
414         OM_uint32 maj_status, min_status;
415         struct hostent *hostinfo = NULL;
416         char *xhost;
417         
418         /* Make a copy of the host name, in case it was returned by a
419          * previous call to gethostbyname(). */ 
420         xhost = xstrdup(host);
421
422         /* Make sure we have the FQDN. Some GSSAPI implementations don't do
423          * this for us themselves */
424         
425         hostinfo = gethostbyname(xhost);
426         
427         if ((hostinfo == NULL) || (hostinfo->h_name == NULL)) {
428                 debug("Unable to get FQDN for \"%s\"", xhost);
429         } else {
430                 xfree(xhost);
431                 xhost = xstrdup(hostinfo->h_name);
432         }
433                 
434         gssbuf.length = sizeof("host@")+strlen(xhost);
435
436         gssbuf.value = xmalloc(gssbuf.length);
437         if (gssbuf.value == NULL) {
438                 xfree(xhost);
439                 return(-1);
440         }
441         snprintf(gssbuf.value,gssbuf.length,"host@%s",xhost);
442         if ((maj_status=gss_import_name(&min_status,
443                                         &gssbuf,
444                                         GSS_C_NT_HOSTBASED_SERVICE,
445                                         &ctx->name))) {
446                 ssh_gssapi_error(maj_status,min_status);
447         }
448         
449         xfree(xhost);
450         xfree(gssbuf.value);
451         return(maj_status);
452 }
453
454 /* Acquire credentials for a server running on the current host.
455  * Requires that the context structure contains a valid OID
456  */      
457 OM_uint32
458 ssh_gssapi_acquire_cred(Gssctxt *ctx) {
459         OM_uint32 maj_status, min_status;
460         char lname[MAXHOSTNAMELEN];
461         gss_OID_set oidset;
462         
463         gss_create_empty_oid_set(&min_status,&oidset);
464         gss_add_oid_set_member(&min_status,ctx->oid,&oidset);
465         
466         if (gethostname(lname, MAXHOSTNAMELEN)) {
467                 return(-1);
468         }
469
470         if ((maj_status=ssh_gssapi_import_name(ctx,lname))) {
471                 return(maj_status);
472         }
473         if ((maj_status=gss_acquire_cred(&min_status,
474                                     ctx->name,
475                                     0,
476                                     oidset,
477                                     GSS_C_ACCEPT,
478                                     &ctx->creds,
479                                     NULL,
480                                     NULL))) {
481                 ssh_gssapi_error(maj_status,min_status);
482         }
483                                 
484         gss_release_oid_set(&min_status, &oidset);
485         return(maj_status);
486 }
487
488 /* Extract the client details from a given context. This can only reliably
489  * be called once for a context */
490
491 OM_uint32 
492 ssh_gssapi_getclient(Gssctxt *ctx, enum ssh_gss_id *type,
493                      gss_buffer_desc *name, gss_cred_id_t *creds) {
494
495         OM_uint32 maj_status,min_status;
496         
497         *type=ssh_gssapi_get_ctype(ctx);
498         if ((maj_status=gss_display_name(&min_status,ctx->client,name,NULL))) {
499                 ssh_gssapi_error(maj_status,min_status);
500         }
501         
502         /* This is icky. There appears to be no way to copy this structure,
503          * rather than the pointer to it, so we simply copy the pointer and
504          * mark the originator as empty so we don't destroy it. 
505          */
506         *creds=ctx->client_creds;
507         ctx->client_creds=GSS_C_NO_CREDENTIAL;
508         return(maj_status);
509 }
510         
511 #endif /* GSSAPI */
This page took 0.07152 seconds and 5 git commands to generate.