#include "../curl_setup.h"
#if defined(HAVE_GSSAPI) && defined(USE_KERBEROS5)
#include <curl/curl.h>
#include "vauth.h"
#include "../curl_sasl.h"
#include "../urldata.h"
#include "../curl_gssapi.h"
#include "../sendf.h"
#include "../curl_memory.h"
#include "../memdebug.h"
#if defined(__GNUC__) && defined(__APPLE__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
bool Curl_auth_is_gssapi_supported(void)
{
return TRUE;
}
CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const char *service,
const char *host,
const bool mutual_auth,
const struct bufref *chlg,
struct kerberos5data *krb5,
struct bufref *out)
{
CURLcode result = CURLE_OK;
OM_uint32 major_status;
OM_uint32 minor_status;
OM_uint32 unused_status;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
(void)userp;
(void)passwdp;
if(!krb5->spn) {
gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER;
char *spn = Curl_auth_build_spn(service, NULL, host);
if(!spn)
return CURLE_OUT_OF_MEMORY;
spn_token.value = spn;
spn_token.length = strlen(spn);
major_status = gss_import_name(&minor_status, &spn_token,
GSS_C_NT_HOSTBASED_SERVICE, &krb5->spn);
if(GSS_ERROR(major_status)) {
Curl_gss_log_error(data, "gss_import_name() failed: ",
major_status, minor_status);
free(spn);
return CURLE_AUTH_ERROR;
}
free(spn);
}
if(chlg) {
if(!Curl_bufref_len(chlg)) {
infof(data, "GSSAPI handshake failure (empty challenge message)");
return CURLE_BAD_CONTENT_ENCODING;
}
input_token.value = CURL_UNCONST(Curl_bufref_ptr(chlg));
input_token.length = Curl_bufref_len(chlg);
}
major_status = Curl_gss_init_sec_context(data,
&minor_status,
&krb5->context,
krb5->spn,
&Curl_krb5_mech_oid,
GSS_C_NO_CHANNEL_BINDINGS,
&input_token,
&output_token,
mutual_auth,
NULL);
if(GSS_ERROR(major_status)) {
if(output_token.value)
gss_release_buffer(&unused_status, &output_token);
Curl_gss_log_error(data, "gss_init_sec_context() failed: ",
major_status, minor_status);
return CURLE_AUTH_ERROR;
}
if(output_token.value && output_token.length) {
result = Curl_bufref_memdup(out, output_token.value, output_token.length);
gss_release_buffer(&unused_status, &output_token);
}
else
Curl_bufref_set(out, mutual_auth ? "": NULL, 0, NULL);
return result;
}
CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
const char *authzid,
const struct bufref *chlg,
struct kerberos5data *krb5,
struct bufref *out)
{
CURLcode result = CURLE_OK;
size_t messagelen = 0;
unsigned char *message = NULL;
OM_uint32 major_status;
OM_uint32 minor_status;
OM_uint32 unused_status;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
unsigned char *indata;
gss_qop_t qop = GSS_C_QOP_DEFAULT;
unsigned int sec_layer = 0;
unsigned int max_size = 0;
if(!Curl_bufref_len(chlg)) {
infof(data, "GSSAPI handshake failure (empty security message)");
return CURLE_BAD_CONTENT_ENCODING;
}
input_token.value = CURL_UNCONST(Curl_bufref_ptr(chlg));
input_token.length = Curl_bufref_len(chlg);
major_status = gss_unwrap(&minor_status, krb5->context, &input_token,
&output_token, NULL, &qop);
if(GSS_ERROR(major_status)) {
Curl_gss_log_error(data, "gss_unwrap() failed: ",
major_status, minor_status);
return CURLE_BAD_CONTENT_ENCODING;
}
if(output_token.length != 4) {
infof(data, "GSSAPI handshake failure (invalid security data)");
gss_release_buffer(&unused_status, &output_token);
return CURLE_BAD_CONTENT_ENCODING;
}
indata = output_token.value;
sec_layer = indata[0];
max_size = ((unsigned int)indata[1] << 16) |
((unsigned int)indata[2] << 8) | indata[3];
gss_release_buffer(&unused_status, &output_token);
if(!(sec_layer & GSSAUTH_P_NONE)) {
infof(data, "GSSAPI handshake failure (invalid security layer)");
return CURLE_BAD_CONTENT_ENCODING;
}
sec_layer &= GSSAUTH_P_NONE;
if(max_size > 0) {
max_size = 0;
}
messagelen = 4;
if(authzid)
messagelen += strlen(authzid);
message = malloc(messagelen);
if(!message)
return CURLE_OUT_OF_MEMORY;
message[0] = sec_layer & 0xFF;
message[1] = (max_size >> 16) & 0xFF;
message[2] = (max_size >> 8) & 0xFF;
message[3] = max_size & 0xFF;
if(authzid && *authzid)
memcpy(message + 4, authzid, messagelen - 4);
input_token.value = message;
input_token.length = messagelen;
major_status = gss_wrap(&minor_status, krb5->context, 0,
GSS_C_QOP_DEFAULT, &input_token, NULL,
&output_token);
if(GSS_ERROR(major_status)) {
Curl_gss_log_error(data, "gss_wrap() failed: ",
major_status, minor_status);
free(message);
return CURLE_AUTH_ERROR;
}
result = Curl_bufref_memdup(out, output_token.value, output_token.length);
gss_release_buffer(&unused_status, &output_token);
free(message);
return result;
}
void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5)
{
OM_uint32 minor_status;
if(krb5->context != GSS_C_NO_CONTEXT) {
Curl_gss_delete_sec_context(&minor_status, &krb5->context,
GSS_C_NO_BUFFER);
krb5->context = GSS_C_NO_CONTEXT;
}
if(krb5->spn != GSS_C_NO_NAME) {
gss_release_name(&minor_status, &krb5->spn);
krb5->spn = GSS_C_NO_NAME;
}
}
#if defined(__GNUC__) && defined(__APPLE__)
#pragma GCC diagnostic pop
#endif
#endif