123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546 |
- /* This source code was modified by Martin Hedenfalk <mhe@stacken.kth.se> for
- * use in Curl. His latest changes were done 2000-09-18.
- *
- * It has since been patched and modified a lot by Daniel Stenberg
- * <daniel@haxx.se> to make it better applied to curl conditions, and to make
- * it not use globals, pollute name space and more. This source code awaits a
- * rewrite to work around the paragraph 2 in the BSD licenses as explained
- * below.
- *
- * Copyright (c) 1998, 1999 Kungliga Tekniska Högskolan
- * (Royal Institute of Technology, Stockholm, Sweden).
- *
- * Copyright (C) 2001 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * 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"
- #ifndef CURL_DISABLE_FTP
- #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
- #define _MPRINTF_REPLACE /* we want curl-functions instead of native ones */
- #include <curl/mprintf.h>
- #include <stdlib.h>
- #include <string.h>
- #ifdef HAVE_NETDB_H
- #include <netdb.h>
- #endif
- #ifdef HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #include "urldata.h"
- #include "krb4.h"
- #include "curl_base64.h"
- #include "sendf.h"
- #include "ftp.h"
- #include "curl_memory.h"
- #include "rawstr.h"
- /* The last #include file should be: */
- #include "memdebug.h"
- static const struct {
- enum protection_level level;
- const char *name;
- } level_names[] = {
- { prot_clear, "clear" },
- { prot_safe, "safe" },
- { prot_confidential, "confidential" },
- { prot_private, "private" }
- };
- static enum protection_level
- name_to_level(const char *name)
- {
- int i;
- for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++)
- if(checkprefix(name, level_names[i].name))
- return level_names[i].level;
- return (enum protection_level)-1;
- }
- static const struct Curl_sec_client_mech * const mechs[] = {
- #ifdef HAVE_GSSAPI
- &Curl_krb5_client_mech,
- #endif
- #ifdef HAVE_KRB4
- &Curl_krb4_client_mech,
- #endif
- NULL
- };
- /* TODO: This function isn't actually used anywhere and should be removed */
- int
- Curl_sec_getc(struct connectdata *conn, FILE *F)
- {
- if(conn->sec_complete && conn->data_prot) {
- char c;
- if(Curl_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 && (errno == EINTR || errno == EAGAIN))
- /* TODO: this will busy loop in the EAGAIN case */
- continue;
- else if(b < 0)
- return -1;
- len -= b;
- p += b;
- }
- return p - (unsigned char*)buf;
- }
- static int
- block_write(int fd, const void *buf, size_t len)
- {
- const unsigned char *p = buf;
- int b;
- while(len) {
- b = write(fd, p, len);
- if(b < 0 && (errno == EINTR || errno == EAGAIN))
- continue;
- else 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 len;
- int b;
- b = block_read(fd, &len, sizeof(len));
- if(b == 0)
- return 0;
- else if(b < 0)
- return -1;
- len = ntohl(len);
- /* TODO: This realloc will cause a memory leak in an out of memory
- * condition */
- buf->data = realloc(buf->data, len);
- b = buf->data ? block_read(fd, buf->data, len) : -1;
- if(b == 0)
- return 0;
- else if(b < 0)
- return -1;
- buf->size = (conn->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)
- {
- if(buf->size - buf->index < len)
- 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
- Curl_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) < 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, const char *from, int length)
- {
- int bytes;
- void *buf;
- enum protection_level protlevel = conn->data_prot;
- int iscmd = protlevel == prot_cmd;
- if(iscmd) {
- if(!strncmp(from, "PASS ", 5) || !strncmp(from, "ACCT ", 5))
- protlevel = prot_private;
- else
- protlevel = conn->command_prot;
- }
- bytes = (conn->mech->encode)(conn->app_data, from, length, protlevel,
- &buf, conn);
- if(iscmd) {
- char *cmdbuf;
- bytes = Curl_base64_encode(conn->data, (char *)buf, bytes, &cmdbuf);
- if(bytes > 0) {
- if(protlevel == prot_private)
- block_write(fd, "ENC ", 4);
- else
- block_write(fd, "MIC ", 4);
- block_write(fd, cmdbuf, bytes);
- block_write(fd, "\r\n", 2);
- Curl_infof(conn->data, "%s %s\n",
- protlevel == prot_private ? "ENC" : "MIC", cmdbuf);
- free(cmdbuf);
- }
- }
- else {
- bytes = htonl(bytes);
- block_write(fd, &bytes, sizeof(bytes));
- block_write(fd, buf, ntohl(bytes));
- }
- free(buf);
- return length;
- }
- int
- Curl_sec_fflush_fd(struct connectdata *conn, int fd)
- {
- if(conn->data_prot != prot_clear) {
- if(conn->out_buffer.index > 0){
- Curl_sec_write(conn, fd,
- conn->out_buffer.data, conn->out_buffer.index);
- conn->out_buffer.index = 0;
- }
- sec_send(conn, fd, NULL, 0);
- }
- return 0;
- }
- int
- Curl_sec_write(struct connectdata *conn, int fd, const char *buffer, int length)
- {
- int len = conn->buffer_size;
- int tx = 0;
- if(conn->data_prot == prot_clear)
- return write(fd, buffer, length);
- len -= (conn->mech->overhead)(conn->app_data, conn->data_prot, len);
- if(len <= 0)
- len = length;
- while(length){
- if(length < len)
- len = length;
- sec_send(conn, fd, buffer, len);
- length -= len;
- buffer += len;
- tx += len;
- }
- return tx;
- }
- ssize_t
- Curl_sec_send(struct connectdata *conn, int num, const char *buffer, int length)
- {
- curl_socket_t fd = conn->sock[num];
- return (ssize_t)Curl_sec_write(conn, fd, buffer, length);
- }
- int
- Curl_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 */) {
- Curl_sec_write(conn, fileno(F), conn->out_buffer.data,
- conn->out_buffer.index);
- conn->out_buffer.index = 0;
- }
- return c;
- }
- int
- Curl_sec_read_msg(struct connectdata *conn, char *s, int level)
- {
- int len;
- unsigned char *buf;
- int code;
- len = Curl_base64_decode(s + 4, &buf); /* XXX */
- if(len > 0)
- len = (conn->mech->decode)(conn->app_data, buf, len, level, conn);
- else
- return -1;
- if(len < 0) {
- free(buf);
- return -1;
- }
- if(conn->data->set.verbose) {
- buf[len] = '\n';
- Curl_debug(conn->data, CURLINFO_HEADER_IN, (char *)buf, len + 1, conn);
- }
- buf[len] = '\0';
- if(buf[3] == '-')
- code = 0;
- else
- sscanf((char *)buf, "%d", &code);
- if(buf[len-1] == '\n')
- buf[len-1] = '\0';
- strcpy(s, (char *)buf);
- free(buf);
- return code;
- }
- enum protection_level
- Curl_set_command_prot(struct connectdata *conn, enum protection_level level)
- {
- enum protection_level old = conn->command_prot;
- conn->command_prot = level;
- return old;
- }
- static int
- sec_prot_internal(struct connectdata *conn, int level)
- {
- char *p;
- unsigned int s = 1048576;
- ssize_t nread;
- if(!conn->sec_complete){
- infof(conn->data, "No security data exchange has taken place.\n");
- return -1;
- }
- if(level){
- int code;
- if(Curl_ftpsendf(conn, "PBSZ %u", s))
- return -1;
- if(Curl_GetFTPResponse(&nread, conn, &code))
- return -1;
- if(code/100 != 2){
- failf(conn->data, "Failed to set protection buffer size.");
- return -1;
- }
- conn->buffer_size = s;
- p = strstr(conn->data->state.buffer, "PBSZ=");
- if(p)
- sscanf(p, "PBSZ=%u", &s);
- if(s < conn->buffer_size)
- conn->buffer_size = s;
- }
- if(Curl_ftpsendf(conn, "PROT %c", level["CSEP"]))
- return -1;
- if(Curl_GetFTPResponse(&nread, conn, NULL))
- return -1;
- if(conn->data->state.buffer[0] != '2'){
- failf(conn->data, "Failed to set protection level.");
- return -1;
- }
- conn->data_prot = (enum protection_level)level;
- if(level == prot_private)
- conn->command_prot = (enum protection_level)level;
- return 0;
- }
- void
- Curl_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
- Curl_sec_request_prot(struct connectdata *conn, const char *level)
- {
- int l = name_to_level(level);
- if(l == -1)
- return -1;
- conn->request_data_prot = (enum protection_level)l;
- return 0;
- }
- int
- Curl_sec_login(struct connectdata *conn)
- {
- int ret;
- const struct Curl_sec_client_mech * const *m;
- ssize_t nread;
- struct SessionHandle *data=conn->data;
- int ftpcode;
- 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);
- if(Curl_ftpsendf(conn, "AUTH %s", (*m)->name))
- return -1;
- if(Curl_GetFTPResponse(&nread, conn, &ftpcode))
- return -1;
- if(conn->data->state.buffer[0] != '3'){
- switch(ftpcode) {
- case 504:
- infof(data,
- "%s is not supported by the server.\n", (*m)->name);
- break;
- case 534:
- infof(data, "%s rejected as security mechanism.\n", (*m)->name);
- break;
- default:
- if(conn->data->state.buffer[0] == '5') {
- infof(data, "The server doesn't support the FTP "
- "security extensions.\n");
- return -1;
- }
- break;
- }
- continue;
- }
- ret = (*(*m)->auth)(conn->app_data, conn);
- if(ret == AUTH_CONTINUE)
- continue;
- else if(ret != AUTH_OK){
- /* mechanism is supposed to output error string */
- return -1;
- }
- conn->mech = *m;
- conn->sec_complete = 1;
- conn->command_prot = prot_safe;
- /* Set the requested protection level */
- /* BLOCKING */
- Curl_sec_set_protection_level(conn);
- break;
- }
- return *m == NULL;
- }
- void
- Curl_sec_end(struct connectdata *conn)
- {
- if(conn->mech != NULL) {
- if(conn->mech->end)
- (conn->mech->end)(conn->app_data);
- memset(conn->app_data, 0, conn->mech->size);
- free(conn->app_data);
- conn->app_data = NULL;
- }
- conn->sec_complete = 0;
- conn->data_prot = (enum protection_level)0;
- conn->mech=NULL;
- }
- #endif /* HAVE_KRB4 */
- #endif /* CURL_DISABLE_FTP */
|