aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES4
-rw-r--r--RELEASE-NOTES4
-rw-r--r--docs/FEATURES2
-rw-r--r--docs/MANUAL15
-rw-r--r--docs/curl.111
-rw-r--r--docs/libcurl/curl_easy_setopt.312
-rw-r--r--include/curl/curl.h10
-rw-r--r--lib/Makefile.inc1
-rw-r--r--lib/ftp.c97
-rw-r--r--lib/hostip.c2
-rw-r--r--lib/hostip4.c3
-rw-r--r--lib/hostip6.c7
-rw-r--r--lib/krb4.h5
-rw-r--r--lib/krb5.c316
-rw-r--r--lib/security.c61
-rw-r--r--lib/sendf.c4
-rw-r--r--lib/url.c11
-rw-r--r--lib/urldata.h15
-rw-r--r--src/main.c21
19 files changed, 509 insertions, 92 deletions
diff --git a/CHANGES b/CHANGES
index 88771532d..218d28df5 100644
--- a/CHANGES
+++ b/CHANGES
@@ -7,6 +7,10 @@
Changelog
Daniel S (1 July 2007)
+- Thomas J. Moore provided a patch that introduces Kerberos5 support in
+ libcurl. This also makes the options change name to --krb (from --krb4) and
+ CURLOPT_KRBLEVEL (from CURLOPT_KRB4LEVEL) but the old names are still
+
- Song Ma helped me verify and extend a fix for doing FTP over a SOCKS4/5
proxy.
diff --git a/RELEASE-NOTES b/RELEASE-NOTES
index 134144e90..3fd9ea5a3 100644
--- a/RELEASE-NOTES
+++ b/RELEASE-NOTES
@@ -13,6 +13,7 @@ This release includes the following changes:
o added CURLOPT_NEW_FILE_PERMS and CURLOPT_NEW_DIRECTORY_PERMS
o improved hashing of sockets for the multi_socket API
+ o ftp kerberos5 support added
This release includes the following bugfixes:
@@ -36,6 +37,7 @@ New curl mirrors:
This release would not have looked like this without help, code, reports and
advice from friends like these:
- Robert Iakobashvili, James Housley, Günter Knauf, James Bursa, Song Ma
+ Robert Iakobashvili, James Housley, Günter Knauf, James Bursa, Song Ma,
+ Thomas J. Moore
Thanks! (and sorry if I forgot to mention someone)
diff --git a/docs/FEATURES b/docs/FEATURES
index 75aefce60..08f342cd8 100644
--- a/docs/FEATURES
+++ b/docs/FEATURES
@@ -73,7 +73,7 @@ HTTPS (*1)
FTP
- download
- authentication
- - kerberos4 (*5)
+ - kerberos4 (*5), kerberos5 (*3)
- active/passive using PORT, EPRT, PASV or EPSV
- single file size information (compare to HTTP HEAD)
- 'type=' URL support
diff --git a/docs/MANUAL b/docs/MANUAL
index 1ea13f142..dba224d91 100644
--- a/docs/MANUAL
+++ b/docs/MANUAL
@@ -809,18 +809,19 @@ CUSTOM OUTPUT
curl -w 'We downloaded %{size_download} bytes\n' www.download.com
-KERBEROS4 FTP TRANSFER
+KERBEROS FTP TRANSFER
- Curl supports kerberos4 for FTP transfers. You need the kerberos package
- installed and used at curl build time for it to be used.
+ Curl supports kerberos4 and kerberos5/GSSAPI for FTP transfers. You need
+ the kerberos package installed and used at curl build time for it to be
+ used.
- First, get the krb-ticket the normal way, like with the kauth tool. Then use
- curl in way similar to:
+ First, get the krb-ticket the normal way, like with the kinit/kauth tool.
+ Then use curl in way similar to:
- curl --krb4 private ftp://krb4site.com -u username:fakepwd
+ curl --krb private ftp://krb4site.com -u username:fakepwd
There's no use for a password on the -u switch, but a blank one will make
- curl ask for one and you already entered the real password to kauth.
+ curl ask for one and you already entered the real password to kinit/kauth.
TELNET
diff --git a/docs/curl.1 b/docs/curl.1
index 629f99dc7..59753fa48 100644
--- a/docs/curl.1
+++ b/docs/curl.1
@@ -593,13 +593,14 @@ private key is. DER, PEM and ENG are supported. If not specified, PEM is
assumed.
If this option is used several times, the last one will be used.
-.IP "--krb4 <level>"
-(FTP) Enable Kerberos4 authentication and use. The level must be entered and
+.IP "--krb <level>"
+(FTP) Enable Kerberos authentication and use. The level must be entered and
should be one of 'clear', 'safe', 'confidential' or 'private'. Should you use
a level that is not one of these, 'private' will instead be used.
-This option requires that the library was built with Kerberos4 support. This
-is not very common. Use \fI-V/--version\fP to see if your curl supports it.
+This option requires that the library was built with kerberos4 or GSSAPI
+(GSS-Negotiate) support. This is not very common. Use \fI-V/--version\fP to
+see if your curl supports it.
If this option is used several times, the last one will be used.
.IP "-K/--config <config file>"
@@ -1153,7 +1154,7 @@ Automatic decompression of compressed files over HTTP is supported.
.IP "NTLM"
NTLM authentication is supported.
.IP "GSS-Negotiate"
-Negotiate authentication is supported.
+Negotiate authentication and krb5 for ftp is supported.
.IP "Debug"
This curl uses a libcurl built with Debug. This enables more error-tracking
and memory debugging etc. For curl-developers only!
diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3
index 01e4dc3c0..cbbf411a0 100644
--- a/docs/libcurl/curl_easy_setopt.3
+++ b/docs/libcurl/curl_easy_setopt.3
@@ -1370,12 +1370,12 @@ this to 1 to enable it. By default all transfers are done using the
cache. Note that while nothing ever should get hurt by attempting to reuse SSL
session-IDs, there seem to be broken SSL implementations in the wild that may
require you to disable this in order for you to succeed. (Added in 7.16.0)
-.IP CURLOPT_KRB4LEVEL
-Pass a char * as parameter. Set the krb4 security level, this also enables
-krb4 awareness. This is a string, 'clear', 'safe', 'confidential' or
-\&'private'. If the string is set but doesn't match one of these, 'private'
-will be used. Set the string to NULL to disable Kerberos4. The Kerberos
-support only works for FTP.
+.IP CURLOPT_KRBLEVEL
+Pass a char * as parameter. Set the kerberos security level for FTP; this
+also enables kerberos awareness. This is a string, 'clear', 'safe',
+'confidential' or \&'private'. If the string is set but doesn't match one
+of these, 'private' will be used. Set the string to NULL to disable kerberos
+support for FTP.
.SH SSH OPTIONS
.IP CURLOPT_SSH_AUTH_TYPES
Pass a long set to a bitmask consisting of one or more of
diff --git a/include/curl/curl.h b/include/curl/curl.h
index ca229cb96..fe413d0a7 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -736,10 +736,12 @@ typedef enum {
/* Set the interface string to use as outgoing network interface */
CINIT(INTERFACE, OBJECTPOINT, 62),
- /* Set the krb4 security level, this also enables krb4 awareness. This is a
- * string, 'clear', 'safe', 'confidential' or 'private'. If the string is
- * set but doesn't match one of these, 'private' will be used. */
- CINIT(KRB4LEVEL, OBJECTPOINT, 63),
+ /* Set the krb4/5 security level, this also enables krb4/5 awareness. This
+ * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string
+ * is set but doesn't match one of these, 'private' will be used. */
+ CINIT(KRBLEVEL, OBJECTPOINT, 63),
+ /* This is for compatibility with older curl releases */
+#define CURLOPT_KRB4LEVEL CURLOPT_KRBLEVEL
/* Set if we should verify the peer in ssl handshake, set 1 to verify. */
CINIT(SSL_VERIFYPEER, LONG, 64),
diff --git a/lib/Makefile.inc b/lib/Makefile.inc
index 3985cefbf..714b15f8a 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -4,6 +4,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
cookie.c http.c sendf.c ftp.c url.c dict.c if2ip.c speedcheck.c \
ldap.c ssluse.c version.c getenv.c escape.c mprintf.c telnet.c \
netrc.c getinfo.c transfer.c strequal.c easy.c security.c krb4.c \
+ krb5.c \
memdebug.c http_chunks.c strtok.c connect.c llist.c hash.c multi.c \
content_encoding.c share.c http_digest.c md5.c http_negotiate.c \
http_ntlm.c inet_pton.c strtoofft.c strerror.c hostares.c hostasyn.c \
diff --git a/lib/ftp.c b/lib/ftp.c
index 5e04b7e81..91f6d91f7 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -75,7 +75,7 @@
#include "socks.h"
#include "ftp.h"
-#ifdef HAVE_KRB4
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
#include "krb4.h"
#endif
@@ -319,8 +319,17 @@ static CURLcode ftp_readresp(curl_socket_t sockfd,
ftpc->cache_size = 0; /* zero the size just in case */
}
else {
- int res = Curl_read(conn, sockfd, ptr, BUFSIZE-ftpc->nread_resp,
- &gotbytes);
+ int res;
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+ enum protection_level prot = conn->data_prot;
+
+ conn->data_prot = 0;
+#endif
+ res = Curl_read(conn, sockfd, ptr, BUFSIZE-ftpc->nread_resp,
+ &gotbytes);
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+ conn->data_prot = prot;
+#endif
if(res < 0)
/* EWOULDBLOCK */
return CURLE_OK; /* return */
@@ -360,6 +369,9 @@ static CURLcode ftp_readresp(curl_socket_t sockfd,
the line isn't really terminated until the LF comes */
/* output debug output if that is requested */
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+ if(!conn->sec_complete)
+#endif
if(data->set.verbose)
Curl_debug(data, CURLINFO_HEADER_IN,
ftpc->linestart_resp, (size_t)perline, conn);
@@ -414,18 +426,18 @@ static CURLcode ftp_readresp(curl_socket_t sockfd,
if(!result)
code = atoi(buf);
-#ifdef HAVE_KRB4
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
/* handle the security-oriented responses 6xx ***/
/* FIXME: some errorchecking perhaps... ***/
switch(code) {
case 631:
- Curl_sec_read_msg(conn, buf, prot_safe);
+ code = Curl_sec_read_msg(conn, buf, prot_safe);
break;
case 632:
- Curl_sec_read_msg(conn, buf, prot_private);
+ code = Curl_sec_read_msg(conn, buf, prot_private);
break;
case 633:
- Curl_sec_read_msg(conn, buf, prot_confidential);
+ code = Curl_sec_read_msg(conn, buf, prot_confidential);
break;
default:
/* normal ftp stuff we pass through! */
@@ -553,7 +565,17 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
ftpc->cache_size = 0; /* zero the size just in case */
}
else {
- int res = Curl_read(conn, sockfd, ptr, BUFSIZE-*nreadp, &gotbytes);
+ int res;
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+ enum protection_level prot = conn->data_prot;
+
+ conn->data_prot = 0;
+#endif
+ res = Curl_read(conn, sockfd, ptr, BUFSIZE-*nreadp,
+ &gotbytes);
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+ conn->data_prot = prot;
+#endif
if(res < 0)
/* EWOULDBLOCK */
continue; /* go looping again */
@@ -593,6 +615,9 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
the line isn't really terminated until the LF comes */
/* output debug output if that is requested */
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+ if(!conn->sec_complete)
+#endif
if(data->set.verbose)
Curl_debug(data, CURLINFO_HEADER_IN,
line_start, (size_t)perline, conn);
@@ -646,18 +671,18 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
if(!result)
code = atoi(buf);
-#ifdef HAVE_KRB4
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
/* handle the security-oriented responses 6xx ***/
/* FIXME: some errorchecking perhaps... ***/
switch(code) {
case 631:
- Curl_sec_read_msg(conn, buf, prot_safe);
+ code = Curl_sec_read_msg(conn, buf, prot_safe);
break;
case 632:
- Curl_sec_read_msg(conn, buf, prot_private);
+ code = Curl_sec_read_msg(conn, buf, prot_private);
break;
case 633:
- Curl_sec_read_msg(conn, buf, prot_confidential);
+ code = Curl_sec_read_msg(conn, buf, prot_confidential);
break;
default:
/* normal ftp stuff we pass through! */
@@ -2299,14 +2324,7 @@ static CURLcode ftp_state_loggedin(struct connectdata *conn)
CURLcode result = CURLE_OK;
#ifdef HAVE_KRB4
- if(conn->data->set.krb4) {
- /* We are logged in, asked to use Kerberos. Set the requested
- * protection level
- */
- if(conn->sec_complete)
- /* BLOCKING */
- Curl_sec_set_protection_level(conn);
-
+ if(conn->data->set.krb) {
/* We may need to issue a KAUTH here to have access to the files
* do it if user supplied a password
*/
@@ -2353,7 +2371,8 @@ static CURLcode ftp_state_user_resp(struct connectdata *conn,
struct ftp_conn *ftpc = &conn->proto.ftpc;
(void)instate; /* no use for this yet */
- if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
+ /* some need password anyway, and others just return 2xx ignored */
+ if((ftpcode == 331 || ftpcode/100 == 2) && (ftpc->state == FTP_USER)) {
/* 331 Password required for ...
(the server requires to send the user's password too) */
NBFTPSENDF(conn, "PASS %s", ftp->passwd?ftp->passwd:"");
@@ -2461,15 +2480,15 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
}
/* We have received a 220 response fine, now we proceed. */
-#ifdef HAVE_KRB4
- if(data->set.krb4) {
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+ if(data->set.krb) {
/* If not anonymous login, try a secure login. Note that this
procedure is still BLOCKING. */
Curl_sec_request_prot(conn, "private");
/* We set private first as default, in case the line below fails to
set a valid level */
- Curl_sec_request_prot(conn, data->set.krb4_level);
+ Curl_sec_request_prot(conn, data->set.krb_level);
if(Curl_sec_login(conn) != 0)
infof(data, "Logging in with password in cleartext!\n");
@@ -3086,7 +3105,7 @@ CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status, bool premature
/* free the dir tree and file parts */
freedirs(conn);
-#ifdef HAVE_KRB4
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]);
#endif
@@ -3496,16 +3515,21 @@ CURLcode Curl_nbftpsendf(struct connectdata *conn,
const char *fmt, ...)
{
ssize_t bytes_written;
- char s[256];
+/* may still not be big enough for some krb5 tokens */
+#define SBUF_SIZE 1024
+ char s[SBUF_SIZE];
size_t write_len;
char *sptr=s;
CURLcode res = CURLE_OK;
struct SessionHandle *data = conn->data;
struct ftp_conn *ftpc = &conn->proto.ftpc;
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+ enum protection_level data_sec = conn->data_prot;
+#endif
va_list ap;
va_start(ap, fmt);
- vsnprintf(s, 250, fmt, ap);
+ vsnprintf(s, SBUF_SIZE-3, fmt, ap);
va_end(ap);
strcat(s, "\r\n"); /* append a trailing CRLF */
@@ -3523,8 +3547,14 @@ CURLcode Curl_nbftpsendf(struct connectdata *conn,
}
#endif /* CURL_DOES_CONVERSIONS */
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+ conn->data_prot = prot_cmd;
+#endif
res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
&bytes_written);
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+ conn->data_prot = data_sec;
+#endif
if(CURLE_OK != res)
return res;
@@ -3557,14 +3587,17 @@ CURLcode Curl_ftpsendf(struct connectdata *conn,
const char *fmt, ...)
{
ssize_t bytes_written;
- char s[256];
+ char s[SBUF_SIZE];
size_t write_len;
char *sptr=s;
CURLcode res = CURLE_OK;
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+ enum protection_level data_sec = conn->data_prot;
+#endif
va_list ap;
va_start(ap, fmt);
- vsnprintf(s, 250, fmt, ap);
+ vsnprintf(s, SBUF_SIZE-3, fmt, ap);
va_end(ap);
strcat(s, "\r\n"); /* append a trailing CRLF */
@@ -3581,8 +3614,14 @@ CURLcode Curl_ftpsendf(struct connectdata *conn,
#endif /* CURL_DOES_CONVERSIONS */
while(1) {
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+ conn->data_prot = prot_cmd;
+#endif
res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
&bytes_written);
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+ conn->data_prot = data_sec;
+#endif
if(CURLE_OK != res)
break;
diff --git a/lib/hostip.c b/lib/hostip.c
index 5a7da5016..189d0683d 100644
--- a/lib/hostip.c
+++ b/lib/hostip.c
@@ -575,6 +575,8 @@ void Curl_freeaddrinfo(Curl_addrinfo *ai)
/* walk over the list and free all entries */
while(ai) {
next = ai->ai_next;
+ if(ai->ai_canonname)
+ free(ai->ai_canonname);
free(ai);
ai = next;
}
diff --git a/lib/hostip4.c b/lib/hostip4.c
index 43b7c6961..9ff883892 100644
--- a/lib/hostip4.c
+++ b/lib/hostip4.c
@@ -380,6 +380,9 @@ Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port)
and use that area to store the address */
ai->ai_addr = (struct sockaddr *) ((char*)ai + sizeof(Curl_addrinfo));
+ /* FIXME: need to free this eventually */
+ ai->ai_canonname = strdup(he->h_name);
+
/* leave the rest of the struct filled with zero */
addr = (struct sockaddr_in *)ai->ai_addr; /* storage area for this info */
diff --git a/lib/hostip6.c b/lib/hostip6.c
index 777b1e319..6d49b9c79 100644
--- a/lib/hostip6.c
+++ b/lib/hostip6.c
@@ -279,9 +279,10 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
/* the given address is numerical only, prevent a reverse lookup */
hints.ai_flags = AI_NUMERICHOST;
}
-#if 0 /* removed nov 8 2005 before 7.15.1 */
- else
- hints.ai_flags = AI_CANONNAME;
+#ifdef HAVE_GSSAPI
+ if(conn->data->set.krb)
+ /* if krb is used, we (might) need the canonical host name */
+ hints.ai_flags |= AI_CANONNAME;
#endif
if(port) {
diff --git a/lib/krb4.h b/lib/krb4.h
index f46416e62..405454ccb 100644
--- a/lib/krb4.h
+++ b/lib/krb4.h
@@ -40,7 +40,12 @@ struct Curl_sec_client_mech {
#define AUTH_CONTINUE 1
#define AUTH_ERROR 2
+#ifdef HAVE_KRB4
extern struct Curl_sec_client_mech Curl_krb4_client_mech;
+#endif
+#ifdef HAVE_GSSAPI
+extern struct Curl_sec_client_mech Curl_krb5_client_mech;
+#endif
CURLcode Curl_krb_kauth(struct connectdata *conn);
int Curl_sec_fflush_fd(struct connectdata *conn, int fd);
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 */
diff --git a/lib/security.c b/lib/security.c
index 4c9aed812..5aa6ebfb5 100644
--- a/lib/security.c
+++ b/lib/security.c
@@ -41,7 +41,7 @@
#include "setup.h"
#ifndef CURL_DISABLE_FTP
-#ifdef HAVE_KRB4
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
#define _MPRINTF_REPLACE /* we want curl-functions instead of native ones */
#include <curl/mprintf.h>
@@ -87,8 +87,8 @@ name_to_level(const char *name)
}
static const struct Curl_sec_client_mech * const mechs[] = {
-#ifdef KRB5
- /* not supported */
+#ifdef HAVE_GSSAPI
+ &Curl_krb5_client_mech,
#endif
#ifdef HAVE_KRB4
&Curl_krb4_client_mech,
@@ -118,6 +118,8 @@ block_read(int fd, void *buf, size_t len)
b = read(fd, p, len);
if (b == 0)
return 0;
+ else if (b < 0 && (errno == EINTR || errno == EAGAIN))
+ continue;
else if (b < 0)
return -1;
len -= b;
@@ -133,7 +135,9 @@ block_write(int fd, void *buf, size_t len)
int b;
while(len) {
b = write(fd, p, len);
- if(b < 0)
+ if (b < 0 && (errno == EINTR || errno == EAGAIN))
+ continue;
+ else if(b < 0)
return -1;
len -= b;
p += b;
@@ -155,7 +159,7 @@ sec_get_data(struct connectdata *conn,
return -1;
len = ntohl(len);
buf->data = realloc(buf->data, len);
- b = block_read(fd, buf->data, len);
+ b = buf->data ? block_read(fd, buf->data, len) : -1;
if (b == 0)
return 0;
else if (b < 0)
@@ -234,11 +238,36 @@ sec_send(struct connectdata *conn, int fd, char *from, int length)
{
int bytes;
void *buf;
- bytes = (conn->mech->encode)(conn->app_data, from, length, conn->data_prot,
+ enum protection_level protlevel = conn->data_prot;
+ int iscmd = protlevel == prot_cmd;
+
+ if(iscmd) {
+ if(!strncmp(from, "PASS ", 5) || !strncmp(from, "ACCT ", 5))
+ protlevel = prot_private;
+ else
+ protlevel = conn->command_prot;
+ }
+ bytes = (conn->mech->encode)(conn->app_data, from, length, protlevel,
&buf, conn);
- bytes = htonl(bytes);
- block_write(fd, &bytes, sizeof(bytes));
- block_write(fd, buf, ntohl(bytes));
+ if(iscmd) {
+ char *cmdbuf;
+
+ bytes = Curl_base64_encode(conn->data, (char *)buf, bytes, &cmdbuf);
+ if(bytes > 0) {
+ if(protlevel == prot_private)
+ block_write(fd, "ENC ", 4);
+ else
+ block_write(fd, "MIC ", 4);
+ block_write(fd, cmdbuf, bytes);
+ block_write(fd, "\r\n", 2);
+ Curl_infof(conn->data, "%s %s\n", protlevel == prot_private ? "ENC" : "MIC", cmdbuf);
+ free(cmdbuf);
+ }
+ } else {
+ bytes = htonl(bytes);
+ block_write(fd, &bytes, sizeof(bytes));
+ block_write(fd, buf, ntohl(bytes));
+ }
free(buf);
return length;
}
@@ -267,6 +296,8 @@ Curl_sec_write(struct connectdata *conn, int fd, char *buffer, int length)
return write(fd, buffer, length);
len -= (conn->mech->overhead)(conn->app_data, conn->data_prot, len);
+ if(len <= 0)
+ len = length;
while(length){
if(length < len)
len = length;
@@ -319,6 +350,11 @@ Curl_sec_read_msg(struct connectdata *conn, char *s, int level)
return -1;
}
+ if(conn->data->set.verbose) {
+ buf[len] = '\n';
+ Curl_debug(conn->data, CURLINFO_HEADER_IN, (char *)buf, len + 1, conn);
+ }
+
buf[len] = '\0';
if(buf[3] == '-')
@@ -360,7 +396,7 @@ sec_prot_internal(struct connectdata *conn, int level)
if(Curl_GetFTPResponse(&nread, conn, &code))
return -1;
- if(code/100 != '2'){
+ if(code/100 != 2){
failf(conn->data, "Failed to set protection buffer size.");
return -1;
}
@@ -385,6 +421,8 @@ sec_prot_internal(struct connectdata *conn, int level)
}
conn->data_prot = (enum protection_level)level;
+ if(level == prot_private)
+ conn->command_prot = (enum protection_level)level;
return 0;
}
@@ -468,6 +506,9 @@ Curl_sec_login(struct connectdata *conn)
conn->mech = *m;
conn->sec_complete = 1;
conn->command_prot = prot_safe;
+ /* Set the requested protection level */
+ /* BLOCKING */
+ Curl_sec_set_protection_level(conn);
break;
}
diff --git a/lib/sendf.c b/lib/sendf.c
index a00444f4f..7a6fd5401 100644
--- a/lib/sendf.c
+++ b/lib/sendf.c
@@ -47,7 +47,7 @@
#define _MPRINTF_REPLACE /* use the internal *printf() functions */
#include <curl/mprintf.h>
-#ifdef HAVE_KRB4
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
#include "krb4.h"
#else
#define Curl_sec_send(a,b,c,d) -1
@@ -365,7 +365,7 @@ CURLcode Curl_write(struct connectdata *conn,
bytes_written = Curl_sftp_send(conn, num, mem, len);
#endif /* !USE_LIBSSH2 */
else if(conn->sec_complete)
- /* only TRUE if krb4 enabled */
+ /* only TRUE if krb enabled */
bytes_written = Curl_sec_send(conn, num, mem, len);
else
bytes_written = Curl_plain_send(conn, num, mem, len);
diff --git a/lib/url.c b/lib/url.c
index b5b83effb..aa2aafe09 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -146,9 +146,6 @@ void idn_free (void *ptr); /* prototype from idn-free.h, not provided by
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
-#ifdef HAVE_KRB4
-#include "krb4.h"
-#endif
#include "memory.h"
/* The last #include file should be: */
@@ -1498,12 +1495,12 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
*/
data->set.localportrange = (int) va_arg(param, long);
break;
- case CURLOPT_KRB4LEVEL:
+ case CURLOPT_KRBLEVEL:
/*
- * A string that defines the krb4 security level.
+ * A string that defines the kerberos security level.
*/
- data->set.krb4_level = va_arg(param, char *);
- data->set.krb4 = (bool)(NULL != data->set.krb4_level);
+ data->set.krb_level = va_arg(param, char *);
+ data->set.krb = (bool)(NULL != data->set.krb_level);
break;
case CURLOPT_SSL_VERIFYPEER:
/*
diff --git a/lib/urldata.h b/lib/urldata.h
index bd012b817..29000b210 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -133,8 +133,8 @@
We prefix with CURL to prevent name collisions. */
#define CURLMAX(x,y) ((x)>(y)?(x):(y))
-#ifdef HAVE_KRB4
-/* Types needed for krb4-ftp connections */
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+/* Types needed for krb4/5-ftp connections */
struct krb4buffer {
void *data;
size_t size;
@@ -145,7 +145,8 @@ enum protection_level {
prot_clear,
prot_safe,
prot_confidential,
- prot_private
+ prot_private,
+ prot_cmd
};
#endif
@@ -882,8 +883,8 @@ struct connectdata {
char *cookiehost; /* free later if not NULL */
} allocptr;
- int sec_complete; /* if krb4 is enabled for this connection */
-#ifdef HAVE_KRB4
+ int sec_complete; /* if kerberos is enabled for this connection */
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
enum protection_level command_prot;
enum protection_level data_prot;
enum protection_level request_data_prot;
@@ -1277,7 +1278,7 @@ struct UserDefined {
* to which to send the authorization data to, and no other
* host (which location-following otherwise could lead to)
*/
- char *krb4_level; /* what security level */
+ char *krb_level; /* what security level */
struct ssl_config_data ssl; /* user defined SSL stuff */
curl_proxytype proxytype; /* what kind of proxy that is in use */
@@ -1331,7 +1332,7 @@ struct UserDefined {
char *netrc_file; /* if not NULL, use this instead of trying to find
$HOME/.netrc */
bool verbose;
- bool krb4; /* kerberos4 connection requested */
+ bool krb; /* kerberos connection requested */
bool reuse_forbid; /* forbidden to be reused, close after use */
bool reuse_fresh; /* do not re-use an existing connection */
bool ftp_use_epsv; /* if EPSV is to be attempted or not */
diff --git a/src/main.c b/src/main.c
index 1869ce16e..17a961ee0 100644
--- a/src/main.c
+++ b/src/main.c
@@ -411,7 +411,7 @@ struct Configurable {
bool list_engines;
bool crlf;
char *customrequest;
- char *krb4level;
+ char *krblevel;
char *trace_dump; /* file to dump the network trace to, or NULL */
FILE *trace_stream;
bool trace_fopened;
@@ -665,7 +665,7 @@ static void help(void)
" -I/--head Show document info only",
" -j/--junk-session-cookies Ignore session cookies read from file (H)",
" --interface <interface> Specify network interface/address to use",
- " --krb4 <level> Enable krb4 with specified security level (F)",
+ " --krb <level> Enable kerberos with specified security level (F)",
" -k/--insecure Allow connections to SSL sites without certs (H)",
" -K/--config Specify which config file to read",
" --libcurl <file> Dump libcurl equivalent code of this command line",
@@ -1476,7 +1476,8 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
{"*u", "crlf", FALSE},
{"*v", "stderr", TRUE},
{"*w", "interface", TRUE},
- {"*x", "krb4", TRUE},
+ {"*x", "krb" , TRUE},
+ {"*x", "krb4" , TRUE}, /* this is the previous name */
{"*y", "max-filesize", TRUE},
{"*z", "disable-eprt", FALSE},
{"$a", "ftp-ssl", FALSE},
@@ -1809,10 +1810,10 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
/* interface */
GetStr(&config->iface, nextarg);
break;
- case 'x': /* --krb4 */
- /* krb4 level string */
- if(curlinfo->features & CURL_VERSION_KERBEROS4)
- GetStr(&config->krb4level, nextarg);
+ case 'x': /* --krb */
+ /* kerberos level string */
+ if(curlinfo->features & (CURL_VERSION_KERBEROS4 | CURL_VERSION_GSSNEGOTIATE))
+ GetStr(&config->krblevel, nextarg);
else
return PARAM_LIBCURL_DOESNT_SUPPORT;
break;
@@ -3260,8 +3261,8 @@ static void free_config_fields(struct Configurable *config)
free(config->cookie);
if(config->cookiefile)
free(config->cookiefile);
- if(config->krb4level)
- free(config->krb4level);
+ if(config->krblevel)
+ free(config->krblevel);
if(config->headerfile)
free(config->headerfile);
if(config->ftpport)
@@ -4221,7 +4222,7 @@ operate(struct Configurable *config, int argc, argv_item_t argv[])
/* three new ones in libcurl 7.3: */
my_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, config->proxytunnel);
my_setopt(curl, CURLOPT_INTERFACE, config->iface);
- my_setopt(curl, CURLOPT_KRB4LEVEL, config->krb4level);
+ my_setopt(curl, CURLOPT_KRBLEVEL, config->krblevel);
progressbarinit(&progressbar, config);
if((config->progressmode == CURL_PROGRESS_BAR) &&