/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */
#include "gssapi_gpm.h"
#include <pthread.h>
static pthread_key_t gpm_last_status;
static void gpm_destroy_last_status(void *arg)
{
gssx_status *status = (gssx_status *)arg;
xdr_free((xdrproc_t)xdr_gssx_status, (char *)status);
free(status);
}
void gpm_display_status_init_once(void)
{
(void)pthread_key_create(&gpm_last_status, gpm_destroy_last_status);
}
/* Portable thread local storage for return status. */
void gpm_save_status(gssx_status *status)
{
gssx_status *last_status;
int ret;
last_status = (gssx_status *)pthread_getspecific(gpm_last_status);
if (last_status != NULL) {
/* store NULL first so we do not risk a double free if we are
* racing on a pthread_cancel */
pthread_setspecific(gpm_last_status, NULL);
gpm_destroy_last_status(last_status);
}
ret = gp_copy_gssx_status_alloc(status, &last_status);
if (ret == 0) {
pthread_setspecific(gpm_last_status, last_status);
}
}
gssx_status *gpm_get_saved_status(void)
{
return (gssx_status *)pthread_getspecific(gpm_last_status);
}
/* This funciton is used to record internal mech errors that are
* generated by the proxy client code */
void gpm_save_internal_status(uint32_t err, char *err_str)
{
gssx_status status;
memset(&status, 0, sizeof(gssx_status));
#define STD_MAJ_ERROR_STR "Internal gssproxy error"
status.major_status = GSS_S_FAILURE;
status.major_status_string.utf8string_val = strdup(STD_MAJ_ERROR_STR);
status.major_status_string.utf8string_len = sizeof(STD_MAJ_ERROR_STR);
status.minor_status = err;
status.minor_status_string.utf8string_val = err_str;
status.minor_status_string.utf8string_len = strlen(err_str) + 1;
gpm_save_status(&status);
}
OM_uint32 gpm_display_status(OM_uint32 *minor_status,
OM_uint32 status_value,
int status_type,
const gss_OID mech_type UNUSED,
OM_uint32 *message_context,
gss_buffer_t status_string)
{
gssx_status *last_status = gpm_get_saved_status();
utf8string tmp;
int ret;
switch(status_type) {
case GSS_C_GSS_CODE:
if (last_status &&
last_status->major_status == status_value &&
last_status->major_status_string.utf8string_len) {
ret = gp_copy_utf8string(&last_status->major_status_string,
&tmp);
if (ret) {
*minor_status = ret;
return GSS_S_FAILURE;
}
status_string->value = tmp.utf8string_val;
status_string->length = tmp.utf8string_len;
*minor_status = 0;
return GSS_S_COMPLETE;
} else {
/* if we do not have it, make it clear */
return GSS_S_UNAVAILABLE;
}
case GSS_C_MECH_CODE:
if (last_status &&
last_status->minor_status == status_value &&
last_status->minor_status_string.utf8string_len) {
if (*message_context) {
/* we do not support multiple messages for now */
*minor_status = EINVAL;
return GSS_S_FAILURE;
}
ret = gp_copy_utf8string(&last_status->minor_status_string,
&tmp);
if (ret) {
*minor_status = ret;
return GSS_S_FAILURE;
}
status_string->value = tmp.utf8string_val;
status_string->length = tmp.utf8string_len;
} else {
/* if we do not have it, make it clear */
return GSS_S_UNAVAILABLE;
}
*minor_status = 0;
return GSS_S_COMPLETE;
default:
*minor_status = EINVAL;
return GSS_S_BAD_STATUS;
}
}