curl/lib/krb5.c
Dan Fandrich 35648f2e79 curl_memory: make curl_memory.h the second-last header file loaded
This header file must be included after all header files except
memdebug.h, as it does similar memory function redefinitions and can be
similarly affected by conflicting definitions in system or dependent
library headers.
2015-03-24 23:47:01 +01:00

333 lines
9.4 KiB
C

/* GSSAPI/krb5 support for FTP - loosely based on old krb4.c
*
* Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* Copyright (c) 2004 - 2015 Daniel Stenberg
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. */
#include "curl_setup.h"
#if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_FTP)
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#include "urldata.h"
#include "curl_base64.h"
#include "ftp.h"
#include "curl_gssapi.h"
#include "sendf.h"
#include "curl_sec.h"
#include "warnless.h"
#include "curl_printf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
#define LOCAL_ADDR (&conn->local_addr)
#define REMOTE_ADDR conn->ip_addr->ai_addr
static int
krb5_init(void *app_data)
{
gss_ctx_id_t *context = app_data;
/* Make sure our context is initialized for krb5_end. */
*context = GSS_C_NO_CONTEXT;
return 0;
}
static int
krb5_check_prot(void *app_data, int level)
{
(void)app_data; /* unused */
if(level == PROT_CONFIDENTIAL)
return -1;
return 0;
}
static int
krb5_decode(void *app_data, void *buf, int len,
int level UNUSED_PARAM,
struct connectdata *conn UNUSED_PARAM)
{
gss_ctx_id_t *context = app_data;
OM_uint32 maj, min;
gss_buffer_desc enc, dec;
(void)level;
(void)conn;
enc.value = buf;
enc.length = len;
maj = gss_unseal(&min, *context, &enc, &dec, NULL, NULL);
if(maj != GSS_S_COMPLETE) {
if(len >= 4)
strcpy(buf, "599 ");
return -1;
}
memcpy(buf, dec.value, dec.length);
len = curlx_uztosi(dec.length);
gss_release_buffer(&min, &dec);
return len;
}
static int
krb5_overhead(void *app_data, int level, int len)
{
/* no arguments are used */
(void)app_data;
(void)level;
(void)len;
return 0;
}
static int
krb5_encode(void *app_data, const void *from, int length, int level, void **to)
{
gss_ctx_id_t *context = app_data;
gss_buffer_desc dec, enc;
OM_uint32 maj, min;
int state;
int len;
/* NOTE that the cast is safe, neither of the krb5, gnu gss and heimdal
* libraries modify the input buffer in gss_seal()
*/
dec.value = (void*)from;
dec.length = length;
maj = gss_seal(&min, *context,
level == PROT_PRIVATE,
GSS_C_QOP_DEFAULT,
&dec, &state, &enc);
if(maj != GSS_S_COMPLETE)
return -1;
/* malloc a new buffer, in case gss_release_buffer doesn't work as
expected */
*to = malloc(enc.length);
if(!*to)
return -1;
memcpy(*to, enc.value, enc.length);
len = curlx_uztosi(enc.length);
gss_release_buffer(&min, &enc);
return len;
}
static int
krb5_auth(void *app_data, struct connectdata *conn)
{
int ret = AUTH_OK;
char *p;
const char *host = conn->host.name;
ssize_t nread;
curl_socklen_t l = sizeof(conn->local_addr);
struct SessionHandle *data = conn->data;
CURLcode result;
const char *service = "ftp", *srv_host = "host";
gss_buffer_desc input_buffer, output_buffer, _gssresp, *gssresp;
OM_uint32 maj, min;
gss_name_t gssname;
gss_ctx_id_t *context = app_data;
struct gss_channel_bindings_struct chan;
size_t base64_sz = 0;
if(getsockname(conn->sock[FIRSTSOCKET],
(struct sockaddr *)LOCAL_ADDR, &l) < 0)
perror("getsockname()");
chan.initiator_addrtype = GSS_C_AF_INET;
chan.initiator_address.length = l - 4;
chan.initiator_address.value =
&((struct sockaddr_in *)LOCAL_ADDR)->sin_addr.s_addr;
chan.acceptor_addrtype = GSS_C_AF_INET;
chan.acceptor_address.length = l - 4;
chan.acceptor_address.value =
&((struct sockaddr_in *)REMOTE_ADDR)->sin_addr.s_addr;
chan.application_data.length = 0;
chan.application_data.value = NULL;
/* this loop will execute twice (once for service, once for host) */
for(;;) {
/* this really shouldn't be repeated here, but can't help it */
if(service == srv_host) {
result = Curl_ftpsendf(conn, "AUTH GSSAPI");
if(result)
return -2;
if(Curl_GetFTPResponse(&nread, conn, NULL))
return -1;
if(data->state.buffer[0] != '3')
return -1;
}
input_buffer.value = data->state.buffer;
input_buffer.length = snprintf(input_buffer.value, BUFSIZE, "%s@%s",
service, host);
maj = gss_import_name(&min, &input_buffer, GSS_C_NT_HOSTBASED_SERVICE,
&gssname);
if(maj != GSS_S_COMPLETE) {
gss_release_name(&min, &gssname);
if(service == srv_host) {
Curl_failf(data, "Error importing service name %s",
input_buffer.value);
return AUTH_ERROR;
}
service = srv_host;
continue;
}
/* We pass NULL as |output_name_type| to avoid a leak. */
gss_display_name(&min, gssname, &output_buffer, NULL);
Curl_infof(data, "Trying against %s\n", output_buffer.value);
gssresp = GSS_C_NO_BUFFER;
*context = GSS_C_NO_CONTEXT;
do {
/* Release the buffer at each iteration to avoid leaking: the first time
we are releasing the memory from gss_display_name. The last item is
taken care by a final gss_release_buffer. */
gss_release_buffer(&min, &output_buffer);
ret = AUTH_OK;
maj = Curl_gss_init_sec_context(data,
&min,
context,
gssname,
&Curl_krb5_mech_oid,
&chan,
gssresp,
&output_buffer,
TRUE,
NULL);
if(gssresp) {
free(_gssresp.value);
gssresp = NULL;
}
if(GSS_ERROR(maj)) {
Curl_infof(data, "Error creating security context\n");
ret = AUTH_ERROR;
break;
}
if(output_buffer.length != 0) {
result = Curl_base64_encode(data, (char *)output_buffer.value,
output_buffer.length, &p, &base64_sz);
if(result) {
Curl_infof(data, "base64-encoding: %s\n",
curl_easy_strerror(result));
ret = AUTH_CONTINUE;
break;
}
result = Curl_ftpsendf(conn, "ADAT %s", p);
free(p);
if(result) {
ret = -2;
break;
}
if(Curl_GetFTPResponse(&nread, conn, NULL)) {
ret = -1;
break;
}
if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3') {
Curl_infof(data, "Server didn't accept auth data\n");
ret = AUTH_ERROR;
break;
}
p = data->state.buffer + 4;
p = strstr(p, "ADAT=");
if(p) {
result = Curl_base64_decode(p + 5,
(unsigned char **)&_gssresp.value,
&_gssresp.length);
if(result) {
Curl_failf(data, "base64-decoding: %s",
curl_easy_strerror(result));
ret = AUTH_CONTINUE;
break;
}
}
gssresp = &_gssresp;
}
} while(maj == GSS_S_CONTINUE_NEEDED);
gss_release_name(&min, &gssname);
gss_release_buffer(&min, &output_buffer);
if(gssresp)
free(_gssresp.value);
if(ret == AUTH_OK || service == srv_host)
return ret;
service = srv_host;
}
return ret;
}
static void krb5_end(void *app_data)
{
OM_uint32 min;
gss_ctx_id_t *context = app_data;
if(*context != GSS_C_NO_CONTEXT) {
#ifdef DEBUGBUILD
OM_uint32 maj =
#endif
gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER);
DEBUGASSERT(maj == GSS_S_COMPLETE);
}
}
struct Curl_sec_client_mech Curl_krb5_client_mech = {
"GSSAPI",
sizeof(gss_ctx_id_t),
krb5_init,
krb5_auth,
krb5_end,
krb5_check_prot,
krb5_overhead,
krb5_encode,
krb5_decode
};
#endif /* HAVE_GSSAPI && !CURL_DISABLE_FTP */