cURL / Mailing Lists / curl-library / Single Mail

curl-library

Re: Kerberos v5 FTP?

From: Thomas J. Moore <thomoore_at_iupui.edu>
Date: Tue, 26 Jun 2007 13:29:30 -0400

On 06/21/2007 05:44 PM, Daniel Stenberg wrote:
> On Thu, 21 Jun 2007, Thomas J. Moore wrote:
>> Should I send the patch (1055 lines, 38k/11k.gz) to this list?
>
> Please do!

OK, I've just come back from 4 days away from this, and decided to make a few
quick changes this morning to have it use the same options as the krb4 support
did. That way, anything using libcurl that supported turning on krb4 should
work without modifications using krb5 (assuming the newer libcurl + gssapi
support, and assuming it doesn't check for the krb4 feature). Falling back to
krb4 support was a feature of the old code already, so I didn't need to do
anything for this.

To do the krb5 support, I just copied krb4.c and replaced the functions with the
appropriate ones for gssapi. I didn't really audit the full include file list
(it probably includes too much), and didn't clean everything up. I also didn't
rename krb4.h to krb.h (and maybe krb4buffer to krbbuffer), as I probably should
have.

You didn't say what the preferred format is, so I attached it as MIME/plain
text. If this message is too big, I'll just say I did ask first.

...
> (But I personally have no need for krb5 nor do I have access to anything
> that uses it so I won't even be able to test it.)

Well, most Linux distributions come with a fairly easy to install kerberos
server & associated FTP server. How is the kerberos5/gssapi HTTP stuff being
tested? If you have that working, then adding MIT or Heimdal's kerberized ftpd
should be trivial. As I said, the FTP server I did this for only allows
clear-text data, so I had to run the "standard" FTP server to test that
encrypted transfers worked. It was trivial - all I had to do is run it from the
command line with the -a and -p <port> options and make sure it had access to a
keytab with ftp/<host> and/or host/<host> principals.

On the other hand, I have no need for krb4, and haven't looked into how hard it
would be to test it.

My main purpose for doing this was to see how well curlftpfs worked with our
system. It doesn't do a lot of things that would be needed to make it smooth,
so I may not keep up with this too much.

  - Thomas J. Moore
    UITS/Research Technology/Research Storage
    Indiana University/IUPUI

diff -ruN curl-7.16.2/docs/curl.1 curl-7.16.2/docs/curl.1
--- curl-7.16.2/docs/curl.1 2007-03-23 17:39:43.000000000 -0400
+++ curl-7.16.2/docs/curl.1 2007-06-26 11:22:41.000000000 -0400
@@ -592,13 +592,14 @@
 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>"
@@ -1139,7 +1140,7 @@
 .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 -ruN curl-7.16.2/docs/FEATURES curl-7.16.2/docs/FEATURES
--- curl-7.16.2/docs/FEATURES 2007-02-12 16:27:15.000000000 -0500
+++ curl-7.16.2/docs/FEATURES 2007-06-26 10:17:43.000000000 -0400
@@ -71,7 +71,7 @@
 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 -ruN curl-7.16.2/docs/libcurl/curl_easy_setopt.3 curl-7.16.2/docs/libcurl/curl_easy_setopt.3
--- curl-7.16.2/docs/libcurl/curl_easy_setopt.3 2007-03-23 17:39:43.000000000 -0400
+++ curl-7.16.2/docs/libcurl/curl_easy_setopt.3 2007-06-26 11:33:16.000000000 -0400
@@ -1356,12 +1356,12 @@
 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 -ruN curl-7.16.2/docs/MANUAL curl-7.16.2/docs/MANUAL
--- curl-7.16.2/docs/MANUAL 2007-03-30 15:57:38.000000000 -0400
+++ curl-7.16.2/docs/MANUAL 2007-06-26 11:36:01.000000000 -0400
@@ -809,18 +809,19 @@
 
         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 -ruN curl-7.16.2/include/curl/curl.h curl-7.16.2/include/curl/curl.h
--- curl-7.16.2/include/curl/curl.h 2007-02-20 17:30:18.000000000 -0500
+++ curl-7.16.2/include/curl/curl.h 2007-06-26 10:21:35.000000000 -0400
@@ -731,10 +731,12 @@
   /* 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 -ruN curl-7.16.2/lib/ftp.c curl-7.16.2/lib/ftp.c
--- curl-7.16.2/lib/ftp.c 2007-04-11 09:10:22.000000000 -0400
+++ curl-7.16.2/lib/ftp.c 2007-06-26 11:41:26.000000000 -0400
@@ -75,7 +75,7 @@
 #include "socks.h"
 #include "ftp.h"
 
-#ifdef HAVE_KRB4
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
 #include "krb4.h"
 #endif
 
@@ -300,8 +300,17 @@
       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 */
@@ -341,6 +346,9 @@
              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);
@@ -395,18 +403,18 @@
   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! */
@@ -534,7 +542,17 @@
         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 */
@@ -574,6 +588,9 @@
                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);
@@ -627,18 +644,18 @@
   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! */
@@ -2277,14 +2294,7 @@
   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
      */
@@ -2331,7 +2341,8 @@
   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:"");
@@ -2439,15 +2450,15 @@
       }
 
       /* 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");
@@ -3062,7 +3073,7 @@
   /* 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
 
@@ -3465,16 +3476,21 @@
                        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 */
