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