Thomas J. Moore provided a patch that introduces Kerberos5 support in
authorDaniel Stenberg <daniel@haxx.se>
Sun, 1 Jul 2007 22:01:18 +0000 (22:01 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Sun, 1 Jul 2007 22:01:18 +0000 (22:01 +0000)
libcurl. This also makes the options change name to --krb (from --krb4) and
CURLOPT_KRBLEVEL (from CURLOPT_KRB4LEVEL) but the old names are still

19 files changed:
CHANGES
RELEASE-NOTES
docs/FEATURES
docs/MANUAL
docs/curl.1
docs/libcurl/curl_easy_setopt.3
include/curl/curl.h
lib/Makefile.inc
lib/ftp.c
lib/hostip.c
lib/hostip4.c
lib/hostip6.c
lib/krb4.h
lib/krb5.c [new file with mode: 0644]
lib/security.c
lib/sendf.c
lib/url.c
lib/urldata.h
src/main.c

diff --git a/CHANGES b/CHANGES
index 8877153..218d28d 100644 (file)
--- 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.
 
index 134144e..3fd9ea5 100644 (file)
@@ -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)
index 75aefce..08f342c 100644 (file)
@@ -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
index 1ea13f1..dba224d 100644 (file)
@@ -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
 
index 629f99d..59753fa 100644 (file)
@@ -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!
index 01e4dc3..cbbf411 100644 (file)
@@ -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
index ca229cb..fe413d0 100644 (file)
@@ -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),
index 3985cef..714b15f 100644 (file)
@@ -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 \
index 5e04b7e..91f6d91 100644 (file)
--- 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;
index 5a7da50..189d068 100644 (file)
@@ -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;
   }
index 43b7c69..9ff8838 100644 (file)
@@ -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 */
index 777b1e3..6d49b9c 100644 (file)
@@ -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) {
index f46416e..405454c 100644 (file)
@@ -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 (file)
index 0000000..6655973
--- /dev/null
@@ -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 */
index 4c9aed8..5aa6ebf 100644 (file)
@@ -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;
   }
 
index a00444f..7a6fd54 100644 (file)
@@ -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);
index b5b83ef..aa2aafe 100644 (file)
--- 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:
     /*
index bd012b8..29000b2 100644 (file)
    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 */
index 1869ce1..17a961e 100644 (file)
@@ -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) &&