/*
* Copyright (C) 1999-2004 Joachim Wieland <joe@mcknight.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*/
#include "jftpgw.h"
#include "fw_auth_cmds.h"
#include <sys/mman.h>
#define ANON_USERS " anonymous ftp "
extern int timeout;
extern struct log_cmd_st lcs;
extern struct hostent_list* hostcache;
extern struct conn_info_st conn_info;
extern struct serverinfo srvinfo;
static int login_setforward_user(struct clientinfo*);
static int login_setforward_pass(struct clientinfo*);
static int login_connect(struct clientinfo*);
static int login_mayconnect(struct clientinfo*);
static int login_init_connection(struct clientinfo*);
static int login_connected_setup(struct clientinfo*);
static int login_auth(struct clientinfo*);
static int login_readwelcome(struct clientinfo*);
static int login_sendauth_user(struct clientinfo*);
static int login_sendauth_pass(struct clientinfo*);
static int login_finish_login(struct clientinfo*);
static int login_loggedin_setup(struct clientinfo*);
static int login_failed(struct clientinfo*);
int handle_login(struct clientinfo* clntinfo) {
struct cmdhandlerstruct *cmdhandler;
char *buffer = 0;
int ss, cs;
int i, expected;
int protoviolations = 0;
conn_info.lcs = &lcs;
conn_info.clntinfo = clntinfo;
ss = clntinfo->serversocket;
cs = clntinfo->clientsocket;
if (clntinfo->transparent == TRANSPARENT_YES
/* we are connected */
&&
config_compare_option("logintime", "connect")) {
if (config_get_ioption("loginstyle", 0) != 0) {
jlog(5, "A login at the connection time only works with loginstyle == 0, setting loginstyle = 0");
config_option_list_delete("loginstyle");
config_option_list_add("loginstyle", "0");
}
}
cmdhandler = &login_auth_funcs
[ config_get_ioption("loginstyle", 0) ][0];
expected = QUITFUNC + 1; /* skip reset and quit function */
while (1) {
if (timeout) {
jlog(2, "Timeout in %s line %d\n", __FILE__ ,__LINE__);
return -1;
}
if (buffer) {
free(buffer);
buffer = 0;
}
buffer = readline(cs);
lcs.cmd = (char*) 0;
if (buffer) {
lcs.cmd = buffer;
free(lcs.method);
i = 0;
lcs.method = quotstrtok(lcs.cmd, WHITESPACES, &i);
if (buffer[0] == '\0') {
/* empty line. Prevent logging of the
* command */
free(buffer);
buffer = 0;
continue;
}
/* log the command */
log_cmd(&lcs);
free(lcs.method); lcs.method = (char*) 0;
lcs.respcode = 0;
lcs.transferred = 0;
}
if (!buffer) {
if (timeout) {
jlog(2, "Timeout in %s line %d\n", __FILE__
,__LINE__);
err_time_readline(cs);
} else {
err_readline(cs);
}
return -1;
}
jlog(8, "Expecting %s", cmdhandler[expected].cmd);
if (my_strcasestr(buffer, "PASS") == (char*) 0) {
jlog(8, "Got: %s", buffer);
} else {
jlog(8, "Got the password");
}
/* check for QUIT */
if (checkbegin(buffer, "QUIT")) {
int ret = (cmdhandler[QUITFUNC].func)
(buffer, &conn_info);
ret = (cmdhandler[RESETFUNC].func)
(buffer, &conn_info);
free(buffer);
/* return 1 to prevent the proxy from entering
* handle_cmds */
return 1;
}
if (cmdhandler[expected].cmd
&& checkbegin(buffer, cmdhandler[expected].cmd)) {
int ret = (cmdhandler[expected].func)
(buffer, &conn_info);
memset(buffer, 0, strlen(buffer));
protoviolations = 0;
switch (ret) {
case CMD_DONE:
/* we're done - logged in */
/* CMD_DONE is returned if the USER
* command got a 230 response back
* */
break;
case CMD_HANDLED:
/* expecting the next */
expected++;
break;
case CMD_ERROR:
/* reset counter, skip reset and
* quit function */
ret = (cmdhandler[RESETFUNC].func)
(buffer, &conn_info);
expected = QUITFUNC + 1;
/* errors are handled by the
* command handler functions
* */
break;
case CMD_ABORT:
return -1;
break;
}
/* found and called proper function */
} else {
protoviolations++;
if (protoviolations >=
config_get_ioption("loginprotocolviolations", 10)){
/* like ABORT */
sayf(clntinfo->clientsocket,
"500 Too many consequent protocol "
"violations - Closing connection\r\n");
return -1;
}
sayf(clntinfo->clientsocket,
"530 Login incorrect. Expected %scommand\r\n",
cmdhandler[expected].cmd);
/* reset counter, skip reset and quit function */
(cmdhandler[RESETFUNC].func)
(buffer, &conn_info);
expected = QUITFUNC + 1;
}
if (clntinfo->login.stage == LOGIN_ST_FULL) {
/* we are done */
free(buffer); buffer = (char*) 0;
return 0;
}
}
/* lcs.host and lcs.user are freed at the termination of the
* programm */
}
int login(struct clientinfo* clntinfo, int stage) {
int ret = 0;
if (! clntinfo->user && stage >= LOGIN_ST_USER) {
say(clntinfo->clientsocket, "500 Error logging in\r\n");
jlog(4, "No user was set - Cannot proceed");
return CMD_ERROR;
}
if ((ret = login_setforward_user(clntinfo)) < 0) {
/* the error is logged and say()ed */
return CMD_ERROR;
}
if (! clntinfo->destination) {
say(clntinfo->clientsocket, "500 Error logging in\r\n");
jlog(4, "No destination was set - Cannot proceed");
if (config_compare_option("logintime", "connect")) {
jlog(4, "This may be because of your logintime --> connect setting");
}
return CMD_ERROR;
}
if (stage >= LOGIN_ST_CONNECTED) {
ret = login_connect(clntinfo);
if (ret) { return ret; }
}
if (stage >= LOGIN_ST_USER) {
ret = login_sendauth_user(clntinfo);
if (ret) { return ret; }
}
if (stage >= LOGIN_ST_FULL) {
ret = login_connect(clntinfo);
if (ret) { return ret; }
ret = login_setforward_pass(clntinfo);
if (ret) { return ret; }
ret = login_auth(clntinfo);
if (ret) {
int ret2;
if ((ret2 = login_failed(clntinfo)) < 0) {
return ret2;
}
return ret;
} else {
config_destroy_sectionconfig();
}
ret = login_loggedin_setup(clntinfo);
if (ret) { return ret; }
}
return CMD_HANDLED;
}
static
int login_connect(struct clientinfo* clntinfo) {
int ret;
if (clntinfo->login.stage >= LOGIN_ST_CONNECTED) {
return CMD_HANDLED;
}
if ((ret = login_init_connection(clntinfo)) < 0) {
/* the error is logged and say()ed */
return ret;
}
if ((ret = login_connected_setup(clntinfo)) < 0) {
/* the error is logged and say()ed */
return ret;
}
clntinfo->login.stage = LOGIN_ST_CONNECTED;
return CMD_HANDLED;
}
static
int login_auth(struct clientinfo* clntinfo) {
int ret;
if (clntinfo->login.stage >= LOGIN_ST_LOGGEDIN) {
return CMD_HANDLED;
}
if (clntinfo->login.stage < LOGIN_ST_USER) {
if ((ret = login_sendauth_user(clntinfo)) < 0) {
clntinfo->login.stage = LOGIN_ST_CONNECTED;
return ret;
}
clntinfo->login.stage = LOGIN_ST_USER;
}
if ((ret = login_sendauth_pass(clntinfo)) < 0) {
clntinfo->login.stage = LOGIN_ST_CONNECTED;
return ret;
}
if ((ret = login_finish_login(clntinfo)) < 0) {
clntinfo->login.stage = LOGIN_ST_CONNECTED;
return ret;
}
return CMD_HANDLED;
}
static
int login_mayconnect(struct clientinfo* clntinfo) {
int allowed = 0;
unsigned long int target;
unsigned long int host_ip;
unsigned long int client_ip, server_ip;
int tags;
allowed = 0;
/* we don't know yet if clntinfo->destination is a hostname like
* "somehost.foo.com" or if it is an IP in a char* like
* "212.117.232.20"
*
* So call inet_addr(), it should make the decision :-)
*
* */
target = inet_addr(clntinfo->destination);
/* get the client_ip by asking information about the socket the
* client is connected */
client_ip = get_uint_ip(GET_IP_CLIENT, clntinfo);
server_ip = (unsigned long int) UINT_MAX;
if (target == (unsigned long int) UINT_MAX) {
/* clntinfo->destination may be a hostname, but it needn't.
* It may also be an invalid IP: "393.39.239.500" or
* anything else. Look it up.
* */
host_ip = hostent_get_ip(&hostcache, clntinfo->destination);
if (host_ip != (unsigned long int) UINT_MAX) {
/* successful lookup */
server_ip = host_ip;
} else {
/* it's not a valid IP and we could not look it up,
* so we could not get valid information about the
* destination host */
jlog(7, "Nonsense destination (no IP and could not look up hostname): %s",
clntinfo->destination);
jlog(8, "Please check your nameserver configuration. This may also happen if your chroot-environment does not contain the necessary files which the libc needs for a lookup");
allowed = 0;
}
} else {
/* okay, it's an IP */
server_ip = inet_addr(clntinfo->destination);
}
if (server_ip != (unsigned long) UINT_MAX) {
/* we could get a valid IP, now we can evaluate if client
* is allowed to connect to the destination host */
jlog(9, "Checking all tags");
tags = TAG_ALL_NOT_FORWARDED;
if (clntinfo->before_forward.user) {
/* if we are treating a forward, add this value, it
* won't come up in the option list otherwise */
tags |= TAG_FORWARDED;
}
/* checking of the configuration works such:
*
* no forward: do not evaluate <forwarded>
* always check the normal values
*
* forward: evaluate <forwarded>
* if (!in_forwarded_tag) {
* (1) check before_forward.* values
* } else {
* (2) check normal values
* }
*/
if (clntinfo->before_forward.user) {
/* Yes, there was a forward applied */
config_shrink_config(client_ip,
/* this are the original values */
/* see above (1) */
clntinfo->before_forward.dest_ip,
clntinfo->before_forward.destination,
clntinfo->before_forward.destinationport,
clntinfo->before_forward.user,
/* these are the values set by the forward */
/* pass them as well, if we are in the
* forwarded_tag, config_match_section()
* will overwrite the previous with the
* following values */
server_ip,
clntinfo->destination,
clntinfo->destinationport,
clntinfo->user,
0, /* set no specific time */
clntinfo->proxy_ip,
clntinfo->proxy_port,
srvinfo.servertype,
&hostcache,
tags);
} else {
/* not a forward */
config_shrink_config(client_ip,
server_ip,
clntinfo->destination,
clntinfo->destinationport,
clntinfo->user,
clntinfo->before_forward.dest_ip,
clntinfo->before_forward.destination,
clntinfo->before_forward.destinationport,
clntinfo->before_forward.user,
0, /* set no specific time */
clntinfo->proxy_ip,
clntinfo->proxy_port,
srvinfo.servertype,
&hostcache,
tags);
}
allowed = strcmp(config_get_option("access"), "allow") == 0;
}
if (!allowed) {
say(clntinfo->clientsocket, "531 You are not allowed to connect to that host.\r\n");
jlog(8, "Not allowed to connect to %s", clntinfo->destination);
lcs.respcode = 531;
return CMD_ERROR;
}
/* if the client was allowed, save the clients original IP */
if (!clntinfo->before_forward.user) {
clntinfo->before_forward.dest_ip = server_ip;
}
return allowed; /* a positive int */
}
static
int login_setforward_user(struct clientinfo* clntinfo) {
const char* forward;
struct slist_t* forward_list, *forward_list_cur;
char* tmp;
int newsize;
int config_state = TAG_GLOBAL | TAG_FROM | TAG_PORT
| TAG_TIME | TAG_SERVERTYPE
| TAG_PROXYIP | TAG_PROXYPORT;
/* this is already a forward */
if ( clntinfo->before_forward.user ) {
return CMD_HANDLED;
}
if ( clntinfo->destination ) {
config_state |= TAG_TO;
}
if ( clntinfo->user ) {
config_state |= TAG_USER;
}
config_shrink_config(get_uint_ip(GET_IP_CLIENT, clntinfo),
get_uint_ip(GET_IP_SERVER, clntinfo),
clntinfo->destination,
clntinfo->destinationport,
clntinfo->user,
-1, /* before_forward.dest_ip */
(char*) 0, /* before_forward.destination */
0, /* before_forward.destinationport */
(char*) 0, /* before_forward.user */
0, /* set no specific time */
clntinfo->proxy_ip,
clntinfo->proxy_port,
srvinfo.servertype,
&hostcache,
config_state);
if (config_compare_option("access", "allow") == 0) {
say(clntinfo->clientsocket, "531 You are not allowed to "
"connect to that host. Goodbye.\r\n");
jlog(5, "%s was not allowed to connect.",
conv_ip_to_char(clntinfo->client_ip));
return CMD_ERROR;
}
/* see if there is an option for the forward */
forward = config_get_option("forward");
if (! forward) {
/* no forward */
lcs.userlogin = strnulldup(clntinfo->user);
lcs.usereffective = strnulldup(clntinfo->user);
if (lcs.userforwarded) { free(lcs.userforwarded); }
lcs.userforwarded = strdup("<no forward>");
enough_mem(lcs.userforwarded);
return CMD_HANDLED;
}
/*
* <user ftp>
* forward johnfred@fooserver.com
* forward johnfred@fooserver.com,3949,p
* forward johnfred@fooserver.com * johnspass
* forward johnfred@fooserver.com JiKe94 johnspass
* </user>
* forward *@fooserver.com:2378
* forward %@fooserver.com:2378
*/
/* split the list into tokens */
if ( ! (forward_list = config_split_line( forward, WHITESPACES )) ) {
return CMD_HANDLED;
}
/* if we have a defaultforward setting, the variable user has to be
* defined so that we can decide if we can use defaultforward at
* all. If this is not the case, but there is a defaultforward
* setting, skip this whole part. This can happen by setting
* logintime to "connect" */
if (!clntinfo->user &&
forward_list->value &&
forward_list->value[0] == '%' &&
forward_list->value[1] == '@') {
slist_destroy(forward_list);
return CMD_HANDLED;
}
/* if there is a defaultforward but we already have a destination
* set, we can quit here as well */
if (clntinfo->destination &&
forward_list->value &&
forward_list->value[0] == '%' &&
forward_list->value[1] == '@') {
/* no forward is being used */
lcs.userlogin = strnulldup(clntinfo->user);
lcs.usereffective = strnulldup(clntinfo->user);
if (lcs.userforwarded) { free(lcs.userforwarded); }
lcs.userforwarded = strdup("<no forward>");
enough_mem(lcs.userforwarded);
slist_destroy(forward_list);
return CMD_HANDLED;
}
forward_list_cur = forward_list;
/* read and save the values */
clntinfo->forward.login = strdup(forward_list_cur->value);
enough_mem(clntinfo->forward.login);
forward_list_cur = forward_list_cur->next;
if (forward_list_cur && forward_list_cur->value) {
clntinfo->forward.accept_pw = strdup(forward_list_cur->value);
enough_mem(clntinfo->forward.accept_pw);
} else {
clntinfo->forward.accept_pw = (char*) 0;
}
if (forward_list_cur) {
forward_list_cur = forward_list_cur->next;
}
if (forward_list_cur && forward_list_cur->value) {
clntinfo->forward.send_pw = strdup(forward_list_cur->value);
enough_mem(clntinfo->forward.send_pw);
} else {
clntinfo->forward.send_pw = (char*) 0;
}
/* destroy the list again, we have saved the values into
* clntinfo->forward.<field> */
slist_destroy(forward_list);
/* delete those configuration values that are evaluated by the
* parsing routine - they are not necessary anymore */
config_option_list_delete("transparent-forward");
config_option_list_delete("forward");
/* back up */
clntinfo->before_forward.user = strnulldup(clntinfo->user);
clntinfo->before_forward.destination
= strnulldup(clntinfo->destination);
clntinfo->before_forward.destinationport = clntinfo->destinationport;
clntinfo->before_forward.dest_ip = get_uint_ip(GET_IP_SERVER,clntinfo);
/* special case: We don't have set a username. The client could have
* used logintime == connect and has a forward that already matches */
if ( ! clntinfo->before_forward.user ) {
clntinfo->before_forward.user = malloc(2);
enough_mem(clntinfo->before_forward.user);
clntinfo->before_forward.user[0] = '*';
clntinfo->before_forward.user[1] = '\0';
}
/* if there was only a new destination but no new username given,
* prepend the old one */
if ( ! strchr(clntinfo->forward.login, '@')
&& clntinfo->before_forward.user) {
newsize = strlen(clntinfo->before_forward.user)
+ 1 /* @ */
+ strlen(clntinfo->forward.login)
+ 1 /* term */;
tmp = (char*) malloc(newsize);
enough_mem(tmp);
snprintf( tmp, newsize, "%s@%s", clntinfo->before_forward.user,
clntinfo->forward.login);
free(clntinfo->forward.login);
clntinfo->forward.login = tmp;
}
if (clntinfo->forward.login[0] == '*' &&
clntinfo->forward.login[1] == '@') {
/* passauth */
clntinfo->forward.passauth = 1;
}
/* If there is still no destination, see if we have a defaultforward
* setting */
if (clntinfo->destination == (char*) 0 &&
clntinfo->user &&
clntinfo->forward.login[0] == '%' &&
clntinfo->forward.login[1] == '@') {
/* defaultforward */
int newsize = strlen(&(clntinfo->forward.login[1])) +
strlen(clntinfo->user) + 1;
char* tmp = (char*) malloc(newsize);
enough_mem(tmp);
snprintf(tmp, newsize, "%s%s", clntinfo->user,
&(clntinfo->forward.login[1]));
free(clntinfo->forward.login);
clntinfo->forward.login = tmp;
jlog(8, "No destination was set. Using %s because of defaultforward setting", clntinfo->forward.login);
}
/* call the parsing routine and let it set the values */
if (set_userdest(clntinfo->forward.login, 0, clntinfo, "@,: \t") < 0) {
return CMD_ERROR;
}
/* set values for log info struct */
lcs.userlogin = strnulldup(clntinfo->before_forward.user);
lcs.usereffective = strnulldup(clntinfo->user);
lcs.userforwarded = strnulldup(clntinfo->forward.login);
/* shrink the configuration again - with TAG_FORWARDED this time */
config_shrink_config(get_uint_ip(GET_IP_CLIENT, clntinfo),
/* these are the original values */
/* see above (1) */
clntinfo->before_forward.dest_ip,
clntinfo->before_forward.destination,
clntinfo->before_forward.destinationport,
clntinfo->before_forward.user,
/* these are the values set by the forward */
/* pass them as well, if we are in the
* forwarded_tag, config_match_section()
* will overwrite the previous with the
* following values */
get_uint_ip(GET_IP_SERVER, clntinfo),
clntinfo->destination,
clntinfo->destinationport,
clntinfo->user,
0, /* set no specific time */
clntinfo->proxy_ip,
clntinfo->proxy_port,
srvinfo.servertype, &hostcache,
TAG_ALL);
return CMD_HANDLED;
}
static
int login_setforward_pass(struct clientinfo* clntinfo) {
/* check if the password matches */
if (clntinfo->forward.accept_pw) {
if (strcmp(clntinfo->forward.accept_pw, "*") == 0
/* allow any pw but use destpw on dest */
|| cryptcmp(clntinfo->forward.accept_pw, clntinfo->pass)==0) {
/* check for the exact password */
clntinfo->pass = realloc(clntinfo->pass,
strlen(clntinfo->forward.send_pw) + 1);
enough_mem(clntinfo->pass);
strncpy(clntinfo->pass, clntinfo->forward.send_pw,
strlen(clntinfo->forward.send_pw) + 1
/* always true */);
} else if (cryptcmp(clntinfo->forward.accept_pw,
clntinfo->pass) != 0){
say(clntinfo->clientsocket, "530 Login incorrect\r\n");
return CMD_ERROR;
}
}
return CMD_HANDLED;
}
#define ERR_STR_P2 "Error connecting to %s port %d: %s\r\n"
static
int login_init_connection(struct clientinfo* clntinfo) {
int ss, cs = clntinfo->clientsocket;
int ret, err;
if ((ret = login_mayconnect(clntinfo)) < 0) {
/* the error is logged and say()ed */
return ret;
}
/* connect */
ss = openportname(clntinfo->destination, /* dest name */
clntinfo->destinationport, /* dest port */
/* source address */
config_get_addroption("controlserveraddress",
INADDR_ANY),
(struct portrangestruct*) 0); /* source port */
if (ss < 0) {
err = errno;
sayf(cs, "500 "ERR_STR_P2,
clntinfo->destination,
clntinfo->destinationport,
strerror(err));
jlog(5, ERR_STR_P2, clntinfo->destination,
clntinfo->destinationport,
strerror(err));
return CMD_ERROR;
}
/* connected */
clntinfo->serversocket = ss;
return CMD_HANDLED;
}
#undef ERRSTR
static
int login_connected_setup(struct clientinfo* clntinfo) {
int ret;
char* buffer;
if ((ret = login_readwelcome(clntinfo)) < 0) {
return ret;
}
if (/*clntinfo->transparent == TRANSPARENT_YES*/
/* we are connected */
/*&&*/
config_compare_option("logintime", "connect")) {
say(clntinfo->clientsocket,clntinfo->login.welcomemsg.fullmsg);
clntinfo->login.welcomemsg.fullmsg = (char*) 0;
clntinfo->login.welcomemsg.lastmsg = (char*) 0;
}
if (config_get_bool("initialsyst") == 1) {
say(clntinfo->serversocket, "SYST\r\n");
buffer = ftp_readline(clntinfo->serversocket);
if (!buffer) {
if (timeout) {
jlog(1, "Timeout in %s line %d\n", __FILE__ ,__LINE__);
err_time_readline(clntinfo->clientsocket);
return CMD_ABORT;
} else {
err_readline(clntinfo->clientsocket);
return CMD_ABORT;
}
}
if (!strlen(buffer)) {
jlog(2, "The server did not respond to the initial SYST command");
say(clntinfo->clientsocket,
"The server did not respond correctly\r\n");
free(buffer);
return CMD_ERROR;
}
free(buffer);
} else {
jlog(9, "Suppressing the initial SYST command");
}
buffer = (char*) 0;
return CMD_HANDLED;
}
static
int login_readwelcome(struct clientinfo *clntinfo) {
/* Read the welcome line */
clntinfo->login.welcomemsg = readall(clntinfo->serversocket);
if (!clntinfo->login.welcomemsg.fullmsg) {
/* an error occurred */
if (timeout) {
jlog(2, "Timeout in %s line %d\n", __FILE__ ,__LINE__);
err_time_readline(clntinfo->clientsocket);
} else {
set_errstr("Server closed the connection");
err_readline(clntinfo->clientsocket);
}
return CMD_ABORT;
}
jlog(9, "Connected to %s, got \"%s\" as welcome message",
clntinfo->destination, clntinfo->login.welcomemsg.fullmsg);
if (!checkbegin(clntinfo->login.welcomemsg.lastmsg, "220 ")) {
jlog(2, "Not a valid FTP server response (%s)",
clntinfo->login.welcomemsg.fullmsg);
say(clntinfo->clientsocket,clntinfo->login.welcomemsg.fullmsg);
free(clntinfo->login.welcomemsg.fullmsg);
clntinfo->login.welcomemsg.fullmsg = (char*) 0;
clntinfo->login.welcomemsg.lastmsg = (char*) 0;
return CMD_ERROR;
}
return CMD_HANDLED;
}
static
int login_sendauth_user(struct clientinfo* clntinfo) {
size_t sendbufsize, ret;
char* sendbuf;
if (clntinfo->login.stage >= LOGIN_ST_USER) {
return CMD_HANDLED;
}
sendbufsize = strlen("USER \r\n") + strlen(clntinfo->user) + 1;
sendbuf = (char*) malloc(sendbufsize);
enough_mem(sendbuf);
snprintf(sendbuf, sendbufsize, "USER %s\r\n", clntinfo->user);
ret = say(clntinfo->serversocket, sendbuf);
free(sendbuf);
sendbuf = 0;
if (ret < 0) {
jlog(2, "Error writing the user name to the server: %s",
strerror(errno));
return CMD_ABORT;
}
clntinfo->login.authresp = readall(clntinfo->serversocket);
if (!clntinfo->login.authresp.fullmsg) {
if (timeout) {
jlog(2, "Timeout in %s line %d\n", __FILE__ ,__LINE__);
err_time_readline(clntinfo->clientsocket);
} else {
err_readline(clntinfo->clientsocket);
}
free(clntinfo->login.welcomemsg.fullmsg);
clntinfo->login.welcomemsg.fullmsg = (char*) 0;
clntinfo->login.welcomemsg.lastmsg = (char*) 0;
return CMD_ABORT;
}
clntinfo->login.stage = LOGIN_ST_USER;
return CMD_HANDLED;
}
static
int login_sendauth_pass(struct clientinfo* clntinfo) {
size_t sendbufsize;
int ret;
char* sendbuf, *buffer;
buffer = clntinfo->login.authresp.lastmsg;
if ( !checkdigits(buffer, 331) && !checkdigits(buffer, 230)) {
/* prepend the response from the server with an error code
* and return it to the client */
sendbufsize = strlen(buffer) + 4 + 1;
sendbuf = (char*) malloc(sendbufsize);
enough_mem(sendbuf);
snprintf(sendbuf, sendbufsize, "500 %s", buffer);
say(clntinfo->clientsocket, sendbuf);
free(sendbuf);
jlog(7, "Got \"%s\" after sending the username.", buffer);
free(clntinfo->login.authresp.fullmsg);
free(clntinfo->login.welcomemsg.fullmsg);
clntinfo->login.welcomemsg.fullmsg = (char*) 0;
clntinfo->login.welcomemsg.lastmsg = (char*) 0;
clntinfo->login.authresp.fullmsg = (char*) 0;
clntinfo->login.authresp.lastmsg = (char*) 0;
return CMD_ERROR;
}
if (!checkdigits(buffer, 230)) {
char* userdup;
size_t size;
sendbufsize = strlen("PASS \r\n") + strlen(clntinfo->pass) + 1;
sendbuf = (char*) malloc(sendbufsize);
enough_mem(sendbuf);
snprintf(sendbuf, sendbufsize, "PASS %s\r\n", clntinfo->pass);
ret = say(clntinfo->serversocket, sendbuf);
size = strlen(clntinfo->user) + 3;
userdup = (char*) malloc( size );
snprintf(userdup, size, " %s ", clntinfo->user);
if (strstr(ANON_USERS, userdup)) {
clntinfo->anon_user = clntinfo->pass;
clntinfo->pass = (char*) 0;
} else {
memset(sendbuf, (char) 0, sendbufsize);
clntinfo->anon_user = (char*) 0;
}
free(sendbuf);
free(userdup);
sendbuf = userdup = (char*) 0;
free(clntinfo->login.authresp.fullmsg);
clntinfo->login.authresp.fullmsg = (char*) 0;
clntinfo->login.authresp.lastmsg = (char*) 0;
if (ret < 0) {
jlog(2, "Error writing the password to the server: %s",
strerror(errno));
free(clntinfo->login.welcomemsg.fullmsg);
clntinfo->login.welcomemsg.fullmsg = (char*) 0;
clntinfo->login.welcomemsg.lastmsg = (char*) 0;
return CMD_ABORT;
}
}
return CMD_HANDLED;
}
static
int login_loggedin_setup(struct clientinfo* clntinfo) {
/* we seem to have a successful login */
jlog(7, "Logged in to %s as %s!",
clntinfo->destination, clntinfo->user);
/* initialize the log_cmd_st structure with the values that have to
* be set only once after a successful login */
lcs.anon_user = clntinfo->anon_user;
lcs.svrip = strdup(get_char_ip(GET_IP_SERVER, clntinfo));
lcs.svrname = hostent_get_name(&hostcache, inet_addr(lcs.svrip));
lcs.svrlogin = clntinfo->destination;
/* we don't need the configuration sections and the backup anymore */
config_delete_master();
config_delete_backup();
/* get the throughput rate */
clntinfo->throughput = config_get_foption("throughput", -1.0);
clntinfo->addr_to_server =
socketinfo_get_local_ip(clntinfo->serversocket);
lcs.ifipsvr = strdup(conv_ip_to_char(clntinfo->addr_to_server));
enough_mem(lcs.ifipsvr);
clntinfo->data_addr_to_client
= config_get_addroption("dataclientaddress",
clntinfo->addr_to_client);
jlog(8, "Got %s as data client address",
inet_ntoa(*((struct in_addr*) &clntinfo->data_addr_to_client)));
clntinfo->data_addr_to_server =
config_get_addroption("dataserveraddress",
clntinfo->addr_to_server);
jlog(8, "Got %s as data server address",
inet_ntoa(*((struct in_addr*) &clntinfo->data_addr_to_server)));
if (clntinfo->servermode == UNSPEC) {
clntinfo->servermode = getservermode();
}
clntinfo->login.stage = LOGIN_ST_FULL;
return CMD_HANDLED;
}
static
int login_finish_login(struct clientinfo* clntinfo) {
char* buffer;
if (stage_action("loggedin") < 0) {
say(clntinfo->clientsocket, "421 Error setting up (see logfile)\r\n");
return CMD_ABORT;
}
clntinfo->login.authresp = readall(clntinfo->serversocket);
if (!clntinfo->login.authresp.fullmsg) {
if (timeout) {
jlog(2, "Timeout in %s line %d\n", __FILE__,
__LINE__);
err_time_readline(clntinfo->clientsocket);
}
else {
err_readline(clntinfo->clientsocket);
}
if (clntinfo->login.welcomemsg.fullmsg) {
free(clntinfo->login.welcomemsg.fullmsg);
}
return CMD_ABORT;
}
if (clntinfo->login.welcomemsg.fullmsg) {
/* we have not yet sent the welcome message to the client */
buffer = merge_responses(clntinfo->login.welcomemsg.fullmsg,
clntinfo->login.authresp.fullmsg);
/* free the welcome message */
free(clntinfo->login.welcomemsg.fullmsg);
clntinfo->login.welcomemsg.fullmsg
= clntinfo->login.welcomemsg.lastmsg = (char*) 0;
} else {
/* if the welcome message is sent, just copy the
* authentication response */
buffer = strdup(clntinfo->login.authresp.fullmsg);
enough_mem(buffer);
}
lcs.respcode = getcode(clntinfo->login.authresp.lastmsg);
if (!checkdigits(clntinfo->login.authresp.lastmsg, 230)) {
say(clntinfo->clientsocket,
clntinfo->login.authresp.lastmsg);
jlog(8, "Got \"%s\" after sending the password",
clntinfo->login.authresp.fullmsg);
free(clntinfo->login.authresp.fullmsg);
free(buffer);
clntinfo->login.authresp.fullmsg = (char*) 0;
clntinfo->login.authresp.lastmsg = (char*) 0;
return CMD_ERROR;
}
free(clntinfo->login.authresp.fullmsg);
clntinfo->login.authresp.fullmsg = (char*) 0;
clntinfo->login.authresp.lastmsg = (char*) 0;
/* Now the client receives the welcome message as well as the
* response of the server from the authentication with the same code
* at the beginning */
say(clntinfo->clientsocket, buffer);
free(buffer);
buffer =0;
return CMD_HANDLED;
}
static
int login_failed(struct clientinfo* clntinfo) {
static int failed_logins;
/* if there was a forward, restore the clntinfo fields from the
* before_forward values */
if (clntinfo->before_forward.user) {
free(clntinfo->user);
clntinfo->user = clntinfo->before_forward.user;
clntinfo->before_forward.user = (char*) 0;
}
if (clntinfo->before_forward.destination) {
free(clntinfo->destination);
clntinfo->destination = clntinfo->before_forward.destination;
clntinfo->before_forward.destination = (char*) 0;
clntinfo->destinationport
= clntinfo->before_forward.destinationport;
}
failed_logins ++;
if (failed_logins >= config_get_ioption("failedlogins", 3)) {
return CMD_ABORT;
}
/* switch configuration - remove the shrinked one,
* set the backup into place. If the login failed,
* the backup is used */
if (config_activate_backup() < 0) {
jlog(6, "Backup could not be activated - can't continue with login procedure");
return CMD_ABORT;
}
/* shrink the configuration again - this will update the option list */
config_shrink_config(get_uint_ip(GET_IP_CLIENT, clntinfo),
(unsigned long int) UINT_MAX,
(char*) 0, /* destination */
0, /* destinationport */
(char*) 0, /* user */
(unsigned long int) UINT_MAX,
(char*) 0, /* before_forward.destination */
0, /* before_forward.destinationport */
(char*) 0, /* before_forward.user */
0, /* set no specific time */
clntinfo->proxy_ip,
clntinfo->proxy_port,
srvinfo.servertype,
&hostcache,
TAG_CONNECTED);
return CMD_HANDLED;
}
/* login function flow
shrinks bindport.c: childsetup()
handle_login {login.c 51}
login {login.c 168}
shrinks login_setforward_user {login.c 334}
login_connect {login.c 214}
login_init_connection {login.c 499}
shrinks login_mayconnect {login.c 263}
login_connected_setup {login.c 528}
login_readwelcome {login.c 585}
login_sendauth_user {login.c 618}
login_setforward_pass {login.c 472}
login_auth {login.c 237}
login_sendauth_user ... {70}
login_sendauth_pass {login.c 661}
login_loggedin_setup {login.c 797}
login_successfulp {login.c 731}
shrinks login_failed {login.c 813}
We send USER, server sends 230:
cmds_after_user
sets LOGIN_ST_LOGGEDIN and calls
login(LOGIN_ST_FULL)
sets CMD_DONE to tell the handler that the login
procedure is done
*/
syntax highlighted by Code2HTML, v. 0.9.1