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