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

Revision 22055, 14.8 KB checked in by zacheiss, 19 years ago (diff)
Fix double-free.
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
88        return 1;       
89}                       
90
91/* Check if this user is OK to login. This only works with krb5 - other
92 * GSSAPI mechanisms will need their own.
93 * Returns true if the user is OK to log in, otherwise returns 0
94 */
95
96int
97ssh_gssapi_krb5_userok(Authctxt *authctxt) {
98        krb5_principal princ;
99        int retval;
100
101        if (ssh_gssapi_krb5_init(authctxt) == 0)
102                return 0;
103
104        /* If this isn't a local account and the user hasn't specified
105         * ticket forwarding, fail through to password authentication.
106         * The shell the user gets won't be useful without tickets anyway.
107         */
108        if (!is_local_acct && !gssapi_client_creds)
109                return 0;
110               
111        if ((retval=krb5_parse_name(authctxt->krb5_ctx,
112                                    gssapi_client_name.value,
113                                    &princ))) {
114                log("krb5_parse_name(): %.100s",
115                        krb5_get_err_text(authctxt->krb5_ctx,retval));
116                return 0;
117        }
118        if (krb5_kuserok(authctxt->krb5_ctx, princ, authctxt->user)) {
119                retval = 1;
120                log("Authorized to %s, krb5 principal %s (krb5_kuserok)",
121                    authctxt->user, (char *)gssapi_client_name.value);
122        }
123        else
124                retval = 0;
125       
126        krb5_free_principal(authctxt->krb5_ctx, princ);
127        return retval;
128}
129       
130/* Make sure that this is called _after_ we've setuid to the user */
131
132/* This writes out any forwarded credentials. Its specific to the Kerberos
133 * GSSAPI mechanism
134 *
135 * We assume that our caller has made sure that the user has selected
136 * delegated credentials, and that the client_creds structure is correctly
137 * populated.
138 */
139
140void
141ssh_gssapi_krb5_storecreds(Authctxt *authctxt) {
142        krb5_ccache ccache;
143        krb5_error_code problem;
144        krb5_principal princ;
145        char ccname[35];
146        static char name[40];
147        int tmpfd;
148        OM_uint32 maj_status,min_status;
149
150
151        if (gssapi_client_creds==NULL) {
152                debug("No credentials stored");
153                return;
154        }
155               
156        if (ssh_gssapi_krb5_init(authctxt) == 0)
157                return;
158
159        if (options.gss_use_session_ccache) {
160                snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_p%d",getpid());
161       
162                if (fchmod(tmpfd, S_IRUSR | S_IWUSR) == -1) {
163                        log("fchmod(): %.100s", strerror(errno));
164                        close(tmpfd);
165                        return;
166                }
167        } else {
168                snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_p%d",getpid());
169                tmpfd = open(ccname, O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
170                if (tmpfd == -1) {
171                        log("open(): %.100s", strerror(errno));
172                        return;
173                }
174        }
175
176        close(tmpfd);
177        snprintf(name, sizeof(name), "FILE:%s",ccname);
178 
179        if ((problem = krb5_cc_resolve(authctxt->krb5_ctx, name, &ccache))) {
180                log("krb5_cc_default(): %.100s",
181                        krb5_get_err_text(authctxt->krb5_ctx,problem));
182                return;
183        }
184
185        if ((problem = krb5_parse_name(authctxt->krb5_ctx, gssapi_client_name.value,
186                                       &princ))) {
187                log("krb5_parse_name(): %.100s",
188                        krb5_get_err_text(authctxt->krb5_ctx,problem));
189                krb5_cc_destroy(authctxt->krb5_ctx,ccache);
190                return;
191        }
192       
193        if ((problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache, princ))) {
194                log("krb5_cc_initialize(): %.100s",
195                        krb5_get_err_text(authctxt->krb5_ctx,problem));
196                krb5_free_principal(authctxt->krb5_ctx,princ);
197                krb5_cc_destroy(authctxt->krb5_ctx,ccache);
198                return;
199        }
200       
201        #ifdef HEIMDAL
202        if ((problem = krb5_cc_copy_cache(authctxt->krb5_ctx,
203                                           gssapi_client_creds->ccache,
204                                           ccache))) {
205                log("krb5_cc_copy_cache(): %.100s",
206                        krb5_get_err_text(authctxt->krb5_ctx,problem));
207                krb5_cc_destroy(authctxt->krb5_ctx,ccache);
208                return;
209        }
210        #else
211        if ((maj_status = gss_krb5_copy_ccache(&min_status,
212                                               gssapi_client_creds,
213                                               ccache))) {
214                log("gss_krb5_copy_ccache() failed");
215                ssh_gssapi_error(maj_status,min_status);
216                krb5_cc_destroy(authctxt->krb5_ctx,ccache);
217                return;
218        }
219        #endif
220       
221#ifdef USE_PAM
222        do_pam_putenv("KRB5CCNAME",name);
223#else
224        setenv("KRB5CCNAME", name, 1);
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        /* Populate the Kerberos specific members of the authctxt.
232         * We'll need them later.
233         */
234        krb5_copy_principal(authctxt->krb5_ctx, princ, &authctxt->krb5_user);
235        krb5_free_principal(authctxt->krb5_ctx, princ);
236
237        authctxt->krb5_fwd_ccache = ccache;
238        authctxt->krb5_ticket_file =
239          (char *)krb5_cc_get_name(authctxt->krb5_ctx,
240                                   authctxt->krb5_fwd_ccache);
241
242        return;
243}
244
245#endif /* KRB5 */
246
247#ifdef GSI
248#include <globus_gss_assist.h>
249
250/*
251 * Check if this user is OK to login under GSI. User has been authenticated
252 * as identity in global 'client_name.value' and is trying to log in as passed
253 * username in 'name'.
254 *
255 * Returns non-zero if user is authorized, 0 otherwise.
256 */
257int
258ssh_gssapi_gsi_userok(char *name)
259{
260    int authorized = 0;
261   
262    /* This returns 0 on success */
263    authorized = (globus_gss_assist_userok(gssapi_client_name.value,
264                                           name) == 0);
265   
266    debug("GSI user %s is%s authorized as target user %s",
267          (char *) gssapi_client_name.value,
268          (authorized ? "" : " not"),
269          name);
270   
271    return authorized;
272}
273
274/*
275 * Handle setting up child environment for GSI.
276 *
277 * Make sure that this is called _after_ we've setuid to the user.
278 */
279void
280ssh_gssapi_gsi_storecreds()
281{
282        OM_uint32       major_status;
283        OM_uint32       minor_status;
284       
285       
286        if (gssapi_client_creds != NULL)
287        {
288                char *creds_env = NULL;
289
290                /*
291                 * This is the current hack with the GSI gssapi library to
292                 * export credentials to disk.
293                 */
294
295                debug("Exporting delegated credentials");
296               
297                minor_status = 0xdee0;  /* Magic value */
298                major_status =
299                        gss_inquire_cred(&minor_status,
300                                         gssapi_client_creds,
301                                         (gss_name_t *) &creds_env,
302                                         NULL,
303                                         NULL,
304                                         NULL);
305
306                if ((major_status == GSS_S_COMPLETE) &&
307                    (minor_status == 0xdee1) &&
308                    (creds_env != NULL))
309                {
310                        char            *value;
311                               
312                        /*
313                         * String is of the form:
314                         * X509_USER_DELEG_PROXY=filename
315                         * so we parse out the filename
316                         * and then set X509_USER_PROXY
317                         * to point at it.
318                         */
319                        value = strchr(creds_env, '=');
320                       
321                        if (value != NULL)
322                        {
323                                *value = '\0';
324                                value++;
325#ifdef USE_PAM
326                                do_pam_putenv("X509_USER_PROXY",value);
327#endif
328                                gssapi_cred_store.filename=NULL;
329                                gssapi_cred_store.envvar="X509_USER_PROXY";
330                                gssapi_cred_store.envval=strdup(value);
331
332                                return;
333                        }
334                        else
335                        {
336                                log("Failed to parse delegated credentials string '%s'",
337                                    creds_env);
338                        }
339                }
340                else
341                {
342                        log("Failed to export delegated credentials (error %ld)",
343                            major_status);
344                }
345        }       
346}
347
348#endif /* GSI */
349
350void
351ssh_gssapi_cleanup_creds(void *ignored)
352{
353        if (gssapi_cred_store.filename!=NULL) {
354                /* Unlink probably isn't sufficient */
355                debug("removing gssapi cred file\"%s\"",gssapi_cred_store.filename);
356                unlink(gssapi_cred_store.filename);
357        }
358}
359
360void
361ssh_gssapi_storecreds(Authctxt *authctxt)
362{
363        switch (gssapi_client_type) {
364#ifdef KRB5
365        case GSS_KERBEROS:
366                ssh_gssapi_krb5_storecreds(authctxt);
367                break;
368#endif
369#ifdef GSI
370        case GSS_GSI:
371                ssh_gssapi_gsi_storecreds();
372                break;
373#endif /* GSI */
374        case GSS_LAST_ENTRY:
375                /* GSSAPI not used in this authentication */
376                debug("No GSSAPI credentials stored");
377                break;
378        default:
379                log("ssh_gssapi_do_child: Unknown mechanism");
380       
381        }
382       
383        if (options.gss_cleanup_creds) {
384                fatal_add_cleanup(ssh_gssapi_cleanup_creds, NULL);
385        }
386
387}
388
389/* This allows GSSAPI methods to do things to the childs environment based
390 * on the passed authentication process and credentials.
391 *
392 * Question: If we didn't use userauth_external for some reason, should we
393 * still delegate credentials?
394 */
395void
396ssh_gssapi_do_child(char ***envp, u_int *envsizep)
397{
398
399        if (gssapi_cred_store.envvar!=NULL &&
400            gssapi_cred_store.envval!=NULL) {
401           
402                debug("Setting %s to %s", gssapi_cred_store.envvar,
403                                          gssapi_cred_store.envval);                             
404                child_set_env(envp, envsizep, gssapi_cred_store.envvar,
405                                              gssapi_cred_store.envval);
406        }
407
408        switch(gssapi_client_type) {
409#ifdef KRB5
410        case GSS_KERBEROS: break;
411#endif
412#ifdef GSI
413        case GSS_GSI: break;
414#endif
415        case GSS_LAST_ENTRY:
416                debug("No GSSAPI credentials stored");
417                break;
418        default:
419                log("ssh_gssapi_do_child: Unknown mechanism");
420        }
421}
422
423int
424ssh_gssapi_userok(Authctxt *authctxt)
425{
426        if (gssapi_client_name.length==0 ||
427            gssapi_client_name.value==NULL) {
428                debug("No suitable client data");
429                return 0;
430        }
431        switch (gssapi_client_type) {
432#ifdef KRB5
433        case GSS_KERBEROS:
434                return(ssh_gssapi_krb5_userok(authctxt));
435                break; /* Not reached */
436#endif
437#ifdef GSI
438        case GSS_GSI:
439                return(ssh_gssapi_gsi_userok(authctxt->user));
440                break; /* Not reached */
441#endif /* GSI */
442        case GSS_LAST_ENTRY:
443                debug("Client not GSSAPI");
444                break;
445        default:
446                debug("Unknown client authentication type");
447        }
448        return(0);
449}
450
451/* Stuff to play nicely with privsep */
452
453#if 0
454extern struct monitor *pmonitor;
455
456OM_uint32
457mm_ssh_gssapi_server_ctxt(Gssctxt **ctx, gss_OID oid) {
458        Buffer m;
459       
460        /* Client doesn't get to see the context */
461        *ctx=NULL;
462
463        buffer_init(&m)
464        buffer_put_string(&m,oid->elements,oid->length);
465       
466        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSETUP, &m);
467
468        debug3("%s: waiting for MONITOR_ANS_GSSSIGN",__func__);
469        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_SIGN, &m);
470        major=buffer_get_int(&m);
471
472        return(major);
473}
474       
475int
476mm_answer_gss_server_ctxt(int socket, Buffer *m) {
477        gss_OID_desc oid;
478        OM_uint32 major;
479       
480        oid.elements=buffer_get_string(m,&oid.length);
481               
482        major=ssh_gssapi_server_ctxt(&gsscontext,&oid);
483
484        xfree(oid.elements);
485       
486        buffer_clear(m);
487        buffer_put_int(m,result);
488       
489        mm_request_send(socket,MONITOR_ANS_GSSSIGN,m);
490       
491        return(0);
492}
493
494OM_uint32
495mm_ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *in,
496                         gss_buffer_desc *out, OM_uint32 *flags) {
497
498        Buffer m;
499        OM_uint32, major;
500       
501        buffer_init(&m);
502        buffer_put_string(&m, in->value, in->length);
503        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSTEP, &m);
504       
505        debug3("%s: waiting for MONITOR_ANS_GSSSTEP", &m);
506        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSTEP, &m);
507       
508        major=buffer_get_int(&m);
509        *out->value=buffer->get_string(&m,&out->length);
510        flags=buffer_get_int(&m);
511
512        return(major);
513}
514
515int
516mm_answer_gss_accept_ctxt(int socket, Buffer *m) {
517        gss_buffer_desc in,out;
518        OM_uint32 major;
519        OM_uint32 flags = 0; /* GSI needs this */
520       
521        in.value = buffer_get_string(m,&in.length);
522        major=ssh_gssapi_accept_ctxt(gsscontext,&in,&out,&flags);
523        xfree(in.value);
524       
525        buffer_clear(m);
526        buffer_put_int(m, major);
527        buffer_put_string(m, out.value, out.length);
528        buffer_put_int(m, flags);
529        mm_request_send(socket,MONITOR_ANS_STEP,m);
530       
531        gss_release_buffer(out);
532       
533        return(0);
534}
535
536int
537mm_ssh_gssapi_userok(char *user) {
538        Buffer m;
539       
540        buffer_init(&m);
541        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, &m);
542       
543        debug3("%s: waiting for MONTIOR_ANS_GSSUSEROK", __func__);
544        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUSEROK),
545                                  &m);
546       
547        authenticated = buffer_get_int(&m);
548       
549        buffer_free(&m);
550        debug3("%s: user %sauthetnicated",__func__, authenticated ? "" : "not ");
551        return(authenticated);
552}
553
554int
555mm_answer_gss_userok(int socket, Buffer *m) {
556        authenticated = authctxt->valid && ssh_gssapi_userok(authctxt);
557       
558        buffer_clear(m);
559        buffer_put_int(m, authenticated);
560       
561        debug3("%s: sending result %d", __func__, authenticated);
562        mm_request_send(socket, MONITOR_ANS_GSSUSEROK, m);
563       
564        /* Monitor loop will terminate if authenticated */
565        return(authenticated);
566}
567
568OM_uint32
569mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) {
570        Buffer m;
571        OM_uint32 major, minor;
572       
573        buffer_init(&m);
574        buffer_put_string(&m, data->value, data->length);
575       
576        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m);
577       
578        debug3("%s: waiting for MONITOR_ANS_GSSSIGN",__func__);
579        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m);
580        major=buffer_get_int(&m);
581        *hash->value = buffer_get_string(&m, &hash->length);
582       
583        return(major);
584}
585       
586int
587mm_answer_gss_sign(int socket, Buffer *m) {
588        gss_buffer_desc data,hash;
589        OM_uint32 major;
590       
591        data.value = buffer_get_string(m,&data.length);
592        if (data.length != 20)
593                fatal("%s: data length incorrect: %d", __func__, datlen);
594       
595        /* Save the session ID - only first time round */
596        if (session_id2_len == 0) {
597                session_id2_len=data.length;
598                session_id2 = xmalloc(session_id2_len);
599                memcpy(session_id2, data.value, session_id2_len);
600        }
601        major=ssh_gssapi_sign(gsscontext, &data, &hash);
602       
603        xfree(data.value);
604       
605        buffer_clear(m);
606        buffer_put_int(m, major);
607        buffer_put_string(m, hash.value, hash.length);
608
609        mm_request_send(socket,MONITOR_ANS_GSSSIGN,m);
610               
611        gss_release_buffer(hash);
612       
613        return(0);
614}
615#endif
616#endif /* GSSAPI */
Note: See TracBrowser for help on using the repository browser.