2 * Copyright (c) 2001,2002 Simon Wilkinson. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
37 #include <openssl/evp.h>
48 #include "monitor_wrap.h"
52 extern ServerOptions options;
53 extern u_char *session_id2;
54 extern int session_id2_len;
56 typedef struct ssh_gssapi_cred_cache {
61 } ssh_gssapi_cred_cache;
63 static struct ssh_gssapi_cred_cache gssapi_cred_store = {NULL,NULL,NULL};
66 * Environment variables pointing to delegated credentials
68 static char *delegation_env[] = {
69 "X509_USER_PROXY", /* GSSAPI/SSLeay */
70 "KRB5CCNAME", /* Krb5 and possibly SSLeay */
74 static void gssapi_unsetenv(const char *var);
81 #include <gssapi_krb5.h>
82 #define krb5_get_err_text(context,code) error_message(code)
85 static krb5_context krb_context = NULL;
87 /* Initialise the krb5 library, so we can use it for those bits that
90 int ssh_gssapi_krb5_init() {
91 krb5_error_code problem;
93 if (krb_context !=NULL)
96 problem = krb5_init_context(&krb_context);
98 log("Cannot initialize krb5 context");
101 krb5_init_ets(krb_context);
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
112 ssh_gssapi_krb5_userok(char *name) {
113 krb5_principal princ;
116 if (ssh_gssapi_krb5_init() == 0)
119 if ((retval=krb5_parse_name(krb_context, gssapi_client_name.value,
121 log("krb5_parse_name(): %.100s",
122 krb5_get_err_text(krb_context,retval));
125 if (krb5_kuserok(krb_context, princ, name)) {
127 log("Authorized to %s, krb5 principal %s (krb5_kuserok)",name,
128 (char *)gssapi_client_name.value);
133 krb5_free_principal(krb_context, princ);
138 ssh_gssapi_krb5_localname(char **user)
140 krb5_principal princ;
142 if (ssh_gssapi_krb5_init() == 0)
145 if (krb5_parse_name(krb_context, gssapi_client_name.value, &princ)) {
148 *user = (char *)xmalloc(256);
149 if (krb5_aname_to_localname(krb_context, princ, 256, *user)) {
157 /* Make sure that this is called _after_ we've setuid to the user */
159 /* This writes out any forwarded credentials. Its specific to the Kerberos
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
168 ssh_gssapi_krb5_storecreds(gss_buffer_t export_buffer) {
170 krb5_error_code problem;
171 krb5_principal princ;
173 static char name[40];
175 OM_uint32 maj_status,min_status;
176 gss_cred_id_t krb5_cred_handle;
179 if (gssapi_client_creds==NULL) {
180 debug("No credentials stored");
181 return GSS_S_NO_CRED;
184 if (ssh_gssapi_krb5_init() == 0)
185 return GSS_S_FAILURE;
187 if (options.gss_use_session_ccache) {
188 snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d_XXXXXX",geteuid());
190 if ((tmpfd = mkstemp(ccname))==-1) {
191 log("mkstemp(): %.100s", strerror(errno));
192 return GSS_S_FAILURE;
194 if (fchmod(tmpfd, S_IRUSR | S_IWUSR) == -1) {
195 log("fchmod(): %.100s", strerror(errno));
197 return GSS_S_FAILURE;
200 snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d",geteuid());
201 tmpfd = open(ccname, O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
203 log("open(): %.100s", strerror(errno));
204 return GSS_S_FAILURE;
209 snprintf(name, sizeof(name), "FILE:%s",ccname);
211 if ((problem = krb5_cc_resolve(krb_context, name, &ccache))) {
212 log("krb5_cc_default(): %.100s",
213 krb5_get_err_text(krb_context,problem));
214 return GSS_S_FAILURE;
217 if ((problem = krb5_parse_name(krb_context, gssapi_client_name.value,
219 log("krb5_parse_name(): %.100s",
220 krb5_get_err_text(krb_context,problem));
221 krb5_cc_destroy(krb_context,ccache);
222 return GSS_S_FAILURE;
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);
230 return GSS_S_FAILURE;
233 krb5_free_principal(krb_context,princ);
237 __gss_get_mechanism_cred(gssapi_client_creds,
238 &(supported_mechs[GSS_KERBEROS].oid));
240 krb5_cred_handle = gssapi_client_creds;
243 if ((maj_status = gss_krb5_copy_ccache(&min_status,
246 log("gss_krb5_copy_ccache() failed");
247 ssh_gssapi_error(&supported_mechs[GSS_KERBEROS].oid,
248 maj_status,min_status);
249 krb5_cc_destroy(krb_context,ccache);
250 return GSS_S_FAILURE;
253 krb5_cc_close(krb_context,ccache);
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);
259 return GSS_S_COMPLETE;
265 #include <globus_gss_assist.h>
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'.
272 * Returns non-zero if user is authorized, 0 otherwise.
275 ssh_gssapi_gsi_userok(char *name)
279 #ifdef GLOBUS_GSI_GSS_ASSIST_MODULE
280 if (globus_module_activate(GLOBUS_GSI_GSS_ASSIST_MODULE) != 0) {
285 /* globus_gss_assist_userok() returns 0 on success */
286 authorized = (globus_gss_assist_userok(gssapi_client_name.value,
289 log("GSI user %s is%s authorized as target user %s",
290 (char *) gssapi_client_name.value, (authorized ? "" : " not"), name);
296 * Return the local username associated with the GSI credentials.
299 ssh_gssapi_gsi_localname(char **user)
301 #ifdef GLOBUS_GSI_GSS_ASSIST_MODULE
302 if (globus_module_activate(GLOBUS_GSI_GSS_ASSIST_MODULE) != 0) {
306 return(globus_gss_assist_gridmap(gssapi_client_name.value, user) == 0);
310 * Handle setting up child environment for GSI.
312 * Make sure that this is called _after_ we've setuid to the user.
315 ssh_gssapi_gsi_storecreds(gss_buffer_t export_buffer)
317 OM_uint32 major_status;
318 OM_uint32 minor_status;
320 if (gssapi_client_creds != NULL)
322 char *creds_env = NULL;
325 * This is the current hack with the GSI gssapi library to
326 * export credentials to disk.
329 debug("Exporting delegated credentials");
331 minor_status = 0xdee0; /* Magic value */
333 gss_inquire_cred(&minor_status,
335 (gss_name_t *) &creds_env,
340 if ((major_status == GSS_S_COMPLETE) &&
341 (minor_status == 0xdee1) &&
347 * String is of the form:
348 * X509_USER_DELEG_PROXY=filename
349 * so we parse out the filename
350 * and then set X509_USER_PROXY
353 value = strchr(creds_env, '=');
359 export_buffer->length=
360 strlen("X509_USER_PROXY")+strlen(value)+1;
361 export_buffer->value =
362 xmalloc(export_buffer->length+1);
363 sprintf(export_buffer->value, "%s=%s",
364 "X509_USER_PROXY", value);
366 return GSS_S_COMPLETE;
370 log("Failed to parse delegated credentials string '%s'",
376 log("Failed to export delegated credentials (error %d)",
386 ssh_gssapi_cleanup_creds(void *ignored)
388 /* OM_uint32 min_stat; */
390 if (gssapi_cred_store.filename!=NULL) {
391 /* Unlink probably isn't sufficient */
392 debug("removing gssapi cred file \"%s\"",gssapi_cred_store.filename);
393 unlink(gssapi_cred_store.filename);
396 if (gssapi_client_creds != GSS_C_NO_CREDENTIAL)
397 gss_release_cred(&min_stat, &gssapi_client_creds);
402 ssh_gssapi_export_cred(OM_uint32 * minor_status,
403 const gss_cred_id_t cred_handle,
404 const gss_OID desired_mech,
405 OM_uint32 option_req,
406 gss_buffer_t export_buffer)
408 OM_uint32 maj_stat = GSS_S_FAILURE;
410 if (option_req != 1) return GSS_S_UNAVAILABLE;
411 if (desired_mech != NULL) return GSS_S_BAD_MECH;
413 switch (gssapi_client_type) {
416 maj_stat = ssh_gssapi_krb5_storecreds(export_buffer);
421 maj_stat = ssh_gssapi_gsi_storecreds(export_buffer);
425 /* GSSAPI not used in this authentication */
426 debug("No GSSAPI credentials stored");
429 log("ssh_gssapi_do_child: Unknown mechanism");
433 if (GSS_ERROR(maj_stat)) {
434 *minor_status = GSS_S_FAILURE;
440 ssh_gssapi_storecreds()
442 OM_uint32 maj_stat, min_stat;
443 gss_buffer_desc export_cred = GSS_C_EMPTY_BUFFER;
446 if (gssapi_client_creds == GSS_C_NO_CREDENTIAL)
449 #ifdef HAVE_GSSAPI_EXT
450 maj_stat = gss_export_cred(&min_stat, gssapi_client_creds,
451 GSS_C_NO_OID, 1, &export_cred);
452 if (GSS_ERROR(maj_stat) && maj_stat != GSS_S_UNAVAILABLE) {
453 ssh_gssapi_error(GSS_C_NO_OID, maj_stat, min_stat);
458 /* If gss_export_cred() is not available, use old methods */
459 if (export_cred.length == 0) {
460 ssh_gssapi_export_cred(&min_stat, gssapi_client_creds,
461 GSS_C_NO_OID, 1, &export_cred);
462 if (GSS_ERROR(maj_stat)) {
463 ssh_gssapi_error(GSS_C_NO_OID, maj_stat, min_stat);
467 p = strchr((char *) export_cred.value, '=');
469 log("Failed to parse exported credentials string '%.100s'",
470 (char *)export_cred.value);
471 gss_release_buffer(&min_stat, &export_cred);
476 if (strcmp((char *)export_cred.value,"X509_USER_DELEG_PROXY") == 0)
477 gssapi_cred_store.envvar = strdup("X509_USER_PROXY");
480 gssapi_cred_store.envvar = strdup((char *)export_cred.value);
481 gssapi_cred_store.envval = strdup(p);
483 do_pam_putenv(gssapi_cred_store.envvar, gssapi_cred_store.envval);
485 if (strncmp(p, "FILE:", 5) == 0) {
488 if (access(p, R_OK) == 0) {
489 gssapi_cred_store.filename = strdup(p);
491 gss_release_buffer(&min_stat, &export_cred);
493 if (options.gss_cleanup_creds) {
494 fatal_add_cleanup(ssh_gssapi_cleanup_creds, NULL);
498 /* This allows GSSAPI methods to do things to the childs environment based
499 * on the passed authentication process and credentials.
501 * Question: If we didn't use userauth_external for some reason, should we
502 * still delegate credentials?
505 ssh_gssapi_do_child(char ***envp, u_int *envsizep)
508 if (gssapi_cred_store.envvar!=NULL &&
509 gssapi_cred_store.envval!=NULL) {
511 debug("Setting %s to %s", gssapi_cred_store.envvar,
512 gssapi_cred_store.envval);
513 child_set_env(envp, envsizep, gssapi_cred_store.envvar,
514 gssapi_cred_store.envval);
517 switch(gssapi_client_type) {
519 case GSS_KERBEROS: break;
525 debug("No GSSAPI credentials stored");
528 log("ssh_gssapi_do_child: Unknown mechanism");
533 ssh_gssapi_userok(char *user)
535 if (gssapi_client_name.length==0 ||
536 gssapi_client_name.value==NULL) {
537 debug("No suitable client data");
540 switch (gssapi_client_type) {
543 return(ssh_gssapi_krb5_userok(user));
544 break; /* Not reached */
548 return(ssh_gssapi_gsi_userok(user));
549 break; /* Not reached */
552 debug("Client not GSSAPI");
555 debug("Unknown client authentication type");
561 ssh_gssapi_localname(char **user)
564 if (gssapi_client_name.length==0 ||
565 gssapi_client_name.value==NULL) {
566 debug("No suitable client data");
569 switch (gssapi_client_type) {
572 return(ssh_gssapi_krb5_localname(user));
573 break; /* Not reached */
577 return(ssh_gssapi_gsi_localname(user));
578 break; /* Not reached */
581 debug("Client not GSSAPI");
584 debug("Unknown client authentication type");
590 * Clean our environment on startup. This means removing any environment
591 * strings that might inadvertantly been in root's environment and
592 * could cause serious security problems if we think we set them.
595 ssh_gssapi_clean_env(void)
601 for (envstr_index = 0;
602 (envstr = delegation_env[envstr_index]) != NULL;
605 if (getenv(envstr)) {
606 debug("Clearing environment variable %s", envstr);
607 gssapi_unsetenv(envstr);
613 * Wrapper around unsetenv.
616 gssapi_unsetenv(const char *var)
621 #else /* !HAVE_UNSETENV */
622 extern char **environ;
623 char **p1 = environ; /* New array list */
624 char **p2 = environ; /* Current array list */
625 int len = strlen(var);
628 * Walk through current environ array (p2) copying each pointer
629 * to new environ array (p1) unless the pointer is to the item
630 * we want to delete. Copy happens in place.
633 if ((strncmp(*p2, var, len) == 0) &&
634 ((*p2)[len] == '=')) {
636 * *p2 points at item to be deleted, just skip over it
641 * *p2 points at item we want to save, so copy it
649 /* And make sure new array is NULL terminated */
651 #endif /* HAVE_UNSETENV */