CVE-2016-8619
Description
A double-free vulnerability in curl's Kerberos authentication handler (read_data()) before 7.51.0 can be triggered by a malicious server, leading to denial of service or potential code execution.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A double-free vulnerability in curl's Kerberos authentication handler (read_data()) before 7.51.0 can be triggered by a malicious server, leading to denial of service or potential code execution.
Vulnerability
The vulnerability exists in the read_data() function in security.c in curl before version 7.51.0. When handling Kerberos authentication, the function reads length fields from the socket. It fails to ensure that the length parameter passed to realloc() is not set to 0. On some implementations (notably glibc), calling realloc() with a size of 0 causes the memory to be freed and a NULL pointer to be returned, which differs from the typical behavior where realloc() returns NULL only on failure without freeing the original block [4]. This discrepancy leads to a double-free in curl's error handling path because the code assumes that a NULL return indicates a failure that still leaves the original allocation intact [4].
Exploitation
An attacker must control a server that a victim client connects to using Kerberos authentication (e.g., via --krb in curl or a libcurl-based application). When the server sends a crafted or malformed response with a zero-length field, curl's read_data() calls realloc() with size 0. The resulting NULL pointer is then freed again in the error path, causing a double-free [4]. No authentication or prior trust relationship is needed beyond the attacker's ability to act as the server in the Kerberos exchange.
Impact
A successful double-free can lead to denial of service (curl crash) or, under favorable heap conditions, arbitrary code execution in the context of the client process. The vulnerability is remotely triggerable without credentials if the attacker controls the server endpoint [1][4]. Red Hat rated the issue as Important for certain products [2].
Mitigation
Fixed in curl version 7.51.0 (released 2016-11-02) [4]. Users should upgrade to this version or later. Red Hat issued patches via RHSA-2018:3558 (httpd24-curl) [1] and RHSA-2018:2486 (JBoss Core Services) [2]. Tenable LCE addressed it in version 4.8.2 [3]. No workaround is available if Kerberos functionality is required; disabling Kerberos authentication in curl can eliminate the attack surface.
AI Insight generated on May 24, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
12- osv-coords10 versionspkg:rpm/opensuse/curl&distro=openSUSE%20Tumbleweedpkg:rpm/suse/curl&distro=SUSE%20Linux%20Enterprise%20Desktop%2012%20SP1pkg:rpm/suse/curl&distro=SUSE%20Linux%20Enterprise%20Server%2011%20SP4pkg:rpm/suse/curl&distro=SUSE%20Linux%20Enterprise%20Server%2012%20SP1pkg:rpm/suse/curl&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2011%20SP4pkg:rpm/suse/curl&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2012%20SP1pkg:rpm/suse/curl&distro=SUSE%20Linux%20Enterprise%20Software%20Development%20Kit%2011%20SP4pkg:rpm/suse/curl&distro=SUSE%20Linux%20Enterprise%20Software%20Development%20Kit%2012%20SP1pkg:rpm/suse/curl&distro=SUSE%20Studio%20Onsite%201.3pkg:rpm/suse/curl-openssl1&distro=SUSE%20Linux%20Enterprise%20Server%2011-SECURITY
< 7.51.0-1.1+ 9 more
- (no CPE)range: < 7.51.0-1.1
- (no CPE)range: < 7.37.0-31.1
- (no CPE)range: < 7.19.7-1.64.1
- (no CPE)range: < 7.37.0-31.1
- (no CPE)range: < 7.19.7-1.64.1
- (no CPE)range: < 7.37.0-31.1
- (no CPE)range: < 7.19.7-1.64.1
- (no CPE)range: < 7.37.0-31.1
- (no CPE)range: < 7.19.7-1.20.47.2
- (no CPE)range: < 7.19.7-1.64.1
- The Curl Project/curlv5Range: 7.51.0
Patches
31 file changed · +23 −1
docs/THANKS+23 −1 modified@@ -20,6 +20,7 @@ Adriano Meirelles Ajit Dhumale Aki Koskinen Akos Pasztory +Akshay Vernekar Alain Danteny Alan Pinstein Albert Chin-A-Young @@ -48,6 +49,7 @@ Alexander Krasnostavsky Alexander Lazic Alexander Pepper Alexander Peslyak +Alexander Sinditskiy Alexander Traud Alexander Zhuravlev Alexey Borzov @@ -77,10 +79,12 @@ Andreas Ntaflos Andreas Olsson Andreas Rieke Andreas Schuldei +Andreas Streichardt Andreas Wurf Andrei Benea Andrei Cipu Andrei Kurushin +Andrei Sedoi Andrej E Baranov Andrew Benham Andrew Biggs @@ -215,6 +219,7 @@ Chris Smowton Chris Young Christian Fillion Christian Grothoff +Christian Heimes Christian Hägele Christian Krause Christian Kurz @@ -291,6 +296,7 @@ Daniel Theron Daniel at touchtunes Darryl House Darshan Mody +Darío Hereñú Dave Dribin Dave Halbakken Dave Hamilton @@ -496,6 +502,7 @@ Greg Morse Greg Onufer Greg Pratt Greg Zavertnik +Gregory Szorc Grigory Entin Guenole Bescon Guenter Knauf @@ -817,6 +824,7 @@ Luke Call Luke Dashjr Luo Jinghua Luong Dinh Dung +Luật Nguyễn Lyndon Hill Maciej Karpiuk Maciej Puzio @@ -869,12 +877,13 @@ Marquis de Muesli Martijn Koster Martin C. Martin Martin Drasar +Martin Frodl Martin Hager Martin Hedenfalk Martin Jansen Martin Lemke Martin Skinner -Martin Storsjo +Martin Storsjö Martin Vejnár Marty Kuhrt Maruko @@ -948,6 +957,7 @@ Mike Power Mike Protts Mike Revi Miklos Nemeth +Miloš Ljumović Miroslav Franc Miroslav Spousta Mitz Wark @@ -1031,6 +1041,7 @@ Pau Garcia i Quiles Paul Donohue Paul Harrington Paul Howarth +Paul Joyce Paul Marks Paul Marquis Paul Moore @@ -1101,6 +1112,7 @@ Rafaël Carré Rainer Canavan Rainer Jung Rainer Koenig +Rainer Müller Rajesh Naganathan Rajkumar Mandal Ralf S. Engelschall @@ -1117,6 +1129,7 @@ Razvan Cojocaru Reinhard Max Reinout van Schouwen Remi Gacogne +Remo E Renato Botelho Renaud Chaillat Renaud Duhaut @@ -1145,6 +1158,7 @@ Richard Silverman Richard van den Berg Rick Jones Rick Richardson +Rider Linden Rob Crittenden Rob Davies Rob Jones @@ -1216,9 +1230,11 @@ Scott Cantor Scott Davis Scott McCreary Sean Boudreau +Sebastian Mundry Sebastian Pohlschmidt Sebastian Rasmussen Senthil Raja Velu +Sergei Kuzmin Sergei Nikulov Sergey Tatarincev Sergio Ballestrero @@ -1260,6 +1276,7 @@ Stefan Tomanek Stefan Ulrich Steinar H. Gunderson Stephan Bergmann +Stephen Brokenshire Stephen Collyer Stephen Kick Stephen More @@ -1330,6 +1347,7 @@ Tobias Stoeckmann Toby Peterson Todd A Ouska Todd Kulesza +Todd Short Todd Vierling Tom Benoist Tom Donovan @@ -1357,6 +1375,7 @@ Toni Moreno Tony Kelman Toon Verwaest Tor Arntsen +Torben Dannhauer Torsten Foertsch Toshio Kuratomi Toshiyuki Maezawa @@ -1372,6 +1391,7 @@ Ulf Samuelsson Ulrich Doehner Ulrich Telle Ulrich Zadow +Valentin David Venkat Akella Victor Snezhko Vijay Panghal @@ -1439,9 +1459,11 @@ dkjjr89 on github eXeC64 on github jveazey on github kreshano on github +lukaszgn on github marc-groundctl on github neex on github nk +nopjmp on github silveja1 on github swalkaus at yahoo.com tommink[at]post.pl
3d6460edeee2krb5: avoid realloc(0)
1 file changed · +6 −3
lib/security.c+6 −3 modified@@ -192,15 +192,18 @@ static CURLcode read_data(struct connectdata *conn, struct krb5buffer *buf) { int len; - void* tmp; + void *tmp = NULL; CURLcode result; result = socket_read(fd, &len, sizeof(len)); if(result) return result; - len = ntohl(len); - tmp = realloc(buf->data, len); + if(len) { + /* only realloc if there was a length */ + len = ntohl(len); + tmp = realloc(buf->data, len); + } if(tmp == NULL) return CURLE_OUT_OF_MEMORY;
def69c30879cnew for kerberos support
5 files changed · +1439 −0
lib/base64_krb.c+147 −0 added@@ -0,0 +1,147 @@ +/* + * Copyright (c) 1995 - 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <stdlib.h> +#include <string.h> +#include "base64_krb.h" + +static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static int pos(char c) +{ + char *p; + for(p = base64; *p; p++) + if(*p == c) + return p - base64; + return -1; +} + +#if 1 +int base64_encode(const void *data, int size, char **str) +{ + char *s, *p; + int i; + int c; + const unsigned char *q; + + p = s = (char*)malloc(size*4/3+4); + if (p == NULL) + return -1; + q = (const unsigned char*)data; + i=0; + for(i = 0; i < size;){ + c=q[i++]; + c*=256; + if(i < size) + c+=q[i]; + i++; + c*=256; + if(i < size) + c+=q[i]; + i++; + p[0]=base64[(c&0x00fc0000) >> 18]; + p[1]=base64[(c&0x0003f000) >> 12]; + p[2]=base64[(c&0x00000fc0) >> 6]; + p[3]=base64[(c&0x0000003f) >> 0]; + if(i > size) + p[3]='='; + if(i > size+1) + p[2]='='; + p+=4; + } + *p=0; + *str = s; + return strlen(s); +} +#endif + +int base64_decode(const char *str, void *data) +{ + const char *p; + unsigned char *q; + int c; + int x; + int done = 0; + q=(unsigned char*)data; + for(p=str; *p && !done; p+=4){ + x = pos(p[0]); + if(x >= 0) + c = x; + else{ + done = 3; + break; + } + c*=64; + + x = pos(p[1]); + if(x >= 0) + c += x; + else + return -1; + c*=64; + + if(p[2] == '=') + done++; + else{ + x = pos(p[2]); + if(x >= 0) + c += x; + else + return -1; + } + c*=64; + + if(p[3] == '=') + done++; + else{ + if(done) + return -1; + x = pos(p[3]); + if(x >= 0) + c += x; + else + return -1; + } + if(done < 3) + *q++=(c&0x00ff0000)>>16; + + if(done < 2) + *q++=(c&0x0000ff00)>>8; + if(done < 1) + *q++=(c&0x000000ff)>>0; + } + return q - (unsigned char*)data; +}
lib/base64_krb.h+47 −0 added@@ -0,0 +1,47 @@ +/* + * Copyright (c) 1995, 1996, 1997 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. + */ + +/* $Id$ */ + +#ifndef _BASE64_H_ +#define _BASE64_H_ + +#if 1 +int base64_encode(const void *data, int size, char **str); +#else +#define base64_encode(x,y,z) base64Encode(x,y,z) +#endif + +int base64_decode(const char *str, void *data); + +#endif
lib/krb4.c+471 −0 added@@ -0,0 +1,471 @@ +/* modified by Martin Hedenfalk <mhe@stacken.kth.se> for use in Curl + * last modified 2000-09-18 + */ + +/* + * 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" + +#ifdef KRB4 + +#include "security.h" +#include "base64_krb.h" +#include <stdlib.h> +#include <netdb.h> +#include <syslog.h> +#include <string.h> +#include <krb.h> + +#ifdef FTP_SERVER +#define LOCAL_ADDR ctrl_addr +#define REMOTE_ADDR his_addr +#else +/*#define LOCAL_ADDR myctladdr***/ +/*#define REMOTE_ADDR hisctladdr***/ +#endif + +/*extern struct sockaddr *LOCAL_ADDR, *REMOTE_ADDR;***/ + +#define LOCAL_ADDR (&local_addr) +#define REMOTE_ADDR (&conn->serv_addr) +#define myctladdr LOCAL_ADDR +#define hisctladdr REMOTE_ADDR + +static struct sockaddr_in local_addr; + +struct krb4_data { + des_cblock key; + des_key_schedule schedule; + char name[ANAME_SZ]; + char instance[INST_SZ]; + char realm[REALM_SZ]; +}; + +static int +krb4_check_prot(void *app_data, int level) +{ + if(level == prot_confidential) + return -1; + return 0; +} + +static int +krb4_decode(void *app_data, void *buf, int len, int level, + struct connectdata *conn) +{ + MSG_DAT m; + int e; + struct krb4_data *d = app_data; + + if(level == prot_safe) + e = krb_rd_safe(buf, len, &d->key, + (struct sockaddr_in *)REMOTE_ADDR, + (struct sockaddr_in *)LOCAL_ADDR, &m); + else + e = krb_rd_priv(buf, len, d->schedule, &d->key, + (struct sockaddr_in *)REMOTE_ADDR, + (struct sockaddr_in *)LOCAL_ADDR, &m); + if(e){ + syslog(LOG_ERR, "krb4_decode: %s", krb_get_err_text(e)); + return -1; + } + memmove(buf, m.app_data, m.app_length); + return m.app_length; +} + +static int +krb4_overhead(void *app_data, int level, int len) +{ + return 31; +} + +static int +krb4_encode(void *app_data, void *from, int length, int level, void **to, + struct connectdata *conn) +{ + struct krb4_data *d = app_data; + *to = malloc(length + 31); + if(level == prot_safe) + return krb_mk_safe(from, *to, length, &d->key, + (struct sockaddr_in *)LOCAL_ADDR, + (struct sockaddr_in *)REMOTE_ADDR); + else if(level == prot_private) + return krb_mk_priv(from, *to, length, d->schedule, &d->key, + (struct sockaddr_in *)LOCAL_ADDR, + (struct sockaddr_in *)REMOTE_ADDR); + else + return -1; +} + +#ifdef FTP_SERVER + +static int +krb4_adat(void *app_data, void *buf, size_t len) +{ + KTEXT_ST tkt; + AUTH_DAT auth_dat; + char *p; + int kerror; + u_int32_t cs; + char msg[35]; /* size of encrypted block */ + int tmp_len; + struct krb4_data *d = app_data; + char inst[INST_SZ]; + struct sockaddr_in *his_addr_sin = (struct sockaddr_in *)his_addr; + + memcpy(tkt.dat, buf, len); + tkt.length = len; + + k_getsockinst(0, inst, sizeof(inst)); + kerror = krb_rd_req(&tkt, "ftp", inst, + his_addr_sin->sin_addr.s_addr, &auth_dat, ""); + if(kerror == RD_AP_UNDEC){ + k_getsockinst(0, inst, sizeof(inst)); + kerror = krb_rd_req(&tkt, "rcmd", inst, + his_addr_sin->sin_addr.s_addr, &auth_dat, ""); + } + + if(kerror){ + reply(535, "Error reading request: %s.", krb_get_err_text(kerror)); + return -1; + } + + memcpy(d->key, auth_dat.session, sizeof(d->key)); + des_set_key(&d->key, d->schedule); + + strlcpy(d->name, auth_dat.pname, sizeof(d->name)); + strlcpy(d->instance, auth_dat.pinst, sizeof(d->instance)); + strlcpy(d->realm, auth_dat.prealm, sizeof(d->instance)); + + cs = auth_dat.checksum + 1; + { + unsigned char tmp[4]; + KRB_PUT_INT(cs, tmp, 4, sizeof(tmp)); + tmp_len = krb_mk_safe(tmp, msg, 4, &d->key, + (struct sockaddr_in *)LOCAL_ADDR, + (struct sockaddr_in *)REMOTE_ADDR); + } + if(tmp_len < 0){ + reply(535, "Error creating reply: %s.", strerror(errno)); + return -1; + } + len = tmp_len; + if(base64_encode(msg, len, &p) < 0) { + reply(535, "Out of memory base64-encoding."); + return -1; + } + reply(235, "ADAT=%s", p); + sec_complete = 1; + free(p); + return 0; +} + +static int +krb4_userok(void *app_data, char *user) +{ + struct krb4_data *d = app_data; + return krb_kuserok(d->name, d->instance, d->realm, user); +} + +struct sec_server_mech krb4_server_mech = { + "KERBEROS_V4", + sizeof(struct krb4_data), + NULL, /* init */ + NULL, /* end */ + krb4_check_prot, + krb4_overhead, + krb4_encode, + krb4_decode, + /* */ + NULL, + krb4_adat, + NULL, /* pbsz */ + NULL, /* ccc */ + krb4_userok +}; + +#else /* FTP_SERVER */ + +static int +mk_auth(struct krb4_data *d, KTEXT adat, + char *service, char *host, int checksum) +{ + int ret; + CREDENTIALS cred; + char sname[SNAME_SZ], inst[INST_SZ], realm[REALM_SZ]; + + strlcpy(sname, service, sizeof(sname)); + strlcpy(inst, krb_get_phost(host), sizeof(inst)); + strlcpy(realm, krb_realmofhost(host), sizeof(realm)); + ret = krb_mk_req(adat, sname, inst, realm, checksum); + if(ret) + return ret; + strlcpy(sname, service, sizeof(sname)); + strlcpy(inst, krb_get_phost(host), sizeof(inst)); + strlcpy(realm, krb_realmofhost(host), sizeof(realm)); + ret = krb_get_cred(sname, inst, realm, &cred); + memmove(&d->key, &cred.session, sizeof(des_cblock)); + des_key_sched(&d->key, d->schedule); + memset(&cred, 0, sizeof(cred)); + return ret; +} + +static int +krb4_auth(void *app_data, struct connectdata *conn) +{ + int ret; + char *p; + int len; + KTEXT_ST adat; + MSG_DAT msg_data; + int checksum; + u_int32_t cs; + struct krb4_data *d = app_data; + struct sockaddr_in *localaddr = (struct sockaddr_in *)LOCAL_ADDR; + struct sockaddr_in *remoteaddr = (struct sockaddr_in *)REMOTE_ADDR; + char *host = conn->hp->h_name; + size_t nread; + int l = sizeof(local_addr); + + if(getsockname(conn->data->firstsocket, LOCAL_ADDR, &l) < 0) + perror("getsockname()"); + + checksum = getpid(); + ret = mk_auth(d, &adat, "ftp", host, checksum); + if(ret == KDC_PR_UNKNOWN) + ret = mk_auth(d, &adat, "rcmd", host, checksum); + if(ret){ + printf("%s\n", krb_get_err_text(ret)); + return AUTH_CONTINUE; + } + +#ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM + if (krb_get_config_bool("nat_in_use")) { + struct in_addr natAddr; + + if (krb_get_our_ip_for_realm(krb_realmofhost(host), + &natAddr) != KSUCCESS + && krb_get_our_ip_for_realm(NULL, &natAddr) != KSUCCESS) + printf("Can't get address for realm %s\n", + krb_realmofhost(host)); + else { + if (natAddr.s_addr != localaddr->sin_addr.s_addr) { + printf("Using NAT IP address (%s) for kerberos 4\n", + inet_ntoa(natAddr)); + localaddr->sin_addr = natAddr; + + /* + * This not the best place to do this, but it + * is here we know that (probably) NAT is in + * use! + */ + + /*passivemode = 1;***/ + /*printf("Setting: Passive mode on.\n");***/ + } + } + } +#endif + + /*printf("Local address is %s\n", inet_ntoa(localaddr->sin_addr));***/ + /*printf("Remote address is %s\n", inet_ntoa(remoteaddr->sin_addr));***/ + + if(base64_encode(adat.dat, adat.length, &p) < 0) { + printf("Out of memory base64-encoding.\n"); + return AUTH_CONTINUE; + } + /*ret = command("ADAT %s", p)*/ + ftpsendf(conn->data->firstsocket, conn, "ADAT %s", p); + /* wait for feedback */ + nread = GetLastResponse(conn->data->firstsocket, + conn->data->buffer, conn); + if(nread < 0) + return /*CURLE_OPERATION_TIMEOUTED*/-1; + free(p); + + if(/*ret != COMPLETE*/conn->data->buffer[0] != '2'){ + printf("Server didn't accept auth data.\n"); + return AUTH_ERROR; + } + + p = strstr(/*reply_string*/conn->data->buffer, "ADAT="); + if(!p){ + printf("Remote host didn't send adat reply.\n"); + return AUTH_ERROR; + } + p += 5; + len = base64_decode(p, adat.dat); + if(len < 0){ + printf("Failed to decode base64 from server.\n"); + return AUTH_ERROR; + } + adat.length = len; + ret = krb_rd_safe(adat.dat, adat.length, &d->key, + (struct sockaddr_in *)hisctladdr, + (struct sockaddr_in *)myctladdr, &msg_data); + if(ret){ + printf("Error reading reply from server: %s.\n", + krb_get_err_text(ret)); + return AUTH_ERROR; + } + krb_get_int(msg_data.app_data, &cs, 4, 0); + if(cs - checksum != 1){ + printf("Bad checksum returned from server.\n"); + return AUTH_ERROR; + } + return AUTH_OK; +} + +struct sec_client_mech krb4_client_mech = { + "KERBEROS_V4", + sizeof(struct krb4_data), + NULL, /* init */ + krb4_auth, + NULL, /* end */ + krb4_check_prot, + krb4_overhead, + krb4_encode, + krb4_decode +}; + +#endif /* FTP_SERVER */ + +void krb_kauth(struct connectdata *conn) +{ + int ret; + char buf[1024]; + des_cblock key; + des_key_schedule schedule; + KTEXT_ST tkt, tktcopy; + char *name; + char *p; + int overbose; + char passwd[100]; + int tmp; + size_t nread; + + int save; + + save = set_command_prot(conn, prot_private); + /*ret = command("SITE KAUTH %s", name);***/ + ftpsendf(conn->data->firstsocket, conn, + "SITE KAUTH %s", conn->data->user); + /* wait for feedback */ + nread = GetLastResponse(conn->data->firstsocket, conn->data->buffer, conn); + if(nread < 0) + return /*CURLE_OPERATION_TIMEOUTED*/; + + if(/*ret != CONTINUE*/conn->data->buffer[0] != '3'){ + /*verbose = overbose;***/ + set_command_prot(conn, save); + /*code = -1;***/ + return; + } + /*verbose = overbose;***/ + p = strstr(/*reply_string***/conn->data->buffer, "T="); + if(!p){ + printf("Bad reply from server.\n"); + set_command_prot(conn, save); + /*code = -1;***/ + return; + } + p += 2; + tmp = base64_decode(p, &tkt.dat); + if(tmp < 0){ + printf("Failed to decode base64 in reply.\n"); + set_command_prot(conn, save); + /*code = -1;***/ + return; + } + tkt.length = tmp; + tktcopy.length = tkt.length; + + p = strstr(/*reply_string***/conn->data->buffer, "P="); + if(!p){ + printf("Bad reply from server.\n"); + /*verbose = overbose;***/ + set_command_prot(conn, save); + /*code = -1;***/ + return; + } + name = p + 2; + for(; *p && *p != ' ' && *p != '\r' && *p != '\n'; p++); + *p = 0; + +#if 0 + snprintf(buf, sizeof(buf), "Password for %s:", name); + if (des_read_pw_string (passwd, sizeof(passwd)-1, buf, 0)) + *passwd = '\0'; + des_string_to_key (passwd, &key); +#else + des_string_to_key (conn->data->passwd, &key); +#endif + + des_key_sched(&key, schedule); + + des_pcbc_encrypt((des_cblock*)tkt.dat, (des_cblock*)tktcopy.dat, + tkt.length, + schedule, &key, DES_DECRYPT); + if (strcmp ((char*)tktcopy.dat + 8, + KRB_TICKET_GRANTING_TICKET) != 0) { + afs_string_to_key (passwd, + krb_realmofhost(/*hostname***/conn->hp->h_name), + &key); + des_key_sched (&key, schedule); + des_pcbc_encrypt((des_cblock*)tkt.dat, (des_cblock*)tktcopy.dat, + tkt.length, + schedule, &key, DES_DECRYPT); + } + memset(key, 0, sizeof(key)); + memset(schedule, 0, sizeof(schedule)); + memset(passwd, 0, sizeof(passwd)); + if(base64_encode(tktcopy.dat, tktcopy.length, &p) < 0) { + failf(conn->data, "Out of memory base64-encoding.\n"); + set_command_prot(conn, save); + /*code = -1;***/ + return; + } + memset (tktcopy.dat, 0, tktcopy.length); + /*ret = command("SITE KAUTH %s %s", name, p);***/ + ftpsendf(conn->data->firstsocket, conn, + "SITE KAUTH %s %s", name, p); + /* wait for feedback */ + nread = GetLastResponse(conn->data->firstsocket, conn->data->buffer, conn); + if(nread < 0) + return /*CURLE_OPERATION_TIMEOUTED*/; + free(p); + set_command_prot(conn, save); +} + +#endif /* KRB4 */
lib/security.c+641 −0 added@@ -0,0 +1,641 @@ +/* modified by Martin Hedenfalk <mhe@stacken.kth.se> for use in Curl + * last modified 2000-09-18 + * Even more obscurified to merge better into libcurl by Daniel Stenberg. + */ + +/* + * Copyright (c) 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" + +#include <curl/mprintf.h> + +#ifdef KRB4 + +#include "security.h" +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include "base64_krb.h" + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +static struct { + enum protection_level level; + const char *name; +} level_names[] = { + { prot_clear, "clear" }, + { prot_safe, "safe" }, + { prot_confidential, "confidential" }, + { prot_private, "private" } +}; + +static const char * +level_to_name(enum protection_level level) +{ + int i; + for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++) + if(level_names[i].level == level) + return level_names[i].name; + return "unknown"; +} + +#ifndef FTP_SERVER /* not used in server */ +static enum protection_level +name_to_level(const char *name) +{ + int i; + for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++) + if(!strncasecmp(level_names[i].name, name, strlen(name))) + return level_names[i].level; + return (enum protection_level)-1; +} +#endif + +#ifdef FTP_SERVER + +static struct sec_server_mech *mechs[] = { +#ifdef KRB5 + &gss_server_mech, +#endif +#ifdef KRB4 + &krb4_server_mech, +#endif + NULL +}; + +static struct sec_server_mech *mech; + +#else + +static struct sec_client_mech *mechs[] = { +#ifdef KRB5 + &gss_client_mech, +#endif +#ifdef KRB4 + &krb4_client_mech, +#endif + NULL +}; + +static struct sec_client_mech *mech; + +#endif + +int +sec_getc(struct connectdata *conn, FILE *F) +{ + if(conn->sec_complete && conn->data_prot) { + char c; + if(sec_read(conn, fileno(F), &c, 1) <= 0) + return EOF; + return c; + } else + return getc(F); +} + +static int +block_read(int fd, void *buf, size_t len) +{ + unsigned char *p = buf; + int b; + while(len) { + b = read(fd, p, len); + if (b == 0) + return 0; + else if (b < 0) + return -1; + len -= b; + p += b; + } + return p - (unsigned char*)buf; +} + +static int +block_write(int fd, void *buf, size_t len) +{ + unsigned char *p = buf; + int b; + while(len) { + b = write(fd, p, len); + if(b < 0) + return -1; + len -= b; + p += b; + } + return p - (unsigned char*)buf; +} + +static int +sec_get_data(struct connectdata *conn, + int fd, struct krb4buffer *buf, int level) +{ + int len; + int b; + + b = block_read(fd, &len, sizeof(len)); + if (b == 0) + return 0; + else if (b < 0) + return -1; + len = ntohl(len); + buf->data = realloc(buf->data, len); + b = block_read(fd, buf->data, len); + if (b == 0) + return 0; + else if (b < 0) + return -1; + buf->size = (*mech->decode)(conn->app_data, buf->data, len, + conn->data_prot, conn); + buf->index = 0; + return 0; +} + +static size_t +buffer_read(struct krb4buffer *buf, void *data, size_t len) +{ + len = min(len, buf->size - buf->index); + memcpy(data, (char*)buf->data + buf->index, len); + buf->index += len; + return len; +} + +static size_t +buffer_write(struct krb4buffer *buf, void *data, size_t len) +{ + if(buf->index + len > buf->size) { + void *tmp; + if(buf->data == NULL) + tmp = malloc(1024); + else + tmp = realloc(buf->data, buf->index + len); + if(tmp == NULL) + return -1; + buf->data = tmp; + buf->size = buf->index + len; + } + memcpy((char*)buf->data + buf->index, data, len); + buf->index += len; + return len; +} + +int +sec_read(struct connectdata *conn, int fd, void *buffer, int length) +{ + size_t len; + int rx = 0; + + if(conn->sec_complete == 0 || conn->data_prot == 0) + return read(fd, buffer, length); + + if(conn->in_buffer.eof_flag){ + conn->in_buffer.eof_flag = 0; + return 0; + } + + len = buffer_read(&conn->in_buffer, buffer, length); + length -= len; + rx += len; + buffer = (char*)buffer + len; + + while(length) { + if(sec_get_data(conn, fd, &conn->in_buffer, conn->data_prot) < 0) + return -1; + if(conn->in_buffer.size == 0) { + if(rx) + conn->in_buffer.eof_flag = 1; + return rx; + } + len = buffer_read(&conn->in_buffer, buffer, length); + length -= len; + rx += len; + buffer = (char*)buffer + len; + } + return rx; +} + +static int +sec_send(struct connectdata *conn, int fd, char *from, int length) +{ + int bytes; + void *buf; + bytes = (*mech->encode)(conn->app_data, from, length, conn->data_prot, &buf, conn); + bytes = htonl(bytes); + block_write(fd, &bytes, sizeof(bytes)); + block_write(fd, buf, ntohl(bytes)); + free(buf); + return length; +} + +int +sec_fflush(struct connectdata *conn, FILE *F) +{ + if(conn->data_prot != prot_clear) { + if(conn->out_buffer.index > 0){ + sec_write(conn, fileno(F), + conn->out_buffer.data, conn->out_buffer.index); + conn->out_buffer.index = 0; + } + sec_send(conn, fileno(F), NULL, 0); + } + fflush(F); + return 0; +} + +int +sec_write(struct connectdata *conn, int fd, char *buffer, int length) +{ + int len = conn->buffer_size; + int tx = 0; + + if(conn->data_prot == prot_clear) + return write(fd, buffer, length); + + len -= (*mech->overhead)(conn->app_data, conn->data_prot, len); + while(length){ + if(length < len) + len = length; + sec_send(conn, fd, buffer, len); + length -= len; + buffer += len; + tx += len; + } + return tx; +} + +int +sec_vfprintf2(struct connectdata *conn, FILE *f, const char *fmt, va_list ap) +{ + char *buf; + int ret; + if(conn->data_prot == prot_clear) + return vfprintf(f, fmt, ap); + else { + buf = maprintf(fmt, ap); + ret = buffer_write(&conn->out_buffer, buf, strlen(buf)); + free(buf); + return ret; + } +} + +int +sec_fprintf2(struct connectdata *conn, FILE *f, const char *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = sec_vfprintf2(conn, f, fmt, ap); + va_end(ap); + return ret; +} + +int +sec_putc(struct connectdata *conn, int c, FILE *F) +{ + char ch = c; + if(conn->data_prot == prot_clear) + return putc(c, F); + + buffer_write(&conn->out_buffer, &ch, 1); + if(c == '\n' || conn->out_buffer.index >= 1024 /* XXX */) { + sec_write(conn, fileno(F), conn->out_buffer.data, conn->out_buffer.index); + conn->out_buffer.index = 0; + } + return c; +} + +int +sec_read_msg(struct connectdata *conn, char *s, int level) +{ + int len; + char *buf; + int code; + + buf = malloc(strlen(s)); + len = base64_decode(s + 4, buf); /* XXX */ + + len = (*mech->decode)(conn->app_data, buf, len, level, conn); + if(len < 0) + return -1; + + buf[len] = '\0'; + + if(buf[3] == '-') + code = 0; + else + sscanf(buf, "%d", &code); + if(buf[len-1] == '\n') + buf[len-1] = '\0'; + strcpy(s, buf); + free(buf); + return code; +} + +/* modified to return how many bytes written, or -1 on error ***/ +int +sec_vfprintf(struct connectdata *conn, FILE *f, const char *fmt, va_list ap) +{ + int ret = 0; + char *buf; + void *enc; + int len; + if(!conn->sec_complete) + return vfprintf(f, fmt, ap); + + buf = maprintf(fmt, ap); + len = (*mech->encode)(conn->app_data, buf, strlen(buf), + conn->command_prot, &enc, + conn); + free(buf); + if(len < 0) { + failf(conn->data, "Failed to encode command.\n"); + return -1; + } + if(base64_encode(enc, len, &buf) < 0){ + failf(conn->data, "Out of memory base64-encoding.\n"); + return -1; + } +#ifdef FTP_SERVER + if(command_prot == prot_safe) + fprintf(f, "631 %s\r\n", buf); + else if(command_prot == prot_private) + fprintf(f, "632 %s\r\n", buf); + else if(command_prot == prot_confidential) + fprintf(f, "633 %s\r\n", buf); +#else + if(conn->command_prot == prot_safe) + ret = fprintf(f, "MIC %s", buf); + else if(conn->command_prot == prot_private) + ret = fprintf(f, "ENC %s", buf); + else if(conn->command_prot == prot_confidential) + ret = fprintf(f, "CONF %s", buf); +#endif + free(buf); + return ret; +} + +int +sec_fprintf(struct connectdata *conn, FILE *f, const char *fmt, ...) +{ + va_list ap; + int ret; + va_start(ap, fmt); + ret = sec_vfprintf(conn, f, fmt, ap); + va_end(ap); + return ret; +} + +/* end common stuff */ + +#ifdef FTP_SERVER + +/* snip */ + +#else /* FTP_SERVER */ + +#if 0 +void +sec_status(void) +{ + if(conn->sec_complete){ + printf("Using %s for authentication.\n", mech->name); + printf("Using %s command channel.\n", level_to_name(command_prot)); + printf("Using %s data channel.\n", level_to_name(data_prot)); + if(buffer_size > 0) + printf("Protection buffer size: %lu.\n", + (unsigned long)buffer_size); + }else{ + printf("Not using any security mechanism.\n"); + } +} +#endif + +static int +sec_prot_internal(struct connectdata *conn, int level) +{ + int ret; + char *p; + unsigned int s = 1048576; + size_t nread; + + if(!conn->sec_complete){ + infof(conn->data, "No security data exchange has taken place.\n"); + return -1; + } + + if(level){ + ftpsendf(conn->data->firstsocket, conn, + "PBSZ %u", s); + /* wait for feedback */ + nread = GetLastResponse(conn->data->firstsocket, + conn->data->buffer, conn); + if(nread < 0) + return /*CURLE_OPERATION_TIMEOUTED*/-1; + if(/*ret != COMPLETE*/conn->data->buffer[0] != '2'){ + failf(conn->data, "Failed to set protection buffer size.\n"); + return -1; + } + conn->buffer_size = s; + p = strstr(/*reply_string*/conn->data->buffer, "PBSZ="); + if(p) + sscanf(p, "PBSZ=%u", &s); + if(s < conn->buffer_size) + conn->buffer_size = s; + } + + ftpsendf(conn->data->firstsocket, conn, + "PROT %c", level["CSEP"]); + /* wait for feedback */ + nread = GetLastResponse(conn->data->firstsocket, + conn->data->buffer, conn); + if(nread < 0) + return /*CURLE_OPERATION_TIMEOUTED*/-1; + if(/*ret != COMPLETE*/conn->data->buffer[0] != '2'){ + failf(conn->data, "Failed to set protection level.\n"); + return -1; + } + + conn->data_prot = (enum protection_level)level; + return 0; +} + +enum protection_level +set_command_prot(struct connectdata *conn, enum protection_level level) +{ + enum protection_level old = conn->command_prot; + conn->command_prot = level; + return old; +} + +#if 0 +void +sec_prot(int argc, char **argv) +{ + int level = -1; + + if(argc < 2 || argc > 3) + goto usage; + if(!sec_complete) { + printf("No security data exchange has taken place.\n"); + code = -1; + return; + } + level = name_to_level(argv[argc - 1]); + + if(level == -1) + goto usage; + + if((*mech->check_prot)(conn->app_data, level)) { + printf("%s does not implement %s protection.\n", + mech->name, level_to_name(level)); + code = -1; + return; + } + + if(argc == 2 || strncasecmp(argv[1], "data", strlen(argv[1])) == 0) { + if(sec_prot_internal(level) < 0){ + code = -1; + return; + } + } else if(strncasecmp(argv[1], "command", strlen(argv[1])) == 0) + set_command_prot(level); + else + goto usage; + code = 0; + return; + usage: + printf("usage: %s [command|data] [clear|safe|confidential|private]\n", + argv[0]); + code = -1; +} +#endif + +void +sec_set_protection_level(struct connectdata *conn) +{ + if(conn->sec_complete && conn->data_prot != conn->request_data_prot) + sec_prot_internal(conn, conn->request_data_prot); +} + + +int +sec_request_prot(struct connectdata *conn, char *level) +{ + int l = name_to_level(level); + if(l == -1) + return -1; + conn->request_data_prot = (enum protection_level)l; + return 0; +} + +int +sec_login(struct connectdata *conn) +{ + int ret; + struct sec_client_mech **m; + size_t nread; + struct UrlData *data=conn->data; + + for(m = mechs; *m && (*m)->name; m++) { + void *tmp; + + tmp = realloc(conn->app_data, (*m)->size); + if (tmp == NULL) { + failf (data, "realloc %u failed", (*m)->size); + return -1; + } + conn->app_data = tmp; + + if((*m)->init && (*(*m)->init)(conn->app_data) != 0) { + infof(data, "Skipping %s...\n", (*m)->name); + continue; + } + infof(data, "Trying %s...\n", (*m)->name); + /*ret = command("AUTH %s", (*m)->name);***/ + ftpsendf(conn->data->firstsocket, conn, + "AUTH %s", (*m)->name); + /* wait for feedback */ + nread = GetLastResponse(conn->data->firstsocket, + conn->data->buffer, conn); + if(nread < 0) + return /*CURLE_OPERATION_TIMEOUTED*/-1; + if(/*ret != CONTINUE*/conn->data->buffer[0] != '3'){ + if(/*code == 504*/strncmp(conn->data->buffer,"504",3) == 0) { + infof(data, + "%s is not supported by the server.\n", (*m)->name); + } + else if(/*code == 534*/strncmp(conn->data->buffer,"534",3) == 0) { + infof(data, "%s rejected as security mechanism.\n", (*m)->name); + } + else if(/*ret == ERROR*/conn->data->buffer[0] == '5') { + infof(data, "The server doesn't support the FTP " + "security extensions.\n"); + return -1; + } + continue; + } + + ret = (*(*m)->auth)(conn->app_data, /*host***/conn); + + if(ret == AUTH_CONTINUE) + continue; + else if(ret != AUTH_OK){ + /* mechanism is supposed to output error string */ + return -1; + } + mech = *m; + conn->sec_complete = 1; + conn->command_prot = prot_safe; + break; + } + + return *m == NULL; +} + +void +sec_end(struct connectdata *conn) +{ + if (mech != NULL) { + if(mech->end) + (*mech->end)(conn->app_data); + memset(conn->app_data, 0, mech->size); + free(conn->app_data); + conn->app_data = NULL; + } + conn->sec_complete = 0; + conn->data_prot = (enum protection_level)0; +} + +#endif /* FTP_SERVER */ + +#endif /* KRB4 */
lib/security.h+133 −0 added@@ -0,0 +1,133 @@ +/* modified by Martin Hedenfalk <mhe@stacken.kth.se> for use in Curl + * last modified 2000-09-18 + */ + +/* + * Copyright (c) 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. + */ + +/* $Id$ */ + +#ifndef __security_h__ +#define __security_h__ + +#include <stdarg.h> +#include "urldata.h" /* for struct connectdata * */ + +struct sec_client_mech { + char *name; + size_t size; + int (*init)(void *); + int (*auth)(void *, struct connectdata *); + void (*end)(void *); + int (*check_prot)(void *, int); + int (*overhead)(void *, int, int); + int (*encode)(void *, void*, int, int, void**, struct connectdata *); + int (*decode)(void *, void*, int, int, struct connectdata *); +}; + +struct sec_server_mech { + char *name; + size_t size; + int (*init)(void *); + void (*end)(void *); + int (*check_prot)(void *, int); + int (*overhead)(void *, int, int); + int (*encode)(void *, void*, int, int, void**); + int (*decode)(void *, void*, int, int); + + int (*auth)(void *); + int (*adat)(void *, void*, size_t); + size_t (*pbsz)(void *, size_t); + int (*ccc)(void*); + int (*userok)(void*, char*); +}; + +#define AUTH_OK 0 +#define AUTH_CONTINUE 1 +#define AUTH_ERROR 2 + +#ifdef FTP_SERVER +extern struct sec_server_mech krb4_server_mech, gss_server_mech; +#else +extern struct sec_client_mech krb4_client_mech, gss_client_mech; +#endif + +extern int sec_complete; + +#ifdef FTP_SERVER +extern char *ftp_command; +void new_ftp_command(char*); +void delete_ftp_command(void); +#endif + +/* ---- */ + + +int sec_fflush (struct connectdata *conn, FILE *); +int sec_fprintf (struct connectdata *, FILE *, const char *, ...); +int sec_getc (struct connectdata *conn, FILE *); +int sec_putc (struct connectdata *conn, int, FILE *); +int sec_read (struct connectdata *conn, int, void *, int); +int sec_read_msg (struct connectdata *conn, char *, int); + +int sec_vfprintf(struct connectdata *, FILE *, const char *, va_list); +int sec_fprintf2(struct connectdata *conn, FILE *f, const char *fmt, ...); +int sec_vfprintf2(struct connectdata *conn, FILE *, const char *, va_list); +int sec_write (struct connectdata *conn, int, char *, int); + +#ifdef FTP_SERVER +void adat (char *); +void auth (char *); +void ccc (void); +void mec (char *, enum protection_level); +void pbsz (int); +void prot (char *); +void delete_ftp_command (void); +void new_ftp_command (char *); +int sec_userok (char *); +int secure_command (void); +enum protection_level get_command_prot(void); +#else +void sec_end (struct connectdata *); +int sec_login (struct connectdata *); +void sec_prot (int, char **); +int sec_request_prot (struct connectdata *conn, char *); +void sec_set_protection_level(struct connectdata *conn); +void sec_status (void); + +enum protection_level set_command_prot(struct connectdata *, + enum protection_level); + +#endif + +#endif /* __security_h__ */
Vulnerability mechanics
Root cause
"Missing zero-length check before realloc() in read_data() causes realloc(0) to free the buffer, leading to a double-free on the cleanup path."
Attack vector
An attacker who can control the Kerberos-protected data stream sent to a curl client or server can trigger a double-free. The `read_data()` function reads a 32-bit length from the socket; if the attacker sends a length of zero, `realloc(buf->data, 0)` behaves like `free(buf->data)` but returns NULL [patch_id=2247610]. The code then treats NULL as an allocation failure and returns `CURLE_OUT_OF_MEMORY`, leaving `buf->data` pointing to freed memory. When the cleanup path later calls `free(buf->data)`, a double-free occurs [patch_id=2247610]. The advisory notes this affects curl versions before 7.51.0 [ref_id=1].
Affected code
The vulnerable function is `read_data()` in `lib/security.c` [patch_id=2247610]. The function reads a length value from the network socket, converts it from network byte order, and passes it directly to `realloc(buf->data, len)` without checking whether `len` is zero [patch_id=2247610].
What the fix does
The patch [patch_id=2247610] wraps the `realloc()` call inside an `if(len)` guard so that `realloc()` is only called when the length is non-zero. When `len` is zero, the function returns `CURLE_OUT_OF_MEMORY` without calling `realloc()`, preventing the double-free. The patch also initializes `tmp = NULL` to ensure the NULL check after the conditional block works correctly.
Preconditions
- configThe curl client or server must be using Kerberos (KRB4 or KRB5) authentication for the connection.
- networkThe attacker must be able to send a crafted network message on the Kerberos-protected data channel with a zero-length field.
Generated on May 24, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
10- access.redhat.com/errata/RHSA-2018:2486mitrevendor-advisoryx_refsource_REDHAT
- access.redhat.com/errata/RHSA-2018:3558mitrevendor-advisoryx_refsource_REDHAT
- security.gentoo.org/glsa/201701-47mitrevendor-advisoryx_refsource_GENTOO
- www.oracle.com/technetwork/security-advisory/cpuoct2018-4428296.htmlmitrex_refsource_CONFIRM
- www.securityfocus.com/bid/94100mitrevdb-entryx_refsource_BID
- www.securitytracker.com/id/1037192mitrevdb-entryx_refsource_SECTRACK
- bugzilla.redhat.com/show_bug.cgimitrex_refsource_CONFIRM
- curl.haxx.se/CVE-2016-8619.patchmitrex_refsource_CONFIRM
- curl.haxx.se/docs/adv_20161102E.htmlmitrex_refsource_CONFIRM
- www.tenable.com/security/tns-2016-21mitrex_refsource_CONFIRM
News mentions
0No linked articles in our index yet.