@@ -3492,8 +3508,14 @@
   }
 #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;
@@ -3526,14 +3548,17 @@
                        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 */
@@ -3550,8 +3575,14 @@
 #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 -ruN curl-7.16.2/lib/hostip4.c curl-7.16.2/lib/hostip4.c
--- curl-7.16.2/lib/hostip4.c 2007-03-25 03:42:49.000000000 -0400
+++ curl-7.16.2/lib/hostip4.c 2007-06-26 10:17:43.000000000 -0400
@@ -375,6 +375,9 @@
        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 -ruN curl-7.16.2/lib/hostip6.c curl-7.16.2/lib/hostip6.c
--- curl-7.16.2/lib/hostip6.c 2007-02-27 07:44:31.000000000 -0500
+++ curl-7.16.2/lib/hostip6.c 2007-06-26 10:17:43.000000000 -0400
@@ -279,9 +279,8 @@
     /* 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
+ hints.ai_flags |= AI_CANONNAME;
 #endif
 
   if(port) {
diff -ruN curl-7.16.2/lib/krb4.h curl-7.16.2/lib/krb4.h
--- curl-7.16.2/lib/krb4.h 2006-11-11 16:34:43.000000000 -0500
+++ curl-7.16.2/lib/krb4.h 2007-06-26 10:17:43.000000000 -0400
@@ -40,7 +40,12 @@
 #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 -ruN curl-7.16.2/lib/krb5.c curl-7.16.2/lib/krb5.c
--- curl-7.16.2/lib/krb5.c 1969-12-31 19:00:00.000000000 -0500
+++ curl-7.16.2/lib/krb5.c 2007-06-26 10:17:43.000000000 -0400
@@ -0,0 +1,316 @@
+/* This source code was modified by Martin Hedenfalk <mhe_at_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_at_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 -ruN curl-7.16.2/lib/Makefile.inc curl-7.16.2/lib/Makefile.inc
--- curl-7.16.2/lib/Makefile.inc 2007-02-12 16:43:24.000000000 -0500
+++ curl-7.16.2/lib/Makefile.inc 2007-06-26 10:17:43.000000000 -0400
@@ -4,6 +4,7 @@
   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 -ruN curl-7.16.2/lib/security.c curl-7.16.2/lib/security.c
--- curl-7.16.2/lib/security.c 2006-11-11 16:26:45.000000000 -0500
+++ curl-7.16.2/lib/security.c 2007-06-26 10:17:43.000000000 -0400
@@ -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 @@
 }
 
 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 @@
     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 @@
   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 @@
     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 @@
 {
   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 @@
     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 @@
     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 @@
     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 @@
   }
 
   conn->data_prot = (enum protection_level)level;
+ if(level == prot_private)
+ conn->command_prot = (enum protection_level)level;
   return 0;
 }
 
@@ -468,6 +506,9 @@
     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 -ruN curl-7.16.2/lib/sendf.c curl-7.16.2/lib/sendf.c
--- curl-7.16.2/lib/sendf.c 2007-03-21 17:29:13.000000000 -0400
+++ curl-7.16.2/lib/sendf.c 2007-06-26 10:17:43.000000000 -0400
@@ -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 @@
     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 -ruN curl-7.16.2/lib/url.c curl-7.16.2/lib/url.c
--- curl-7.16.2/lib/url.c 2007-04-10 16:46:40.000000000 -0400
+++ curl-7.16.2/lib/url.c 2007-06-26 11:44:23.000000000 -0400
@@ -146,9 +146,6 @@
 #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: */
@@ -1490,12 +1487,12 @@
      */
     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 -ruN curl-7.16.2/lib/urldata.h curl-7.16.2/lib/urldata.h
--- curl-7.16.2/lib/urldata.h 2007-04-10 16:46:40.000000000 -0400
+++ curl-7.16.2/lib/urldata.h 2007-06-26 11:45:21.000000000 -0400
@@ -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 @@
   prot_clear,
   prot_safe,
   prot_confidential,
- prot_private
+ prot_private,
+ prot_cmd
 };
 #endif
 
@@ -845,8 +846,8 @@
     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;
@@ -1236,7 +1237,7 @@
                     * 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 */
@@ -1290,7 +1291,7 @@
   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 -ruN curl-7.16.2/src/main.c curl-7.16.2/src/main.c
--- curl-7.16.2/src/main.c 2007-04-08 16:37:00.000000000 -0400
+++ curl-7.16.2/src/main.c 2007-06-26 11:47:19.000000000 -0400
@@ -406,7 +406,7 @@
   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;
@@ -656,7 +656,7 @@
     " -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",
@@ -1467,7 +1467,7 @@
     {"*u", "crlf", FALSE},
     {"*v", "stderr", TRUE},
     {"*w", "interface", TRUE},
- {"*x", "krb4", TRUE},
+ {"*x", "krb" , TRUE},
     {"*y", "max-filesize", TRUE},
     {"*z", "disable-eprt", FALSE},
     {"$a", "ftp-ssl", FALSE},
@@ -1800,10 +1800,10 @@
         /* 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;
@@ -3238,8 +3238,8 @@
     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)
@@ -4190,7 +4190,7 @@
         /* 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) &&
Received on 2007-06-26