From def69c30879c0246bccb02d79e06b937e39d0ba4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 21 Sep 2000 08:46:14 +0000 Subject: [PATCH] new for kerberos support --- lib/base64_krb.c | 147 +++++++++++ lib/base64_krb.h | 47 ++++ lib/krb4.c | 471 ++++++++++++++++++++++++++++++++++ lib/security.c | 641 +++++++++++++++++++++++++++++++++++++++++++++++ lib/security.h | 133 ++++++++++ 5 files changed, 1439 insertions(+) create mode 100644 lib/base64_krb.c create mode 100644 lib/base64_krb.h create mode 100644 lib/krb4.c create mode 100644 lib/security.c create mode 100644 lib/security.h diff --git a/lib/base64_krb.c b/lib/base64_krb.c new file mode 100644 index 000000000..6dae1f573 --- /dev/null +++ b/lib/base64_krb.c @@ -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 +#endif +#include +#include +#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; +} diff --git a/lib/base64_krb.h b/lib/base64_krb.h new file mode 100644 index 000000000..3aee73310 --- /dev/null +++ b/lib/base64_krb.h @@ -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 diff --git a/lib/krb4.c b/lib/krb4.c new file mode 100644 index 000000000..79b968c1a --- /dev/null +++ b/lib/krb4.c @@ -0,0 +1,471 @@ +/* modified by Martin Hedenfalk 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 +#include +#include +#include +#include + +#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 */ diff --git a/lib/security.c b/lib/security.c new file mode 100644 index 000000000..81ce35ed8 --- /dev/null +++ b/lib/security.c @@ -0,0 +1,641 @@ +/* modified by Martin Hedenfalk 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 + +#ifdef KRB4 + +#include "security.h" +#include +#include +#include +#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 */ diff --git a/lib/security.h b/lib/security.h new file mode 100644 index 000000000..b496fad71 --- /dev/null +++ b/lib/security.h @@ -0,0 +1,133 @@ +/* modified by Martin Hedenfalk 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 +#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__ */