]> andersk Git - gssapi-openssh.git/blob - openssh/gss-serv.c
pass in mechanism OID to gss_display_status() so mechglue can route the
[gssapi-openssh.git] / openssh / gss-serv.c
1 /*
2  * Copyright (c) 2001,2002 Simon Wilkinson. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "includes.h"
26
27 #ifdef GSSAPI
28
29 #include "ssh.h"
30 #include "ssh1.h"
31 #include "ssh2.h"
32 #include "xmalloc.h"
33 #include "buffer.h"
34 #include "bufaux.h"
35 #include "packet.h"
36 #include "compat.h"
37 #include <openssl/evp.h>
38 #include "cipher.h"
39 #include "kex.h"
40 #include "auth.h"
41 #include "log.h"
42 #include "channels.h"
43 #include "session.h"
44 #include "dispatch.h"
45 #include "servconf.h"
46 #include "compat.h"
47 #include "misc.h"
48 #include "monitor_wrap.h"
49
50 #include "ssh-gss.h"
51
52 extern ServerOptions options;
53 extern u_char *session_id2;
54 extern int session_id2_len;
55
56 typedef struct ssh_gssapi_cred_cache {
57         char *filename;
58         char *envvar;
59         char *envval;
60         void *data;
61 } ssh_gssapi_cred_cache;
62
63 static struct ssh_gssapi_cred_cache gssapi_cred_store = {NULL,NULL,NULL};
64
65 /*
66  * Environment variables pointing to delegated credentials
67  */
68 static char *delegation_env[] = {
69   "X509_USER_PROXY",            /* GSSAPI/SSLeay */
70   "KRB5CCNAME",                 /* Krb5 and possibly SSLeay */
71   NULL
72 };
73
74 static void gssapi_unsetenv(const char *var);
75
76 #ifdef KRB5
77
78 #ifdef HEIMDAL
79 #include <krb5.h>
80 #else
81 #include <gssapi_krb5.h>
82 #define krb5_get_err_text(context,code) error_message(code)
83 #endif
84
85 static krb5_context krb_context = NULL;
86
87 /* Initialise the krb5 library, so we can use it for those bits that
88  * GSSAPI won't do */
89
90 int ssh_gssapi_krb5_init() {
91         krb5_error_code problem;
92         
93         if (krb_context !=NULL)
94                 return 1;
95                 
96         problem = krb5_init_context(&krb_context);
97         if (problem) {
98                 log("Cannot initialize krb5 context");
99                 return 0;
100         }
101         krb5_init_ets(krb_context);
102
103         return 1;       
104 }                       
105
106 /* Check if this user is OK to login. This only works with krb5 - other 
107  * GSSAPI mechanisms will need their own.
108  * Returns true if the user is OK to log in, otherwise returns 0
109  */
110
111 int
112 ssh_gssapi_krb5_userok(char *name) {
113         krb5_principal princ;
114         int retval;
115
116         if (ssh_gssapi_krb5_init() == 0)
117                 return 0;
118                 
119         if ((retval=krb5_parse_name(krb_context, gssapi_client_name.value, 
120                                     &princ))) {
121                 log("krb5_parse_name(): %.100s", 
122                         krb5_get_err_text(krb_context,retval));
123                 return 0;
124         }
125         if (krb5_kuserok(krb_context, princ, name)) {
126                 retval = 1;
127                 log("Authorized to %s, krb5 principal %s (krb5_kuserok)",name,
128                     (char *)gssapi_client_name.value);
129         }
130         else
131                 retval = 0;
132         
133         krb5_free_principal(krb_context, princ);
134         return retval;
135 }
136
137 int
138 ssh_gssapi_krb5_localname(char **user)
139 {
140     krb5_principal princ;
141
142     if (ssh_gssapi_krb5_init() == 0)
143         return 0;
144
145     if (krb5_parse_name(krb_context, gssapi_client_name.value, &princ)) {
146         return(0);
147     }
148     *user = (char *)xmalloc(256);
149     if (krb5_aname_to_localname(krb_context, princ, 256, *user)) {
150         xfree(*user);
151         *user = NULL;
152         return(0);
153     }
154     return(1);
155 }
156         
157 /* Make sure that this is called _after_ we've setuid to the user */
158
159 /* This writes out any forwarded credentials. Its specific to the Kerberos
160  * GSSAPI mechanism
161  *
162  * We assume that our caller has made sure that the user has selected
163  * delegated credentials, and that the client_creds structure is correctly
164  * populated.
165  */
166
167 OM_uint32
168 ssh_gssapi_krb5_storecreds(gss_buffer_t export_buffer) {
169         krb5_ccache ccache;
170         krb5_error_code problem;
171         krb5_principal princ;
172         char ccname[35];
173         static char name[40];
174         int tmpfd;
175         OM_uint32 maj_status,min_status;
176         gss_cred_id_t krb5_cred_handle;
177
178
179         if (gssapi_client_creds==NULL) {
180                 debug("No credentials stored"); 
181                 return GSS_S_NO_CRED;
182         }
183                 
184         if (ssh_gssapi_krb5_init() == 0)
185                 return GSS_S_FAILURE;
186
187         if (options.gss_use_session_ccache) {
188                 snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d_XXXXXX",geteuid());
189        
190                 if ((tmpfd = mkstemp(ccname))==-1) {
191                         log("mkstemp(): %.100s", strerror(errno));
192                         return GSS_S_FAILURE;
193                 }
194                 if (fchmod(tmpfd, S_IRUSR | S_IWUSR) == -1) {
195                         log("fchmod(): %.100s", strerror(errno));
196                         close(tmpfd);
197                         return GSS_S_FAILURE;
198                 }
199         } else {
200                 snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d",geteuid());
201                 tmpfd = open(ccname, O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
202                 if (tmpfd == -1) {
203                         log("open(): %.100s", strerror(errno));
204                         return GSS_S_FAILURE;
205                 }
206         }
207
208         close(tmpfd);
209         snprintf(name, sizeof(name), "FILE:%s",ccname);
210  
211         if ((problem = krb5_cc_resolve(krb_context, name, &ccache))) {
212                 log("krb5_cc_default(): %.100s", 
213                         krb5_get_err_text(krb_context,problem));
214                 return GSS_S_FAILURE;
215         }
216
217         if ((problem = krb5_parse_name(krb_context, gssapi_client_name.value, 
218                                        &princ))) {
219                 log("krb5_parse_name(): %.100s", 
220                         krb5_get_err_text(krb_context,problem));
221                 krb5_cc_destroy(krb_context,ccache);
222                 return GSS_S_FAILURE;
223         }
224         
225         if ((problem = krb5_cc_initialize(krb_context, ccache, princ))) {
226                 log("krb5_cc_initialize(): %.100s", 
227                         krb5_get_err_text(krb_context,problem));
228                 krb5_free_principal(krb_context,princ);
229                 krb5_cc_destroy(krb_context,ccache);
230                 return GSS_S_FAILURE;
231         }
232         
233         krb5_free_principal(krb_context,princ);
234
235 #ifdef MECHGLUE
236         krb5_cred_handle =
237             __gss_get_mechanism_cred(gssapi_client_creds,
238                                      &(supported_mechs[GSS_KERBEROS].oid));
239 #else
240         krb5_cred_handle = gssapi_client_creds;
241 #endif
242
243         if ((maj_status = gss_krb5_copy_ccache(&min_status, 
244                                                krb5_cred_handle, 
245                                                ccache))) {
246                 log("gss_krb5_copy_ccache() failed");
247                 ssh_gssapi_error(&supported_mechs[GSS_KERBEROS].oid,
248                                  maj_status,min_status);
249                 krb5_cc_destroy(krb_context,ccache);
250                 return GSS_S_FAILURE;
251         }
252         
253         krb5_cc_close(krb_context,ccache);
254
255         export_buffer->length = strlen("KRB5CCNAME")+strlen(name)+1;
256         export_buffer->value = xmalloc(export_buffer->length+1);
257         sprintf(export_buffer->value, "%s=%s", "KRB5CCNAME", name);
258
259         return GSS_S_COMPLETE;
260 }
261
262 #endif /* KRB5 */
263
264 #ifdef GSI
265 #include <globus_gss_assist.h>
266
267 /*
268  * Check if this user is OK to login under GSI. User has been authenticated
269  * as identity in global 'client_name.value' and is trying to log in as passed
270  * username in 'name'.
271  *
272  * Returns non-zero if user is authorized, 0 otherwise.
273  */
274 int
275 ssh_gssapi_gsi_userok(char *name)
276 {
277     int authorized = 0;
278     
279     /* This returns 0 on success */
280     authorized = (globus_gss_assist_userok(gssapi_client_name.value,
281                                            name) == 0);
282     
283     log("GSI user %s is%s authorized as target user %s",
284         (char *) gssapi_client_name.value, (authorized ? "" : " not"), name);
285     
286     return authorized;
287 }
288
289 /*
290  * Return the local username associated with the GSI credentials.
291  */
292 int
293 ssh_gssapi_gsi_localname(char **user)
294 {
295     return(globus_gss_assist_gridmap(gssapi_client_name.value, user) == 0);
296 }
297
298 /*
299  * Handle setting up child environment for GSI.
300  *
301  * Make sure that this is called _after_ we've setuid to the user.
302  */
303 OM_uint32
304 ssh_gssapi_gsi_storecreds(gss_buffer_t export_buffer)
305 {
306         OM_uint32       major_status;
307         OM_uint32       minor_status;
308
309         if (gssapi_client_creds != NULL)
310         {
311                 char *creds_env = NULL;
312
313                 /*
314                  * This is the current hack with the GSI gssapi library to
315                  * export credentials to disk.
316                  */
317
318                 debug("Exporting delegated credentials");
319                 
320                 minor_status = 0xdee0;  /* Magic value */
321                 major_status =
322                         gss_inquire_cred(&minor_status,
323                                          gssapi_client_creds,
324                                          (gss_name_t *) &creds_env,
325                                          NULL,
326                                          NULL,
327                                          NULL);
328
329                 if ((major_status == GSS_S_COMPLETE) &&
330                     (minor_status == 0xdee1) &&
331                     (creds_env != NULL))
332                 {
333                         char            *value;
334                                 
335                         /*
336                          * String is of the form:
337                          * X509_USER_DELEG_PROXY=filename
338                          * so we parse out the filename
339                          * and then set X509_USER_PROXY
340                          * to point at it.
341                          */
342                         value = strchr(creds_env, '=');
343                         
344                         if (value != NULL)
345                         {
346                                 *value = '\0';
347                                 value++;
348                                 export_buffer->length=
349                                     strlen("X509_USER_PROXY")+strlen(value)+1;
350                                 export_buffer->value =
351                                     xmalloc(export_buffer->length+1);
352                                 sprintf(export_buffer->value, "%s=%s",
353                                         "X509_USER_PROXY", value);
354                                 
355                                 return GSS_S_COMPLETE;
356                         }
357                         else
358                         {
359                                 log("Failed to parse delegated credentials string '%s'",
360                                     creds_env);
361                         }
362                 }
363                 else
364                 {
365                         log("Failed to export delegated credentials (error %ld)",
366                             major_status);
367                 }
368         }
369         return 0;
370 }
371
372 #endif /* GSI */
373
374 void
375 ssh_gssapi_cleanup_creds(void *ignored)
376 {
377         /* OM_uint32 min_stat; */
378
379         if (gssapi_cred_store.filename!=NULL) {
380                 /* Unlink probably isn't sufficient */
381                 debug("removing gssapi cred file \"%s\"",gssapi_cred_store.filename);
382                 unlink(gssapi_cred_store.filename);
383         }
384         /* DK ?? 
385         if (gssapi_client_creds != GSS_C_NO_CREDENTIAL)
386                 gss_release_cred(&min_stat, &gssapi_client_creds);
387         */
388 }
389
390 OM_uint32
391 ssh_gssapi_export_cred(OM_uint32 *            minor_status,
392                        const gss_cred_id_t    cred_handle,
393                        const gss_OID          desired_mech,
394                        OM_uint32              option_req,
395                        gss_buffer_t           export_buffer)
396 {
397         OM_uint32 maj_stat = GSS_S_FAILURE;
398
399         if (option_req != 1) return GSS_S_UNAVAILABLE;
400         if (desired_mech != NULL) return GSS_S_BAD_MECH;
401
402         switch (gssapi_client_type) {
403 #ifdef KRB5
404         case GSS_KERBEROS:
405                 maj_stat = ssh_gssapi_krb5_storecreds(export_buffer);
406                 break;
407 #endif
408 #ifdef GSI
409         case GSS_GSI:
410                 maj_stat = ssh_gssapi_gsi_storecreds(export_buffer);
411                 break;
412 #endif /* GSI */
413         case GSS_LAST_ENTRY:
414                 /* GSSAPI not used in this authentication */
415                 debug("No GSSAPI credentials stored");
416                 break;
417         default:
418                 log("ssh_gssapi_do_child: Unknown mechanism");
419         
420         }
421
422         if (GSS_ERROR(maj_stat)) {
423                 *minor_status = GSS_S_FAILURE;
424         }
425         return maj_stat;
426 }
427
428 void 
429 ssh_gssapi_storecreds()
430 {
431         OM_uint32 maj_stat, min_stat;
432         gss_buffer_desc export_cred = GSS_C_EMPTY_BUFFER;
433         char *p;
434
435         if (gssapi_client_creds == GSS_C_NO_CREDENTIAL)
436                 return;
437
438 #ifdef HAVE_GSSAPI_EXT
439         maj_stat = gss_export_cred(&min_stat, gssapi_client_creds,
440                                    GSS_C_NO_OID, 1, &export_cred);
441         if (GSS_ERROR(maj_stat) && maj_stat != GSS_S_UNAVAILABLE) {
442                 ssh_gssapi_error(GSS_C_NO_OID, maj_stat, min_stat);
443                 return;
444         }
445 #endif
446
447         /* If gss_export_cred() is not available, use old methods */
448         if (export_cred.length == 0) {
449             ssh_gssapi_export_cred(&min_stat, gssapi_client_creds,
450                                    GSS_C_NO_OID, 1, &export_cred);
451             if (GSS_ERROR(maj_stat)) {
452                 ssh_gssapi_error(GSS_C_NO_OID, maj_stat, min_stat);
453             }
454         }
455
456         p = strchr((char *) export_cred.value, '=');
457         if (p == NULL) {
458                 log("Failed to parse exported credentials string '%.100s'",
459                     (char *)export_cred.value);
460                 gss_release_buffer(&min_stat, &export_cred);
461                 return;
462         }
463         *p++ = '\0';
464         gssapi_cred_store.envvar = strdup((char *)export_cred.value);
465         gssapi_cred_store.envval = strdup(p);
466 #ifdef USE_PAM
467         do_pam_putenv(gssapi_cred_store.envvar, gssapi_cred_store.envval);
468 #endif
469         if (strncmp(p, "FILE:", 5) == 0) {
470             p += 5;
471         }
472         if (access(p, R_OK) == 0) {
473             gssapi_cred_store.filename = strdup(p);
474         }
475         gss_release_buffer(&min_stat, &export_cred);
476
477         if (options.gss_cleanup_creds) {
478                 fatal_add_cleanup(ssh_gssapi_cleanup_creds, NULL);
479         }
480 }
481
482 /* This allows GSSAPI methods to do things to the childs environment based
483  * on the passed authentication process and credentials.
484  *
485  * Question: If we didn't use userauth_external for some reason, should we
486  * still delegate credentials?
487  */
488 void 
489 ssh_gssapi_do_child(char ***envp, u_int *envsizep) 
490 {
491
492         if (gssapi_cred_store.envvar!=NULL && 
493             gssapi_cred_store.envval!=NULL) {
494             
495                 debug("Setting %s to %s", gssapi_cred_store.envvar,
496                                           gssapi_cred_store.envval);                              
497                 child_set_env(envp, envsizep, gssapi_cred_store.envvar, 
498                                               gssapi_cred_store.envval);
499         }
500
501         switch(gssapi_client_type) {
502 #ifdef KRB5
503         case GSS_KERBEROS: break;
504 #endif
505 #ifdef GSI
506         case GSS_GSI: break;
507 #endif
508         case GSS_LAST_ENTRY:
509                 debug("No GSSAPI credentials stored");
510                 break;
511         default:
512                 log("ssh_gssapi_do_child: Unknown mechanism");
513         }
514 }
515
516 int
517 ssh_gssapi_userok(char *user)
518 {
519         if (gssapi_client_name.length==0 || 
520             gssapi_client_name.value==NULL) {
521                 debug("No suitable client data");
522                 return 0;
523         }
524         switch (gssapi_client_type) {
525 #ifdef KRB5
526         case GSS_KERBEROS:
527                 return(ssh_gssapi_krb5_userok(user));
528                 break; /* Not reached */
529 #endif
530 #ifdef GSI
531         case GSS_GSI:
532                 return(ssh_gssapi_gsi_userok(user));
533                 break; /* Not reached */
534 #endif /* GSI */
535         case GSS_LAST_ENTRY:
536                 debug("Client not GSSAPI");
537                 break;
538         default:
539                 debug("Unknown client authentication type");
540         }
541         return(0);
542 }
543
544 int
545 ssh_gssapi_localname(char **user)
546 {
547         *user = NULL;
548         if (gssapi_client_name.length==0 || 
549             gssapi_client_name.value==NULL) {
550                 debug("No suitable client data");
551                 return(0);;
552         }
553         switch (gssapi_client_type) {
554 #ifdef KRB5
555         case GSS_KERBEROS:
556                 return(ssh_gssapi_krb5_localname(user));
557                 break; /* Not reached */
558 #endif
559 #ifdef GSI
560         case GSS_GSI:
561                 return(ssh_gssapi_gsi_localname(user));
562                 break; /* Not reached */
563 #endif /* GSI */
564         case GSS_LAST_ENTRY:
565                 debug("Client not GSSAPI");
566                 break;
567         default:
568                 debug("Unknown client authentication type");
569         }
570         return(0);
571 }
572
573 /*
574  * Clean our environment on startup. This means removing any environment
575  * strings that might inadvertantly been in root's environment and 
576  * could cause serious security problems if we think we set them.
577  */
578 void
579 ssh_gssapi_clean_env(void)
580 {
581   char *envstr;
582   int envstr_index;
583
584   
585    for (envstr_index = 0;
586        (envstr = delegation_env[envstr_index]) != NULL;
587        envstr_index++) {
588
589      if (getenv(envstr)) {
590        debug("Clearing environment variable %s", envstr);
591        gssapi_unsetenv(envstr);
592      }
593    }
594 }
595
596 /*
597  * Wrapper around unsetenv.
598  */
599 static void
600 gssapi_unsetenv(const char *var)
601 {
602 #ifdef HAVE_UNSETENV
603     unsetenv(var);
604
605 #else /* !HAVE_UNSETENV */
606     extern char **environ;
607     char **p1 = environ;        /* New array list */
608     char **p2 = environ;        /* Current array list */
609     int len = strlen(var);
610
611     /*
612      * Walk through current environ array (p2) copying each pointer
613      * to new environ array (p1) unless the pointer is to the item
614      * we want to delete. Copy happens in place.
615      */
616     while (*p2) {
617         if ((strncmp(*p2, var, len) == 0) &&
618             ((*p2)[len] == '=')) {
619             /*
620              * *p2 points at item to be deleted, just skip over it
621              */
622             p2++;
623         } else {
624             /*
625              * *p2 points at item we want to save, so copy it
626              */
627             *p1 = *p2;
628             p1++;
629             p2++;
630         }
631     }
632
633     /* And make sure new array is NULL terminated */
634     *p1 = NULL;
635 #endif /* HAVE_UNSETENV */
636 }
637
638 #endif /* GSSAPI */
This page took 0.083426 seconds and 5 git commands to generate.