Path: blob/main/crypto/heimdal/appl/ftp/ftpd/security.c
34907 views
/*1* Copyright (c) 1998-2002, 2005 Kungliga Tekniska Högskolan2* (Royal Institute of Technology, Stockholm, Sweden).3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8*9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11*12* 2. Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in the14* documentation and/or other materials provided with the distribution.15*16* 3. Neither the name of the Institute nor the names of its contributors17* may be used to endorse or promote products derived from this software18* without specific prior written permission.19*20* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND21* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE23* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL25* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS26* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)27* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT28* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY29* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF30* SUCH DAMAGE.31*/3233#ifdef FTP_SERVER34#include "ftpd_locl.h"35#else36#include "ftp_locl.h"37#endif3839RCSID("$Id$");4041static enum protection_level command_prot;42static enum protection_level data_prot;43static size_t buffer_size;4445struct buffer {46void *data;47size_t size;48size_t index;49int eof_flag;50};5152static struct buffer in_buffer, out_buffer;53int sec_complete;5455static struct {56enum protection_level level;57const char *name;58} level_names[] = {59{ prot_clear, "clear" },60{ prot_safe, "safe" },61{ prot_confidential, "confidential" },62{ prot_private, "private" }63};6465static const char *66level_to_name(enum protection_level level)67{68int i;69for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)70if(level_names[i].level == level)71return level_names[i].name;72return "unknown";73}7475#ifndef FTP_SERVER /* not used in server */76static enum protection_level77name_to_level(const char *name)78{79int i;80for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)81if(!strncasecmp(level_names[i].name, name, strlen(name)))82return level_names[i].level;83return prot_invalid;84}85#endif8687#ifdef FTP_SERVER8889static struct sec_server_mech *mechs[] = {90#ifdef KRB591&gss_server_mech,92#endif93NULL94};9596static struct sec_server_mech *mech;9798#else99100static struct sec_client_mech *mechs[] = {101#ifdef KRB5102&gss_client_mech,103#endif104NULL105};106107static struct sec_client_mech *mech;108109#endif110111static void *app_data;112113int114sec_getc(FILE *F)115{116if(sec_complete && data_prot) {117char c;118if(sec_read(fileno(F), &c, 1) <= 0)119return EOF;120return c;121} else122return getc(F);123}124125static int126block_read(int fd, void *buf, size_t len)127{128unsigned char *p = buf;129int b;130while(len) {131b = read(fd, p, len);132if (b == 0)133return 0;134else if (b < 0)135return -1;136len -= b;137p += b;138}139return p - (unsigned char*)buf;140}141142static int143block_write(int fd, void *buf, size_t len)144{145unsigned char *p = buf;146int b;147while(len) {148b = write(fd, p, len);149if(b < 0)150return -1;151len -= b;152p += b;153}154return p - (unsigned char*)buf;155}156157static int158sec_get_data(int fd, struct buffer *buf, int level)159{160int len;161int b;162void *tmp;163164b = block_read(fd, &len, sizeof(len));165if (b == 0)166return 0;167else if (b < 0)168return -1;169len = ntohl(len);170tmp = realloc(buf->data, len);171if (tmp == NULL)172return -1;173buf->data = tmp;174b = block_read(fd, buf->data, len);175if (b == 0)176return 0;177else if (b < 0)178return -1;179buf->size = (*mech->decode)(app_data, buf->data, len, data_prot);180buf->index = 0;181return 0;182}183184static size_t185buffer_read(struct buffer *buf, void *dataptr, size_t len)186{187len = min(len, buf->size - buf->index);188memcpy(dataptr, (char*)buf->data + buf->index, len);189buf->index += len;190return len;191}192193static size_t194buffer_write(struct buffer *buf, void *dataptr, size_t len)195{196if(buf->index + len > buf->size) {197void *tmp;198if(buf->data == NULL)199tmp = malloc(1024);200else201tmp = realloc(buf->data, buf->index + len);202if(tmp == NULL)203return -1;204buf->data = tmp;205buf->size = buf->index + len;206}207memcpy((char*)buf->data + buf->index, dataptr, len);208buf->index += len;209return len;210}211212int213sec_read(int fd, void *dataptr, int length)214{215size_t len;216int rx = 0;217218if(sec_complete == 0 || data_prot == 0)219return read(fd, dataptr, length);220221if(in_buffer.eof_flag){222in_buffer.eof_flag = 0;223return 0;224}225226len = buffer_read(&in_buffer, dataptr, length);227length -= len;228rx += len;229dataptr = (char*)dataptr + len;230231while(length){232int ret;233234ret = sec_get_data(fd, &in_buffer, data_prot);235if (ret < 0)236return -1;237if(ret == 0 && in_buffer.size == 0) {238if(rx)239in_buffer.eof_flag = 1;240return rx;241}242len = buffer_read(&in_buffer, dataptr, length);243length -= len;244rx += len;245dataptr = (char*)dataptr + len;246}247return rx;248}249250static int251sec_send(int fd, char *from, int length)252{253int bytes;254void *buf;255bytes = (*mech->encode)(app_data, from, length, data_prot, &buf);256bytes = htonl(bytes);257block_write(fd, &bytes, sizeof(bytes));258block_write(fd, buf, ntohl(bytes));259free(buf);260return length;261}262263int264sec_fflush(FILE *F)265{266if(data_prot != prot_clear) {267if(out_buffer.index > 0){268sec_write(fileno(F), out_buffer.data, out_buffer.index);269out_buffer.index = 0;270}271sec_send(fileno(F), NULL, 0);272}273fflush(F);274return 0;275}276277int278sec_write(int fd, char *dataptr, int length)279{280int len = buffer_size;281int tx = 0;282283if(data_prot == prot_clear)284return write(fd, dataptr, length);285286len -= (*mech->overhead)(app_data, data_prot, len);287while(length){288if(length < len)289len = length;290sec_send(fd, dataptr, len);291length -= len;292dataptr += len;293tx += len;294}295return tx;296}297298int299sec_vfprintf2(FILE *f, const char *fmt, va_list ap)300{301char *buf;302int ret;303if(data_prot == prot_clear)304return vfprintf(f, fmt, ap);305else {306int len;307len = vasprintf(&buf, fmt, ap);308if (len == -1)309return len;310ret = buffer_write(&out_buffer, buf, len);311free(buf);312return ret;313}314}315316int317sec_fprintf2(FILE *f, const char *fmt, ...)318{319int ret;320va_list ap;321va_start(ap, fmt);322ret = sec_vfprintf2(f, fmt, ap);323va_end(ap);324return ret;325}326327int328sec_putc(int c, FILE *F)329{330char ch = c;331if(data_prot == prot_clear)332return putc(c, F);333334buffer_write(&out_buffer, &ch, 1);335if(c == '\n' || out_buffer.index >= 1024 /* XXX */) {336sec_write(fileno(F), out_buffer.data, out_buffer.index);337out_buffer.index = 0;338}339return c;340}341342int343sec_read_msg(char *s, int level)344{345int len;346char *buf;347int return_code;348349buf = malloc(strlen(s));350len = base64_decode(s + 4, buf); /* XXX */351352len = (*mech->decode)(app_data, buf, len, level);353if(len < 0)354return -1;355356buf[len] = '\0';357358if(buf[3] == '-')359return_code = 0;360else361sscanf(buf, "%d", &return_code);362if(buf[len-1] == '\n')363buf[len-1] = '\0';364strcpy(s, buf);365free(buf);366return return_code;367}368369int370sec_vfprintf(FILE *f, const char *fmt, va_list ap)371{372char *buf;373void *enc;374int len;375if(!sec_complete)376return vfprintf(f, fmt, ap);377378if (vasprintf(&buf, fmt, ap) == -1) {379printf("Failed to allocate command.\n");380return -1;381}382len = (*mech->encode)(app_data, buf, strlen(buf), command_prot, &enc);383free(buf);384if(len < 0) {385printf("Failed to encode command.\n");386return -1;387}388if(base64_encode(enc, len, &buf) < 0){389free(enc);390printf("Out of memory base64-encoding.\n");391return -1;392}393free(enc);394#ifdef FTP_SERVER395if(command_prot == prot_safe)396fprintf(f, "631 %s\r\n", buf);397else if(command_prot == prot_private)398fprintf(f, "632 %s\r\n", buf);399else if(command_prot == prot_confidential)400fprintf(f, "633 %s\r\n", buf);401#else402if(command_prot == prot_safe)403fprintf(f, "MIC %s", buf);404else if(command_prot == prot_private)405fprintf(f, "ENC %s", buf);406else if(command_prot == prot_confidential)407fprintf(f, "CONF %s", buf);408#endif409free(buf);410return 0;411}412413int414sec_fprintf(FILE *f, const char *fmt, ...)415{416va_list ap;417int ret;418va_start(ap, fmt);419ret = sec_vfprintf(f, fmt, ap);420va_end(ap);421return ret;422}423424/* end common stuff */425426#ifdef FTP_SERVER427428int ccc_passed;429430void431auth(char *auth_name)432{433int i;434void *tmp;435436for(i = 0; (mech = mechs[i]) != NULL; i++){437if(!strcasecmp(auth_name, mech->name)){438tmp = realloc(app_data, mech->size);439if (tmp == NULL) {440reply(431, "Unable to accept %s at this time", mech->name);441return;442}443app_data = tmp;444445if(mech->init && (*mech->init)(app_data) != 0) {446reply(431, "Unable to accept %s at this time", mech->name);447return;448}449if(mech->auth) {450(*mech->auth)(app_data);451return;452}453if(mech->adat)454reply(334, "Send authorization data.");455else456reply(234, "Authorization complete.");457return;458}459}460free (app_data);461app_data = NULL;462reply(504, "%s is unknown to me", auth_name);463}464465void466adat(char *auth_data)467{468if(mech && !sec_complete) {469void *buf = malloc(strlen(auth_data));470size_t len;471len = base64_decode(auth_data, buf);472(*mech->adat)(app_data, buf, len);473free(buf);474} else475reply(503, "You must %sissue an AUTH first.", mech ? "re-" : "");476}477478void pbsz(int size)479{480size_t new = size;481if(!sec_complete)482reply(503, "Incomplete security data exchange.");483if(mech->pbsz)484new = (*mech->pbsz)(app_data, size);485if(buffer_size != new){486buffer_size = size;487}488if(new != size)489reply(200, "PBSZ=%lu", (unsigned long)new);490else491reply(200, "OK");492}493494void495prot(char *pl)496{497int p = -1;498499if(buffer_size == 0){500reply(503, "No protection buffer size negotiated.");501return;502}503504if(!strcasecmp(pl, "C"))505p = prot_clear;506else if(!strcasecmp(pl, "S"))507p = prot_safe;508else if(!strcasecmp(pl, "E"))509p = prot_confidential;510else if(!strcasecmp(pl, "P"))511p = prot_private;512else {513reply(504, "Unrecognized protection level.");514return;515}516517if(sec_complete){518if((*mech->check_prot)(app_data, p)){519reply(536, "%s does not support %s protection.",520mech->name, level_to_name(p));521}else{522data_prot = (enum protection_level)p;523reply(200, "Data protection is %s.", level_to_name(p));524}525}else{526reply(503, "Incomplete security data exchange.");527}528}529530void ccc(void)531{532if(sec_complete){533if(mech->ccc && (*mech->ccc)(app_data) == 0) {534command_prot = data_prot = prot_clear;535ccc_passed = 1;536} else537reply(534, "You must be joking.");538}else539reply(503, "Incomplete security data exchange.");540}541542void mec(char *msg, enum protection_level level)543{544void *buf;545size_t len, buf_size;546if(!sec_complete) {547reply(503, "Incomplete security data exchange.");548return;549}550buf_size = strlen(msg) + 2;551buf = malloc(buf_size);552if (buf == NULL) {553reply(501, "Failed to allocate %lu", (unsigned long)buf_size);554return;555}556len = base64_decode(msg, buf);557command_prot = level;558if(len == (size_t)-1) {559free(buf);560reply(501, "Failed to base64-decode command");561return;562}563len = (*mech->decode)(app_data, buf, len, level);564if(len == (size_t)-1) {565free(buf);566reply(535, "Failed to decode command");567return;568}569((char*)buf)[len] = '\0';570if(strstr((char*)buf, "\r\n") == NULL)571strlcat((char*)buf, "\r\n", buf_size);572new_ftp_command(buf);573}574575/* ------------------------------------------------------------ */576577int578sec_userok(char *userstr)579{580if(sec_complete)581return (*mech->userok)(app_data, userstr);582return 0;583}584585int586sec_session(char *user)587{588if(sec_complete && mech->session)589return (*mech->session)(app_data, user);590return 0;591}592593char *ftp_command;594595void596new_ftp_command(char *command)597{598ftp_command = command;599}600601void602delete_ftp_command(void)603{604free(ftp_command);605ftp_command = NULL;606}607608int609secure_command(void)610{611return ftp_command != NULL;612}613614enum protection_level615get_command_prot(void)616{617return command_prot;618}619620#else /* FTP_SERVER */621622void623sec_status(void)624{625if(sec_complete){626printf("Using %s for authentication.\n", mech->name);627printf("Using %s command channel.\n", level_to_name(command_prot));628printf("Using %s data channel.\n", level_to_name(data_prot));629if(buffer_size > 0)630printf("Protection buffer size: %lu.\n",631(unsigned long)buffer_size);632}else{633printf("Not using any security mechanism.\n");634}635}636637static int638sec_prot_internal(int level)639{640int ret;641char *p;642unsigned int s = 1048576;643644int old_verbose = verbose;645verbose = 0;646647if(!sec_complete){648printf("No security data exchange has taken place.\n");649return -1;650}651652if(level){653ret = command("PBSZ %u", s);654if(ret != COMPLETE){655printf("Failed to set protection buffer size.\n");656return -1;657}658buffer_size = s;659p = strstr(reply_string, "PBSZ=");660if(p)661sscanf(p, "PBSZ=%u", &s);662if(s < buffer_size)663buffer_size = s;664}665verbose = old_verbose;666ret = command("PROT %c", level["CSEP"]); /* XXX :-) */667if(ret != COMPLETE){668printf("Failed to set protection level.\n");669return -1;670}671672data_prot = (enum protection_level)level;673return 0;674}675676enum protection_level677set_command_prot(enum protection_level level)678{679int ret;680enum protection_level old = command_prot;681if(level != command_prot && level == prot_clear) {682ret = command("CCC");683if(ret != COMPLETE) {684printf("Failed to clear command channel.\n");685return prot_invalid;686}687}688command_prot = level;689return old;690}691692void693sec_prot(int argc, char **argv)694{695int level = -1;696697if(argc > 3)698goto usage;699700if(argc == 1) {701sec_status();702return;703}704if(!sec_complete) {705printf("No security data exchange has taken place.\n");706code = -1;707return;708}709level = name_to_level(argv[argc - 1]);710711if(level == -1)712goto usage;713714if((*mech->check_prot)(app_data, level)) {715printf("%s does not implement %s protection.\n",716mech->name, level_to_name(level));717code = -1;718return;719}720721if(argc == 2 || strncasecmp(argv[1], "data", strlen(argv[1])) == 0) {722if(sec_prot_internal(level) < 0){723code = -1;724return;725}726} else if(strncasecmp(argv[1], "command", strlen(argv[1])) == 0) {727if(set_command_prot(level) < 0) {728code = -1;729return;730}731} else732goto usage;733code = 0;734return;735usage:736printf("usage: %s [command|data] [clear|safe|confidential|private]\n",737argv[0]);738code = -1;739}740741void742sec_prot_command(int argc, char **argv)743{744int level;745746if(argc > 2)747goto usage;748749if(!sec_complete) {750printf("No security data exchange has taken place.\n");751code = -1;752return;753}754755if(argc == 1) {756sec_status();757} else {758level = name_to_level(argv[1]);759if(level == -1)760goto usage;761762if((*mech->check_prot)(app_data, level)) {763printf("%s does not implement %s protection.\n",764mech->name, level_to_name(level));765code = -1;766return;767}768if(set_command_prot(level) < 0) {769code = -1;770return;771}772}773code = 0;774return;775usage:776printf("usage: %s [clear|safe|confidential|private]\n",777argv[0]);778code = -1;779}780781static enum protection_level request_data_prot;782783void784sec_set_protection_level(void)785{786if(sec_complete && data_prot != request_data_prot)787sec_prot_internal(request_data_prot);788}789790791int792sec_request_prot(char *level)793{794int l = name_to_level(level);795if(l == -1)796return -1;797request_data_prot = (enum protection_level)l;798return 0;799}800801int802sec_login(char *host)803{804int ret;805struct sec_client_mech **m;806int old_verbose = verbose;807808verbose = -1; /* shut up all messages this will produce (they809are usually not very user friendly) */810811for(m = mechs; *m && (*m)->name; m++) {812void *tmp;813814tmp = realloc(app_data, (*m)->size);815if (tmp == NULL) {816warnx ("realloc %lu failed", (unsigned long)(*m)->size);817return -1;818}819app_data = tmp;820821if((*m)->init && (*(*m)->init)(app_data) != 0) {822printf("Skipping %s...\n", (*m)->name);823continue;824}825printf("Trying %s...\n", (*m)->name);826ret = command("AUTH %s", (*m)->name);827if(ret != CONTINUE){828if(code == 504){829printf("%s is not supported by the server.\n", (*m)->name);830}else if(code == 534){831printf("%s rejected as security mechanism.\n", (*m)->name);832}else if(ret == ERROR) {833printf("The server doesn't support the FTP "834"security extensions.\n");835verbose = old_verbose;836return -1;837}838continue;839}840841ret = (*(*m)->auth)(app_data, host);842843if(ret == AUTH_CONTINUE)844continue;845else if(ret != AUTH_OK){846/* mechanism is supposed to output error string */847verbose = old_verbose;848return -1;849}850mech = *m;851sec_complete = 1;852if(doencrypt) {853command_prot = prot_private;854request_data_prot = prot_private;855} else {856command_prot = prot_safe;857}858break;859}860861verbose = old_verbose;862return *m == NULL;863}864865void866sec_end(void)867{868if (mech != NULL) {869if(mech->end)870(*mech->end)(app_data);871if (app_data != NULL) {872memset(app_data, 0, mech->size);873free(app_data);874app_data = NULL;875}876}877sec_complete = 0;878data_prot = (enum protection_level)0;879}880881#endif /* FTP_SERVER */882883884885