001: /*
002: * This file is part of DrFTPD, Distributed FTP Daemon.
003: *
004: * DrFTPD is free software; you can redistribute it and/or modify
005: * it under the terms of the GNU General Public License as published by
006: * the Free Software Foundation; either version 2 of the License, or
007: * (at your option) any later version.
008: *
009: * DrFTPD is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU General Public License for more details.
013: *
014: * You should have received a copy of the GNU General Public License
015: * along with DrFTPD; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018: package net.sf.drftpd.master.command.plugins;
019:
020: import java.io.IOException;
021: import java.net.InetAddress;
022: import java.net.UnknownHostException;
023:
024: import net.sf.drftpd.event.UserEvent;
025: import net.sf.drftpd.master.BaseFtpConnection;
026: import net.sf.drftpd.master.FtpRequest;
027: import net.sf.drftpd.master.command.CommandManager;
028: import net.sf.drftpd.master.command.CommandManagerFactory;
029:
030: import org.apache.log4j.Logger;
031: import org.apache.oro.text.regex.MalformedPatternException;
032: import org.drftpd.commands.CommandHandler;
033: import org.drftpd.commands.CommandHandlerFactory;
034: import org.drftpd.commands.Reply;
035: import org.drftpd.commands.ReplyException;
036: import org.drftpd.commands.UnhandledCommandException;
037: import org.drftpd.commands.UserManagement;
038: import org.drftpd.usermanager.NoSuchUserException;
039: import org.drftpd.usermanager.User;
040: import org.drftpd.usermanager.UserFileException;
041:
042: /**
043: * @author mog
044: * @version $Id: Login.java 1243 2005-09-04 02:21:12Z zubov $
045: */
046: public class Login implements CommandHandler, CommandHandlerFactory,
047: Cloneable {
048: private static final Logger logger = Logger.getLogger(Login.class);
049:
050: /**
051: * If _idntAddress == null, IDNT hasn't been used.
052: */
053: protected InetAddress _idntAddress;
054: protected String _idntIdent;
055:
056: /**
057: * Syntax: IDNT ident@ip:dns
058: * Returns nothing on success.
059: */
060: private Reply doIDNT(BaseFtpConnection conn) {
061: if (_idntAddress != null) {
062: logger.error("Multiple IDNT commands");
063: return new Reply(530, "Multiple IDNT commands");
064: }
065:
066: if (!conn.getGlobalContext().getConfig().getBouncerIps()
067: .contains(conn.getClientAddress())) {
068: logger.warn("IDNT from non-bnc");
069:
070: return Reply.RESPONSE_530_ACCESS_DENIED;
071: }
072:
073: String arg = conn.getRequest().getArgument();
074: int pos1 = arg.indexOf('@');
075:
076: if (pos1 == -1) {
077: return Reply.RESPONSE_501_SYNTAX_ERROR;
078: }
079:
080: int pos2 = arg.indexOf(':', pos1 + 1);
081:
082: if (pos2 == -1) {
083: return Reply.RESPONSE_501_SYNTAX_ERROR;
084: }
085:
086: try {
087: _idntAddress = InetAddress.getByName(arg.substring(
088: pos1 + 1, pos2));
089: _idntIdent = arg.substring(0, pos1);
090: } catch (UnknownHostException e) {
091: logger.info("Invalid hostname passed to IDNT", e);
092:
093: //this will most likely cause control connection to become unsynchronized
094: //but give error anyway, this error is unlikely to happen
095: return new Reply(501, "IDNT FAILED: " + e.getMessage());
096: }
097:
098: // bnc doesn't expect any reply
099: return null;
100: }
101:
102: /**
103: * <code>PASS <SP> <password> <CRLF></code><br>
104: *
105: * The argument field is a Telnet string specifying the user's
106: * password. This command must be immediately preceded by the
107: * user name command.
108: */
109: private Reply doPASS(BaseFtpConnection conn) {
110: if (conn.getUserNull() == null) {
111: return Reply.RESPONSE_503_BAD_SEQUENCE_OF_COMMANDS;
112: }
113:
114: FtpRequest request = conn.getRequest();
115:
116: // set user password and login
117: String pass = request.hasArgument() ? request.getArgument()
118: : "";
119:
120: // login failure - close connection
121: if (conn.getUserNull().checkPassword(pass)) {
122: conn.setAuthenticated(true);
123: conn.getGlobalContext().dispatchFtpEvent(
124: new UserEvent(conn.getUserNull(), "LOGIN", System
125: .currentTimeMillis()));
126:
127: Reply response = new Reply(230, conn.jprintf(Login.class,
128: "pass.success"));
129:
130: try {
131: Textoutput.addTextToResponse(response, "welcome");
132: } catch (IOException e) {
133: logger.warn("Error reading welcome", e);
134: }
135:
136: return response;
137: }
138:
139: return new Reply(530, conn.jprintf(Login.class, "pass.fail"));
140: }
141:
142: /**
143: * <code>QUIT <CRLF></code><br>
144: *
145: * This command terminates a USER and if file transfer is not
146: * in progress, the server closes the control connection.
147: */
148: private Reply doQUIT(BaseFtpConnection conn) {
149: conn.stop();
150:
151: return new Reply(221, conn.jprintf(Login.class, "quit.success"));
152: }
153:
154: /**
155: * <code>USER <SP> <username> <CRLF></code><br>
156: *
157: * The argument field is a Telnet string identifying the user.
158: * The user identification is that which is required by the
159: * server for access to its file system. This command will
160: * normally be the first command transmitted by the user after
161: * the control connections are made.
162: */
163: private Reply doUSER(BaseFtpConnection conn) throws ReplyException {
164: FtpRequest request = conn.getRequest();
165: conn.setAuthenticated(false);
166: conn.setUser(null);
167:
168: // argument check
169: if (!request.hasArgument()) {
170: return Reply.RESPONSE_501_SYNTAX_ERROR;
171: }
172:
173: User newUser;
174:
175: try {
176: newUser = conn.getGlobalContext().getUserManager()
177: .getUserByNameIncludeDeleted(request.getArgument());
178: } catch (NoSuchUserException ex) {
179: return new Reply(530, ex.getMessage());
180: } catch (UserFileException ex) {
181: logger.warn("", ex);
182: return new Reply(530, "IOException: " + ex.getMessage());
183: } catch (RuntimeException ex) {
184: logger.error("", ex);
185:
186: throw new ReplyException(ex);
187: }
188:
189: if (newUser.isDeleted()) {
190: return new Reply(530, (String) newUser.getKeyedMap()
191: .getObject(
192: UserManagement.REASON,
193: Reply.RESPONSE_530_ACCESS_DENIED
194: .getMessage()));
195: }
196: if (!conn.getGlobalContext().getConfig()
197: .isLoginAllowed(newUser)) {
198: return Reply.RESPONSE_530_ACCESS_DENIED;
199: }
200:
201: try {
202: if (((_idntAddress != null) && newUser
203: .getHostMaskCollection().check(_idntIdent,
204: _idntAddress, null))
205: || ((_idntAddress == null) && (newUser
206: .getHostMaskCollection().check(null, conn
207: .getClientAddress(), conn
208: .getControlSocket())))) {
209: //success
210: // max_users and num_logins restriction
211: Reply response = conn.getGlobalContext()
212: .getConnectionManager().canLogin(conn, newUser);
213:
214: if (response != null) {
215: return response;
216: }
217: conn.setUser(newUser.getName());
218:
219: return new Reply(331, conn.jprintf(Login.class,
220: "user.success"));
221: }
222: } catch (MalformedPatternException e) {
223: return new Reply(530, e.getMessage());
224: }
225:
226: //fail
227: logger.warn("Failed hostmask check");
228:
229: return Reply.RESPONSE_530_ACCESS_DENIED;
230: }
231:
232: public Reply execute(BaseFtpConnection conn) throws ReplyException {
233: String cmd = conn.getRequest().getCommand();
234:
235: if ("USER".equals(cmd)) {
236: return doUSER(conn);
237: }
238:
239: if ("PASS".equals(cmd)) {
240: return doPASS(conn);
241: }
242:
243: if ("QUIT".equals(cmd)) {
244: return doQUIT(conn);
245: }
246:
247: if ("IDNT".equals(cmd)) {
248: return doIDNT(conn);
249: }
250:
251: throw UnhandledCommandException.create(Login.class, conn
252: .getRequest());
253: }
254:
255: public String[] getFeatReplies() {
256: return null;
257: }
258:
259: public CommandHandler initialize(BaseFtpConnection conn,
260: CommandManager initializer) {
261: try {
262: return (Login) clone();
263: } catch (CloneNotSupportedException e) {
264: throw new RuntimeException(e);
265: }
266: }
267:
268: public void load(CommandManagerFactory initializer) {
269: }
270:
271: public void unload() {
272: }
273: }
|