aboutsummaryrefslogtreecommitdiff
path: root/lib/krb5.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/krb5.c')
-rw-r--r--lib/krb5.c316
1 files changed, 316 insertions, 0 deletions
diff --git a/lib/krb5.c b/lib/krb5.c
new file mode 100644
index 000000000..6655973d4
--- /dev/null
+++ b/lib/krb5.c
@@ -0,0 +1,316 @@
+/* This source code was modified by Martin Hedenfalk <mhe@stacken.kth.se> for
+ * use in Curl. His latest changes were done 2000-09-18.
+ *
+ * It has since been patched away like a madman by Daniel Stenberg
+ * <daniel@haxx.se> to make it better applied to curl conditions, and to make
+ * it not use globals, pollute name space and more. This source code awaits a
+ * rewrite to work around the paragraph 2 in the BSD licenses as explained
+ * below.
+ *
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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 "setup.h"
+
+#ifndef CURL_DISABLE_FTP
+#ifdef HAVE_GSSAPI
+
+#include <stdlib.h>
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#include <string.h>
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for getpid() */
+#endif
+
+#include "urldata.h"
+#include "base64.h"
+#include "ftp.h"
+#include "sendf.h"
+#include "krb4.h"
+#include "memory.h"
+
+#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
+#include "inet_ntoa_r.h"
+#endif
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+#define LOCAL_ADDR (&conn->local_addr)
+#define REMOTE_ADDR conn->ip_addr->ai_addr
+#define myctladdr LOCAL_ADDR
+#define hisctladdr REMOTE_ADDR
+
+static int
+krb5_check_prot(void *app_data, int level)
+{
+ gss_ctx_id_t *context = app_data;
+ app_data = NULL; /* prevent compiler warning */
+ if(level == prot_confidential)
+ return -1;
+ return 0;
+}
+
+static int
+krb5_decode(void *app_data, void *buf, int len, int level,
+ struct connectdata *conn)
+{
+ gss_ctx_id_t *context = app_data;
+ OM_uint32 maj, min;
+ gss_buffer_desc enc, dec;
+
+ 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 = dec.length;
+ gss_release_buffer(&min, &dec);
+
+ return len;
+}
+
+static int
+krb5_overhead(void *app_data, int level, int len)
+{
+ gss_ctx_id_t *context = app_data;
+ /* no arguments are used, just init them to prevent compiler warnings */
+ app_data = NULL;
+ level = 0;
+ len = 0;
+ return 0;
+}
+
+static int
+krb5_encode(void *app_data, void *from, int length, int level, void **to,
+ struct connectdata *conn)
+{
+ gss_ctx_id_t *context = app_data;
+ gss_buffer_desc dec, enc;
+ OM_uint32 maj, min;
+ int state;
+ int len;
+
+ dec.value = 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 = enc.length;
+ gss_release_buffer(&min, &enc);
+ return len;
+}
+
+static int
+krb5_auth(void *app_data, struct connectdata *conn)
+{
+ int ret;
+ char *p;
+ unsigned char *ptr;
+ size_t len;
+ u_int32_t cs;
+ const char *host = conn->dns_entry->addr->ai_canonname;
+ ssize_t nread;
+ unsigned int l = sizeof(conn->local_addr);
+ struct SessionHandle *data = conn->data;
+ CURLcode result;
+ const char *service = "ftp", *srv_host = "host";
+ gss_buffer_desc gssbuf, _gssresp, *gssresp;
+ OM_uint32 maj, min;
+ gss_name_t gssname;
+ gss_ctx_id_t *context = app_data;
+ struct gss_channel_bindings_struct chan;
+
+ 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) */
+ while(1) {
+ /* 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;
+ }
+
+ gssbuf.value = data->state.buffer;
+ gssbuf.length = snprintf(gssbuf.value, BUFSIZE, "%s@%s", service, host);
+ maj = gss_import_name(&min, &gssbuf, gss_nt_service_name, &gssname);
+ if(maj != GSS_S_COMPLETE) {
+ gss_release_name(&min, &gssname);
+ if(service == srv_host) {
+ Curl_failf(data, "Error importing service name %s", gssbuf.value);
+ return AUTH_ERROR;
+ }
+ service = srv_host;
+ continue;
+ }
+ {
+ gss_OID t;
+ gss_display_name(&min, gssname, &gssbuf, &t);
+ Curl_infof(data, "Trying against %s\n", gssbuf.value);
+ gss_release_buffer(&min, &gssbuf);
+ }
+ gssresp = GSS_C_NO_BUFFER;
+ *context = GSS_C_NO_CONTEXT;
+
+ do {
+ ret = AUTH_OK;
+ maj = gss_init_sec_context(&min,
+ GSS_C_NO_CREDENTIAL,
+ context,
+ gssname,
+ GSS_C_NO_OID,
+ GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
+ 0,
+ &chan,
+ gssresp,
+ NULL,
+ &gssbuf,
+ NULL,
+ NULL);
+
+ if(gssresp) {
+ free(_gssresp.value);
+ gssresp = NULL;
+ }
+
+ if(maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) {
+ Curl_infof(data, "Error creating security context");
+ ret = AUTH_ERROR;
+ break;
+ }
+
+ if(gssbuf.length != 0) {
+ if(Curl_base64_encode(data, (char *)gssbuf.value, gssbuf.length, &p) < 1) {
+ Curl_infof(data, "Out of memory base64-encoding");
+ 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) {
+ _gssresp.length = Curl_base64_decode(p + 5, (unsigned char **)&_gssresp.value);
+ if(_gssresp.length < 1) {
+ Curl_failf(data, "Out of memory base64-encoding");
+ ret = AUTH_CONTINUE;
+ break;
+ }
+ }
+
+ gssresp = &_gssresp;
+ }
+ } while(maj == GSS_S_CONTINUE_NEEDED);
+
+ gss_release_name(&min, &gssname);
+
+ if(gssresp)
+ free(_gssresp.value);
+
+ if(ret == AUTH_OK || service == srv_host)
+ return ret;
+
+ service = srv_host;
+ }
+}
+
+struct Curl_sec_client_mech Curl_krb5_client_mech = {
+ "GSSAPI",
+ sizeof(gss_ctx_id_t),
+ NULL, /* init */
+ krb5_auth,
+ NULL, /* end */
+ krb5_check_prot,
+ krb5_overhead,
+ krb5_encode,
+ krb5_decode
+};
+
+#endif /* HAVE_GSSAPI */
+#endif /* CURL_DISABLE_FTP */