]> andersk Git - gssapi-openssh.git/blob - openssh/gss-serv.c
rename gssapi_clean_env() to ssh_gssapi_clean_env() and add prototype to
[gssapi-openssh.git] / openssh / gss-serv.c
1 /*
2  * Copyright (c) 2001 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
48 #include "ssh-gss.h"
49
50 extern ServerOptions options;
51 extern u_char *session_id2;
52 extern int session_id2_len;
53
54 void    userauth_reply(Authctxt *authctxt, int authenticated);
55 static void gssapi_unsetenv(const char *var);
56
57 typedef struct ssh_gssapi_cred_cache {
58         char *filename;
59         char *envvar;
60         char *envval;
61         void *data;
62 } ssh_gssapi_cred_cache;
63
64 static struct ssh_gssapi_cred_cache gssapi_cred_store = {NULL,NULL,NULL};
65 unsigned char ssh1_key_digest[16]; /* used for ssh1 gssapi */
66
67 /*
68  * Environment variables pointing to delegated credentials
69  */
70 static char *delegation_env[] = {
71   "X509_USER_PROXY",            /* GSSAPI/SSLeay */
72   "KRB5CCNAME",                 /* Krb5 and possibly SSLeay */
73   NULL
74 };
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 /* Make sure that this is called _after_ we've setuid to the user */
138
139 /* This writes out any forwarded credentials. Its specific to the Kerberos
140  * GSSAPI mechanism
141  *
142  * We assume that our caller has made sure that the user has selected
143  * delegated credentials, and that the client_creds structure is correctly
144  * populated.
145  */
146
147 void
148 ssh_gssapi_krb5_storecreds() {
149         krb5_ccache ccache;
150         krb5_error_code problem;
151         krb5_principal princ;
152         char ccname[35];
153         static char name[40];
154         int tmpfd;
155         OM_uint32 maj_status,min_status;
156
157
158         if (gssapi_client_creds==NULL) {
159                 debug("No credentials stored"); 
160                 return;
161         }
162                 
163         if (ssh_gssapi_krb5_init() == 0)
164                 return;
165
166         if (options.gss_use_session_ccache) {
167                 snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d_XXXXXX",geteuid());
168        
169                 if ((tmpfd = mkstemp(ccname))==-1) {
170                         log("mkstemp(): %.100s", strerror(errno));
171                         return;
172                 }
173                 if (fchmod(tmpfd, S_IRUSR | S_IWUSR) == -1) {
174                         log("fchmod(): %.100s", strerror(errno));
175                         close(tmpfd);
176                         return;
177                 }
178         } else {
179                 snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d",geteuid());
180                 tmpfd = open(ccname, O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
181                 if (tmpfd == -1) {
182                         log("open(): %.100s", strerror(errno));
183                         return;
184                 }
185         }
186
187         close(tmpfd);
188         snprintf(name, sizeof(name), "FILE:%s",ccname);
189  
190         if ((problem = krb5_cc_resolve(krb_context, name, &ccache))) {
191                 log("krb5_cc_default(): %.100s", 
192                         krb5_get_err_text(krb_context,problem));
193                 return;
194         }
195
196         if ((problem = krb5_parse_name(krb_context, gssapi_client_name.value, 
197                                        &princ))) {
198                 log("krb5_parse_name(): %.100s", 
199                         krb5_get_err_text(krb_context,problem));
200                 krb5_cc_destroy(krb_context,ccache);
201                 return;
202         }
203         
204         if ((problem = krb5_cc_initialize(krb_context, ccache, princ))) {
205                 log("krb5_cc_initialize(): %.100s", 
206                         krb5_get_err_text(krb_context,problem));
207                 krb5_free_principal(krb_context,princ);
208                 krb5_cc_destroy(krb_context,ccache);
209                 return;
210         }
211         
212         krb5_free_principal(krb_context,princ);
213
214         #ifdef HEIMDAL
215         if ((problem = krb5_cc_copy_cache(krb_context, 
216                                            gssapi_client_creds->ccache,
217                                            ccache))) {
218                 log("krb5_cc_copy_cache(): %.100s", 
219                         krb5_get_err_text(krb_context,problem));
220                 krb5_cc_destroy(krb_context,ccache);
221                 return;
222         }
223         #else
224         if ((maj_status = gss_krb5_copy_ccache(&min_status, 
225                                                gssapi_client_creds, 
226                                                ccache))) {
227                 log("gss_krb5_copy_ccache() failed");
228                 ssh_gssapi_error(maj_status,min_status);
229                 krb5_cc_destroy(krb_context,ccache);
230                 return;
231         }
232         #endif
233         
234         krb5_cc_close(krb_context,ccache);
235
236
237 #ifdef USE_PAM
238         do_pam_putenv("KRB5CCNAME",name);
239 #endif
240
241         gssapi_cred_store.filename=strdup(ccname);
242         gssapi_cred_store.envvar="KRB5CCNAME";
243         gssapi_cred_store.envval=strdup(name);
244
245         return;
246 }
247
248 #endif /* KRB5 */
249
250 #ifdef GSI
251 #include <globus_gss_assist.h>
252
253 /*
254  * Check if this user is OK to login under GSI. User has been authenticated
255  * as identity in global 'client_name.value' and is trying to log in as passed
256  * username in 'name'.
257  *
258  * Returns non-zero if user is authorized, 0 otherwise.
259  */
260 int
261 ssh_gssapi_gsi_userok(char *name)
262 {
263     int authorized = 0;
264     
265     /* This returns 0 on success */
266     authorized = (globus_gss_assist_userok(gssapi_client_name.value,
267                                            name) == 0);
268     
269     debug("GSI user %s is%s authorized as target user %s",
270           (char *) gssapi_client_name.value,
271           (authorized ? "" : " not"),
272           name);
273     
274     return authorized;
275 }
276
277 /*
278  * Handle setting up child environment for GSI.
279  *
280  * Make sure that this is called _after_ we've setuid to the user.
281  */
282 void
283 ssh_gssapi_gsi_storecreds()
284 {
285         OM_uint32       major_status;
286         OM_uint32       minor_status;
287         
288         
289         if (gssapi_client_creds != NULL)
290         {
291                 char *creds_env = NULL;
292
293                 /*
294                  * This is the current hack with the GSI gssapi library to
295                  * export credentials to disk.
296                  */
297
298                 debug("Exporting delegated credentials");
299                 
300                 minor_status = 0xdee0;  /* Magic value */
301                 major_status =
302                         gss_inquire_cred(&minor_status,
303                                          gssapi_client_creds,
304                                          (gss_name_t *) &creds_env,
305                                          NULL,
306                                          NULL,
307                                          NULL);
308
309                 if ((major_status == GSS_S_COMPLETE) &&
310                     (minor_status == 0xdee1) &&
311                     (creds_env != NULL))
312                 {
313                         char            *value;
314                                 
315                         /*
316                          * String is of the form:
317                          * X509_USER_DELEG_PROXY=filename
318                          * so we parse out the filename
319                          * and then set X509_USER_PROXY
320                          * to point at it.
321                          */
322                         value = strchr(creds_env, '=');
323                         
324                         if (value != NULL)
325                         {
326                                 *value = '\0';
327                                 value++;
328 #ifdef USE_PAM
329                                 do_pam_putenv("X509_USER_PROXY",value);
330 #endif
331                                 gssapi_cred_store.filename=NULL;
332                                 gssapi_cred_store.envvar="X509_USER_PROXY";
333                                 gssapi_cred_store.envval=strdup(value);
334
335                                 return;
336                         }
337                         else
338                         {
339                                 log("Failed to parse delegated credentials string '%s'",
340                                     creds_env);
341                         }
342                 }
343                 else
344                 {
345                         log("Failed to export delegated credentials (error %ld)",
346                             major_status);
347                 }
348         }       
349 }
350
351 #endif /* GSI */
352
353 void
354 ssh_gssapi_cleanup_creds(void *ignored)
355 {
356         if (gssapi_cred_store.filename!=NULL) {
357                 /* Unlink probably isn't sufficient */
358                 debug("removing gssapi cred file\"%s\"",gssapi_cred_store.filename);
359                 unlink(gssapi_cred_store.filename);
360         }
361 }
362
363 void 
364 ssh_gssapi_storecreds()
365 {
366         switch (gssapi_client_type) {
367 #ifdef KRB5
368         case GSS_KERBEROS:
369                 ssh_gssapi_krb5_storecreds();
370                 break;
371 #endif
372 #ifdef GSI
373         case GSS_GSI:
374                 ssh_gssapi_gsi_storecreds();
375                 break;
376 #endif /* GSI */
377         case GSS_LAST_ENTRY:
378                 /* GSSAPI not used in this authentication */
379                 debug("No GSSAPI credentials stored");
380                 break;
381         default:
382                 log("ssh_gssapi_do_child: Unknown mechanism");
383         
384         }
385         
386         if (options.gss_cleanup_creds) {
387                 fatal_add_cleanup(ssh_gssapi_cleanup_creds, NULL);
388         }
389
390 }
391
392 /* This allows GSSAPI methods to do things to the childs environment based
393  * on the passed authentication process and credentials.
394  *
395  * Question: If we didn't use userauth_external for some reason, should we
396  * still delegate credentials?
397  */
398 void 
399 ssh_gssapi_do_child(char ***envp, u_int *envsizep) 
400 {
401
402         if (gssapi_cred_store.envvar!=NULL && 
403             gssapi_cred_store.envval!=NULL) {
404             
405                 debug("Setting %s to %s", gssapi_cred_store.envvar,
406                                           gssapi_cred_store.envval);                              
407                 child_set_env(envp, envsizep, gssapi_cred_store.envvar, 
408                                               gssapi_cred_store.envval);
409         }
410
411         switch(gssapi_client_type) {
412 #ifdef KRB5
413         case GSS_KERBEROS: break;
414 #endif
415 #ifdef GSI
416         case GSS_GSI: break;
417 #endif
418         case GSS_LAST_ENTRY:
419                 debug("No GSSAPI credentials stored");
420                 break;
421         default:
422                 log("ssh_gssapi_do_child: Unknown mechanism");
423         }
424 }
425
426 int
427 ssh_gssapi_userok(char *user)
428 {
429         if (gssapi_client_name.length==0 || 
430             gssapi_client_name.value==NULL) {
431                 debug("No suitable client data");
432                 return 0;
433         }
434         switch (gssapi_client_type) {
435 #ifdef KRB5
436         case GSS_KERBEROS:
437                 return(ssh_gssapi_krb5_userok(user));
438                 break; /* Not reached */
439 #endif
440 #ifdef GSI
441         case GSS_GSI:
442                 return(ssh_gssapi_gsi_userok(user));
443                 break; /* Not reached */
444 #endif /* GSI */
445         case GSS_LAST_ENTRY:
446                 debug("Client not GSSAPI");
447                 break;
448         default:
449                 debug("Unknown client authentication type");
450         }
451         return(0);
452 }
453
454 int
455 userauth_external(Authctxt *authctxt)
456 {
457         packet_check_eom();
458
459         return(ssh_gssapi_userok(authctxt->user));
460 }
461
462 void input_gssapi_token(int type, u_int32_t plen, void *ctxt);
463 void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
464
465 /* We only support those mechanisms that we know about (ie ones that we know
466  * how to check local user kuserok and the like
467  */
468 int
469 userauth_gssapi(Authctxt *authctxt)
470 {
471         gss_OID_desc    oid= {0,NULL};
472         Gssctxt         *ctxt;
473         int             mechs;
474         gss_OID_set     supported;
475         int             present;
476         OM_uint32       ms;
477         u_int           len;
478         
479         if (!authctxt->valid || authctxt->user == NULL)
480                 return 0;
481                 
482         if (datafellows & SSH_OLD_GSSAPI) {
483                 debug("Early drafts of GSSAPI userauth not supported");
484                 return 0;
485         }
486         
487         mechs=packet_get_int();
488         if (mechs==0) {
489                 debug("Mechanism negotiation is not supported");
490                 return 0;
491         }
492
493         ssh_gssapi_supported_oids(&supported);
494         do {
495                 if (oid.elements)
496                         xfree(oid.elements);
497                 oid.elements = packet_get_string(&len);
498                 oid.length = len;
499                 gss_test_oid_set_member(&ms, &oid, supported, &present);
500                 mechs--;
501         } while (mechs>0 && !present);
502         
503         if (!present) {
504                 xfree(oid.elements);
505                 return(0);
506         }
507         
508         ctxt=xmalloc(sizeof(Gssctxt));
509         authctxt->methoddata=(void *)ctxt;
510         
511         ssh_gssapi_build_ctx(ctxt);
512         ssh_gssapi_set_oid(ctxt,&oid);
513
514         if (ssh_gssapi_acquire_cred(ctxt))
515                 return 0;
516
517         /* Send SSH_MSG_USERAUTH_GSSAPI_RESPONSE */
518
519         if (!compat20)
520         packet_start(SSH_SMSG_AUTH_GSSAPI_RESPONSE);
521         else
522         packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE);
523         packet_put_string(oid.elements,oid.length);
524         packet_send();
525         packet_write_wait();
526         xfree(oid.elements);
527                 
528         if (!compat20)
529         dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN,
530                                 &input_gssapi_token);
531         else
532         dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, 
533                      &input_gssapi_token);
534         authctxt->postponed = 1;
535         
536         return 0;
537 }
538
539 void
540 input_gssapi_token(int type, u_int32_t plen, void *ctxt)
541 {
542         Authctxt *authctxt = ctxt;
543         Gssctxt *gssctxt;
544         gss_buffer_desc send_tok,recv_tok;
545         OM_uint32 maj_status, min_status;
546         
547         if (authctxt == NULL || authctxt->methoddata == NULL)
548                 fatal("No authentication or GSSAPI context");
549                 
550         gssctxt=authctxt->methoddata;
551
552         recv_tok.value=packet_get_string(&recv_tok.length);
553         
554         maj_status=ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok, NULL);
555         packet_check_eom();
556         
557         if (GSS_ERROR(maj_status)) {
558                 /* Failure <sniff> */
559                 authctxt->postponed = 0;
560                 dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN, NULL);
561                 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
562                 userauth_finish(authctxt, 0, "gssapi");
563         }
564                         
565         if (send_tok.length != 0) {
566                 /* Send a packet back to the client */
567                 if (!compat20)
568                 packet_start(SSH_MSG_AUTH_GSSAPI_TOKEN);
569                 else
570                 packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
571                 packet_put_string(send_tok.value,send_tok.length);
572                 packet_send();
573                 packet_write_wait();
574                 gss_release_buffer(&min_status, &send_tok);
575         }
576         
577         if (maj_status == GSS_S_COMPLETE) {
578                 dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN, NULL);
579                 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,NULL);
580                 /* ssh1 does not have an extra message here */
581                 if (!compat20)
582                 input_gssapi_exchange_complete(0, 0, ctxt);
583                 else
584                 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,
585                              &input_gssapi_exchange_complete);
586         }
587 }
588
589 /* This is called when the client thinks we've completed authentication.
590  * It should only be enabled in the dispatch handler by the function above,
591  * which only enables it once the GSSAPI exchange is complete.
592  */
593  
594 void
595 input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)
596 {
597         Authctxt *authctxt = ctxt;
598         Gssctxt *gssctxt;
599         int authenticated;
600         
601         if (authctxt == NULL || authctxt->methoddata == NULL)
602                 fatal("No authentication or GSSAPI context");
603                 
604         gssctxt=authctxt->methoddata;
605
606         /* This should never happen, but better safe than sorry. */
607         if (gssctxt->status != GSS_S_COMPLETE) {
608                 packet_disconnect("Context negotiation is not complete");
609         }
610
611         if (ssh_gssapi_getclient(gssctxt,&gssapi_client_type,
612                                  &gssapi_client_name,
613                                  &gssapi_client_creds)) {
614                 fatal("Couldn't convert client name");
615         }
616                                                 
617         authenticated = ssh_gssapi_userok(authctxt->user);
618
619         /* ssh1 needs to exchange the hash of the keys */
620         if (!compat20) {
621                 if (authenticated) {
622
623                         OM_uint32 maj_status, min_status;
624                         gss_buffer_desc gssbuf,msg_tok;
625
626                         /* ssh1 uses wrap */
627                         gssbuf.value=ssh1_key_digest;
628                         gssbuf.length=sizeof(ssh1_key_digest);
629                         if ((maj_status=gss_wrap(&min_status,
630                                         gssctxt->context,
631                                         0,
632                                         GSS_C_QOP_DEFAULT,
633                                         &gssbuf,
634                                         NULL,
635                                         &msg_tok))) {
636                                 ssh_gssapi_error(maj_status,min_status);
637                                 fatal("Couldn't wrap keys");
638                         }
639                         packet_start(SSH_SMSG_AUTH_GSSAPI_HASH);
640                         packet_put_string((char *)msg_tok.value,msg_tok.length);
641                         packet_send();
642                         packet_write_wait();
643                         gss_release_buffer(&min_status,&msg_tok);
644                 }
645         }
646
647         authctxt->postponed = 0;
648         dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
649         dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
650         userauth_finish(authctxt, authenticated, "gssapi");
651 }
652
653 /*
654  * Clean our environment on startup. This means removing any environment
655  * strings that might inadvertantly been in root's environment and 
656  * could cause serious security problems if we think we set them.
657  */
658 void
659 ssh_gssapi_clean_env(void)
660 {
661   char *envstr;
662   int envstr_index;
663
664   
665    for (envstr_index = 0;
666        (envstr = delegation_env[envstr_index]) != NULL;
667        envstr_index++) {
668
669      if (getenv(envstr)) {
670        debug("Clearing environment variable %s", envstr);
671        gssapi_unsetenv(envstr);
672      }
673    }
674 }
675
676 /*
677  * Wrapper around unsetenv.
678  */
679 static void
680 gssapi_unsetenv(const char *var)
681 {
682 #ifdef HAVE_UNSETENV
683     unsetenv(var);
684
685 #else /* !HAVE_UNSETENV */
686     extern char **environ;
687     char **p1 = environ;        /* New array list */
688     char **p2 = environ;        /* Current array list */
689     int len = strlen(var);
690
691     /*
692      * Walk through current environ array (p2) copying each pointer
693      * to new environ array (p1) unless the pointer is to the item
694      * we want to delete. Copy happens in place.
695      */
696     while (*p2) {
697         if ((strncmp(*p2, var, len) == 0) &&
698             ((*p2)[len] == '=')) {
699             /*
700              * *p2 points at item to be deleted, just skip over it
701              */
702             p2++;
703         } else {
704             /*
705              * *p2 points at item we want to save, so copy it
706              */
707             *p1 = *p2;
708             p1++;
709             p2++;
710         }
711     }
712
713     /* And make sure new array is NULL terminated */
714     *p1 = NULL;
715 #endif /* HAVE_UNSETENV */
716 }
717
718 #endif /* GSSAPI */
This page took 0.24462 seconds and 5 git commands to generate.