113498266Sopenharmony_ci/* GSSAPI/krb5 support for FTP - loosely based on old krb4.c 213498266Sopenharmony_ci * 313498266Sopenharmony_ci * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan 413498266Sopenharmony_ci * (Royal Institute of Technology, Stockholm, Sweden). 513498266Sopenharmony_ci * Copyright (C) Daniel Stenberg 613498266Sopenharmony_ci * All rights reserved. 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * SPDX-License-Identifier: BSD-3-Clause 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 1113498266Sopenharmony_ci * modification, are permitted provided that the following conditions 1213498266Sopenharmony_ci * are met: 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 1513498266Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 1613498266Sopenharmony_ci * 1713498266Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 1813498266Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 1913498266Sopenharmony_ci * documentation and/or other materials provided with the distribution. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * 3. Neither the name of the Institute nor the names of its contributors 2213498266Sopenharmony_ci * may be used to endorse or promote products derived from this software 2313498266Sopenharmony_ci * without specific prior written permission. 2413498266Sopenharmony_ci * 2513498266Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 2613498266Sopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2713498266Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2813498266Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 2913498266Sopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3013498266Sopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3113498266Sopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3213498266Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3313498266Sopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3413498266Sopenharmony_ci * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3513498266Sopenharmony_ci * SUCH DAMAGE. */ 3613498266Sopenharmony_ci 3713498266Sopenharmony_ci#include "curl_setup.h" 3813498266Sopenharmony_ci 3913498266Sopenharmony_ci#if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_FTP) 4013498266Sopenharmony_ci 4113498266Sopenharmony_ci#ifdef HAVE_NETDB_H 4213498266Sopenharmony_ci#include <netdb.h> 4313498266Sopenharmony_ci#endif 4413498266Sopenharmony_ci#ifdef HAVE_ARPA_INET_H 4513498266Sopenharmony_ci#include <arpa/inet.h> 4613498266Sopenharmony_ci#endif 4713498266Sopenharmony_ci 4813498266Sopenharmony_ci#include "urldata.h" 4913498266Sopenharmony_ci#include "cfilters.h" 5013498266Sopenharmony_ci#include "cf-socket.h" 5113498266Sopenharmony_ci#include "curl_base64.h" 5213498266Sopenharmony_ci#include "ftp.h" 5313498266Sopenharmony_ci#include "curl_gssapi.h" 5413498266Sopenharmony_ci#include "sendf.h" 5513498266Sopenharmony_ci#include "curl_krb5.h" 5613498266Sopenharmony_ci#include "warnless.h" 5713498266Sopenharmony_ci#include "strcase.h" 5813498266Sopenharmony_ci#include "strdup.h" 5913498266Sopenharmony_ci 6013498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 6113498266Sopenharmony_ci#include "curl_printf.h" 6213498266Sopenharmony_ci#include "curl_memory.h" 6313498266Sopenharmony_ci#include "memdebug.h" 6413498266Sopenharmony_ci 6513498266Sopenharmony_cistatic CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn, 6613498266Sopenharmony_ci const char *cmd) 6713498266Sopenharmony_ci{ 6813498266Sopenharmony_ci ssize_t bytes_written; 6913498266Sopenharmony_ci#define SBUF_SIZE 1024 7013498266Sopenharmony_ci char s[SBUF_SIZE]; 7113498266Sopenharmony_ci size_t write_len; 7213498266Sopenharmony_ci char *sptr = s; 7313498266Sopenharmony_ci CURLcode result = CURLE_OK; 7413498266Sopenharmony_ci#ifdef HAVE_GSSAPI 7513498266Sopenharmony_ci unsigned char data_sec = conn->data_prot; 7613498266Sopenharmony_ci#endif 7713498266Sopenharmony_ci 7813498266Sopenharmony_ci DEBUGASSERT(cmd); 7913498266Sopenharmony_ci 8013498266Sopenharmony_ci write_len = strlen(cmd); 8113498266Sopenharmony_ci if(!write_len || write_len > (sizeof(s) -3)) 8213498266Sopenharmony_ci return CURLE_BAD_FUNCTION_ARGUMENT; 8313498266Sopenharmony_ci 8413498266Sopenharmony_ci memcpy(&s, cmd, write_len); 8513498266Sopenharmony_ci strcpy(&s[write_len], "\r\n"); /* append a trailing CRLF */ 8613498266Sopenharmony_ci write_len += 2; 8713498266Sopenharmony_ci bytes_written = 0; 8813498266Sopenharmony_ci 8913498266Sopenharmony_ci for(;;) { 9013498266Sopenharmony_ci#ifdef HAVE_GSSAPI 9113498266Sopenharmony_ci conn->data_prot = PROT_CMD; 9213498266Sopenharmony_ci#endif 9313498266Sopenharmony_ci result = Curl_nwrite(data, FIRSTSOCKET, sptr, write_len, 9413498266Sopenharmony_ci &bytes_written); 9513498266Sopenharmony_ci#ifdef HAVE_GSSAPI 9613498266Sopenharmony_ci DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); 9713498266Sopenharmony_ci conn->data_prot = data_sec; 9813498266Sopenharmony_ci#endif 9913498266Sopenharmony_ci 10013498266Sopenharmony_ci if(result) 10113498266Sopenharmony_ci break; 10213498266Sopenharmony_ci 10313498266Sopenharmony_ci Curl_debug(data, CURLINFO_HEADER_OUT, sptr, (size_t)bytes_written); 10413498266Sopenharmony_ci 10513498266Sopenharmony_ci if(bytes_written != (ssize_t)write_len) { 10613498266Sopenharmony_ci write_len -= bytes_written; 10713498266Sopenharmony_ci sptr += bytes_written; 10813498266Sopenharmony_ci } 10913498266Sopenharmony_ci else 11013498266Sopenharmony_ci break; 11113498266Sopenharmony_ci } 11213498266Sopenharmony_ci 11313498266Sopenharmony_ci return result; 11413498266Sopenharmony_ci} 11513498266Sopenharmony_ci 11613498266Sopenharmony_cistatic int 11713498266Sopenharmony_cikrb5_init(void *app_data) 11813498266Sopenharmony_ci{ 11913498266Sopenharmony_ci gss_ctx_id_t *context = app_data; 12013498266Sopenharmony_ci /* Make sure our context is initialized for krb5_end. */ 12113498266Sopenharmony_ci *context = GSS_C_NO_CONTEXT; 12213498266Sopenharmony_ci return 0; 12313498266Sopenharmony_ci} 12413498266Sopenharmony_ci 12513498266Sopenharmony_cistatic int 12613498266Sopenharmony_cikrb5_check_prot(void *app_data, int level) 12713498266Sopenharmony_ci{ 12813498266Sopenharmony_ci (void)app_data; /* unused */ 12913498266Sopenharmony_ci if(level == PROT_CONFIDENTIAL) 13013498266Sopenharmony_ci return -1; 13113498266Sopenharmony_ci return 0; 13213498266Sopenharmony_ci} 13313498266Sopenharmony_ci 13413498266Sopenharmony_cistatic int 13513498266Sopenharmony_cikrb5_decode(void *app_data, void *buf, int len, 13613498266Sopenharmony_ci int level UNUSED_PARAM, 13713498266Sopenharmony_ci struct connectdata *conn UNUSED_PARAM) 13813498266Sopenharmony_ci{ 13913498266Sopenharmony_ci gss_ctx_id_t *context = app_data; 14013498266Sopenharmony_ci OM_uint32 maj, min; 14113498266Sopenharmony_ci gss_buffer_desc enc, dec; 14213498266Sopenharmony_ci 14313498266Sopenharmony_ci (void)level; 14413498266Sopenharmony_ci (void)conn; 14513498266Sopenharmony_ci 14613498266Sopenharmony_ci enc.value = buf; 14713498266Sopenharmony_ci enc.length = len; 14813498266Sopenharmony_ci maj = gss_unwrap(&min, *context, &enc, &dec, NULL, NULL); 14913498266Sopenharmony_ci if(maj != GSS_S_COMPLETE) 15013498266Sopenharmony_ci return -1; 15113498266Sopenharmony_ci 15213498266Sopenharmony_ci memcpy(buf, dec.value, dec.length); 15313498266Sopenharmony_ci len = curlx_uztosi(dec.length); 15413498266Sopenharmony_ci gss_release_buffer(&min, &dec); 15513498266Sopenharmony_ci 15613498266Sopenharmony_ci return len; 15713498266Sopenharmony_ci} 15813498266Sopenharmony_ci 15913498266Sopenharmony_cistatic int 16013498266Sopenharmony_cikrb5_encode(void *app_data, const void *from, int length, int level, void **to) 16113498266Sopenharmony_ci{ 16213498266Sopenharmony_ci gss_ctx_id_t *context = app_data; 16313498266Sopenharmony_ci gss_buffer_desc dec, enc; 16413498266Sopenharmony_ci OM_uint32 maj, min; 16513498266Sopenharmony_ci int state; 16613498266Sopenharmony_ci int len; 16713498266Sopenharmony_ci 16813498266Sopenharmony_ci /* NOTE that the cast is safe, neither of the krb5, gnu gss and heimdal 16913498266Sopenharmony_ci * libraries modify the input buffer in gss_wrap() 17013498266Sopenharmony_ci */ 17113498266Sopenharmony_ci dec.value = (void *)from; 17213498266Sopenharmony_ci dec.length = length; 17313498266Sopenharmony_ci maj = gss_wrap(&min, *context, 17413498266Sopenharmony_ci level == PROT_PRIVATE, 17513498266Sopenharmony_ci GSS_C_QOP_DEFAULT, 17613498266Sopenharmony_ci &dec, &state, &enc); 17713498266Sopenharmony_ci 17813498266Sopenharmony_ci if(maj != GSS_S_COMPLETE) 17913498266Sopenharmony_ci return -1; 18013498266Sopenharmony_ci 18113498266Sopenharmony_ci /* malloc a new buffer, in case gss_release_buffer doesn't work as 18213498266Sopenharmony_ci expected */ 18313498266Sopenharmony_ci *to = malloc(enc.length); 18413498266Sopenharmony_ci if(!*to) 18513498266Sopenharmony_ci return -1; 18613498266Sopenharmony_ci memcpy(*to, enc.value, enc.length); 18713498266Sopenharmony_ci len = curlx_uztosi(enc.length); 18813498266Sopenharmony_ci gss_release_buffer(&min, &enc); 18913498266Sopenharmony_ci return len; 19013498266Sopenharmony_ci} 19113498266Sopenharmony_ci 19213498266Sopenharmony_cistatic int 19313498266Sopenharmony_cikrb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) 19413498266Sopenharmony_ci{ 19513498266Sopenharmony_ci int ret = AUTH_OK; 19613498266Sopenharmony_ci char *p; 19713498266Sopenharmony_ci const char *host = conn->host.name; 19813498266Sopenharmony_ci ssize_t nread; 19913498266Sopenharmony_ci curl_socklen_t l = sizeof(conn->local_addr); 20013498266Sopenharmony_ci CURLcode result; 20113498266Sopenharmony_ci const char *service = data->set.str[STRING_SERVICE_NAME] ? 20213498266Sopenharmony_ci data->set.str[STRING_SERVICE_NAME] : 20313498266Sopenharmony_ci "ftp"; 20413498266Sopenharmony_ci const char *srv_host = "host"; 20513498266Sopenharmony_ci gss_buffer_desc input_buffer, output_buffer, _gssresp, *gssresp; 20613498266Sopenharmony_ci OM_uint32 maj, min; 20713498266Sopenharmony_ci gss_name_t gssname; 20813498266Sopenharmony_ci gss_ctx_id_t *context = app_data; 20913498266Sopenharmony_ci struct gss_channel_bindings_struct chan; 21013498266Sopenharmony_ci size_t base64_sz = 0; 21113498266Sopenharmony_ci struct sockaddr_in *remote_addr = 21213498266Sopenharmony_ci (struct sockaddr_in *)(void *)&conn->remote_addr->sa_addr; 21313498266Sopenharmony_ci char *stringp; 21413498266Sopenharmony_ci 21513498266Sopenharmony_ci if(getsockname(conn->sock[FIRSTSOCKET], 21613498266Sopenharmony_ci (struct sockaddr *)&conn->local_addr, &l) < 0) 21713498266Sopenharmony_ci perror("getsockname()"); 21813498266Sopenharmony_ci 21913498266Sopenharmony_ci chan.initiator_addrtype = GSS_C_AF_INET; 22013498266Sopenharmony_ci chan.initiator_address.length = l - 4; 22113498266Sopenharmony_ci chan.initiator_address.value = &conn->local_addr.sin_addr.s_addr; 22213498266Sopenharmony_ci chan.acceptor_addrtype = GSS_C_AF_INET; 22313498266Sopenharmony_ci chan.acceptor_address.length = l - 4; 22413498266Sopenharmony_ci chan.acceptor_address.value = &remote_addr->sin_addr.s_addr; 22513498266Sopenharmony_ci chan.application_data.length = 0; 22613498266Sopenharmony_ci chan.application_data.value = NULL; 22713498266Sopenharmony_ci 22813498266Sopenharmony_ci /* this loop will execute twice (once for service, once for host) */ 22913498266Sopenharmony_ci for(;;) { 23013498266Sopenharmony_ci /* this really shouldn't be repeated here, but can't help it */ 23113498266Sopenharmony_ci if(service == srv_host) { 23213498266Sopenharmony_ci result = ftpsend(data, conn, "AUTH GSSAPI"); 23313498266Sopenharmony_ci if(result) 23413498266Sopenharmony_ci return -2; 23513498266Sopenharmony_ci 23613498266Sopenharmony_ci if(Curl_GetFTPResponse(data, &nread, NULL)) 23713498266Sopenharmony_ci return -1; 23813498266Sopenharmony_ci else { 23913498266Sopenharmony_ci struct pingpong *pp = &conn->proto.ftpc.pp; 24013498266Sopenharmony_ci char *line = Curl_dyn_ptr(&pp->recvbuf); 24113498266Sopenharmony_ci if(line[0] != '3') 24213498266Sopenharmony_ci return -1; 24313498266Sopenharmony_ci } 24413498266Sopenharmony_ci } 24513498266Sopenharmony_ci 24613498266Sopenharmony_ci stringp = aprintf("%s@%s", service, host); 24713498266Sopenharmony_ci if(!stringp) 24813498266Sopenharmony_ci return -2; 24913498266Sopenharmony_ci 25013498266Sopenharmony_ci input_buffer.value = stringp; 25113498266Sopenharmony_ci input_buffer.length = strlen(stringp); 25213498266Sopenharmony_ci maj = gss_import_name(&min, &input_buffer, GSS_C_NT_HOSTBASED_SERVICE, 25313498266Sopenharmony_ci &gssname); 25413498266Sopenharmony_ci free(stringp); 25513498266Sopenharmony_ci if(maj != GSS_S_COMPLETE) { 25613498266Sopenharmony_ci gss_release_name(&min, &gssname); 25713498266Sopenharmony_ci if(service == srv_host) { 25813498266Sopenharmony_ci failf(data, "Error importing service name %s@%s", service, host); 25913498266Sopenharmony_ci return AUTH_ERROR; 26013498266Sopenharmony_ci } 26113498266Sopenharmony_ci service = srv_host; 26213498266Sopenharmony_ci continue; 26313498266Sopenharmony_ci } 26413498266Sopenharmony_ci /* We pass NULL as |output_name_type| to avoid a leak. */ 26513498266Sopenharmony_ci gss_display_name(&min, gssname, &output_buffer, NULL); 26613498266Sopenharmony_ci infof(data, "Trying against %s", (char *)output_buffer.value); 26713498266Sopenharmony_ci gssresp = GSS_C_NO_BUFFER; 26813498266Sopenharmony_ci *context = GSS_C_NO_CONTEXT; 26913498266Sopenharmony_ci 27013498266Sopenharmony_ci do { 27113498266Sopenharmony_ci /* Release the buffer at each iteration to avoid leaking: the first time 27213498266Sopenharmony_ci we are releasing the memory from gss_display_name. The last item is 27313498266Sopenharmony_ci taken care by a final gss_release_buffer. */ 27413498266Sopenharmony_ci gss_release_buffer(&min, &output_buffer); 27513498266Sopenharmony_ci ret = AUTH_OK; 27613498266Sopenharmony_ci maj = Curl_gss_init_sec_context(data, 27713498266Sopenharmony_ci &min, 27813498266Sopenharmony_ci context, 27913498266Sopenharmony_ci gssname, 28013498266Sopenharmony_ci &Curl_krb5_mech_oid, 28113498266Sopenharmony_ci &chan, 28213498266Sopenharmony_ci gssresp, 28313498266Sopenharmony_ci &output_buffer, 28413498266Sopenharmony_ci TRUE, 28513498266Sopenharmony_ci NULL); 28613498266Sopenharmony_ci 28713498266Sopenharmony_ci if(gssresp) { 28813498266Sopenharmony_ci free(_gssresp.value); 28913498266Sopenharmony_ci gssresp = NULL; 29013498266Sopenharmony_ci } 29113498266Sopenharmony_ci 29213498266Sopenharmony_ci if(GSS_ERROR(maj)) { 29313498266Sopenharmony_ci infof(data, "Error creating security context"); 29413498266Sopenharmony_ci ret = AUTH_ERROR; 29513498266Sopenharmony_ci break; 29613498266Sopenharmony_ci } 29713498266Sopenharmony_ci 29813498266Sopenharmony_ci if(output_buffer.length) { 29913498266Sopenharmony_ci char *cmd; 30013498266Sopenharmony_ci 30113498266Sopenharmony_ci result = Curl_base64_encode((char *)output_buffer.value, 30213498266Sopenharmony_ci output_buffer.length, &p, &base64_sz); 30313498266Sopenharmony_ci if(result) { 30413498266Sopenharmony_ci infof(data, "base64-encoding: %s", curl_easy_strerror(result)); 30513498266Sopenharmony_ci ret = AUTH_ERROR; 30613498266Sopenharmony_ci break; 30713498266Sopenharmony_ci } 30813498266Sopenharmony_ci 30913498266Sopenharmony_ci cmd = aprintf("ADAT %s", p); 31013498266Sopenharmony_ci if(cmd) 31113498266Sopenharmony_ci result = ftpsend(data, conn, cmd); 31213498266Sopenharmony_ci else 31313498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 31413498266Sopenharmony_ci 31513498266Sopenharmony_ci free(p); 31613498266Sopenharmony_ci free(cmd); 31713498266Sopenharmony_ci 31813498266Sopenharmony_ci if(result) { 31913498266Sopenharmony_ci ret = -2; 32013498266Sopenharmony_ci break; 32113498266Sopenharmony_ci } 32213498266Sopenharmony_ci 32313498266Sopenharmony_ci if(Curl_GetFTPResponse(data, &nread, NULL)) { 32413498266Sopenharmony_ci ret = -1; 32513498266Sopenharmony_ci break; 32613498266Sopenharmony_ci } 32713498266Sopenharmony_ci else { 32813498266Sopenharmony_ci struct pingpong *pp = &conn->proto.ftpc.pp; 32913498266Sopenharmony_ci size_t len = Curl_dyn_len(&pp->recvbuf); 33013498266Sopenharmony_ci p = Curl_dyn_ptr(&pp->recvbuf); 33113498266Sopenharmony_ci if((len < 4) || (p[0] != '2' && p[0] != '3')) { 33213498266Sopenharmony_ci infof(data, "Server didn't accept auth data"); 33313498266Sopenharmony_ci ret = AUTH_ERROR; 33413498266Sopenharmony_ci break; 33513498266Sopenharmony_ci } 33613498266Sopenharmony_ci } 33713498266Sopenharmony_ci 33813498266Sopenharmony_ci _gssresp.value = NULL; /* make sure it is initialized */ 33913498266Sopenharmony_ci p += 4; /* over '789 ' */ 34013498266Sopenharmony_ci p = strstr(p, "ADAT="); 34113498266Sopenharmony_ci if(p) { 34213498266Sopenharmony_ci result = Curl_base64_decode(p + 5, 34313498266Sopenharmony_ci (unsigned char **)&_gssresp.value, 34413498266Sopenharmony_ci &_gssresp.length); 34513498266Sopenharmony_ci if(result) { 34613498266Sopenharmony_ci failf(data, "base64-decoding: %s", curl_easy_strerror(result)); 34713498266Sopenharmony_ci ret = AUTH_CONTINUE; 34813498266Sopenharmony_ci break; 34913498266Sopenharmony_ci } 35013498266Sopenharmony_ci } 35113498266Sopenharmony_ci 35213498266Sopenharmony_ci gssresp = &_gssresp; 35313498266Sopenharmony_ci } 35413498266Sopenharmony_ci } while(maj == GSS_S_CONTINUE_NEEDED); 35513498266Sopenharmony_ci 35613498266Sopenharmony_ci gss_release_name(&min, &gssname); 35713498266Sopenharmony_ci gss_release_buffer(&min, &output_buffer); 35813498266Sopenharmony_ci 35913498266Sopenharmony_ci if(gssresp) 36013498266Sopenharmony_ci free(_gssresp.value); 36113498266Sopenharmony_ci 36213498266Sopenharmony_ci if(ret == AUTH_OK || service == srv_host) 36313498266Sopenharmony_ci return ret; 36413498266Sopenharmony_ci 36513498266Sopenharmony_ci service = srv_host; 36613498266Sopenharmony_ci } 36713498266Sopenharmony_ci return ret; 36813498266Sopenharmony_ci} 36913498266Sopenharmony_ci 37013498266Sopenharmony_cistatic void krb5_end(void *app_data) 37113498266Sopenharmony_ci{ 37213498266Sopenharmony_ci OM_uint32 min; 37313498266Sopenharmony_ci gss_ctx_id_t *context = app_data; 37413498266Sopenharmony_ci if(*context != GSS_C_NO_CONTEXT) { 37513498266Sopenharmony_ci OM_uint32 maj = gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER); 37613498266Sopenharmony_ci (void)maj; 37713498266Sopenharmony_ci DEBUGASSERT(maj == GSS_S_COMPLETE); 37813498266Sopenharmony_ci } 37913498266Sopenharmony_ci} 38013498266Sopenharmony_ci 38113498266Sopenharmony_cistatic const struct Curl_sec_client_mech Curl_krb5_client_mech = { 38213498266Sopenharmony_ci "GSSAPI", 38313498266Sopenharmony_ci sizeof(gss_ctx_id_t), 38413498266Sopenharmony_ci krb5_init, 38513498266Sopenharmony_ci krb5_auth, 38613498266Sopenharmony_ci krb5_end, 38713498266Sopenharmony_ci krb5_check_prot, 38813498266Sopenharmony_ci 38913498266Sopenharmony_ci krb5_encode, 39013498266Sopenharmony_ci krb5_decode 39113498266Sopenharmony_ci}; 39213498266Sopenharmony_ci 39313498266Sopenharmony_cistatic const struct { 39413498266Sopenharmony_ci unsigned char level; 39513498266Sopenharmony_ci const char *name; 39613498266Sopenharmony_ci} level_names[] = { 39713498266Sopenharmony_ci { PROT_CLEAR, "clear" }, 39813498266Sopenharmony_ci { PROT_SAFE, "safe" }, 39913498266Sopenharmony_ci { PROT_CONFIDENTIAL, "confidential" }, 40013498266Sopenharmony_ci { PROT_PRIVATE, "private" } 40113498266Sopenharmony_ci}; 40213498266Sopenharmony_ci 40313498266Sopenharmony_cistatic unsigned char name_to_level(const char *name) 40413498266Sopenharmony_ci{ 40513498266Sopenharmony_ci int i; 40613498266Sopenharmony_ci for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++) 40713498266Sopenharmony_ci if(curl_strequal(name, level_names[i].name)) 40813498266Sopenharmony_ci return level_names[i].level; 40913498266Sopenharmony_ci return PROT_NONE; 41013498266Sopenharmony_ci} 41113498266Sopenharmony_ci 41213498266Sopenharmony_ci/* Convert a protocol |level| to its char representation. 41313498266Sopenharmony_ci We take an int to catch programming mistakes. */ 41413498266Sopenharmony_cistatic char level_to_char(int level) 41513498266Sopenharmony_ci{ 41613498266Sopenharmony_ci switch(level) { 41713498266Sopenharmony_ci case PROT_CLEAR: 41813498266Sopenharmony_ci return 'C'; 41913498266Sopenharmony_ci case PROT_SAFE: 42013498266Sopenharmony_ci return 'S'; 42113498266Sopenharmony_ci case PROT_CONFIDENTIAL: 42213498266Sopenharmony_ci return 'E'; 42313498266Sopenharmony_ci case PROT_PRIVATE: 42413498266Sopenharmony_ci return 'P'; 42513498266Sopenharmony_ci case PROT_CMD: 42613498266Sopenharmony_ci default: 42713498266Sopenharmony_ci /* Those 2 cases should not be reached! */ 42813498266Sopenharmony_ci break; 42913498266Sopenharmony_ci } 43013498266Sopenharmony_ci DEBUGASSERT(0); 43113498266Sopenharmony_ci /* Default to the most secure alternative. */ 43213498266Sopenharmony_ci return 'P'; 43313498266Sopenharmony_ci} 43413498266Sopenharmony_ci 43513498266Sopenharmony_ci/* Send an FTP command defined by |message| and the optional arguments. The 43613498266Sopenharmony_ci function returns the ftp_code. If an error occurs, -1 is returned. */ 43713498266Sopenharmony_cistatic int ftp_send_command(struct Curl_easy *data, const char *message, ...) 43813498266Sopenharmony_ci CURL_PRINTF(2, 3); 43913498266Sopenharmony_ci 44013498266Sopenharmony_cistatic int ftp_send_command(struct Curl_easy *data, const char *message, ...) 44113498266Sopenharmony_ci{ 44213498266Sopenharmony_ci int ftp_code; 44313498266Sopenharmony_ci ssize_t nread = 0; 44413498266Sopenharmony_ci va_list args; 44513498266Sopenharmony_ci char print_buffer[50]; 44613498266Sopenharmony_ci 44713498266Sopenharmony_ci va_start(args, message); 44813498266Sopenharmony_ci mvsnprintf(print_buffer, sizeof(print_buffer), message, args); 44913498266Sopenharmony_ci va_end(args); 45013498266Sopenharmony_ci 45113498266Sopenharmony_ci if(ftpsend(data, data->conn, print_buffer)) { 45213498266Sopenharmony_ci ftp_code = -1; 45313498266Sopenharmony_ci } 45413498266Sopenharmony_ci else { 45513498266Sopenharmony_ci if(Curl_GetFTPResponse(data, &nread, &ftp_code)) 45613498266Sopenharmony_ci ftp_code = -1; 45713498266Sopenharmony_ci } 45813498266Sopenharmony_ci 45913498266Sopenharmony_ci (void)nread; /* Unused */ 46013498266Sopenharmony_ci return ftp_code; 46113498266Sopenharmony_ci} 46213498266Sopenharmony_ci 46313498266Sopenharmony_ci/* Read |len| from the socket |fd| and store it in |to|. Return a CURLcode 46413498266Sopenharmony_ci saying whether an error occurred or CURLE_OK if |len| was read. */ 46513498266Sopenharmony_cistatic CURLcode 46613498266Sopenharmony_cisocket_read(struct Curl_easy *data, int sockindex, void *to, size_t len) 46713498266Sopenharmony_ci{ 46813498266Sopenharmony_ci char *to_p = to; 46913498266Sopenharmony_ci CURLcode result; 47013498266Sopenharmony_ci ssize_t nread = 0; 47113498266Sopenharmony_ci 47213498266Sopenharmony_ci while(len > 0) { 47313498266Sopenharmony_ci nread = Curl_conn_recv(data, sockindex, to_p, len, &result); 47413498266Sopenharmony_ci if(nread > 0) { 47513498266Sopenharmony_ci len -= nread; 47613498266Sopenharmony_ci to_p += nread; 47713498266Sopenharmony_ci } 47813498266Sopenharmony_ci else { 47913498266Sopenharmony_ci if(result == CURLE_AGAIN) 48013498266Sopenharmony_ci continue; 48113498266Sopenharmony_ci return result; 48213498266Sopenharmony_ci } 48313498266Sopenharmony_ci } 48413498266Sopenharmony_ci return CURLE_OK; 48513498266Sopenharmony_ci} 48613498266Sopenharmony_ci 48713498266Sopenharmony_ci 48813498266Sopenharmony_ci/* Write |len| bytes from the buffer |to| to the socket |fd|. Return a 48913498266Sopenharmony_ci CURLcode saying whether an error occurred or CURLE_OK if |len| was 49013498266Sopenharmony_ci written. */ 49113498266Sopenharmony_cistatic CURLcode 49213498266Sopenharmony_cisocket_write(struct Curl_easy *data, int sockindex, const void *to, 49313498266Sopenharmony_ci size_t len) 49413498266Sopenharmony_ci{ 49513498266Sopenharmony_ci const char *to_p = to; 49613498266Sopenharmony_ci CURLcode result; 49713498266Sopenharmony_ci ssize_t written; 49813498266Sopenharmony_ci 49913498266Sopenharmony_ci while(len > 0) { 50013498266Sopenharmony_ci written = Curl_conn_send(data, sockindex, to_p, len, &result); 50113498266Sopenharmony_ci if(written > 0) { 50213498266Sopenharmony_ci len -= written; 50313498266Sopenharmony_ci to_p += written; 50413498266Sopenharmony_ci } 50513498266Sopenharmony_ci else { 50613498266Sopenharmony_ci if(result == CURLE_AGAIN) 50713498266Sopenharmony_ci continue; 50813498266Sopenharmony_ci return result; 50913498266Sopenharmony_ci } 51013498266Sopenharmony_ci } 51113498266Sopenharmony_ci return CURLE_OK; 51213498266Sopenharmony_ci} 51313498266Sopenharmony_ci 51413498266Sopenharmony_cistatic CURLcode read_data(struct Curl_easy *data, int sockindex, 51513498266Sopenharmony_ci struct krb5buffer *buf) 51613498266Sopenharmony_ci{ 51713498266Sopenharmony_ci struct connectdata *conn = data->conn; 51813498266Sopenharmony_ci int len; 51913498266Sopenharmony_ci CURLcode result; 52013498266Sopenharmony_ci int nread; 52113498266Sopenharmony_ci 52213498266Sopenharmony_ci result = socket_read(data, sockindex, &len, sizeof(len)); 52313498266Sopenharmony_ci if(result) 52413498266Sopenharmony_ci return result; 52513498266Sopenharmony_ci 52613498266Sopenharmony_ci if(len) { 52713498266Sopenharmony_ci /* only realloc if there was a length */ 52813498266Sopenharmony_ci len = ntohl(len); 52913498266Sopenharmony_ci if(len > CURL_MAX_INPUT_LENGTH) 53013498266Sopenharmony_ci len = 0; 53113498266Sopenharmony_ci else 53213498266Sopenharmony_ci buf->data = Curl_saferealloc(buf->data, len); 53313498266Sopenharmony_ci } 53413498266Sopenharmony_ci if(!len || !buf->data) 53513498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 53613498266Sopenharmony_ci 53713498266Sopenharmony_ci result = socket_read(data, sockindex, buf->data, len); 53813498266Sopenharmony_ci if(result) 53913498266Sopenharmony_ci return result; 54013498266Sopenharmony_ci nread = conn->mech->decode(conn->app_data, buf->data, len, 54113498266Sopenharmony_ci conn->data_prot, conn); 54213498266Sopenharmony_ci if(nread < 0) 54313498266Sopenharmony_ci return CURLE_RECV_ERROR; 54413498266Sopenharmony_ci buf->size = (size_t)nread; 54513498266Sopenharmony_ci buf->index = 0; 54613498266Sopenharmony_ci return CURLE_OK; 54713498266Sopenharmony_ci} 54813498266Sopenharmony_ci 54913498266Sopenharmony_cistatic size_t 55013498266Sopenharmony_cibuffer_read(struct krb5buffer *buf, void *data, size_t len) 55113498266Sopenharmony_ci{ 55213498266Sopenharmony_ci if(buf->size - buf->index < len) 55313498266Sopenharmony_ci len = buf->size - buf->index; 55413498266Sopenharmony_ci memcpy(data, (char *)buf->data + buf->index, len); 55513498266Sopenharmony_ci buf->index += len; 55613498266Sopenharmony_ci return len; 55713498266Sopenharmony_ci} 55813498266Sopenharmony_ci 55913498266Sopenharmony_ci/* Matches Curl_recv signature */ 56013498266Sopenharmony_cistatic ssize_t sec_recv(struct Curl_easy *data, int sockindex, 56113498266Sopenharmony_ci char *buffer, size_t len, CURLcode *err) 56213498266Sopenharmony_ci{ 56313498266Sopenharmony_ci size_t bytes_read; 56413498266Sopenharmony_ci size_t total_read = 0; 56513498266Sopenharmony_ci struct connectdata *conn = data->conn; 56613498266Sopenharmony_ci 56713498266Sopenharmony_ci *err = CURLE_OK; 56813498266Sopenharmony_ci 56913498266Sopenharmony_ci /* Handle clear text response. */ 57013498266Sopenharmony_ci if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR) 57113498266Sopenharmony_ci return Curl_conn_recv(data, sockindex, buffer, len, err); 57213498266Sopenharmony_ci 57313498266Sopenharmony_ci if(conn->in_buffer.eof_flag) { 57413498266Sopenharmony_ci conn->in_buffer.eof_flag = 0; 57513498266Sopenharmony_ci return 0; 57613498266Sopenharmony_ci } 57713498266Sopenharmony_ci 57813498266Sopenharmony_ci bytes_read = buffer_read(&conn->in_buffer, buffer, len); 57913498266Sopenharmony_ci len -= bytes_read; 58013498266Sopenharmony_ci total_read += bytes_read; 58113498266Sopenharmony_ci buffer += bytes_read; 58213498266Sopenharmony_ci 58313498266Sopenharmony_ci while(len > 0) { 58413498266Sopenharmony_ci if(read_data(data, sockindex, &conn->in_buffer)) 58513498266Sopenharmony_ci return -1; 58613498266Sopenharmony_ci if(conn->in_buffer.size == 0) { 58713498266Sopenharmony_ci if(bytes_read > 0) 58813498266Sopenharmony_ci conn->in_buffer.eof_flag = 1; 58913498266Sopenharmony_ci return bytes_read; 59013498266Sopenharmony_ci } 59113498266Sopenharmony_ci bytes_read = buffer_read(&conn->in_buffer, buffer, len); 59213498266Sopenharmony_ci len -= bytes_read; 59313498266Sopenharmony_ci total_read += bytes_read; 59413498266Sopenharmony_ci buffer += bytes_read; 59513498266Sopenharmony_ci } 59613498266Sopenharmony_ci return total_read; 59713498266Sopenharmony_ci} 59813498266Sopenharmony_ci 59913498266Sopenharmony_ci/* Send |length| bytes from |from| to the |fd| socket taking care of encoding 60013498266Sopenharmony_ci and negotiating with the server. |from| can be NULL. */ 60113498266Sopenharmony_cistatic void do_sec_send(struct Curl_easy *data, struct connectdata *conn, 60213498266Sopenharmony_ci curl_socket_t fd, const char *from, int length) 60313498266Sopenharmony_ci{ 60413498266Sopenharmony_ci int bytes, htonl_bytes; /* 32-bit integers for htonl */ 60513498266Sopenharmony_ci char *buffer = NULL; 60613498266Sopenharmony_ci char *cmd_buffer; 60713498266Sopenharmony_ci size_t cmd_size = 0; 60813498266Sopenharmony_ci CURLcode error; 60913498266Sopenharmony_ci enum protection_level prot_level = conn->data_prot; 61013498266Sopenharmony_ci bool iscmd = (prot_level == PROT_CMD)?TRUE:FALSE; 61113498266Sopenharmony_ci 61213498266Sopenharmony_ci DEBUGASSERT(prot_level > PROT_NONE && prot_level < PROT_LAST); 61313498266Sopenharmony_ci 61413498266Sopenharmony_ci if(iscmd) { 61513498266Sopenharmony_ci if(!strncmp(from, "PASS ", 5) || !strncmp(from, "ACCT ", 5)) 61613498266Sopenharmony_ci prot_level = PROT_PRIVATE; 61713498266Sopenharmony_ci else 61813498266Sopenharmony_ci prot_level = conn->command_prot; 61913498266Sopenharmony_ci } 62013498266Sopenharmony_ci bytes = conn->mech->encode(conn->app_data, from, length, prot_level, 62113498266Sopenharmony_ci (void **)&buffer); 62213498266Sopenharmony_ci if(!buffer || bytes <= 0) 62313498266Sopenharmony_ci return; /* error */ 62413498266Sopenharmony_ci 62513498266Sopenharmony_ci if(iscmd) { 62613498266Sopenharmony_ci error = Curl_base64_encode(buffer, curlx_sitouz(bytes), 62713498266Sopenharmony_ci &cmd_buffer, &cmd_size); 62813498266Sopenharmony_ci if(error) { 62913498266Sopenharmony_ci free(buffer); 63013498266Sopenharmony_ci return; /* error */ 63113498266Sopenharmony_ci } 63213498266Sopenharmony_ci if(cmd_size > 0) { 63313498266Sopenharmony_ci static const char *enc = "ENC "; 63413498266Sopenharmony_ci static const char *mic = "MIC "; 63513498266Sopenharmony_ci if(prot_level == PROT_PRIVATE) 63613498266Sopenharmony_ci socket_write(data, fd, enc, 4); 63713498266Sopenharmony_ci else 63813498266Sopenharmony_ci socket_write(data, fd, mic, 4); 63913498266Sopenharmony_ci 64013498266Sopenharmony_ci socket_write(data, fd, cmd_buffer, cmd_size); 64113498266Sopenharmony_ci socket_write(data, fd, "\r\n", 2); 64213498266Sopenharmony_ci infof(data, "Send: %s%s", prot_level == PROT_PRIVATE?enc:mic, 64313498266Sopenharmony_ci cmd_buffer); 64413498266Sopenharmony_ci free(cmd_buffer); 64513498266Sopenharmony_ci } 64613498266Sopenharmony_ci } 64713498266Sopenharmony_ci else { 64813498266Sopenharmony_ci htonl_bytes = htonl(bytes); 64913498266Sopenharmony_ci socket_write(data, fd, &htonl_bytes, sizeof(htonl_bytes)); 65013498266Sopenharmony_ci socket_write(data, fd, buffer, curlx_sitouz(bytes)); 65113498266Sopenharmony_ci } 65213498266Sopenharmony_ci free(buffer); 65313498266Sopenharmony_ci} 65413498266Sopenharmony_ci 65513498266Sopenharmony_cistatic ssize_t sec_write(struct Curl_easy *data, struct connectdata *conn, 65613498266Sopenharmony_ci curl_socket_t fd, const char *buffer, size_t length) 65713498266Sopenharmony_ci{ 65813498266Sopenharmony_ci ssize_t tx = 0, len = conn->buffer_size; 65913498266Sopenharmony_ci 66013498266Sopenharmony_ci if(len <= 0) 66113498266Sopenharmony_ci len = length; 66213498266Sopenharmony_ci while(length) { 66313498266Sopenharmony_ci if(length < (size_t)len) 66413498266Sopenharmony_ci len = length; 66513498266Sopenharmony_ci 66613498266Sopenharmony_ci do_sec_send(data, conn, fd, buffer, curlx_sztosi(len)); 66713498266Sopenharmony_ci length -= len; 66813498266Sopenharmony_ci buffer += len; 66913498266Sopenharmony_ci tx += len; 67013498266Sopenharmony_ci } 67113498266Sopenharmony_ci return tx; 67213498266Sopenharmony_ci} 67313498266Sopenharmony_ci 67413498266Sopenharmony_ci/* Matches Curl_send signature */ 67513498266Sopenharmony_cistatic ssize_t sec_send(struct Curl_easy *data, int sockindex, 67613498266Sopenharmony_ci const void *buffer, size_t len, CURLcode *err) 67713498266Sopenharmony_ci{ 67813498266Sopenharmony_ci struct connectdata *conn = data->conn; 67913498266Sopenharmony_ci curl_socket_t fd = conn->sock[sockindex]; 68013498266Sopenharmony_ci *err = CURLE_OK; 68113498266Sopenharmony_ci return sec_write(data, conn, fd, buffer, len); 68213498266Sopenharmony_ci} 68313498266Sopenharmony_ci 68413498266Sopenharmony_ciint Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn, 68513498266Sopenharmony_ci char *buffer, enum protection_level level) 68613498266Sopenharmony_ci{ 68713498266Sopenharmony_ci /* decoded_len should be size_t or ssize_t but conn->mech->decode returns an 68813498266Sopenharmony_ci int */ 68913498266Sopenharmony_ci int decoded_len; 69013498266Sopenharmony_ci char *buf; 69113498266Sopenharmony_ci int ret_code = 0; 69213498266Sopenharmony_ci size_t decoded_sz = 0; 69313498266Sopenharmony_ci CURLcode error; 69413498266Sopenharmony_ci 69513498266Sopenharmony_ci (void) data; 69613498266Sopenharmony_ci 69713498266Sopenharmony_ci if(!conn->mech) 69813498266Sopenharmony_ci /* not initialized, return error */ 69913498266Sopenharmony_ci return -1; 70013498266Sopenharmony_ci 70113498266Sopenharmony_ci DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); 70213498266Sopenharmony_ci 70313498266Sopenharmony_ci error = Curl_base64_decode(buffer + 4, (unsigned char **)&buf, &decoded_sz); 70413498266Sopenharmony_ci if(error || decoded_sz == 0) 70513498266Sopenharmony_ci return -1; 70613498266Sopenharmony_ci 70713498266Sopenharmony_ci if(decoded_sz > (size_t)INT_MAX) { 70813498266Sopenharmony_ci free(buf); 70913498266Sopenharmony_ci return -1; 71013498266Sopenharmony_ci } 71113498266Sopenharmony_ci decoded_len = curlx_uztosi(decoded_sz); 71213498266Sopenharmony_ci 71313498266Sopenharmony_ci decoded_len = conn->mech->decode(conn->app_data, buf, decoded_len, 71413498266Sopenharmony_ci level, conn); 71513498266Sopenharmony_ci if(decoded_len <= 0) { 71613498266Sopenharmony_ci free(buf); 71713498266Sopenharmony_ci return -1; 71813498266Sopenharmony_ci } 71913498266Sopenharmony_ci 72013498266Sopenharmony_ci { 72113498266Sopenharmony_ci buf[decoded_len] = '\n'; 72213498266Sopenharmony_ci Curl_debug(data, CURLINFO_HEADER_IN, buf, decoded_len + 1); 72313498266Sopenharmony_ci } 72413498266Sopenharmony_ci 72513498266Sopenharmony_ci buf[decoded_len] = '\0'; 72613498266Sopenharmony_ci if(decoded_len <= 3) 72713498266Sopenharmony_ci /* suspiciously short */ 72813498266Sopenharmony_ci return 0; 72913498266Sopenharmony_ci 73013498266Sopenharmony_ci if(buf[3] != '-') 73113498266Sopenharmony_ci ret_code = atoi(buf); 73213498266Sopenharmony_ci 73313498266Sopenharmony_ci if(buf[decoded_len - 1] == '\n') 73413498266Sopenharmony_ci buf[decoded_len - 1] = '\0'; 73513498266Sopenharmony_ci strcpy(buffer, buf); 73613498266Sopenharmony_ci free(buf); 73713498266Sopenharmony_ci return ret_code; 73813498266Sopenharmony_ci} 73913498266Sopenharmony_ci 74013498266Sopenharmony_cistatic int sec_set_protection_level(struct Curl_easy *data) 74113498266Sopenharmony_ci{ 74213498266Sopenharmony_ci int code; 74313498266Sopenharmony_ci struct connectdata *conn = data->conn; 74413498266Sopenharmony_ci unsigned char level = conn->request_data_prot; 74513498266Sopenharmony_ci 74613498266Sopenharmony_ci DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); 74713498266Sopenharmony_ci 74813498266Sopenharmony_ci if(!conn->sec_complete) { 74913498266Sopenharmony_ci infof(data, "Trying to change the protection level after the" 75013498266Sopenharmony_ci " completion of the data exchange."); 75113498266Sopenharmony_ci return -1; 75213498266Sopenharmony_ci } 75313498266Sopenharmony_ci 75413498266Sopenharmony_ci /* Bail out if we try to set up the same level */ 75513498266Sopenharmony_ci if(conn->data_prot == level) 75613498266Sopenharmony_ci return 0; 75713498266Sopenharmony_ci 75813498266Sopenharmony_ci if(level) { 75913498266Sopenharmony_ci char *pbsz; 76013498266Sopenharmony_ci unsigned int buffer_size = 1 << 20; /* 1048576 */ 76113498266Sopenharmony_ci struct pingpong *pp = &conn->proto.ftpc.pp; 76213498266Sopenharmony_ci char *line; 76313498266Sopenharmony_ci 76413498266Sopenharmony_ci code = ftp_send_command(data, "PBSZ %u", buffer_size); 76513498266Sopenharmony_ci if(code < 0) 76613498266Sopenharmony_ci return -1; 76713498266Sopenharmony_ci 76813498266Sopenharmony_ci if(code/100 != 2) { 76913498266Sopenharmony_ci failf(data, "Failed to set the protection's buffer size."); 77013498266Sopenharmony_ci return -1; 77113498266Sopenharmony_ci } 77213498266Sopenharmony_ci conn->buffer_size = buffer_size; 77313498266Sopenharmony_ci 77413498266Sopenharmony_ci line = Curl_dyn_ptr(&pp->recvbuf); 77513498266Sopenharmony_ci pbsz = strstr(line, "PBSZ="); 77613498266Sopenharmony_ci if(pbsz) { 77713498266Sopenharmony_ci /* stick to default value if the check fails */ 77813498266Sopenharmony_ci if(ISDIGIT(pbsz[5])) 77913498266Sopenharmony_ci buffer_size = atoi(&pbsz[5]); 78013498266Sopenharmony_ci if(buffer_size < conn->buffer_size) 78113498266Sopenharmony_ci conn->buffer_size = buffer_size; 78213498266Sopenharmony_ci } 78313498266Sopenharmony_ci } 78413498266Sopenharmony_ci 78513498266Sopenharmony_ci /* Now try to negotiate the protection level. */ 78613498266Sopenharmony_ci code = ftp_send_command(data, "PROT %c", level_to_char(level)); 78713498266Sopenharmony_ci 78813498266Sopenharmony_ci if(code < 0) 78913498266Sopenharmony_ci return -1; 79013498266Sopenharmony_ci 79113498266Sopenharmony_ci if(code/100 != 2) { 79213498266Sopenharmony_ci failf(data, "Failed to set the protection level."); 79313498266Sopenharmony_ci return -1; 79413498266Sopenharmony_ci } 79513498266Sopenharmony_ci 79613498266Sopenharmony_ci conn->data_prot = level; 79713498266Sopenharmony_ci if(level == PROT_PRIVATE) 79813498266Sopenharmony_ci conn->command_prot = level; 79913498266Sopenharmony_ci 80013498266Sopenharmony_ci return 0; 80113498266Sopenharmony_ci} 80213498266Sopenharmony_ci 80313498266Sopenharmony_ciint 80413498266Sopenharmony_ciCurl_sec_request_prot(struct connectdata *conn, const char *level) 80513498266Sopenharmony_ci{ 80613498266Sopenharmony_ci unsigned char l = name_to_level(level); 80713498266Sopenharmony_ci if(l == PROT_NONE) 80813498266Sopenharmony_ci return -1; 80913498266Sopenharmony_ci DEBUGASSERT(l > PROT_NONE && l < PROT_LAST); 81013498266Sopenharmony_ci conn->request_data_prot = l; 81113498266Sopenharmony_ci return 0; 81213498266Sopenharmony_ci} 81313498266Sopenharmony_ci 81413498266Sopenharmony_cistatic CURLcode choose_mech(struct Curl_easy *data, struct connectdata *conn) 81513498266Sopenharmony_ci{ 81613498266Sopenharmony_ci int ret; 81713498266Sopenharmony_ci void *tmp_allocation; 81813498266Sopenharmony_ci const struct Curl_sec_client_mech *mech = &Curl_krb5_client_mech; 81913498266Sopenharmony_ci 82013498266Sopenharmony_ci tmp_allocation = realloc(conn->app_data, mech->size); 82113498266Sopenharmony_ci if(!tmp_allocation) { 82213498266Sopenharmony_ci failf(data, "Failed realloc of size %zu", mech->size); 82313498266Sopenharmony_ci mech = NULL; 82413498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 82513498266Sopenharmony_ci } 82613498266Sopenharmony_ci conn->app_data = tmp_allocation; 82713498266Sopenharmony_ci 82813498266Sopenharmony_ci if(mech->init) { 82913498266Sopenharmony_ci ret = mech->init(conn->app_data); 83013498266Sopenharmony_ci if(ret) { 83113498266Sopenharmony_ci infof(data, "Failed initialization for %s. Skipping it.", 83213498266Sopenharmony_ci mech->name); 83313498266Sopenharmony_ci return CURLE_FAILED_INIT; 83413498266Sopenharmony_ci } 83513498266Sopenharmony_ci } 83613498266Sopenharmony_ci 83713498266Sopenharmony_ci infof(data, "Trying mechanism %s...", mech->name); 83813498266Sopenharmony_ci ret = ftp_send_command(data, "AUTH %s", mech->name); 83913498266Sopenharmony_ci if(ret < 0) 84013498266Sopenharmony_ci return CURLE_COULDNT_CONNECT; 84113498266Sopenharmony_ci 84213498266Sopenharmony_ci if(ret/100 != 3) { 84313498266Sopenharmony_ci switch(ret) { 84413498266Sopenharmony_ci case 504: 84513498266Sopenharmony_ci infof(data, "Mechanism %s is not supported by the server (server " 84613498266Sopenharmony_ci "returned ftp code: 504).", mech->name); 84713498266Sopenharmony_ci break; 84813498266Sopenharmony_ci case 534: 84913498266Sopenharmony_ci infof(data, "Mechanism %s was rejected by the server (server returned " 85013498266Sopenharmony_ci "ftp code: 534).", mech->name); 85113498266Sopenharmony_ci break; 85213498266Sopenharmony_ci default: 85313498266Sopenharmony_ci if(ret/100 == 5) { 85413498266Sopenharmony_ci infof(data, "server does not support the security extensions"); 85513498266Sopenharmony_ci return CURLE_USE_SSL_FAILED; 85613498266Sopenharmony_ci } 85713498266Sopenharmony_ci break; 85813498266Sopenharmony_ci } 85913498266Sopenharmony_ci return CURLE_LOGIN_DENIED; 86013498266Sopenharmony_ci } 86113498266Sopenharmony_ci 86213498266Sopenharmony_ci /* Authenticate */ 86313498266Sopenharmony_ci ret = mech->auth(conn->app_data, data, conn); 86413498266Sopenharmony_ci 86513498266Sopenharmony_ci if(ret != AUTH_CONTINUE) { 86613498266Sopenharmony_ci if(ret != AUTH_OK) { 86713498266Sopenharmony_ci /* Mechanism has dumped the error to stderr, don't error here. */ 86813498266Sopenharmony_ci return CURLE_USE_SSL_FAILED; 86913498266Sopenharmony_ci } 87013498266Sopenharmony_ci DEBUGASSERT(ret == AUTH_OK); 87113498266Sopenharmony_ci 87213498266Sopenharmony_ci conn->mech = mech; 87313498266Sopenharmony_ci conn->sec_complete = 1; 87413498266Sopenharmony_ci conn->recv[FIRSTSOCKET] = sec_recv; 87513498266Sopenharmony_ci conn->send[FIRSTSOCKET] = sec_send; 87613498266Sopenharmony_ci conn->recv[SECONDARYSOCKET] = sec_recv; 87713498266Sopenharmony_ci conn->send[SECONDARYSOCKET] = sec_send; 87813498266Sopenharmony_ci conn->command_prot = PROT_SAFE; 87913498266Sopenharmony_ci /* Set the requested protection level */ 88013498266Sopenharmony_ci /* BLOCKING */ 88113498266Sopenharmony_ci (void)sec_set_protection_level(data); 88213498266Sopenharmony_ci } 88313498266Sopenharmony_ci 88413498266Sopenharmony_ci return CURLE_OK; 88513498266Sopenharmony_ci} 88613498266Sopenharmony_ci 88713498266Sopenharmony_ciCURLcode 88813498266Sopenharmony_ciCurl_sec_login(struct Curl_easy *data, struct connectdata *conn) 88913498266Sopenharmony_ci{ 89013498266Sopenharmony_ci return choose_mech(data, conn); 89113498266Sopenharmony_ci} 89213498266Sopenharmony_ci 89313498266Sopenharmony_ci 89413498266Sopenharmony_civoid 89513498266Sopenharmony_ciCurl_sec_end(struct connectdata *conn) 89613498266Sopenharmony_ci{ 89713498266Sopenharmony_ci if(conn->mech && conn->mech->end) 89813498266Sopenharmony_ci conn->mech->end(conn->app_data); 89913498266Sopenharmony_ci free(conn->app_data); 90013498266Sopenharmony_ci conn->app_data = NULL; 90113498266Sopenharmony_ci if(conn->in_buffer.data) { 90213498266Sopenharmony_ci free(conn->in_buffer.data); 90313498266Sopenharmony_ci conn->in_buffer.data = NULL; 90413498266Sopenharmony_ci conn->in_buffer.size = 0; 90513498266Sopenharmony_ci conn->in_buffer.index = 0; 90613498266Sopenharmony_ci conn->in_buffer.eof_flag = 0; 90713498266Sopenharmony_ci } 90813498266Sopenharmony_ci conn->sec_complete = 0; 90913498266Sopenharmony_ci conn->data_prot = PROT_CLEAR; 91013498266Sopenharmony_ci conn->mech = NULL; 91113498266Sopenharmony_ci} 91213498266Sopenharmony_ci 91313498266Sopenharmony_ci#endif /* HAVE_GSSAPI && !CURL_DISABLE_FTP */ 914