]> andersk Git - gssapi-openssh.git/blob - openssh/gss-serv.c
add missing return values to GSSAPI_EXT functions
[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 (krb5_parse_name(krb_context, gssapi_client_name.value, &princ)) {
143         return(0);
144     }
145     *user = (char *)xmalloc(256);
146     if (krb5_aname_to_localname(krb_context, princ, 256, *user)) {
147         xfree(*user);
148         *user = NULL;
149         return(0);
150     }
151     return(1);
152 }
153         
154 #ifndef HAVE_GSSAPI_EXT
155 /* Make sure that this is called _after_ we've setuid to the user */
156
157 /* This writes out any forwarded credentials. Its specific to the Kerberos
158  * GSSAPI mechanism
159  *
160  * We assume that our caller has made sure that the user has selected
161  * delegated credentials, and that the client_creds structure is correctly
162  * populated.
163  */
164
165 OM_uint32
166 ssh_gssapi_krb5_storecreds() {
167         krb5_ccache ccache;
168         krb5_error_code problem;
169         krb5_principal princ;
170         char ccname[35];
171         static char name[40];
172         int tmpfd;
173         OM_uint32 maj_status,min_status;
174
175
176         if (gssapi_client_creds==NULL) {
177                 debug("No credentials stored"); 
178                 return GSS_S_NO_CRED;
179         }
180                 
181         if (ssh_gssapi_krb5_init() == 0)
182                 return GSS_S_FAILURE;
183
184         if (options.gss_use_session_ccache) {
185                 snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d_XXXXXX",geteuid());
186        
187                 if ((tmpfd = mkstemp(ccname))==-1) {
188                         log("mkstemp(): %.100s", strerror(errno));
189                         return GSS_S_FAILURE;
190                 }
191                 if (fchmod(tmpfd, S_IRUSR | S_IWUSR) == -1) {
192                         log("fchmod(): %.100s", strerror(errno));
193                         close(tmpfd);
194                         return GSS_S_FAILURE;
195                 }
196         } else {
197                 snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d",geteuid());
198                 tmpfd = open(ccname, O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
199                 if (tmpfd == -1) {
200                         log("open(): %.100s", strerror(errno));
201                         return GSS_S_FAILURE;
202                 }
203         }
204
205         close(tmpfd);
206         snprintf(name, sizeof(name), "FILE:%s",ccname);
207  
208         if ((problem = krb5_cc_resolve(krb_context, name, &ccache))) {
209                 log("krb5_cc_default(): %.100s", 
210                         krb5_get_err_text(krb_context,problem));
211                 return GSS_S_FAILURE;
212         }
213
214         if ((problem = krb5_parse_name(krb_context, gssapi_client_name.value, 
215                                        &princ))) {
216                 log("krb5_parse_name(): %.100s", 
217                         krb5_get_err_text(krb_context,problem));
218                 krb5_cc_destroy(krb_context,ccache);
219                 return GSS_S_FAILURE;
220         }
221         
222         if ((problem = krb5_cc_initialize(krb_context, ccache, princ))) {
223                 log("krb5_cc_initialize(): %.100s", 
224                         krb5_get_err_text(krb_context,problem));
225                 krb5_free_principal(krb_context,princ);
226                 krb5_cc_destroy(krb_context,ccache);
227                 return GSS_S_FAILURE;
228         }
229         
230         krb5_free_principal(krb_context,princ);
231
232         if ((maj_status = gss_krb5_copy_ccache(&min_status, 
233                                                gssapi_client_creds, 
234                                                ccache))) {
235                 log("gss_krb5_copy_ccache() failed");
236                 ssh_gssapi_error(maj_status,min_status);
237                 krb5_cc_destroy(krb_context,ccache);
238                 return GSS_S_FAILURE;
239         }
240         
241         krb5_cc_close(krb_context,ccache);
242
243
244 #ifdef USE_PAM
245         do_pam_putenv("KRB5CCNAME",name);
246 #endif
247
248         gssapi_cred_store.filename=strdup(ccname);
249         gssapi_cred_store.envvar="KRB5CCNAME";
250         gssapi_cred_store.envval=strdup(name);
251
252         return GSS_S_COMPLETE;
253 }
254 #endif /* HAVE_GSSAPI_EXT */
255
256 #endif /* KRB5 */
257
258 #ifdef GSI
259 #include <globus_gss_assist.h>
260
261 /*
262  * Check if this user is OK to login under GSI. User has been authenticated
263  * as identity in global 'client_name.value' and is trying to log in as passed
264  * username in 'name'.
265  *
266  * Returns non-zero if user is authorized, 0 otherwise.
267  */
268 int
269 ssh_gssapi_gsi_userok(char *name)
270 {
271     int authorized = 0;
272     
273     /* This returns 0 on success */
274     authorized = (globus_gss_assist_userok(gssapi_client_name.value,
275                                            name) == 0);
276     
277     log("GSI user %s is%s authorized as target user %s",
278         (char *) gssapi_client_name.value, (authorized ? "" : " not"), name);
279     
280     return authorized;
281 }
282
283 /*
284  * Return the local username associated with the GSI credentials.
285  */
286 int
287 ssh_gssapi_gsi_localname(char **user)
288 {
289     return(globus_gss_assist_gridmap(gssapi_client_name.value, user) == 0);
290 }
291
292 #ifndef HAVE_GSSAPI_EXT
293 /*
294  * Handle setting up child environment for GSI.
295  *
296  * Make sure that this is called _after_ we've setuid to the user.
297  */
298 OM_uint32
299 ssh_gssapi_gsi_storecreds()
300 {
301         OM_uint32       major_status;
302         OM_uint32       minor_status;
303
304         if (gssapi_client_creds != NULL)
305         {
306                 char *creds_env = NULL;
307
308                 /*
309                  * This is the current hack with the GSI gssapi library to
310                  * export credentials to disk.
311                  */
312
313                 debug("Exporting delegated credentials");
314                 
315                 minor_status = 0xdee0;  /* Magic value */
316                 major_status =
317                         gss_inquire_cred(&minor_status,
318                                          gssapi_client_creds,
319                                          (gss_name_t *) &creds_env,
320                                          NULL,
321                                          NULL,
322                                          NULL);
323
324                 if ((major_status == GSS_S_COMPLETE) &&
325                     (minor_status == 0xdee1) &&
326                     (creds_env != NULL))
327                 {
328                         char            *value;
329                                 
330                         /*
331                          * String is of the form:
332                          * X509_USER_DELEG_PROXY=filename
333                          * so we parse out the filename
334                          * and then set X509_USER_PROXY
335                          * to point at it.
336                          */
337                         value = strchr(creds_env, '=');
338                         
339                         if (value != NULL)
340                         {
341                                 *value = '\0';
342                                 value++;
343 #ifdef USE_PAM
344                                 do_pam_putenv("X509_USER_PROXY",value);
345 #endif
346                                 gssapi_cred_store.filename=strdup(value);
347                                 gssapi_cred_store.envvar="X509_USER_PROXY";
348                                 gssapi_cred_store.envval=strdup(value);
349
350                                 return GSS_S_COMPLETE;
351                         }
352                         else
353                         {
354                                 log("Failed to parse delegated credentials string '%s'",
355                                     creds_env);
356                         }
357                 }
358                 else
359                 {
360                         log("Failed to export delegated credentials (error %ld)",
361                             major_status);
362                 }
363         }
364         return 0;
365 }
366 #endif /* HAVE_GSSAPI_EXT */
367
368 #endif /* GSI */
369
370 void
371 ssh_gssapi_cleanup_creds(void *ignored)
372 {
373         /* OM_uint32 min_stat; */
374
375         if (gssapi_cred_store.filename!=NULL) {
376                 /* Unlink probably isn't sufficient */
377                 debug("removing gssapi cred file\"%s\"",gssapi_cred_store.filename);
378                 unlink(gssapi_cred_store.filename);
379         }
380         /* DK ?? 
381         if (gssapi_client_creds != GSS_C_NO_CREDENTIAL)
382                 gss_release_cred(&min_stat, &gssapi_client_creds);
383         */
384 }
385
386 #ifndef HAVE_GSSAPI_EXT
387 OM_uint32
388 gss_export_cred(OM_uint32 *            minor_status,
389                 const gss_cred_id_t    cred_handle,
390                 const gss_OID          desired_mech,
391                 OM_uint32              option_req,
392                 gss_buffer_t           export_buffer)
393 {
394         OM_uint32 maj_stat = GSS_S_FAILURE;
395
396         switch (gssapi_client_type) {
397 #ifdef KRB5
398         case GSS_KERBEROS:
399                 maj_stat = ssh_gssapi_krb5_storecreds();
400                 break;
401 #endif
402 #ifdef GSI
403         case GSS_GSI:
404                 maj_stat = ssh_gssapi_gsi_storecreds();
405                 break;
406 #endif /* GSI */
407         case GSS_LAST_ENTRY:
408                 /* GSSAPI not used in this authentication */
409                 debug("No GSSAPI credentials stored");
410                 break;
411         default:
412                 log("ssh_gssapi_do_child: Unknown mechanism");
413         
414         }
415
416         if (GSS_ERROR(maj_stat)) {
417                 *minor_status = GSS_S_FAILURE;
418                 return maj_stat;
419         }
420         return 0;
421 }
422 #endif /* HAVE_GSSAPI_EXT */
423
424 void 
425 ssh_gssapi_storecreds()
426 {
427         OM_uint32 maj_stat, min_stat;
428         gss_buffer_desc export_cred = GSS_C_EMPTY_BUFFER;
429         char *p;
430
431         if (gssapi_client_creds == GSS_C_NO_CREDENTIAL)
432                 return;
433
434         maj_stat = gss_export_cred(&min_stat, gssapi_client_creds,
435                                    GSS_C_NO_OID, 1, &export_cred);
436         if (GSS_ERROR(maj_stat)) {
437                 ssh_gssapi_error(maj_stat, min_stat);
438                 return;
439         }
440
441         p = strchr((char *) export_cred.value, '=');
442         if (p == NULL) {
443                 log("Failed to parse exported credentials string '%.100s'",
444                     (char *)export_cred.value);
445                 gss_release_buffer(&min_stat, &export_cred);
446                 return;
447         }
448         *p++ = '\0';
449         gssapi_cred_store.envvar = strdup((char *)export_cred.value);
450         gssapi_cred_store.envval = strdup(p);
451         gss_release_buffer(&min_stat, &export_cred);
452
453         if (options.gss_cleanup_creds) {
454                 fatal_add_cleanup(ssh_gssapi_cleanup_creds, NULL);
455         }
456 }
457
458 /* This allows GSSAPI methods to do things to the childs environment based
459  * on the passed authentication process and credentials.
460  *
461  * Question: If we didn't use userauth_external for some reason, should we
462  * still delegate credentials?
463  */
464 void 
465 ssh_gssapi_do_child(char ***envp, u_int *envsizep) 
466 {
467
468         if (gssapi_cred_store.envvar!=NULL && 
469             gssapi_cred_store.envval!=NULL) {
470             
471                 debug("Setting %s to %s", gssapi_cred_store.envvar,
472                                           gssapi_cred_store.envval);                              
473                 child_set_env(envp, envsizep, gssapi_cred_store.envvar, 
474                                               gssapi_cred_store.envval);
475         }
476
477         switch(gssapi_client_type) {
478 #ifdef KRB5
479         case GSS_KERBEROS: break;
480 #endif
481 #ifdef GSI
482         case GSS_GSI: break;
483 #endif
484         case GSS_LAST_ENTRY:
485                 debug("No GSSAPI credentials stored");
486                 break;
487         default:
488                 log("ssh_gssapi_do_child: Unknown mechanism");
489         }
490 }
491
492 int
493 ssh_gssapi_userok(char *user)
494 {
495         if (gssapi_client_name.length==0 || 
496             gssapi_client_name.value==NULL) {
497                 debug("No suitable client data");
498                 return 0;
499         }
500         switch (gssapi_client_type) {
501 #ifdef KRB5
502         case GSS_KERBEROS:
503                 return(ssh_gssapi_krb5_userok(user));
504                 break; /* Not reached */
505 #endif
506 #ifdef GSI
507         case GSS_GSI:
508                 return(ssh_gssapi_gsi_userok(user));
509                 break; /* Not reached */
510 #endif /* GSI */
511         case GSS_LAST_ENTRY:
512                 debug("Client not GSSAPI");
513                 break;
514         default:
515                 debug("Unknown client authentication type");
516         }
517         return(0);
518 }
519
520 int
521 ssh_gssapi_localname(char **user)
522 {
523         *user = NULL;
524         if (gssapi_client_name.length==0 || 
525             gssapi_client_name.value==NULL) {
526                 debug("No suitable client data");
527                 return(0);;
528         }
529         switch (gssapi_client_type) {
530 #ifdef KRB5
531         case GSS_KERBEROS:
532                 return(ssh_gssapi_krb5_localname(user));
533                 break; /* Not reached */
534 #endif
535 #ifdef GSI
536         case GSS_GSI:
537                 return(ssh_gssapi_gsi_localname(user));
538                 break; /* Not reached */
539 #endif /* GSI */
540         case GSS_LAST_ENTRY:
541                 debug("Client not GSSAPI");
542                 break;
543         default:
544                 debug("Unknown client authentication type");
545         }
546         return(0);
547 }
548
549 /*
550  * Clean our environment on startup. This means removing any environment
551  * strings that might inadvertantly been in root's environment and 
552  * could cause serious security problems if we think we set them.
553  */
554 void
555 ssh_gssapi_clean_env(void)
556 {
557   char *envstr;
558   int envstr_index;
559
560   
561    for (envstr_index = 0;
562        (envstr = delegation_env[envstr_index]) != NULL;
563        envstr_index++) {
564
565      if (getenv(envstr)) {
566        debug("Clearing environment variable %s", envstr);
567        gssapi_unsetenv(envstr);
568      }
569    }
570 }
571
572 /*
573  * Wrapper around unsetenv.
574  */
575 static void
576 gssapi_unsetenv(const char *var)
577 {
578 #ifdef HAVE_UNSETENV
579     unsetenv(var);
580
581 #else /* !HAVE_UNSETENV */
582     extern char **environ;
583     char **p1 = environ;        /* New array list */
584     char **p2 = environ;        /* Current array list */
585     int len = strlen(var);
586
587     /*
588      * Walk through current environ array (p2) copying each pointer
589      * to new environ array (p1) unless the pointer is to the item
590      * we want to delete. Copy happens in place.
591      */
592     while (*p2) {
593         if ((strncmp(*p2, var, len) == 0) &&
594             ((*p2)[len] == '=')) {
595             /*
596              * *p2 points at item to be deleted, just skip over it
597              */
598             p2++;
599         } else {
600             /*
601              * *p2 points at item we want to save, so copy it
602              */
603             *p1 = *p2;
604             p1++;
605             p2++;
606         }
607     }
608
609     /* And make sure new array is NULL terminated */
610     *p1 = NULL;
611 #endif /* HAVE_UNSETENV */
612 }
613
614 #endif /* GSSAPI */
This page took 0.855735 seconds and 5 git commands to generate.