Logo Search packages:      
Sourcecode: yafc version File versions  Download package

security.c

/* modified by Martin Hedenfalk <mhe@home.se> Dec 2002
 */

/*
 * 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 "syshdr.h"

#ifdef SECFTP

#include "ftp.h"
#include "base64.h"
#include "commands.h"

/*RCSID("$Id: security.c,v 1.10 2002/12/05 22:12:37 mhe Exp $");*/

static struct
{
      enum protection_level level;
      const char *name;
} level_names[] =
{
      {prot_clear, "clear"},
      {prot_safe, "safe"},
      {prot_confidential, "confidential"},
      {prot_private, "private"}
};

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");
}

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;
}

static struct sec_client_mech *mechs[] = {
#ifdef HAVE_KRB5
      &gss_client_mech,
#endif
#ifdef HAVE_KRB4
      &krb4_client_mech,
#endif
#ifdef USE_SSL
      &ssl_client_mech,
#endif
      NULL
};

int sec_getc(FILE * F)
{
      if (ftp->sec_complete && ftp->data_prot) {
            char c;

            if (sec_read(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(int fd, struct buffer *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 =
            (*ftp->mech->decode) (ftp->app_data, buf->data, len, ftp->data_prot);
      buf->index = 0;
      return 0;
}

static size_t buffer_read(struct buffer *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 buffer *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(int fd, void *data, int length)
{
      size_t len;
      int rx = 0;

      if (ftp->sec_complete == 0 || ftp->data_prot == 0)
            return read(fd, data, length);

      if (ftp->in_buffer.eof_flag) {
            ftp->in_buffer.eof_flag = 0;
            return 0;
      }

      len = buffer_read(&ftp->in_buffer, data, length);
      length -= len;
      rx += len;
      data = (char *)data + len;

      while (length) {
            if (sec_get_data(fd, &ftp->in_buffer, ftp->data_prot) < 0)
                  return -1;
            if (ftp->in_buffer.size == 0) {
                  if (rx)
                        ftp->in_buffer.eof_flag = 1;
                  return rx;
            }
            len = buffer_read(&ftp->in_buffer, data, length);
            length -= len;
            rx += len;
            data = (char *)data + len;
      }
      return rx;
}

static int sec_send(int fd, char *from, int length)
{
      int bytes;
      void *buf;

      bytes =
            (*ftp->mech->encode) (ftp->app_data, from, length, ftp->data_prot,
                                            &buf);
      bytes = htonl(bytes);
      block_write(fd, &bytes, sizeof(bytes));
      block_write(fd, buf, ntohl(bytes));
      free(buf);
      return length;
}

int sec_fflush(FILE * F)
{
      if (ftp->data_prot != prot_clear) {
            if (ftp->out_buffer.index > 0) {
                  sec_write(fileno(F), ftp->out_buffer.data, ftp->out_buffer.index);
                  ftp->out_buffer.index = 0;
            }
            sec_send(fileno(F), NULL, 0);
      }
      fflush(F);
      return 0;
}

int sec_write(int fd, char *data, int length)
{
      int len = ftp->buffer_size;
      int tx = 0;

      if (ftp->data_prot == prot_clear)
            return write(fd, data, length);

      len -= (*ftp->mech->overhead) (ftp->app_data, ftp->data_prot, len);
      while (length) {
            if (length < len)
                  len = length;
            sec_send(fd, data, len);
            length -= len;
            data += len;
            tx += len;
      }
      return tx;
}

int sec_vfprintf2(FILE * f, const char *fmt, va_list ap)
{
      char *buf;
      int ret;

      if (ftp->data_prot == prot_clear)
            return vfprintf(f, fmt, ap);
      else {
            vasprintf(&buf, fmt, ap);
            ret = buffer_write(&ftp->out_buffer, buf, strlen(buf));
            free(buf);
            return ret;
      }
}

int sec_fprintf2(FILE * f, const char *fmt, ...)
{
      int ret;
      va_list ap;

      va_start(ap, fmt);
      ret = sec_vfprintf2(f, fmt, ap);
      va_end(ap);
      return ret;
}

int sec_putc(int c, FILE * F)
{
      char ch = c;

      if (ftp->data_prot == prot_clear)
            return putc(c, F);

      buffer_write(&ftp->out_buffer, &ch, 1);
      if (c == '\n' || ftp->out_buffer.index >= 1024 /* XXX */ ) {
            sec_write(fileno(F), ftp->out_buffer.data, ftp->out_buffer.index);
            ftp->out_buffer.index = 0;
      }
      return c;
}

int sec_read_msg(char *s, int level)
{
      int len;
      char *buf;
      int code;

      buf = malloc(strlen(s));
      len = base64_decode(s + 4, buf);    /* XXX */

      len = (*ftp->mech->decode) (ftp->app_data, buf, len, level);
      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;
}

int sec_vfprintf(FILE * f, const char *fmt, va_list ap)
{
      char *buf;
      void *enc;
      int len;

      if (!ftp->sec_complete)
            return vfprintf(f, fmt, ap);

      vasprintf(&buf, fmt, ap);
      len =
            (*ftp->mech->encode) (ftp->app_data, buf, strlen(buf),
                                            ftp->command_prot, &enc);
      free(buf);
      if (len < 0) {
            printf(_("Failed to encode command.\n"));
            return -1;
      }
      if (base64_encode(enc, len, &buf) < 0) {
            printf(_("Out of memory base64-encoding.\n"));
            return -1;
      }
      if (ftp->command_prot == prot_safe)
            fprintf(f, "MIC %s", buf);
      else if (ftp->command_prot == prot_private)
            fprintf(f, "ENC %s", buf);
      else if (ftp->command_prot == prot_confidential)
            fprintf(f, "CONF %s", buf);
      free(buf);
      return 0;
}

int sec_fprintf(FILE * f, const char *fmt, ...)
{
      va_list ap;
      int ret;

      va_start(ap, fmt);
      ret = sec_vfprintf(f, fmt, ap);
      va_end(ap);
      return ret;
}

/* end common stuff */

void sec_status(void)
{
      if (ftp->sec_complete) {
            printf(_("Using %s for authentication.\n"), ftp->mech->name);
            printf(_("Using %s command channel.\n"),
                     level_to_name(ftp->command_prot));
            printf(_("Using %s data channel.\n"), level_to_name(ftp->data_prot));
            if (ftp->buffer_size > 0)
                  printf(_("Protection buffer size: %lu.\n"),
                           (unsigned long)ftp->buffer_size);
      } else {
            printf(_("Not using any security mechanism.\n"));
      }
}

static int sec_prot_internal(int level)
{
      int ret;
      char *p;
      unsigned int s = 1048576;

      ftp_set_tmp_verbosity(vbError);

      if (!ftp->sec_complete) {
            ftp_err(_("No security data exchange has taken place.\n"));
            return -1;
      }

      if (level) {
            ret = ftp_cmd("PBSZ %u", s);
            if (ftp->code != ctComplete) {
                  ftp_err(_("Failed to set protection buffer size.\n"));
                  return -1;
            }
            ftp->buffer_size = s;
            p = strstr(ftp->reply, "PBSZ=");
            if (p)
                  sscanf(p, "PBSZ=%u", &s);
            if (s < ftp->buffer_size)
                  ftp->buffer_size = s;
      }

      ret = ftp_cmd("PROT %c", level["CSEP"]);  /* XXX :-) */
      if (ftp->code != ctComplete) {
            ftp_err(_("Failed to set protection level.\n"));
            return -1;
      }

      ftp->data_prot = (enum protection_level)level;
      url_setprotlevel(ftp->url, level_to_name(ftp->data_prot));
      return 0;
}

enum protection_level set_command_prot(enum protection_level level)
{
      enum protection_level old = ftp->command_prot;

      ftp->command_prot = level;
      return old;
}

void cmd_prot(int argc, char **argv)
{
      int level = -1;

      OPT_HELP
            ("Set Kerberos protection level for command or data channel.  Usage:\n"
             "  idle [options] [command|data] level\n" "Options:\n"
             "  -h, --help    show this help\n"
             "level should be one of the following:\n" "  clear\n" "  safe\n"
             "  confidential\n" "  private\n");

      minargs(optind);
      maxargs(optind + 1);

      if (!ftp->sec_complete) {
            ftp_err(_("No security data exchange has taken place\n"));
            return;
      }
      level = name_to_level(argv[argc - 1]);

      if (level == -1) {
            ftp_err(_("Unrecognized protection level %s\n"), argv[argc - 1]);
            return;
      }

      if ((*ftp->mech->check_prot) (ftp->app_data, level)) {
            ftp_err(_("%s does not implement %s protection\n"),
                     ftp->mech->name, level_to_name(level));
            return;
      }

      if (argc == optind + 1 || strncasecmp(argv[optind], "data",
                                                              strlen(argv[optind])) == 0) {
            if (sec_prot_internal(level) < 0) {
                  return;
            }
      } else if (strncasecmp(argv[optind], "command", strlen(argv[optind])) ==
                     0) set_command_prot(level);
      else {
            ftp_err(_("Syntax error, try %s --help for more information\n"),
                     argv[0]);
      }

      return;
}

static enum protection_level request_data_prot;

void sec_set_protection_level(void)
{
      if (ftp->sec_complete && ftp->data_prot != request_data_prot)
            sec_prot_internal(request_data_prot);
}

int sec_request_prot(char *level)
{
      int l = name_to_level(level);

      if (l == -1)
            return -1;
      request_data_prot = (enum protection_level)l;
      return 0;
}

static struct sec_client_mech **find_mech(const char *name)
{
      struct sec_client_mech **m;

      for(m = mechs; *m && (*m)->name; m++) {
            if(strcmp(name, (*m)->name) == 0)
                  return m;
      }
      return 0;
}

/* return values:
 * 0 - success
 * 1 - failed, try another mechanism
 * -1 - failed, security extensions not supported
 */
int sec_login(char *host, const char *mech_to_try)
{
      int ret;
      struct sec_client_mech **m;
      void *tmp;

      /* shut up all messages this will produce (they
       are usually not very user friendly) */
      ftp_set_tmp_verbosity(vbError);

      if(!mech_to_try || strcasecmp(mech_to_try, "none") == 0)
            return 0;

      m = find_mech(mech_to_try);
      if(!m)
            return 1;

      tmp = realloc(ftp->app_data, (*m)->size);
      if (tmp == NULL) {
            ftp_err(_("realloc %u failed"), (*m)->size);
            return -1;
      }
      ftp->app_data = tmp;

      if ((*m)->init && (*(*m)->init) (ftp->app_data) != 0) {
            printf(_("Skipping %s...\n"), (*m)->name);
            return 1;
      }
      printf(_("Trying %s...\n"), (*m)->name);
      ret = ftp_cmd("AUTH %s", (*m)->name);
      if (ftp->code != ctContinue) {
            if (ret == 504) {
                  printf(_("%s is not supported by the server.\n"), (*m)->name);
            } else if (ret == 534) {
                  printf(_("%s rejected as security mechanism.\n"), (*m)->name);
            } else if (ftp->code == ctError) {
                  printf(_("The server doesn't support the FTP "
                               "security extensions.\n"));
                  return -1;
            }
            return 1;
      }

      ret = (*(*m)->auth) (ftp->app_data, host);

      if (ret == AUTH_CONTINUE)
            return 1;
      else if (ret != AUTH_OK) {
            /* mechanism is supposed to output error string */
            return -1;
      }
      ftp->mech = *m;
      ftp->sec_complete = 1;
      ftp->command_prot = prot_safe;

      return *m == NULL;
}

void sec_end(void)
{
      if (ftp->mech != NULL && ftp->app_data != NULL) {
            if (ftp->mech->end)
                  (*ftp->mech->end) (ftp->app_data);
            memset(ftp->app_data, 0, ftp->mech->size);
            free(ftp->app_data);
            ftp->app_data = NULL;
      }
      ftp->sec_complete = false;
      ftp->data_prot = (enum protection_level)0;
}

#endif /* SECFTP */

Generated by  Doxygen 1.6.0   Back to index