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