/* * Copyright (C) 1999-2004 Joachim Wieland * * 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 "cmds.h" /* static functions here */ static int cmds_after_user(struct conn_info_st*); static int cmds_after_pass(struct conn_info_st*); int login(struct clientinfo*, int); struct destination_t { char* hostname; unsigned int port; }; /* returns 0 if the user has supplied the correct password */ static int fw_validate_user(const char* user, const char* fwpass, const char* method, const char* pass) { if (cryptcmp(pass, fwpass) == 0) { return 0; } return 1; } /* returns 0 if the user has supplied the correct password */ static int fw_validate(const char* fwuser, const char* fwpass) { struct slist_t* acct_list = config_get_option_array("account"); struct slist_t* account, *acct_line; const char* user, *method, *pass; if ( ! acct_list ) { jlog(5, "No account information found for %s", fwuser); return 1; } /* reverse the list to keep the paradigma: if there are several * similar options, the last one is taken */ acct_list = slist_reverse(acct_list); account = acct_list; do { acct_line = config_split_line( account -> value, WHITESPACES ); if (!acct_line) { goto error_line; } user = acct_line->value; if (!user || !strlen(user) || !acct_line->next) { goto error_line; } method = acct_line->next->value; if (!method || !strlen(method) || !acct_line->next->next) { goto error_line; } pass = acct_line->next->next->value; if (!pass || !strlen(pass)) { goto error_line; } /* parsing went fine down here */ if (strcmp(user, fwuser) == 0) { /* jump out of the loop */ int ret; slist_destroy(acct_list); ret = fw_validate_user(fwuser, fwpass, method, pass); slist_destroy(acct_line); return ret; } continue; error_line: slist_destroy(acct_line); jlog(5, "Incorrect account specification: %s", account->value); } while ( (account = account -> next) ); slist_destroy(acct_list); return 1; } int std_reset(const char* args, struct conn_info_st* conn_info) { /* set all values to (char*) 0 after free()ing */ struct clientinfo* c = conn_info->clntinfo; jlog(8, "resetting login information"); if (!c->transparent == TRANSPARENT_YES || !config_compare_option("logintime", "connect")) { c->login.stage = LOGIN_ST_NOT_CONNECTED; if (c->destination) { free(c->destination); c->destination = (char*) 0; } c->destinationport = 0; } if (c->user) { free(c->user); c->user = (char*) 0; } if (c->pass) { free(c->pass); c->pass = (char*) 0; } if (c->anon_user) { free(c->anon_user); c->anon_user = (char*) 0; } if (c->fw_auth.user) { free(c->fw_auth.user); c->fw_auth.user = (char*) 0; } if (c->fw_auth.pass) { free(c->fw_auth.pass); c->fw_auth.pass = (char*) 0; } if (c->before_forward.user) { free(c->before_forward.user); c->before_forward.user = (char*) 0; } if (c->before_forward.destination) { free(c->before_forward.destination); c->before_forward.destination = (char*) 0; } if (c->login.authresp.fullmsg) { free(c->login.authresp.fullmsg); c->login.authresp.fullmsg = (char*) 0; c->login.authresp.lastmsg = (char*) 0; } return CMD_HANDLED; } #define DELIMITERS "@,: \t" static struct destination_t fw_auth_parse_host_port(const char* cmd) { struct destination_t dest = { (char*) 0, 0 }; char* portstr; int offset = 0; if (! cmd) { return dest; } dest.hostname = quotstrtok(cmd, DELIMITERS, &offset); if (!dest.hostname) { return dest; } if (strlen(dest.hostname) == 0) { free(dest.hostname); dest.hostname = (char*) 0; return dest; } portstr = quotstrtok(cmd, DELIMITERS, &offset); dest.port = conv_char2long(portstr, config_get_ioption(("serverport"), DEFAULTSERVERPORT)); return dest; } static int fw_forward(const char* user, const char* dest, struct clientinfo* clntinfo) { if ( ! user ) { say(clntinfo->clientsocket, "500 Expecting user name\r\n"); jlog(5, "No username given"); return CMD_ERROR; } if (clntinfo->forward.passauth && user && dest) { clntinfo->destination = strdup(dest); enough_mem(clntinfo->destination); } if (strcmp(user, "*") == 0) { /* keep the old user name */ jlog(8, "no new user name - keeping old one: %s", clntinfo->user); } else { free(clntinfo->user); clntinfo->user = strdup(user); enough_mem(clntinfo->user); } if (!dest || strlen(dest) == 0 || strcmp(dest, "*") == 0) { /* keep the old one */ jlog(8, "no new destination - keeping old one: %s", clntinfo->destination); } else { free(clntinfo->destination); clntinfo->destination = strdup(dest); enough_mem(clntinfo->destination); } return CMD_HANDLED; } static int fw_transparent(struct clientinfo* clntinfo) { const char* transfor_opt = config_get_option("transparent-forward"); /* check for transdest */ /* opt => dest => forwardhost */ struct sockaddr_in t_in; if (clntinfo->transparent == TRANSPARENT_NO) { /* not in transparent mode */ jlog(9, "fw_transparent: not in transparent mode"); return CMD_HANDLED; } if (transfor_opt) { /* transparent forward active */ jlog(9, "fw_transparent: transparent forward active"); return CMD_HANDLED; } if (clntinfo->destination) { jlog(9, "fw_transparent: Destination already set"); return CMD_HANDLED; } if (config_get_bool("transparent-proxy") == 0) { jlog(4, "No destination set and transparent proxy support disabled"); say(clntinfo->clientsocket, "500 No destination\r\n"); return CMD_ERROR; } t_in = clntinfo->transparent_destination; clntinfo->destination = strdup(inet_ntoa(t_in.sin_addr)); clntinfo->destinationport = ntohs(clntinfo->transparent_destination.sin_port); /* Determine the IP the client sees of us */ if (strcasecmp(config_get_option("getinternalip"), "configuration") == 0) { clntinfo->addr_to_client = config_get_addroption("dataclientaddress", INADDR_ANY); } else { clntinfo->addr_to_client = socketinfo_get_local_addr_by_sending(clntinfo->clientsocket); } jlog(8, "Using transparent proxy. Connecting to %s port %d. User: %s", clntinfo->destination, clntinfo->destinationport, clntinfo->user); return CMD_HANDLED; } static int fw_transparent_forward(struct clientinfo* clntinfo) { const char* transforward_opt = config_get_option("transparent-forward"); size_t usersize; struct destination_t destination; char* transparent_target; if (! transforward_opt) { return CMD_HANDLED; } if (clntinfo->transparent == TRANSPARENT_NO) { /* not in transparent mode */ return CMD_HANDLED; } /* determine the intended destination */ transparent_target = socketinfo_get_transparent_target_char( clntinfo->clientsocket); if (!transparent_target) { jlog(4, "Could not get transparent destination"); say(clntinfo->clientsocket, "500 Error logging in\r\n"); return CMD_ERROR; } if (!clntinfo->before_forward.user) { char* myself; struct in_addr myself_in; clntinfo->before_forward.user = strdup(clntinfo->user); /* if there was a transparent forward, the original * destination was the proxy's interface */ clntinfo->before_forward.dest_ip = socketinfo_get_local_ip(clntinfo->clientsocket); myself_in.s_addr = clntinfo->before_forward.dest_ip; myself = inet_ntoa(myself_in); if (myself) { clntinfo->before_forward.destination = strdup(myself); } else { clntinfo->before_forward.destination = " -error- "; } clntinfo->before_forward.destinationport = socketinfo_get_local_port(clntinfo->clientsocket); } if (config_get_bool("transparent-forward-include-port") == 0) { char* colon = strrchr(transparent_target, ':'); if (colon) { *colon = '\0'; } } /* resize the new USER buffer */ usersize = strlen(clntinfo->user) + 1 /* @ */ + strlen(transparent_target) + 1 /* scnprintf seems to need it */ + 1; /* Terminate */ /* write user@dest into user field. dest is the intended destination */ clntinfo->user = realloc(clntinfo->user, usersize); enough_mem(clntinfo->user); scnprintf(clntinfo->user, usersize, "@"); scnprintf(clntinfo->user, usersize, transparent_target); free(transparent_target); /* replace dest by the specified forward */ if (!config_compare_option("logintime", "connect")) { destination = fw_auth_parse_host_port(transforward_opt); if (! destination.hostname) { say(clntinfo->clientsocket, "500 Error logging in\r\n"); jlog(4, "Could not parse transparent forward target\r\n"); return CMD_ERROR; } free(clntinfo->destination); clntinfo->destination = destination.hostname; clntinfo->destinationport = destination.port; destination.hostname = (char*) 0; } else { /* this has already been done in bindport.c */ } /* Determine the IP the client sees of us */ if (strcasecmp(config_get_option("getinternalip"), "configuration") == 0) { clntinfo->addr_to_client = config_get_addroption("dataclientaddress", INADDR_ANY); } else { clntinfo->addr_to_client = socketinfo_get_local_addr_by_sending(clntinfo->clientsocket); } clntinfo->transparent = TRANSPARENT_YES; jlog(8, "Using transparent forward. Connecting to %s port %d. User: %s", clntinfo->destination, clntinfo->destinationport, clntinfo->user); return CMD_HANDLED; } static int fw_port_mode(const char* portstr, const char* modestr, struct clientinfo* clntinfo) { long int pno; if (portstr) { pno = strtol(portstr, NULL, 10); if ((errno == ERANGE && (pno == LONG_MIN || pno == LONG_MAX)) || pno == 0) { /* it was not a number */ clntinfo->destinationport = 0; /* maybe it is a mode */ modestr = portstr; portstr = (char*) 0; } else { clntinfo->destinationport = pno; } } if (!clntinfo->destinationport) { clntinfo->destinationport = config_get_ioption("serverport", DEFAULTSERVERPORT); } clntinfo->servermode = UNSPEC; if (modestr) { if (modestr && (strchr("ap", *modestr))) { jlog(9, "mode specified: %s", modestr); switch (*modestr) { case 'a': clntinfo->servermode = ACTIVE; jlog(9, "p-s: active"); break; case 'p': clntinfo->servermode = PASSIVE; jlog(9, "p-s: passive"); break; } } } return CMD_HANDLED; } int set_userdest(const char *buffer, int offset, struct clientinfo* clntinfo, const char* delimiters) { char* user, *host, *port, *mode; /* joe@host,21,p */ /* joe */ /* joe@host */ /* joe,host,21,p */ if (buffer[0] == '@') { user = (char*) 0; } else { user = quotstrtok(buffer, delimiters, &offset); } host = quotstrtok(buffer, delimiters, &offset); port = quotstrtok(buffer, delimiters, &offset); mode = quotstrtok(buffer, delimiters, &offset); if (fw_forward(user, host, clntinfo) != CMD_HANDLED) { free(user); free(host); free(port); free(mode); return CMD_ERROR; } if (fw_transparent_forward(clntinfo) != CMD_HANDLED) { free(user); free(host); free(port); free(mode); return CMD_ERROR; } if (fw_transparent(clntinfo) != CMD_HANDLED) { free(user); free(host); free(port); free(mode); return CMD_ERROR; } if (fw_port_mode(port, mode, clntinfo) != CMD_HANDLED) { free(user); free(host); free(port); free(mode); return CMD_ERROR; } free(user); free(host); free(port); free(mode); return CMD_HANDLED; } static int std_user(const char* args, struct conn_info_st* conn_info, const char* delimiters) { int ret; char* args_copy = strdup(args); enough_mem(args_copy); if (conn_info->clntinfo->forward.passauth == 0) { /* passallauth was not set */ } else { delimiters = ""; } if (set_userdest(args_copy, strlen("USER "), conn_info->clntinfo, delimiters) != CMD_HANDLED) { free(args_copy); return CMD_ERROR; } free(args_copy); ret = cmds_after_user(conn_info); if (ret != CMD_HANDLED && ret != CMD_DONE) { return CMD_ERROR; } jlog(7, "Client logged in: User: %s, Dest: %s:%d", conn_info->clntinfo->user, conn_info->clntinfo->destination, conn_info->clntinfo->destinationport); /* ret can be CMD_HANDLED or CMD_DONE */ return ret; } int std_user_plain(const char* args, struct conn_info_st* conn_info) { return std_user(args, conn_info, ""); } int std_user_split(const char* args, struct conn_info_st* conn_info) { return std_user(args, conn_info, DELIMITERS); } static int cmds_after_user(struct conn_info_st* conn_info) { if (config_compare_option("logintime", "user") || config_compare_option("logintime", "connect")) { /* connect if we should connect after having * received the USER command. Do not connect, if we * are already connected or should connect later on * */ int ret, code; char* buffer; ret = login(conn_info->clntinfo, LOGIN_ST_USER); if (ret) { return CMD_ERROR; } if (conn_info->clntinfo->login.welcomemsg.fullmsg) { buffer = merge_responses( conn_info->clntinfo->login.welcomemsg.fullmsg, conn_info->clntinfo->login.authresp.fullmsg); } else { buffer = strdup(conn_info->clntinfo->login.authresp.fullmsg); } say(conn_info->clntinfo->clientsocket, buffer); conn_info->lcs->respcode = getcode(buffer); free(buffer); /* okay, there was no problem sending the user name and * receiving the result, now check the result */ code = getcode(conn_info->clntinfo->login.authresp.fullmsg); if (code != 331 && code != 230) { jlog(6, "Didn't get successful message after sending " "the user name: %s\n", conn_info->clntinfo->login.authresp.fullmsg); return CMD_ERROR; } /* Free the welcome message. The authentication * response is always free'ed in login_send_auth */ free(conn_info->clntinfo->login.welcomemsg.fullmsg); conn_info->clntinfo->login.welcomemsg.fullmsg = (char*) 0; conn_info->clntinfo->login.auth_resp_sent = 1; if (code == 230) { /* already logged in */ free(conn_info->clntinfo->login.authresp.fullmsg); conn_info->clntinfo->login.authresp.fullmsg = (char*)0; conn_info->clntinfo->login.stage = LOGIN_ST_LOGGEDIN; if (login(conn_info->clntinfo, LOGIN_ST_FULL) < 0) { return CMD_ERROR; } /* this is the only case where we return CMD_DONE so * that the login handler doesn't wait for the * command that would follow but knows that his job * is finished */ return CMD_DONE; } } else { char* user; if (conn_info->clntinfo->before_forward.user) { user = conn_info->clntinfo->before_forward.user; } else { user = conn_info->clntinfo->user; } sayf(conn_info->clntinfo->clientsocket, "331 Password required for %s.\r\n", user); conn_info->lcs->respcode = 331; } return CMD_HANDLED; } int std_pass(const char* args, struct conn_info_st* conn_info) { conn_info->clntinfo->pass = strdup(args + strlen("PASS ")); enough_mem(conn_info->clntinfo->pass); return cmds_after_pass(conn_info); } static int cmds_after_pass(struct conn_info_st* conn_info) { int ret; /* we are not yet connected to a server */ ret = login(conn_info->clntinfo, LOGIN_ST_FULL); if (ret == CMD_ABORT) { return ret; } if (ret < 0) { /* login failed - do not print an error * message */ /* conn_info->clntinfo->serversocket = ss = -1; */ return CMD_ERROR; } return CMD_HANDLED; } static int cmds_after_fwpass(struct conn_info_st* conn_info) { if (fw_validate(conn_info->clntinfo->fw_auth.user, conn_info->clntinfo->fw_auth.pass) == 0) { say(conn_info->clntinfo->clientsocket, "230 Login to firewall successful\r\n"); } else { say(conn_info->clntinfo->clientsocket, "530 Login failed.\r\n"); return CMD_ERROR; } return CMD_HANDLED; } int fw_open(const char* args, struct conn_info_st* conn_info) { struct destination_t destination; char* space = (char*) 0; if (args) { space = strchr(args, ' '); } if ( !args || ! space || *(space + 1) == '\0') { say(conn_info->clntinfo->clientsocket, "530-Not a valid password\r\n" "530 Login failed.\r\n"); return CMD_ERROR; } destination = fw_auth_parse_host_port(space + 1); if (destination.hostname == (char*) 0) { say(conn_info->clntinfo->clientsocket, "530-Not a valid destination\r\n" "530 Login failed.\r\n"); return CMD_ERROR; } conn_info->clntinfo->destination = destination.hostname; conn_info->clntinfo->destinationport = destination.port; /* I don't know if 332 is the correct code */ say(conn_info->clntinfo->clientsocket, "220 Welcome. Please proceed.\r\n"); return CMD_HANDLED; } int fw_site(const char* args, struct conn_info_st* conn_info) { return fw_open(args, conn_info); } static int fw_set_user(const char* args, struct conn_info_st* conn_info) { /* just chop off the command and put everything in the user name */ if ( ! args || *(args + strlen("USER ")) == '\0') { say(conn_info->clntinfo->clientsocket, "530-Not a valid user name\r\n" "530 Login failed.\r\n"); return CMD_ERROR; } conn_info->clntinfo->user = strdup(args + strlen("USER ")); enough_mem(conn_info->clntinfo->user); return CMD_HANDLED; } int fw_user(const char* args, struct conn_info_st* conn_info) { if (fw_set_user(args, conn_info) != CMD_HANDLED) { return CMD_ERROR; } return cmds_after_user(conn_info); } static int fw_set_pass(const char* args, struct conn_info_st* conn_info) { /* just chop off the command and put everything in the password */ if ( ! args ) { say(conn_info->clntinfo->clientsocket, "530-Not a valid password\r\n" "530 Login failed.\r\n"); return CMD_ERROR; } if (*(args + strlen("PASS")) == '\0' || *(args + strlen("PASS ")) == '\0') { /* allow empty passwords, too */ conn_info->clntinfo->pass = strdup(""); } else { conn_info->clntinfo->pass = strdup(args + strlen("PASS ")); } enough_mem(conn_info->clntinfo->pass); return CMD_HANDLED; } int fw_pass(const char* args, struct conn_info_st* conn_info) { if (fw_set_pass(args, conn_info) != CMD_HANDLED) { return CMD_ERROR; } return cmds_after_pass(conn_info); } static int fw_set_fwpass(const char* args, struct conn_info_st* conn_info) { /* just chop off the command and put everything in the password */ char* space = (char*) 0; if (args) { space = strchr(args, ' '); } if ( ! args || ! space || *(space + 1) == '\0') { say(conn_info->clntinfo->clientsocket, "530-Not a valid password\r\n" "530 Login failed.\r\n"); return CMD_ERROR; } conn_info->clntinfo->fw_auth.pass = strdup(space + 1); enough_mem(conn_info->clntinfo->fw_auth.pass); return CMD_HANDLED; } int fw_fwpass(const char* args, struct conn_info_st* conn_info) { if (fw_set_fwpass(args, conn_info) != CMD_HANDLED) { return CMD_ERROR; } return cmds_after_fwpass(conn_info); } static int fw_set_fwuser(const char* args, struct conn_info_st* conn_info) { /* just chop off the command and put everything in the user name */ char* space = (char*) 0; if (args) { space = strchr(args, ' '); } if ( ! args || ! space || *(space + 1) == '\0') { say(conn_info->clntinfo->clientsocket, "530-Not a valid user name\r\n" "530 Login failed.\r\n"); return CMD_ERROR; } conn_info->clntinfo->fw_auth.user = strdup(space + 1); enough_mem(conn_info->clntinfo->fw_auth.user); return CMD_HANDLED; } int fw_fwuser(const char* args, struct conn_info_st* conn_info) { if (fw_set_fwuser(args, conn_info) != CMD_HANDLED) { return CMD_ERROR; } say(conn_info->clntinfo->clientsocket, "331 User name okay, send password.\r\n"); return CMD_HANDLED; } int fw_user_type8(const char* args, struct conn_info_st* conn_info) { /* expecting "USER fwuser@real.host.name" */ char* cmd_copy = strdup(args); char* atsign; struct destination_t destination = { (char*) 0, 0 }; enough_mem(cmd_copy); atsign = strchr(cmd_copy, '@'); if (atsign) { destination = fw_auth_parse_host_port(atsign + 1); *atsign ='\0'; if (fw_fwuser(cmd_copy, conn_info) != CMD_HANDLED) { free(cmd_copy); return CMD_ERROR; } } free(cmd_copy); if (! atsign || destination.hostname == (char*) 0) { say(conn_info->clntinfo->clientsocket, "530-Not a valid user name\r\n" "530 Login failed.\r\n"); return CMD_ERROR; } conn_info->clntinfo->destination = destination.hostname; conn_info->clntinfo->destinationport = destination.port; return CMD_HANDLED; } int fw_user_type7(const char* args, struct conn_info_st* conn_info) { char* user, *fwuser, *destchar; int offset = strlen("USER "); struct destination_t destination; if (config_compare_option("logintime", "pass") == 0) { jlog(4, "logintime has to be \"pass\" with loginstyle 7"); say(conn_info->clntinfo->clientsocket, "550 Login incorrect\r\n"); return CMD_ERROR; } user = quotstrtok_prepend("USER ", args, "@", &offset); if ( fw_set_user(user, conn_info) != CMD_HANDLED) { free(user); return CMD_ERROR; } free(user); fwuser = quotstrtok_prepend("USER ", args, "@", &offset); if ( !fwuser || fw_set_fwuser(fwuser, conn_info) != CMD_HANDLED) { if (! fwuser) { say(conn_info->clntinfo->clientsocket, "550 USER not recognized\r\n"); } free(fwuser); return CMD_ERROR; } free(fwuser); destchar = quotstrtok(args, "@\n", &offset); destination = fw_auth_parse_host_port(destchar); free(destchar); if ( ! destination.hostname ) { say(conn_info->clntinfo->clientsocket, "550 USER not recognized\r\n"); return CMD_ERROR; } conn_info->clntinfo->destination = destination.hostname; conn_info->clntinfo->destinationport = destination.port; return cmds_after_user(conn_info); } int fw_pass_type7(const char* args, struct conn_info_st* conn_info) { char* pass, *fwpass, *argscopy; const char* last_at; int offset; /* we have pass@fwpass but pass is likely to contain a "@" sign. * So we find the last @ sign and everything before is the * destination password, everything after is the firewall password */ last_at = strrchr(args, '@'); if (! last_at) { say(conn_info->clntinfo->clientsocket, "550 PASS not recognized\r\n"); return CMD_ERROR; } offset = 1; fwpass = quotstrtok_prepend("PASS ", last_at, "\n", &offset); if ( !fwpass || fw_set_fwpass(fwpass, conn_info) != CMD_HANDLED) { if (! fwpass) { say(conn_info->clntinfo->clientsocket, "550 PASS not recognized\r\n"); } free(fwpass); return CMD_ERROR; } free(fwpass); argscopy = strdup(args); enough_mem(argscopy); /* the at sign is at * args [ last_at - args ] * and, since argscopy is a copy of args, it is at * argscopy [ last_at - args ] * as well. */ argscopy[ last_at - args ] = '\0'; /* But if we set a \0 at the location of the at-sign, the remaining * part is "PASS destpass" and this is what we're looking for */ pass = argscopy; if ( !pass || fw_set_pass(pass, conn_info) != CMD_HANDLED) { if (! pass) { say(conn_info->clntinfo->clientsocket, "550 PASS not recognized\r\n"); } free(pass); return CMD_ERROR; } free(pass); if (fw_validate(conn_info->clntinfo->fw_auth.user, conn_info->clntinfo->fw_auth.pass) != 0) { say(conn_info->clntinfo->clientsocket, "550 Login incorrect\r\n"); return CMD_ERROR; } return cmds_after_pass(conn_info); } int fw_login_type2(const char* args, struct conn_info_st* conn_info) { static int stage; if (stage == 0) { if (fw_user(args, conn_info) == CMD_HANDLED) { stage++; } else { stage = 0; } } if (stage == 1) { if (std_user_split(args, conn_info) == CMD_HANDLED) { stage++; } } return 0; } int fw_user_type9(const char* args, struct conn_info_st* conn_info) { /* "USER user@real.host.name fwuser" */ char* remoteuser; char* fwuser; int offset = strlen("USER "); remoteuser = quotstrtok_prepend("USER ", args, WHITESPACES, &offset); if (std_user_split(remoteuser, conn_info) != CMD_HANDLED) { free(remoteuser); return CMD_ERROR; } free(remoteuser); fwuser = quotstrtok_prepend("USER ", args, WHITESPACES, &offset); if (fw_set_fwuser(fwuser, conn_info) != CMD_HANDLED) { free(fwuser); return CMD_ERROR; } free(fwuser); return CMD_HANDLED; } int fw_pass_type9(const char* args, struct conn_info_st* conn_info) { /* Just register the password */ if (fw_set_pass(args, conn_info) != CMD_HANDLED) { return CMD_ERROR; } say(conn_info->clntinfo->clientsocket, "332 Need account for login.\r\n"); return CMD_HANDLED; } int fw_acct_type9(const char* args, struct conn_info_st* conn_info) { if (fw_set_fwpass(args, conn_info) != CMD_HANDLED) { return CMD_ERROR; } return cmds_after_pass(conn_info); }