/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */
#include "config.h"
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include "gp_conv.h"
#include "src/gp_common.h"
void *gp_memdup(void *in, size_t len)
{
void *out;
out = malloc(len);
if (!out) {
return NULL;
}
memcpy(out, in, len);
return out;
}
int gp_conv_octet_string(size_t length, void *value, octet_string *out)
{
if (length == 0) {
out->octet_string_val = NULL;
out->octet_string_len = 0;
return 0;
}
out->octet_string_val = gp_memdup(value, length);
if (!out->octet_string_val) {
return ENOMEM;
}
out->octet_string_len = length;
return 0;
}
int gp_conv_octet_string_alloc(size_t length, void *value,
octet_string **out)
{
octet_string *o;
int ret;
o = calloc(1, sizeof(octet_string));
if (!o) {
return ENOMEM;
}
ret = gp_conv_octet_string(length, value, o);
if (ret) {
free(o);
return ret;
}
*out = o;
return 0;
}
void gp_conv_gssx_to_oid(gssx_OID *in, gss_OID out)
{
if (in == NULL) {
out->length = 0;
out->elements = NULL;
return;
}
out->length = in->octet_string_len;
out->elements = (void *)in->octet_string_val;
}
int gp_conv_gssx_to_oid_alloc(gssx_OID *in, gss_OID *out)
{
gss_OID o;
if (in == NULL || in->octet_string_len == 0) {
*out = GSS_C_NO_OID;
return 0;
}
o = calloc(1, sizeof(gss_OID_desc));
if (!o) {
return ENOMEM;
}
o->elements = gp_memdup(in->octet_string_val,
in->octet_string_len);
if (!o->elements) {
free(o);
return ENOMEM;
}
o->length = in->octet_string_len;
*out = o;
return 0;
}
int gp_conv_oid_to_gssx(gss_OID in, gssx_OID *out)
{
if (in == GSS_C_NO_OID) {
return gp_conv_octet_string(0, NULL, out);
}
return gp_conv_octet_string(in->length, in->elements, out);
}
int gp_conv_oid_to_gssx_alloc(gss_OID in, gssx_OID **out)
{
if (in == GSS_C_NO_OID) {
*out = NULL;
return 0;
}
return gp_conv_octet_string_alloc(in->length, in->elements, out);
}
void gp_conv_gssx_to_buffer(gssx_buffer *in, gss_buffer_t out)
{
out->length = in->octet_string_len;
out->value = (void *)in->octet_string_val;
}
int gp_conv_gssx_to_buffer_alloc(gssx_buffer *in, gss_buffer_t *out)
{
gss_buffer_desc *o;
if (in->octet_string_len == 0) {
*out = GSS_C_NO_BUFFER;
return 0;
}
o = malloc(sizeof(gss_buffer_desc));
if (!o) {
return ENOMEM;
}
o->value = gp_memdup(in->octet_string_val,
in->octet_string_len);
if (!o->value) {
free(o);
return ENOMEM;
}
o->length = in->octet_string_len;
*out = o;
return 0;
}
int gp_copy_gssx_to_buffer(gssx_buffer *in, gss_buffer_t out)
{
gss_buffer_desc empty = GSS_C_EMPTY_BUFFER;
if (in->octet_string_len == 0) {
*out = empty;
return 0;
}
out->value = gp_memdup(in->octet_string_val,
in->octet_string_len);
if (!out->value) {
return ENOMEM;
}
out->length = in->octet_string_len;
return 0;
}
int gp_copy_gssx_to_string_buffer(gssx_buffer *in, gss_buffer_t out)
{
gss_buffer_desc empty = GSS_C_EMPTY_BUFFER;
char *str;
if (in->octet_string_len == 0) {
*out = empty;
return 0;
}
str = malloc(in->octet_string_len + 1);
if (!str) {
return ENOMEM;
}
memcpy(str, in->octet_string_val, in->octet_string_len);
str[in->octet_string_len] = '\0';
out->length = in->octet_string_len;
out->value = str;
return 0;
}
int gp_conv_buffer_to_gssx(gss_buffer_t in, gssx_buffer *out)
{
return gp_conv_octet_string(in->length, in->value, out);
}
int gp_conv_buffer_to_gssx_alloc(gss_buffer_t in, gssx_buffer **out)
{
return gp_conv_octet_string_alloc(in->length, in->value, out);
}
void gp_conv_gssx_to_cb(gssx_cb *in, gss_channel_bindings_t out)
{
out->initiator_addrtype = in->initiator_addrtype;
gp_conv_gssx_to_buffer(&in->initiator_address, &out->initiator_address);
out->acceptor_addrtype = in->acceptor_addrtype;
gp_conv_gssx_to_buffer(&in->acceptor_address, &out->acceptor_address);
gp_conv_gssx_to_buffer(&in->application_data, &out->application_data);
}
int gp_conv_cb_to_gssx(gss_channel_bindings_t in, gssx_cb *out)
{
int ret;
out->initiator_addrtype = in->initiator_addrtype;
ret = gp_conv_buffer_to_gssx(&in->initiator_address,
&out->initiator_address);
if (ret) {
goto done;
}
out->acceptor_addrtype = in->acceptor_addrtype;
ret = gp_conv_buffer_to_gssx(&in->acceptor_address,
&out->acceptor_address);
if (ret) {
goto done;
}
ret = gp_conv_buffer_to_gssx(&in->application_data,
&out->application_data);
if (ret) {
goto done;
}
ret = 0;
done:
if (ret) {
xdr_free((xdrproc_t)xdr_gssx_buffer, (char *)&out->initiator_address);
xdr_free((xdrproc_t)xdr_gssx_buffer, (char *)&out->acceptor_address);
xdr_free((xdrproc_t)xdr_gssx_buffer, (char *)&out->application_data);
}
return ret;
}
int gp_conv_cb_to_gssx_alloc(gss_channel_bindings_t in, gssx_cb **out)
{
gssx_cb *o;
int ret;
o = malloc(sizeof(gssx_cb));
if (!o) {
return ENOMEM;
}
ret = gp_conv_cb_to_gssx(in, o);
if (ret) {
free(o);
return ENOMEM;
}
*out = o;
return 0;
}
gssx_cred_usage gp_conv_cred_usage_to_gssx(gss_cred_usage_t in)
{
switch (in) {
case GSS_C_BOTH:
return GSSX_C_BOTH;
case GSS_C_INITIATE:
return GSSX_C_INITIATE;
case GSS_C_ACCEPT:
return GSSX_C_ACCEPT;
default:
return 0;
}
}
gss_cred_usage_t gp_conv_gssx_to_cred_usage(gssx_cred_usage in)
{
switch (in) {
case GSSX_C_BOTH:
return GSS_C_BOTH;
case GSSX_C_INITIATE:
return GSS_C_INITIATE;
case GSSX_C_ACCEPT:
return GSS_C_ACCEPT;
default:
return 0;
}
}
int gp_conv_err_to_gssx_string(uint32_t status, int type, gss_OID oid,
utf8string *ret_str)
{
uint32_t ret_maj;
uint32_t ret_min;
uint32_t msg_ctx;
gss_buffer_desc gssbuf;
char *str, *t;
int ret = 0;
msg_ctx = 0;
str = NULL;
do {
ret_maj = gss_display_status(&ret_min,
status, type, oid,
&msg_ctx, &gssbuf);
if (ret_maj == GSS_S_COMPLETE) {
if (str) {
ret = asprintf(&t, "%s, %s", str, (char *)gssbuf.value);
if (ret == -1) {
ret = ENOMEM;
} else {
free(str);
str = t;
}
} else {
str = strdup((char *)gssbuf.value);
if (!str) {
ret = ENOMEM;
}
}
gss_release_buffer(&ret_min, &gssbuf);
} else {
ret = EINVAL;
}
if (ret) {
goto done;
}
} while (msg_ctx);
ret_str->utf8string_len = strlen(str) + 1;
ret_str->utf8string_val = str;
ret = 0;
done:
if (ret) {
free(str);
}
return ret;
}
uint32_t gp_conv_name_to_gssx(uint32_t *min, gss_name_t in, gssx_name *_out)
{
uint32_t ret_maj;
uint32_t ret_min;
gss_buffer_desc name_buffer = GSS_C_EMPTY_BUFFER;
gss_OID name_type;
gss_buffer_desc exported_name = GSS_C_EMPTY_BUFFER;
gss_buffer_desc exported_composite_name = GSS_C_EMPTY_BUFFER;
gssx_name out = { .display_name.octet_string_len = 0 };
int ret;
ret_maj = gss_display_name(&ret_min, in, &name_buffer, &name_type);
if (ret_maj) {
goto done;
}
ret = gp_conv_buffer_to_gssx(&name_buffer, &out.display_name);
if (ret) {
ret_maj = GSS_S_FAILURE;
ret_min = ret;
goto done;
}
ret = gp_conv_oid_to_gssx(name_type, &out.name_type);
if (ret) {
ret_maj = GSS_S_FAILURE;
ret_min = ret;
goto done;
}
ret_maj = gss_export_name(&ret_min, in, &exported_name);
if (ret_maj == 0) {
ret = gp_conv_buffer_to_gssx(&exported_name, &out.exported_name);
if (ret) {
ret_maj = GSS_S_FAILURE;
ret_min = ret;
goto done;
}
} else {
/* In case the error is GSS_S_NAME_NOT_MN the name was not
* canonicalized but that is ok we simply do not export the name
* in this case */
if (ret_maj != GSS_S_NAME_NOT_MN) {
goto done;
}
}
ret_maj = gss_export_name_composite(&ret_min, in, &exported_composite_name);
if (ret_maj == 0) {
ret = gp_conv_buffer_to_gssx(&exported_composite_name, &out.exported_composite_name);
if (ret) {
ret_maj = GSS_S_FAILURE;
ret_min = ret;
goto done;
}
} else {
/* In case the error is GSS_S_NAME_NOT_MN the name was not
* canonicalized but that is ok we simply do not export the name
* in this case */
if (ret_maj != GSS_S_NAME_NOT_MN &&
ret_maj != GSS_S_UNAVAILABLE) {
goto done;
}
}
ret_maj = GSS_S_COMPLETE;
/* out->name_attributes */
done:
*min = ret_min;
gss_release_buffer(&ret_min, &name_buffer);
gss_release_buffer(&ret_min, &exported_name);
gss_release_buffer(&ret_min, &exported_composite_name);
if (ret_maj) {
xdr_free((xdrproc_t)xdr_gssx_buffer, (char *)&out.display_name);
xdr_free((xdrproc_t)xdr_gssx_OID, (char *)&out.name_type);
xdr_free((xdrproc_t)xdr_gssx_buffer, (char *)&out.exported_name);
xdr_free((xdrproc_t)xdr_gssx_buffer, (char *)&out.exported_composite_name);
} else {
*_out = out;
}
return ret_maj;
}
uint32_t gp_conv_name_to_gssx_alloc(uint32_t *min,
gss_name_t in, gssx_name **out)
{
gssx_name *o;
uint32_t ret_maj;
o = calloc(1, sizeof(gssx_name));
if (!o) {
return ENOMEM;
}
ret_maj = gp_conv_name_to_gssx(min, in, o);
if (ret_maj) {
free(o);
} else {
*out = o;
}
return ret_maj;
}
uint32_t gp_conv_gssx_to_name(uint32_t *min, gssx_name *in, gss_name_t *out)
{
gss_buffer_t input_name = GSS_C_NO_BUFFER;
gss_OID name_type = GSS_C_NO_OID;
gss_buffer_desc name_buffer;
uint32_t ret_maj;
uint32_t ret_min;
int ret;
if (in->display_name.octet_string_len != 0) {
/* ok we have a display name.
* In this case always import and canonicalize it so we can
* safely export the name using the original form, even if we
* already have exported_name */
ret = gp_conv_gssx_to_buffer_alloc(&in->display_name, &input_name);
if (ret) {
ret_maj = GSS_S_FAILURE;
ret_min = ret;
goto done;
}
ret = gp_conv_gssx_to_oid_alloc(&in->name_type, &name_type);
if (ret) {
ret_maj = GSS_S_FAILURE;
ret_min = ret;
goto done;
}
ret_maj = gss_import_name(&ret_min, input_name, name_type, out);
if (ret_maj) {
goto done;
}
} else {
gp_conv_gssx_to_buffer(&in->exported_name, &name_buffer);
ret_maj = gss_import_name(&ret_min, &name_buffer,
GSS_C_NT_EXPORT_NAME, out);
if (ret_maj) {
goto done;
}
}
done:
*min = ret_min;
gss_release_buffer(&ret_min, input_name);
free(input_name);
gss_release_oid(&ret_min, &name_type);
return ret_maj;
}
int gp_conv_status_to_gssx(uint32_t ret_maj, uint32_t ret_min,
gss_OID mech, struct gssx_status *status)
{
int ret;
status->major_status = ret_maj;
if (mech) {
ret = gp_conv_oid_to_gssx(mech, &status->mech);
if (ret) {
goto done;
}
}
status->minor_status = ret_min;
if (ret_maj) {
ret = gp_conv_err_to_gssx_string(ret_maj, GSS_C_GSS_CODE, mech,
&status->major_status_string);
if (ret) {
goto done;
}
}
if (ret_min) {
ret = gp_conv_err_to_gssx_string(ret_min, GSS_C_MECH_CODE, mech,
&status->minor_status_string);
if (ret) {
goto done;
}
}
ret = 0;
done:
return ret;
}
int gp_copy_utf8string(utf8string *in, utf8string *out)
{
out->utf8string_val = gp_memdup(in->utf8string_val,
in->utf8string_len);
if (!out->utf8string_val) {
return ENOMEM;
}
out->utf8string_len = in->utf8string_len;
return 0;
}
int gp_copy_gssx_status_alloc(gssx_status *in, gssx_status **out)
{
gssx_status *o;
int ret;
o = calloc(1, sizeof(gssx_status));
if (!o) {
return ENOMEM;
}
o->major_status = in->major_status;
o->minor_status = in->minor_status;
if (in->mech.octet_string_len) {
ret = gp_conv_octet_string(in->mech.octet_string_len,
in->mech.octet_string_val,
&o->mech);
if (ret) {
goto done;
}
}
if (in->major_status_string.utf8string_len) {
ret = gp_copy_utf8string(&in->major_status_string,
&o->major_status_string);
if (ret) {
goto done;
}
}
if (in->minor_status_string.utf8string_len) {
ret = gp_copy_utf8string(&in->minor_status_string,
&o->minor_status_string);
if (ret) {
goto done;
}
}
if (in->server_ctx.octet_string_len) {
ret = gp_conv_octet_string(in->server_ctx.octet_string_len,
in->server_ctx.octet_string_val,
&o->server_ctx);
if (ret) {
goto done;
}
}
*out = o;
ret = 0;
done:
if (ret) {
xdr_free((xdrproc_t)xdr_gssx_status, (char *)o);
free(o);
}
return ret;
}
int gp_conv_gssx_to_oid_set(gssx_OID_set *in, gss_OID_set *out)
{
gss_OID_set o;
if (in->gssx_OID_set_len == 0) {
*out = GSS_C_NO_OID_SET;
return 0;
}
o = malloc(sizeof(gss_OID_set_desc));
if (!o) {
return ENOMEM;
}
o->count = in->gssx_OID_set_len;
o->elements = calloc(o->count, sizeof(gss_OID_desc));
if (!o->elements) {
free(o);
return ENOMEM;
}
for (size_t i = 0; i < o->count; i++) {
o->elements[i].elements =
gp_memdup(in->gssx_OID_set_val[i].octet_string_val,
in->gssx_OID_set_val[i].octet_string_len);
if (!o->elements[i].elements) {
while (i > 0) {
i--;
free(o->elements[i].elements);
}
free(o->elements);
free(o);
return ENOMEM;
}
o->elements[i].length = in->gssx_OID_set_val[i].octet_string_len;
}
*out = o;
return 0;
}
int gp_conv_oid_set_to_gssx(gss_OID_set in, gssx_OID_set *out)
{
int ret;
if (in->count == 0) {
return 0;
}
out->gssx_OID_set_len = in->count;
out->gssx_OID_set_val = calloc(in->count, sizeof(gssx_OID));
if (!out->gssx_OID_set_val) {
return ENOMEM;
}
for (size_t i = 0; i < in->count; i++) {
ret = gp_conv_octet_string(in->elements[i].length,
in->elements[i].elements,
&out->gssx_OID_set_val[i]);
if (ret) {
while (i > 0) {
i--;
free(out->gssx_OID_set_val[i].octet_string_val);
}
free(out->gssx_OID_set_val);
return ENOMEM;
}
}
return 0;
}
int gp_copy_gssx_name(gssx_name *in, gssx_name *out)
{
int ret;
ret = gp_conv_octet_string(in->display_name.octet_string_len,
in->display_name.octet_string_val,
&out->display_name);
if (ret) {
goto done;
}
ret = gp_conv_octet_string(in->name_type.octet_string_len,
in->name_type.octet_string_val,
&out->name_type);
if (ret) {
goto done;
}
ret = gp_conv_octet_string(in->exported_name.octet_string_len,
in->exported_name.octet_string_val,
&out->exported_name);
if (ret) {
goto done;
}
ret = gp_conv_octet_string(in->exported_composite_name.octet_string_len,
in->exported_composite_name.octet_string_val,
&out->exported_composite_name);
if (ret) {
goto done;
}
done:
if (ret) {
xdr_free((xdrproc_t)xdr_gssx_name, (char *)out);
}
return ret;
}
int gp_copy_gssx_name_alloc(gssx_name *in, gssx_name **out)
{
gssx_name *o;
int ret;
o = calloc(1, sizeof(gssx_name));
if (!o) {
return ENOMEM;
}
ret = gp_copy_gssx_name(in, o);
if (ret) {
free(o);
return ret;
}
*out = o;
return 0;
}