krb5.c
Go to the documentation of this file.
00001 /* GSSAPI/krb5 support for FTP - loosely based on old krb4.c
00002  *
00003  * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
00004  * (Royal Institute of Technology, Stockholm, Sweden).
00005  * Copyright (c) 2004 - 2016 Daniel Stenberg
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions
00010  * are met:
00011  *
00012  * 1. Redistributions of source code must retain the above copyright
00013  *    notice, this list of conditions and the following disclaimer.
00014  *
00015  * 2. Redistributions in binary form must reproduce the above copyright
00016  *    notice, this list of conditions and the following disclaimer in the
00017  *    documentation and/or other materials provided with the distribution.
00018  *
00019  * 3. Neither the name of the Institute nor the names of its contributors
00020  *    may be used to endorse or promote products derived from this software
00021  *    without specific prior written permission.
00022  *
00023  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
00024  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00026  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
00027  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00028  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00029  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00030  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00032  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00033  * SUCH DAMAGE.  */
00034 
00035 #include "curl_setup.h"
00036 
00037 #if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_FTP)
00038 
00039 #ifdef HAVE_NETDB_H
00040 #include <netdb.h>
00041 #endif
00042 
00043 #include "urldata.h"
00044 #include "curl_base64.h"
00045 #include "ftp.h"
00046 #include "curl_gssapi.h"
00047 #include "sendf.h"
00048 #include "curl_sec.h"
00049 #include "warnless.h"
00050 
00051 /* The last 3 #include files should be in this order */
00052 #include "curl_printf.h"
00053 #include "curl_memory.h"
00054 #include "memdebug.h"
00055 
00056 static int
00057 krb5_init(void *app_data)
00058 {
00059   gss_ctx_id_t *context = app_data;
00060   /* Make sure our context is initialized for krb5_end. */
00061   *context = GSS_C_NO_CONTEXT;
00062   return 0;
00063 }
00064 
00065 static int
00066 krb5_check_prot(void *app_data, int level)
00067 {
00068   (void)app_data; /* unused */
00069   if(level == PROT_CONFIDENTIAL)
00070     return -1;
00071   return 0;
00072 }
00073 
00074 static int
00075 krb5_decode(void *app_data, void *buf, int len,
00076             int level UNUSED_PARAM,
00077             struct connectdata *conn UNUSED_PARAM)
00078 {
00079   gss_ctx_id_t *context = app_data;
00080   OM_uint32 maj, min;
00081   gss_buffer_desc enc, dec;
00082 
00083   (void)level;
00084   (void)conn;
00085 
00086   enc.value = buf;
00087   enc.length = len;
00088   maj = gss_unseal(&min, *context, &enc, &dec, NULL, NULL);
00089   if(maj != GSS_S_COMPLETE) {
00090     if(len >= 4)
00091       strcpy(buf, "599 ");
00092     return -1;
00093   }
00094 
00095   memcpy(buf, dec.value, dec.length);
00096   len = curlx_uztosi(dec.length);
00097   gss_release_buffer(&min, &dec);
00098 
00099   return len;
00100 }
00101 
00102 static int
00103 krb5_overhead(void *app_data, int level, int len)
00104 {
00105   /* no arguments are used */
00106   (void)app_data;
00107   (void)level;
00108   (void)len;
00109   return 0;
00110 }
00111 
00112 static int
00113 krb5_encode(void *app_data, const void *from, int length, int level, void **to)
00114 {
00115   gss_ctx_id_t *context = app_data;
00116   gss_buffer_desc dec, enc;
00117   OM_uint32 maj, min;
00118   int state;
00119   int len;
00120 
00121   /* NOTE that the cast is safe, neither of the krb5, gnu gss and heimdal
00122    * libraries modify the input buffer in gss_seal()
00123    */
00124   dec.value = (void *)from;
00125   dec.length = length;
00126   maj = gss_seal(&min, *context,
00127                  level == PROT_PRIVATE,
00128                  GSS_C_QOP_DEFAULT,
00129                  &dec, &state, &enc);
00130 
00131   if(maj != GSS_S_COMPLETE)
00132     return -1;
00133 
00134   /* malloc a new buffer, in case gss_release_buffer doesn't work as
00135      expected */
00136   *to = malloc(enc.length);
00137   if(!*to)
00138     return -1;
00139   memcpy(*to, enc.value, enc.length);
00140   len = curlx_uztosi(enc.length);
00141   gss_release_buffer(&min, &enc);
00142   return len;
00143 }
00144 
00145 static int
00146 krb5_auth(void *app_data, struct connectdata *conn)
00147 {
00148   int ret = AUTH_OK;
00149   char *p;
00150   const char *host = conn->host.name;
00151   ssize_t nread;
00152   curl_socklen_t l = sizeof(conn->local_addr);
00153   struct Curl_easy *data = conn->data;
00154   CURLcode result;
00155   const char *service = data->set.str[STRING_SERVICE_NAME] ?
00156                         data->set.str[STRING_SERVICE_NAME] :
00157                         "ftp";
00158   const char *srv_host = "host";
00159   gss_buffer_desc input_buffer, output_buffer, _gssresp, *gssresp;
00160   OM_uint32 maj, min;
00161   gss_name_t gssname;
00162   gss_ctx_id_t *context = app_data;
00163   struct gss_channel_bindings_struct chan;
00164   size_t base64_sz = 0;
00165   struct sockaddr_in **remote_addr =
00166     (struct sockaddr_in **)&conn->ip_addr->ai_addr;
00167 
00168   if(getsockname(conn->sock[FIRSTSOCKET],
00169                  (struct sockaddr *)&conn->local_addr, &l) < 0)
00170     perror("getsockname()");
00171 
00172   chan.initiator_addrtype = GSS_C_AF_INET;
00173   chan.initiator_address.length = l - 4;
00174   chan.initiator_address.value = &conn->local_addr.sin_addr.s_addr;
00175   chan.acceptor_addrtype = GSS_C_AF_INET;
00176   chan.acceptor_address.length = l - 4;
00177   chan.acceptor_address.value = &(*remote_addr)->sin_addr.s_addr;
00178   chan.application_data.length = 0;
00179   chan.application_data.value = NULL;
00180 
00181   /* this loop will execute twice (once for service, once for host) */
00182   for(;;) {
00183     /* this really shouldn't be repeated here, but can't help it */
00184     if(service == srv_host) {
00185       result = Curl_ftpsend(conn, "AUTH GSSAPI");
00186       if(result)
00187         return -2;
00188 
00189       if(Curl_GetFTPResponse(&nread, conn, NULL))
00190         return -1;
00191 
00192       if(data->state.buffer[0] != '3')
00193         return -1;
00194     }
00195 
00196     input_buffer.value = data->state.buffer;
00197     input_buffer.length = snprintf(input_buffer.value, BUFSIZE, "%s@%s",
00198                                    service, host);
00199     maj = gss_import_name(&min, &input_buffer, GSS_C_NT_HOSTBASED_SERVICE,
00200                           &gssname);
00201     if(maj != GSS_S_COMPLETE) {
00202       gss_release_name(&min, &gssname);
00203       if(service == srv_host) {
00204         Curl_failf(data, "Error importing service name %s",
00205                    input_buffer.value);
00206         return AUTH_ERROR;
00207       }
00208       service = srv_host;
00209       continue;
00210     }
00211     /* We pass NULL as |output_name_type| to avoid a leak. */
00212     gss_display_name(&min, gssname, &output_buffer, NULL);
00213     Curl_infof(data, "Trying against %s\n", output_buffer.value);
00214     gssresp = GSS_C_NO_BUFFER;
00215     *context = GSS_C_NO_CONTEXT;
00216 
00217     do {
00218       /* Release the buffer at each iteration to avoid leaking: the first time
00219          we are releasing the memory from gss_display_name. The last item is
00220          taken care by a final gss_release_buffer. */
00221       gss_release_buffer(&min, &output_buffer);
00222       ret = AUTH_OK;
00223       maj = Curl_gss_init_sec_context(data,
00224                                       &min,
00225                                       context,
00226                                       gssname,
00227                                       &Curl_krb5_mech_oid,
00228                                       &chan,
00229                                       gssresp,
00230                                       &output_buffer,
00231                                       TRUE,
00232                                       NULL);
00233 
00234       if(gssresp) {
00235         free(_gssresp.value);
00236         gssresp = NULL;
00237       }
00238 
00239       if(GSS_ERROR(maj)) {
00240         Curl_infof(data, "Error creating security context\n");
00241         ret = AUTH_ERROR;
00242         break;
00243       }
00244 
00245       if(output_buffer.length != 0) {
00246         char *cmd;
00247 
00248         result = Curl_base64_encode(data, (char *)output_buffer.value,
00249                                     output_buffer.length, &p, &base64_sz);
00250         if(result) {
00251           Curl_infof(data, "base64-encoding: %s\n",
00252                      curl_easy_strerror(result));
00253           ret = AUTH_ERROR;
00254           break;
00255         }
00256 
00257         cmd = aprintf("ADAT %s", p);
00258         if(cmd)
00259           result = Curl_ftpsend(conn, cmd);
00260         else
00261           result = CURLE_OUT_OF_MEMORY;
00262 
00263         free(p);
00264 
00265         if(result) {
00266           ret = -2;
00267           break;
00268         }
00269 
00270         if(Curl_GetFTPResponse(&nread, conn, NULL)) {
00271           ret = -1;
00272           break;
00273         }
00274 
00275         if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3') {
00276           Curl_infof(data, "Server didn't accept auth data\n");
00277           ret = AUTH_ERROR;
00278           break;
00279         }
00280 
00281         p = data->state.buffer + 4;
00282         p = strstr(p, "ADAT=");
00283         if(p) {
00284           result = Curl_base64_decode(p + 5,
00285                                       (unsigned char **)&_gssresp.value,
00286                                       &_gssresp.length);
00287           if(result) {
00288             Curl_failf(data, "base64-decoding: %s",
00289                        curl_easy_strerror(result));
00290             ret = AUTH_CONTINUE;
00291             break;
00292           }
00293         }
00294 
00295         gssresp = &_gssresp;
00296       }
00297     } while(maj == GSS_S_CONTINUE_NEEDED);
00298 
00299     gss_release_name(&min, &gssname);
00300     gss_release_buffer(&min, &output_buffer);
00301 
00302     if(gssresp)
00303       free(_gssresp.value);
00304 
00305     if(ret == AUTH_OK || service == srv_host)
00306       return ret;
00307 
00308     service = srv_host;
00309   }
00310   return ret;
00311 }
00312 
00313 static void krb5_end(void *app_data)
00314 {
00315     OM_uint32 min;
00316     gss_ctx_id_t *context = app_data;
00317     if(*context != GSS_C_NO_CONTEXT) {
00318 #ifdef DEBUGBUILD
00319       OM_uint32 maj =
00320 #endif
00321       gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER);
00322       DEBUGASSERT(maj == GSS_S_COMPLETE);
00323     }
00324 }
00325 
00326 struct Curl_sec_client_mech Curl_krb5_client_mech = {
00327     "GSSAPI",
00328     sizeof(gss_ctx_id_t),
00329     krb5_init,
00330     krb5_auth,
00331     krb5_end,
00332     krb5_check_prot,
00333     krb5_overhead,
00334     krb5_encode,
00335     krb5_decode
00336 };
00337 
00338 #endif /* HAVE_GSSAPI && !CURL_DISABLE_FTP */


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:05