source: trunk/third/openssh/gss-serv.c @ 19678

Revision 19678, 14.8 KB checked in by zacheiss, 21 years ago (diff)
Need to set KRB5CCNAME in the environment earlier so an attach that looks at krb5 tickets can work.
Line 
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 "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#include "compat.h"
46#include "monitor_wrap.h"
47
48#include "ssh-gss.h"
49
50extern ServerOptions options;
51extern u_char *session_id2;
52extern int session_id2_len;
53extern int is_local_acct;
54
55typedef struct ssh_gssapi_cred_cache {
56        char *filename;
57        char *envvar;
58        char *envval;
59        void *data;
60} ssh_gssapi_cred_cache;
61
62static struct ssh_gssapi_cred_cache gssapi_cred_store = {NULL,NULL,NULL};
63
64#ifdef KRB5
65
66#ifdef HEIMDAL
67#include <krb5.h>
68#else
69#include <gssapi_krb5.h>
70#define krb5_get_err_text(context,code) error_message(code)
71#endif
72
73/* Initialise the krb5 library, so we can use it for those bits that
74 * GSSAPI won't do */
75
76int ssh_gssapi_krb5_init(Authctxt *authctxt) {
77        krb5_error_code problem;
78       
79        if (authctxt->krb5_ctx != NULL)
80          return 1;
81
82        problem = krb5_init_context(&authctxt->krb5_ctx);
83        if (problem) {
84                log("Cannot initialize krb5 context");
85                return 0;
86        }
87        krb5_init_ets(authctxt->krb5_ctx);
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
97int
98ssh_gssapi_krb5_userok(Authctxt *authctxt) {
99        krb5_principal princ;
100        int retval;
101
102        if (ssh_gssapi_krb5_init(authctxt) == 0)
103                return 0;
104
105        /* If this isn't a local account and the user hasn't specified
106         * ticket forwarding, fail through to password authentication.
107         * The shell the user gets won't be useful without tickets anyway.
108         */
109        if (!is_local_acct && !gssapi_client_creds)
110                return 0;
111               
112        if ((retval=krb5_parse_name(authctxt->krb5_ctx,
113                                    gssapi_client_name.value,
114                                    &princ))) {
115                log("krb5_parse_name(): %.100s",
116                        krb5_get_err_text(authctxt->krb5_ctx,retval));
117                return 0;
118        }
119        if (krb5_kuserok(authctxt->krb5_ctx, princ, authctxt->user)) {
120                retval = 1;
121                log("Authorized to %s, krb5 principal %s (krb5_kuserok)",
122                    authctxt->user, (char *)gssapi_client_name.value);
123        }
124        else
125                retval = 0;
126       
127        krb5_free_principal(authctxt->krb5_ctx, princ);
128        return retval;
129}
130       
131/* Make sure that this is called _after_ we've setuid to the user */
132
133/* This writes out any forwarded credentials. Its specific to the Kerberos
134 * GSSAPI mechanism
135 *
136 * We assume that our caller has made sure that the user has selected
137 * delegated credentials, and that the client_creds structure is correctly
138 * populated.
139 */
140
141void
142ssh_gssapi_krb5_storecreds(Authctxt *authctxt) {
143        krb5_ccache ccache;
144        krb5_error_code problem;
145        krb5_principal princ;
146        char ccname[35];
147        static char name[40];
148        int tmpfd;
149        OM_uint32 maj_status,min_status;
150
151
152        if (gssapi_client_creds==NULL) {
153                debug("No credentials stored");
154                return;
155        }
156               
157        if (ssh_gssapi_krb5_init(authctxt) == 0)
158                return;
159
160        if (options.gss_use_session_ccache) {
161                snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_p%d",getpid());
162       
163                if (fchmod(tmpfd, S_IRUSR | S_IWUSR) == -1) {
164                        log("fchmod(): %.100s", strerror(errno));
165                        close(tmpfd);
166                        return;
167                }
168        } else {
169                snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_p%d",getpid());
170                tmpfd = open(ccname, O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
171                if (tmpfd == -1) {
172                        log("open(): %.100s", strerror(errno));
173                        return;
174                }
175        }
176
177        close(tmpfd);
178        snprintf(name, sizeof(name), "FILE:%s",ccname);
179 
180        if ((problem = krb5_cc_resolve(authctxt->krb5_ctx, name, &ccache))) {
181                log("krb5_cc_default(): %.100s",
182                        krb5_get_err_text(authctxt->krb5_ctx,problem));
183                return;
184        }
185
186        if ((problem = krb5_parse_name(authctxt->krb5_ctx, gssapi_client_name.value,
187                                       &princ))) {
188                log("krb5_parse_name(): %.100s",
189                        krb5_get_err_text(authctxt->krb5_ctx,problem));
190                krb5_cc_destroy(authctxt->krb5_ctx,ccache);
191                return;
192        }
193       
194        if ((problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache, princ))) {
195                log("krb5_cc_initialize(): %.100s",
196                        krb5_get_err_text(authctxt->krb5_ctx,problem));
197                krb5_free_principal(authctxt->krb5_ctx,princ);
198                krb5_cc_destroy(authctxt->krb5_ctx,ccache);
199                return;
200        }
201       
202        krb5_free_principal(authctxt->krb5_ctx,princ);
203
204        #ifdef HEIMDAL
205        if ((problem = krb5_cc_copy_cache(authctxt->krb5_ctx,
206                                           gssapi_client_creds->ccache,
207                                           ccache))) {
208                log("krb5_cc_copy_cache(): %.100s",
209                        krb5_get_err_text(authctxt->krb5_ctx,problem));
210                krb5_cc_destroy(authctxt->krb5_ctx,ccache);
211                return;
212        }
213        #else
214        if ((maj_status = gss_krb5_copy_ccache(&min_status,
215                                               gssapi_client_creds,
216                                               ccache))) {
217                log("gss_krb5_copy_ccache() failed");
218                ssh_gssapi_error(maj_status,min_status);
219                krb5_cc_destroy(authctxt->krb5_ctx,ccache);
220                return;
221        }
222        #endif
223       
224#ifdef USE_PAM
225        do_pam_putenv("KRB5CCNAME",name);
226#else
227        setenv("KRB5CCNAME", name, 1);
228#endif
229
230        gssapi_cred_store.filename=strdup(ccname);
231        gssapi_cred_store.envvar="KRB5CCNAME";
232        gssapi_cred_store.envval=strdup(name);
233
234        /* Populate the Kerberos specific members of the authctxt.
235         * We'll need them later.
236         */
237        krb5_copy_principal(authctxt->krb5_ctx, princ, &authctxt->krb5_user);
238        krb5_free_principal(authctxt->krb5_ctx, princ);
239
240        authctxt->krb5_fwd_ccache = ccache;
241        authctxt->krb5_ticket_file =
242          (char *)krb5_cc_get_name(authctxt->krb5_ctx,
243                                   authctxt->krb5_fwd_ccache);
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 */
260int
261ssh_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 */
282void
283ssh_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
353void
354ssh_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
363void
364ssh_gssapi_storecreds(Authctxt *authctxt)
365{
366        switch (gssapi_client_type) {
367#ifdef KRB5
368        case GSS_KERBEROS:
369                ssh_gssapi_krb5_storecreds(authctxt);
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 */
398void
399ssh_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
426int
427ssh_gssapi_userok(Authctxt *authctxt)
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(authctxt));
438                break; /* Not reached */
439#endif
440#ifdef GSI
441        case GSS_GSI:
442                return(ssh_gssapi_gsi_userok(authctxt->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/* Stuff to play nicely with privsep */
455
456#if 0
457extern struct monitor *pmonitor;
458
459OM_uint32
460mm_ssh_gssapi_server_ctxt(Gssctxt **ctx, gss_OID oid) {
461        Buffer m;
462       
463        /* Client doesn't get to see the context */
464        *ctx=NULL;
465
466        buffer_init(&m)
467        buffer_put_string(&m,oid->elements,oid->length);
468       
469        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSETUP, &m);
470
471        debug3("%s: waiting for MONITOR_ANS_GSSSIGN",__func__);
472        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_SIGN, &m);
473        major=buffer_get_int(&m);
474
475        return(major);
476}
477       
478int
479mm_answer_gss_server_ctxt(int socket, Buffer *m) {
480        gss_OID_desc oid;
481        OM_uint32 major;
482       
483        oid.elements=buffer_get_string(m,&oid.length);
484               
485        major=ssh_gssapi_server_ctxt(&gsscontext,&oid);
486
487        xfree(oid.elements);
488       
489        buffer_clear(m);
490        buffer_put_int(m,result);
491       
492        mm_request_send(socket,MONITOR_ANS_GSSSIGN,m);
493       
494        return(0);
495}
496
497OM_uint32
498mm_ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *in,
499                         gss_buffer_desc *out, OM_uint32 *flags) {
500
501        Buffer m;
502        OM_uint32, major;
503       
504        buffer_init(&m);
505        buffer_put_string(&m, in->value, in->length);
506        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSTEP, &m);
507       
508        debug3("%s: waiting for MONITOR_ANS_GSSSTEP", &m);
509        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSTEP, &m);
510       
511        major=buffer_get_int(&m);
512        *out->value=buffer->get_string(&m,&out->length);
513        flags=buffer_get_int(&m);
514
515        return(major);
516}
517
518int
519mm_answer_gss_accept_ctxt(int socket, Buffer *m) {
520        gss_buffer_desc in,out;
521        OM_uint32 major;
522        OM_uint32 flags = 0; /* GSI needs this */
523       
524        in.value = buffer_get_string(m,&in.length);
525        major=ssh_gssapi_accept_ctxt(gsscontext,&in,&out,&flags);
526        xfree(in.value);
527       
528        buffer_clear(m);
529        buffer_put_int(m, major);
530        buffer_put_string(m, out.value, out.length);
531        buffer_put_int(m, flags);
532        mm_request_send(socket,MONITOR_ANS_STEP,m);
533       
534        gss_release_buffer(out);
535       
536        return(0);
537}
538
539int
540mm_ssh_gssapi_userok(char *user) {
541        Buffer m;
542       
543        buffer_init(&m);
544        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, &m);
545       
546        debug3("%s: waiting for MONTIOR_ANS_GSSUSEROK", __func__);
547        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUSEROK),
548                                  &m);
549       
550        authenticated = buffer_get_int(&m);
551       
552        buffer_free(&m);
553        debug3("%s: user %sauthetnicated",__func__, authenticated ? "" : "not ");
554        return(authenticated);
555}
556
557int
558mm_answer_gss_userok(int socket, Buffer *m) {
559        authenticated = authctxt->valid && ssh_gssapi_userok(authctxt);
560       
561        buffer_clear(m);
562        buffer_put_int(m, authenticated);
563       
564        debug3("%s: sending result %d", __func__, authenticated);
565        mm_request_send(socket, MONITOR_ANS_GSSUSEROK, m);
566       
567        /* Monitor loop will terminate if authenticated */
568        return(authenticated);
569}
570
571OM_uint32
572mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) {
573        Buffer m;
574        OM_uint32 major, minor;
575       
576        buffer_init(&m);
577        buffer_put_string(&m, data->value, data->length);
578       
579        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m);
580       
581        debug3("%s: waiting for MONITOR_ANS_GSSSIGN",__func__);
582        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m);
583        major=buffer_get_int(&m);
584        *hash->value = buffer_get_string(&m, &hash->length);
585       
586        return(major);
587}
588       
589int
590mm_answer_gss_sign(int socket, Buffer *m) {
591        gss_buffer_desc data,hash;
592        OM_uint32 major;
593       
594        data.value = buffer_get_string(m,&data.length);
595        if (data.length != 20)
596                fatal("%s: data length incorrect: %d", __func__, datlen);
597       
598        /* Save the session ID - only first time round */
599        if (session_id2_len == 0) {
600                session_id2_len=data.length;
601                session_id2 = xmalloc(session_id2_len);
602                memcpy(session_id2, data.value, session_id2_len);
603        }
604        major=ssh_gssapi_sign(gsscontext, &data, &hash);
605       
606        xfree(data.value);
607       
608        buffer_clear(m);
609        buffer_put_int(m, major);
610        buffer_put_string(m, hash.value, hash.length);
611
612        mm_request_send(socket,MONITOR_ANS_GSSSIGN,m);
613               
614        gss_release_buffer(hash);
615       
616        return(0);
617}
618#endif
619#endif /* GSSAPI */
Note: See TracBrowser for help on using the repository browser.