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

Revision 18772, 14.8 KB checked in by zacheiss, 21 years ago (diff)
remerge local change to fail gssapi auth for non-local accounts that aren't forwarding credentials
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#endif
227
228        gssapi_cred_store.filename=strdup(ccname);
229        gssapi_cred_store.envvar="KRB5CCNAME";
230        gssapi_cred_store.envval=strdup(name);
231
232        /* Populate the Kerberos specific members of the authctxt.
233         * We'll need them later.
234         */
235        krb5_copy_principal(authctxt->krb5_ctx, princ, &authctxt->krb5_user);
236        krb5_free_principal(authctxt->krb5_ctx, princ);
237
238        authctxt->krb5_fwd_ccache = ccache;
239        authctxt->krb5_ticket_file =
240          (char *)krb5_cc_get_name(authctxt->krb5_ctx,
241                                   authctxt->krb5_fwd_ccache);
242
243        return;
244}
245
246#endif /* KRB5 */
247
248#ifdef GSI
249#include <globus_gss_assist.h>
250
251/*
252 * Check if this user is OK to login under GSI. User has been authenticated
253 * as identity in global 'client_name.value' and is trying to log in as passed
254 * username in 'name'.
255 *
256 * Returns non-zero if user is authorized, 0 otherwise.
257 */
258int
259ssh_gssapi_gsi_userok(char *name)
260{
261    int authorized = 0;
262   
263    /* This returns 0 on success */
264    authorized = (globus_gss_assist_userok(gssapi_client_name.value,
265                                           name) == 0);
266   
267    debug("GSI user %s is%s authorized as target user %s",
268          (char *) gssapi_client_name.value,
269          (authorized ? "" : " not"),
270          name);
271   
272    return authorized;
273}
274
275/*
276 * Handle setting up child environment for GSI.
277 *
278 * Make sure that this is called _after_ we've setuid to the user.
279 */
280void
281ssh_gssapi_gsi_storecreds()
282{
283        OM_uint32       major_status;
284        OM_uint32       minor_status;
285       
286       
287        if (gssapi_client_creds != NULL)
288        {
289                char *creds_env = NULL;
290
291                /*
292                 * This is the current hack with the GSI gssapi library to
293                 * export credentials to disk.
294                 */
295
296                debug("Exporting delegated credentials");
297               
298                minor_status = 0xdee0;  /* Magic value */
299                major_status =
300                        gss_inquire_cred(&minor_status,
301                                         gssapi_client_creds,
302                                         (gss_name_t *) &creds_env,
303                                         NULL,
304                                         NULL,
305                                         NULL);
306
307                if ((major_status == GSS_S_COMPLETE) &&
308                    (minor_status == 0xdee1) &&
309                    (creds_env != NULL))
310                {
311                        char            *value;
312                               
313                        /*
314                         * String is of the form:
315                         * X509_USER_DELEG_PROXY=filename
316                         * so we parse out the filename
317                         * and then set X509_USER_PROXY
318                         * to point at it.
319                         */
320                        value = strchr(creds_env, '=');
321                       
322                        if (value != NULL)
323                        {
324                                *value = '\0';
325                                value++;
326#ifdef USE_PAM
327                                do_pam_putenv("X509_USER_PROXY",value);
328#endif
329                                gssapi_cred_store.filename=NULL;
330                                gssapi_cred_store.envvar="X509_USER_PROXY";
331                                gssapi_cred_store.envval=strdup(value);
332
333                                return;
334                        }
335                        else
336                        {
337                                log("Failed to parse delegated credentials string '%s'",
338                                    creds_env);
339                        }
340                }
341                else
342                {
343                        log("Failed to export delegated credentials (error %ld)",
344                            major_status);
345                }
346        }       
347}
348
349#endif /* GSI */
350
351void
352ssh_gssapi_cleanup_creds(void *ignored)
353{
354        if (gssapi_cred_store.filename!=NULL) {
355                /* Unlink probably isn't sufficient */
356                debug("removing gssapi cred file\"%s\"",gssapi_cred_store.filename);
357                unlink(gssapi_cred_store.filename);
358        }
359}
360
361void
362ssh_gssapi_storecreds(Authctxt *authctxt)
363{
364        switch (gssapi_client_type) {
365#ifdef KRB5
366        case GSS_KERBEROS:
367                ssh_gssapi_krb5_storecreds(authctxt);
368                break;
369#endif
370#ifdef GSI
371        case GSS_GSI:
372                ssh_gssapi_gsi_storecreds();
373                break;
374#endif /* GSI */
375        case GSS_LAST_ENTRY:
376                /* GSSAPI not used in this authentication */
377                debug("No GSSAPI credentials stored");
378                break;
379        default:
380                log("ssh_gssapi_do_child: Unknown mechanism");
381       
382        }
383       
384        if (options.gss_cleanup_creds) {
385                fatal_add_cleanup(ssh_gssapi_cleanup_creds, NULL);
386        }
387
388}
389
390/* This allows GSSAPI methods to do things to the childs environment based
391 * on the passed authentication process and credentials.
392 *
393 * Question: If we didn't use userauth_external for some reason, should we
394 * still delegate credentials?
395 */
396void
397ssh_gssapi_do_child(char ***envp, u_int *envsizep)
398{
399
400        if (gssapi_cred_store.envvar!=NULL &&
401            gssapi_cred_store.envval!=NULL) {
402           
403                debug("Setting %s to %s", gssapi_cred_store.envvar,
404                                          gssapi_cred_store.envval);                             
405                child_set_env(envp, envsizep, gssapi_cred_store.envvar,
406                                              gssapi_cred_store.envval);
407        }
408
409        switch(gssapi_client_type) {
410#ifdef KRB5
411        case GSS_KERBEROS: break;
412#endif
413#ifdef GSI
414        case GSS_GSI: break;
415#endif
416        case GSS_LAST_ENTRY:
417                debug("No GSSAPI credentials stored");
418                break;
419        default:
420                log("ssh_gssapi_do_child: Unknown mechanism");
421        }
422}
423
424int
425ssh_gssapi_userok(Authctxt *authctxt)
426{
427        if (gssapi_client_name.length==0 ||
428            gssapi_client_name.value==NULL) {
429                debug("No suitable client data");
430                return 0;
431        }
432        switch (gssapi_client_type) {
433#ifdef KRB5
434        case GSS_KERBEROS:
435                return(ssh_gssapi_krb5_userok(authctxt));
436                break; /* Not reached */
437#endif
438#ifdef GSI
439        case GSS_GSI:
440                return(ssh_gssapi_gsi_userok(authctxt->user));
441                break; /* Not reached */
442#endif /* GSI */
443        case GSS_LAST_ENTRY:
444                debug("Client not GSSAPI");
445                break;
446        default:
447                debug("Unknown client authentication type");
448        }
449        return(0);
450}
451
452/* Stuff to play nicely with privsep */
453
454#if 0
455extern struct monitor *pmonitor;
456
457OM_uint32
458mm_ssh_gssapi_server_ctxt(Gssctxt **ctx, gss_OID oid) {
459        Buffer m;
460       
461        /* Client doesn't get to see the context */
462        *ctx=NULL;
463
464        buffer_init(&m)
465        buffer_put_string(&m,oid->elements,oid->length);
466       
467        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSETUP, &m);
468
469        debug3("%s: waiting for MONITOR_ANS_GSSSIGN",__func__);
470        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_SIGN, &m);
471        major=buffer_get_int(&m);
472
473        return(major);
474}
475       
476int
477mm_answer_gss_server_ctxt(int socket, Buffer *m) {
478        gss_OID_desc oid;
479        OM_uint32 major;
480       
481        oid.elements=buffer_get_string(m,&oid.length);
482               
483        major=ssh_gssapi_server_ctxt(&gsscontext,&oid);
484
485        xfree(oid.elements);
486       
487        buffer_clear(m);
488        buffer_put_int(m,result);
489       
490        mm_request_send(socket,MONITOR_ANS_GSSSIGN,m);
491       
492        return(0);
493}
494
495OM_uint32
496mm_ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *in,
497                         gss_buffer_desc *out, OM_uint32 *flags) {
498
499        Buffer m;
500        OM_uint32, major;
501       
502        buffer_init(&m);
503        buffer_put_string(&m, in->value, in->length);
504        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSTEP, &m);
505       
506        debug3("%s: waiting for MONITOR_ANS_GSSSTEP", &m);
507        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSTEP, &m);
508       
509        major=buffer_get_int(&m);
510        *out->value=buffer->get_string(&m,&out->length);
511        flags=buffer_get_int(&m);
512
513        return(major);
514}
515
516int
517mm_answer_gss_accept_ctxt(int socket, Buffer *m) {
518        gss_buffer_desc in,out;
519        OM_uint32 major;
520        OM_uint32 flags = 0; /* GSI needs this */
521       
522        in.value = buffer_get_string(m,&in.length);
523        major=ssh_gssapi_accept_ctxt(gsscontext,&in,&out,&flags);
524        xfree(in.value);
525       
526        buffer_clear(m);
527        buffer_put_int(m, major);
528        buffer_put_string(m, out.value, out.length);
529        buffer_put_int(m, flags);
530        mm_request_send(socket,MONITOR_ANS_STEP,m);
531       
532        gss_release_buffer(out);
533       
534        return(0);
535}
536
537int
538mm_ssh_gssapi_userok(char *user) {
539        Buffer m;
540       
541        buffer_init(&m);
542        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, &m);
543       
544        debug3("%s: waiting for MONTIOR_ANS_GSSUSEROK", __func__);
545        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUSEROK),
546                                  &m);
547       
548        authenticated = buffer_get_int(&m);
549       
550        buffer_free(&m);
551        debug3("%s: user %sauthetnicated",__func__, authenticated ? "" : "not ");
552        return(authenticated);
553}
554
555int
556mm_answer_gss_userok(int socket, Buffer *m) {
557        authenticated = authctxt->valid && ssh_gssapi_userok(authctxt);
558       
559        buffer_clear(m);
560        buffer_put_int(m, authenticated);
561       
562        debug3("%s: sending result %d", __func__, authenticated);
563        mm_request_send(socket, MONITOR_ANS_GSSUSEROK, m);
564       
565        /* Monitor loop will terminate if authenticated */
566        return(authenticated);
567}
568
569OM_uint32
570mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) {
571        Buffer m;
572        OM_uint32 major, minor;
573       
574        buffer_init(&m);
575        buffer_put_string(&m, data->value, data->length);
576       
577        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m);
578       
579        debug3("%s: waiting for MONITOR_ANS_GSSSIGN",__func__);
580        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m);
581        major=buffer_get_int(&m);
582        *hash->value = buffer_get_string(&m, &hash->length);
583       
584        return(major);
585}
586       
587int
588mm_answer_gss_sign(int socket, Buffer *m) {
589        gss_buffer_desc data,hash;
590        OM_uint32 major;
591       
592        data.value = buffer_get_string(m,&data.length);
593        if (data.length != 20)
594                fatal("%s: data length incorrect: %d", __func__, datlen);
595       
596        /* Save the session ID - only first time round */
597        if (session_id2_len == 0) {
598                session_id2_len=data.length;
599                session_id2 = xmalloc(session_id2_len);
600                memcpy(session_id2, data.value, session_id2_len);
601        }
602        major=ssh_gssapi_sign(gsscontext, &data, &hash);
603       
604        xfree(data.value);
605       
606        buffer_clear(m);
607        buffer_put_int(m, major);
608        buffer_put_string(m, hash.value, hash.length);
609
610        mm_request_send(socket,MONITOR_ANS_GSSSIGN,m);
611               
612        gss_release_buffer(hash);
613       
614        return(0);
615}
616#endif
617#endif /* GSSAPI */
Note: See TracBrowser for help on using the repository browser.