]> andersk Git - gssapi-openssh.git/blob - openssh/gss-serv.c
- packet_done() is now called packet_check_eom()
[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 "ssh2.h"
31 #include "xmalloc.h"
32 #include "buffer.h"
33 #include "bufaux.h"
34 #include "packet.h"
35 #include "compat.h"
36 #include <openssl/evp.h>
37 #include "cipher.h"
38 #include "kex.h"
39 #include "auth.h"
40 #include "log.h"
41 #include "channels.h"
42 #include "session.h"
43 #include "dispatch.h"
44 #include "servconf.h"
45
46 #include "ssh-gss.h"
47
48 extern ServerOptions options;
49 extern u_char *session_id2;
50 extern int session_id2_len;
51
52
53 typedef struct ssh_gssapi_cred_cache {
54         char *filename;
55         char *envvar;
56         char *envval;
57         void *data;
58 } ssh_gssapi_cred_cache;
59
60 static struct ssh_gssapi_cred_cache gssapi_cred_store = {NULL,NULL,NULL};
61
62 #ifdef KRB5
63
64 #ifdef HEIMDAL
65 #include <krb5.h>
66 #else
67 #include <gssapi_krb5.h>
68 #define krb5_get_err_text(context,code) error_message(code)
69 #endif
70
71 static krb5_context krb_context = NULL;
72
73 /* Initialise the krb5 library, so we can use it for those bits that
74  * GSSAPI won't do */
75
76 int ssh_gssapi_krb5_init() {
77         krb5_error_code problem;
78         
79         if (krb_context !=NULL)
80                 return 1;
81                 
82         problem = krb5_init_context(&krb_context);
83         if (problem) {
84                 log("Cannot initialize krb5 context");
85                 return 0;
86         }
87         krb5_init_ets(krb_context);
88
89         return 1;       
90 }                       
91
92 /* Check if this user is OK to login. This only works with krb5 - other 
93  * GSSAPI mechanisms will need their own.
94  * Returns true if the user is OK to log in, otherwise returns 0
95  */
96
97 int
98 ssh_gssapi_krb5_userok(char *name) {
99         krb5_principal princ;
100         int retval;
101
102         if (ssh_gssapi_krb5_init() == 0)
103                 return 0;
104                 
105         if ((retval=krb5_parse_name(krb_context, gssapi_client_name.value, 
106                                     &princ))) {
107                 log("krb5_parse_name(): %.100s", 
108                         krb5_get_err_text(krb_context,retval));
109                 return 0;
110         }
111         if (krb5_kuserok(krb_context, princ, name)) {
112                 retval = 1;
113                 log("Authorized to %s, krb5 principal %s (krb5_kuserok)",name,
114                     (char *)gssapi_client_name.value);
115         }
116         else
117                 retval = 0;
118         
119         krb5_free_principal(krb_context, princ);
120         return retval;
121 }
122         
123 /* Make sure that this is called _after_ we've setuid to the user */
124
125 /* This writes out any forwarded credentials. Its specific to the Kerberos
126  * GSSAPI mechanism
127  *
128  * We assume that our caller has made sure that the user has selected
129  * delegated credentials, and that the client_creds structure is correctly
130  * populated.
131  */
132
133 void
134 ssh_gssapi_krb5_storecreds() {
135         krb5_ccache ccache;
136         krb5_error_code problem;
137         krb5_principal princ;
138         char ccname[35];
139         static char name[40];
140         int tmpfd;
141         OM_uint32 maj_status,min_status;
142
143
144         if (gssapi_client_creds==NULL) {
145                 debug("No credentials stored"); 
146                 return;
147         }
148                 
149         if (ssh_gssapi_krb5_init() == 0)
150                 return;
151
152         if (options.gss_use_session_ccache) {
153                 snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d_XXXXXX",geteuid());
154        
155                 if ((tmpfd = mkstemp(ccname))==-1) {
156                         log("mkstemp(): %.100s", strerror(errno));
157                         return;
158                 }
159                 if (fchmod(tmpfd, S_IRUSR | S_IWUSR) == -1) {
160                         log("fchmod(): %.100s", strerror(errno));
161                         close(tmpfd);
162                         return;
163                 }
164         } else {
165                 snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d",geteuid());
166                 tmpfd = open(ccname, O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
167                 if (tmpfd == -1) {
168                         log("open(): %.100s", strerror(errno));
169                         return;
170                 }
171         }
172
173         close(tmpfd);
174         snprintf(name, sizeof(name), "FILE:%s",ccname);
175  
176         if ((problem = krb5_cc_resolve(krb_context, name, &ccache))) {
177                 log("krb5_cc_default(): %.100s", 
178                         krb5_get_err_text(krb_context,problem));
179                 return;
180         }
181
182         if ((problem = krb5_parse_name(krb_context, gssapi_client_name.value, 
183                                        &princ))) {
184                 log("krb5_parse_name(): %.100s", 
185                         krb5_get_err_text(krb_context,problem));
186                 krb5_cc_destroy(krb_context,ccache);
187                 return;
188         }
189         
190         if ((problem = krb5_cc_initialize(krb_context, ccache, princ))) {
191                 log("krb5_cc_initialize(): %.100s", 
192                         krb5_get_err_text(krb_context,problem));
193                 krb5_free_principal(krb_context,princ);
194                 krb5_cc_destroy(krb_context,ccache);
195                 return;
196         }
197         
198         krb5_free_principal(krb_context,princ);
199
200         #ifdef HEIMDAL
201         if ((problem = krb5_cc_copy_cache(krb_context, 
202                                            gssapi_client_creds->ccache,
203                                            ccache))) {
204                 log("krb5_cc_copy_cache(): %.100s", 
205                         krb5_get_err_text(krb_context,problem));
206                 krb5_cc_destroy(krb_context,ccache);
207                 return;
208         }
209         #else
210         if ((maj_status = gss_krb5_copy_ccache(&min_status, 
211                                                gssapi_client_creds, 
212                                                ccache))) {
213                 log("gss_krb5_copy_ccache() failed");
214                 ssh_gssapi_error(maj_status,min_status);
215                 krb5_cc_destroy(krb_context,ccache);
216                 return;
217         }
218         #endif
219         
220         krb5_cc_close(krb_context,ccache);
221
222
223 #ifdef USE_PAM
224         do_pam_putenv("KRB5CCNAME",name);
225 #endif
226
227         gssapi_cred_store.filename=strdup(ccname);
228         gssapi_cred_store.envvar="KRB5CCNAME";
229         gssapi_cred_store.envval=strdup(name);
230
231         return;
232 }
233
234 #endif /* KRB5 */
235
236 #ifdef GSI
237 #include <globus_gss_assist.h>
238
239 /*
240  * Check if this user is OK to login under GSI. User has been authenticated
241  * as identity in global 'client_name.value' and is trying to log in as passed
242  * username in 'name'.
243  *
244  * Returns non-zero if user is authorized, 0 otherwise.
245  */
246 int
247 ssh_gssapi_gsi_userok(char *name)
248 {
249     int authorized = 0;
250     
251     /* This returns 0 on success */
252     authorized = (globus_gss_assist_userok(gssapi_client_name.value,
253                                            name) == 0);
254     
255     debug("GSI user %s is%s authorized as target user %s",
256           (char *) gssapi_client_name.value,
257           (authorized ? "" : " not"),
258           name);
259     
260     return authorized;
261 }
262
263 /*
264  * Handle setting up child environment for GSI.
265  *
266  * Make sure that this is called _after_ we've setuid to the user.
267  */
268 void
269 ssh_gssapi_gsi_storecreds()
270 {
271         OM_uint32       major_status;
272         OM_uint32       minor_status;
273         
274         
275         if (gssapi_client_creds != NULL)
276         {
277                 char *creds_env = NULL;
278
279                 /*
280                  * This is the current hack with the GSI gssapi library to
281                  * export credentials to disk.
282                  */
283
284                 debug("Exporting delegated credentials");
285                 
286                 minor_status = 0xdee0;  /* Magic value */
287                 major_status =
288                         gss_inquire_cred(&minor_status,
289                                          gssapi_client_creds,
290                                          (gss_name_t *) &creds_env,
291                                          NULL,
292                                          NULL,
293                                          NULL);
294
295                 if ((major_status == GSS_S_COMPLETE) &&
296                     (minor_status == 0xdee1) &&
297                     (creds_env != NULL))
298                 {
299                         char            *value;
300                                 
301                         /*
302                          * String is of the form:
303                          * X509_USER_DELEG_PROXY=filename
304                          * so we parse out the filename
305                          * and then set X509_USER_PROXY
306                          * to point at it.
307                          */
308                         value = strchr(creds_env, '=');
309                         
310                         if (value != NULL)
311                         {
312                                 *value = '\0';
313                                 value++;
314 #ifdef USE_PAM
315                                 do_pam_putenv("X509_USER_PROXY",value);
316 #endif
317                                 gssapi_cred_store.filename=NULL;
318                                 gssapi_cred_store.envvar="X509_USER_PROXY";
319                                 gssapi_cred_store.envval=strdup(value);
320
321                                 return;
322                         }
323                         else
324                         {
325                                 log("Failed to parse delegated credentials string '%s'",
326                                     creds_env);
327                         }
328                 }
329                 else
330                 {
331                         log("Failed to export delegated credentials (error %ld)",
332                             major_status);
333                 }
334         }       
335 }
336
337 #endif /* GSI */
338
339 void
340 ssh_gssapi_cleanup_creds(void *ignored)
341 {
342         if (gssapi_cred_store.filename!=NULL) {
343                 /* Unlink probably isn't sufficient */
344                 debug("removing gssapi cred file\"%s\"",gssapi_cred_store.filename);
345                 unlink(gssapi_cred_store.filename);
346         }
347 }
348
349 void 
350 ssh_gssapi_storecreds()
351 {
352         switch (gssapi_client_type) {
353 #ifdef KRB5
354         case GSS_KERBEROS:
355                 ssh_gssapi_krb5_storecreds();
356                 break;
357 #endif
358 #ifdef GSI
359         case GSS_GSI:
360                 ssh_gssapi_gsi_storecreds();
361                 break;
362 #endif /* GSI */
363         case GSS_LAST_ENTRY:
364                 /* GSSAPI not used in this authentication */
365                 debug("No GSSAPI credentials stored");
366                 break;
367         default:
368                 log("ssh_gssapi_do_child: Unknown mechanism");
369         
370         }
371         
372         if (options.gss_cleanup_creds) {
373                 fatal_add_cleanup(ssh_gssapi_cleanup_creds, NULL);
374         }
375
376 }
377
378 /* This allows GSSAPI methods to do things to the childs environment based
379  * on the passed authentication process and credentials.
380  *
381  * Question: If we didn't use userauth_external for some reason, should we
382  * still delegate credentials?
383  */
384 void 
385 ssh_gssapi_do_child(char ***envp, u_int *envsizep) 
386 {
387
388         if (gssapi_cred_store.envvar!=NULL && 
389             gssapi_cred_store.envval!=NULL) {
390             
391                 debug("Setting %s to %s", gssapi_cred_store.envvar,
392                                           gssapi_cred_store.envval);                              
393                 child_set_env(envp, envsizep, gssapi_cred_store.envvar, 
394                                               gssapi_cred_store.envval);
395         }
396
397         switch(gssapi_client_type) {
398 #ifdef KRB5
399         case GSS_KERBEROS: break;
400 #endif
401 #ifdef GSI
402         case GSS_GSI: break;
403 #endif
404         case GSS_LAST_ENTRY:
405                 debug("No GSSAPI credentials stored");
406                 
407         default:
408                 log("ssh_gssapi_do_child: Unknown mechanism");
409         }
410 }
411
412 int
413 ssh_gssapi_userok(char *user)
414 {
415         if (gssapi_client_name.length==0 || 
416             gssapi_client_name.value==NULL) {
417                 debug("No suitable client data");
418                 return 0;
419         }
420         switch (gssapi_client_type) {
421 #ifdef KRB5
422         case GSS_KERBEROS:
423                 return(ssh_gssapi_krb5_userok(user));
424                 break; /* Not reached */
425 #endif
426 #ifdef GSI
427         case GSS_GSI:
428                 return(ssh_gssapi_gsi_userok(user));
429                 break; /* Not reached */
430 #endif /* GSI */
431         case GSS_LAST_ENTRY:
432                 debug("Client not GSSAPI");
433                 break;
434         default:
435                 debug("Unknown client authentication type");
436         }
437         return(0);
438 }
439
440 int
441 userauth_external(Authctxt *authctxt)
442 {
443         packet_check_eom();
444
445         return(ssh_gssapi_userok(authctxt->user));
446 }
447
448 void input_gssapi_token(int type, u_int32_t plen, void *ctxt);
449 void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
450
451 /* We only support those mechanisms that we know about (ie ones that we know
452  * how to check local user kuserok and the like
453  */
454 int
455 userauth_gssapi(Authctxt *authctxt)
456 {
457         gss_OID_desc    oid= {0,NULL};
458         Gssctxt         *ctxt;
459         int             mechs;
460         gss_OID_set     supported;
461         int             present;
462         OM_uint32       ms;
463         
464         if (!authctxt->valid || authctxt->user == NULL)
465                 return 0;
466         mechs=packet_get_int();
467         if (mechs==0) {
468                 debug("Mechanism negotiation is not supported");
469                 return 0;
470         }
471
472         ssh_gssapi_supported_oids(&supported);
473         do {
474                 if (oid.elements)
475                         xfree(oid.elements);
476                 oid.elements = packet_get_string(NULL);
477                 gss_test_oid_set_member(&ms, &oid, supported, &present);
478                 mechs--;
479         } while (mechs>0 && !present);
480         
481         if (!present) {
482                 xfree(oid.elements);
483                 return(0);
484         }
485         
486         ctxt=xmalloc(sizeof(Gssctxt));
487         authctxt->methoddata=(void *)ctxt;
488         
489         ssh_gssapi_build_ctx(ctxt);
490         ssh_gssapi_set_oid(ctxt,&oid);
491
492         if (ssh_gssapi_acquire_cred(ctxt))
493                 return 0;
494
495         /* Send SSH_MSG_USERAUTH_GSSAPI_RESPONSE */
496
497         packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE);
498         packet_put_string(oid.elements,oid.length);
499         packet_send();
500         packet_write_wait();
501         xfree(oid.elements);
502                 
503         dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, 
504                      &input_gssapi_token);
505         authctxt->postponed = 1;
506         
507         return 0;
508 }
509
510 void
511 input_gssapi_token(int type, u_int32_t plen, void *ctxt)
512 {
513         Authctxt *authctxt = ctxt;
514         Gssctxt *gssctxt;
515         gss_buffer_desc send_tok,recv_tok;
516         OM_uint32 maj_status, min_status;
517         
518         if (authctxt == NULL || authctxt->methoddata == NULL)
519                 fatal("No authentication or GSSAPI context");
520                 
521         gssctxt=authctxt->methoddata;
522
523         recv_tok.value=packet_get_string(&recv_tok.length);
524         
525         maj_status=ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok, NULL);
526         packet_check_eom();
527         
528         if (GSS_ERROR(maj_status)) {
529                 /* Failure <sniff> */
530                 authctxt->postponed = 0;
531                 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
532                 userauth_finish(authctxt, 0, "gssapi");
533         }
534                         
535         if (send_tok.length != 0) {
536                 /* Send a packet back to the client */
537                 packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
538                 packet_put_string(send_tok.value,send_tok.length);
539                 packet_send();
540                 packet_write_wait();
541                 gss_release_buffer(&min_status, &send_tok);                                     
542         }
543         
544         if (maj_status == GSS_S_COMPLETE) {
545                 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,NULL);
546                 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,
547                              &input_gssapi_exchange_complete);
548         }
549 }
550
551 /* This is called when the client thinks we've completed authentication.
552  * It should only be enabled in the dispatch handler by the function above,
553  * which only enables it once the GSSAPI exchange is complete.
554  */
555  
556 void
557 input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)
558 {
559         Authctxt *authctxt = ctxt;
560         Gssctxt *gssctxt;
561         int authenticated;
562         
563         if (authctxt == NULL || authctxt->methoddata == NULL)
564                 fatal("No authentication or GSSAPI context");
565                 
566         gssctxt=authctxt->methoddata;
567
568         /* This should never happen, but better safe than sorry. */
569         if (gssctxt->status != GSS_S_COMPLETE) {
570                 packet_disconnect("Context negotiation is not complete");
571         }
572
573         if (ssh_gssapi_getclient(gssctxt,&gssapi_client_type,
574                                  &gssapi_client_name,
575                                  &gssapi_client_creds)) {
576                 fatal("Couldn't convert client name");
577         }
578                                                 
579         authenticated = ssh_gssapi_userok(authctxt->user);
580
581         authctxt->postponed = 0;
582         dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
583         dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
584         userauth_finish(authctxt, authenticated, "gssapi");
585 }
586
587 #endif /* GSSAPI */
This page took 0.080005 seconds and 5 git commands to generate.