1 /************************************************************************
2 * Copyright (c) 1999-2006 The Apache Software Foundation. *
3 * All rights reserved. *
4 * ------------------------------------------------------------------- *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you *
6 * may not use this file except in compliance with the License. You *
7 * may obtain a copy of the License at: *
8 * *
9 * http://www.apache.org/licenses/LICENSE-2.0 *
10 * *
11 * Unless required by applicable law or agreed to in writing, software *
12 * distributed under the License is distributed on an "AS IS" BASIS, *
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or *
14 * implied. See the License for the specific language governing *
15 * permissions and limitations under the License. *
16 ***********************************************************************/
17
18 package org.apache.james.smtpserver;
19
20 import org.apache.james.util.mail.dsn.DSNStatus;
21 import org.apache.avalon.framework.logger.AbstractLogEnabled;
22 import java.util.Locale;
23 import java.util.StringTokenizer;
24 import org.apache.james.util.Base64;
25 import java.io.IOException;
26
27
28 /***
29 * handles AUTH command
30 */
31 public class AuthCmdHandler
32 extends AbstractLogEnabled
33 implements CommandHandler {
34
35 /***
36 * The text string for the SMTP AUTH type PLAIN.
37 */
38 private final static String AUTH_TYPE_PLAIN = "PLAIN";
39
40 /***
41 * The text string for the SMTP AUTH type LOGIN.
42 */
43 private final static String AUTH_TYPE_LOGIN = "LOGIN";
44
45
46 /***
47 * handles AUTH command
48 *
49 * @see org.apache.james.smtpserver.CommandHandler#onCommand(SMTPSession)
50 */
51 public void onCommand(SMTPSession session) {
52
53
54 try{
55 doAUTH(session, session.getCommandArgument());
56 } catch (Exception ex) {
57 getLogger().error("Exception occured:" + ex.getMessage());
58 session.endSession();
59 }
60 }
61
62
63
64 /***
65 * Handler method called upon receipt of a AUTH command.
66 * Handles client authentication to the SMTP server.
67 *
68 * @param session SMTP session
69 * @param argument the argument passed in with the command by the SMTP client
70 */
71 private void doAUTH(SMTPSession session, String argument)
72 throws Exception {
73 String responseString = null;
74 if (session.getUser() != null) {
75 responseString = "503 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_OTHER)+" User has previously authenticated. "
76 + " Further authentication is not required!";
77 session.writeResponse(responseString);
78 } else if (argument == null) {
79 responseString = "501 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_INVALID_ARG)+" Usage: AUTH (authentication type) <challenge>";
80 session.writeResponse(responseString);
81 } else {
82 String initialResponse = null;
83 if ((argument != null) && (argument.indexOf(" ") > 0)) {
84 initialResponse = argument.substring(argument.indexOf(" ") + 1);
85 argument = argument.substring(0,argument.indexOf(" "));
86 }
87 String authType = argument.toUpperCase(Locale.US);
88 if (authType.equals(AUTH_TYPE_PLAIN)) {
89 doPlainAuth(session, initialResponse);
90 return;
91 } else if (authType.equals(AUTH_TYPE_LOGIN)) {
92 doLoginAuth(session, initialResponse);
93 return;
94 } else {
95 doUnknownAuth(session, authType, initialResponse);
96 return;
97 }
98 }
99 }
100
101 /***
102 * Carries out the Plain AUTH SASL exchange.
103 *
104 * According to RFC 2595 the client must send: [authorize-id] \0 authenticate-id \0 password.
105 *
106 * >>> AUTH PLAIN dGVzdAB0ZXN0QHdpei5leGFtcGxlLmNvbQB0RXN0NDI=
107 * Decoded: test\000test@wiz.example.com\000tEst42
108 *
109 * >>> AUTH PLAIN dGVzdAB0ZXN0AHRFc3Q0Mg==
110 * Decoded: test\000test\000tEst42
111 *
112 * @param session SMTP session object
113 * @param initialResponse the initial response line passed in with the AUTH command
114 */
115 private void doPlainAuth(SMTPSession session, String initialResponse)
116 throws IOException {
117 String userpass = null, user = null, pass = null, responseString = null;
118 if (initialResponse == null) {
119 responseString = "334 OK. Continue authentication";
120 session.writeResponse(responseString);
121 userpass = session.readCommandLine();
122 } else {
123 userpass = initialResponse.trim();
124 }
125 try {
126 if (userpass != null) {
127 userpass = Base64.decodeAsString(userpass);
128 }
129 if (userpass != null) {
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144 StringTokenizer authTokenizer = new StringTokenizer(userpass, "\0");
145 String authorize_id = authTokenizer.nextToken();
146 user = authTokenizer.nextToken();
147 try {
148 pass = authTokenizer.nextToken();
149 }
150 catch (java.util.NoSuchElementException _) {
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166 pass = user;
167 user = authorize_id;
168 }
169
170 authTokenizer = null;
171 }
172 }
173 catch (Exception e) {
174
175
176 }
177
178 if ((user == null) || (pass == null)) {
179 responseString = "501 Could not decode parameters for AUTH PLAIN";
180 session.writeResponse(responseString);
181 } else if (session.getConfigurationData().getUsersRepository().test(user, pass)) {
182 session.setUser(user);
183 responseString = "235 Authentication Successful";
184 session.writeResponse(responseString);
185 getLogger().info("AUTH method PLAIN succeeded");
186 } else {
187 responseString = "535 Authentication Failed";
188 session.writeResponse(responseString);
189 getLogger().error("AUTH method PLAIN failed");
190 }
191 return;
192 }
193
194 /***
195 * Carries out the Login AUTH SASL exchange.
196 *
197 * @param session SMTP session object
198 * @param initialResponse the initial response line passed in with the AUTH command
199 */
200 private void doLoginAuth(SMTPSession session, String initialResponse)
201 throws IOException {
202 String user = null, pass = null, responseString = null;
203 if (initialResponse == null) {
204 responseString = "334 VXNlcm5hbWU6";
205 session.writeResponse(responseString);
206 user = session.readCommandLine();
207 } else {
208 user = initialResponse.trim();
209 }
210 if (user != null) {
211 try {
212 user = Base64.decodeAsString(user);
213 } catch (Exception e) {
214
215
216 user = null;
217 }
218 }
219 responseString = "334 UGFzc3dvcmQ6";
220 session.writeResponse(responseString);
221 pass = session.readCommandLine();
222 if (pass != null) {
223 try {
224 pass = Base64.decodeAsString(pass);
225 } catch (Exception e) {
226
227
228 pass = null;
229 }
230 }
231
232 if ((user == null) || (pass == null)) {
233 responseString = "501 Could not decode parameters for AUTH LOGIN";
234 } else if (session.getConfigurationData().getUsersRepository().test(user, pass)) {
235 session.setUser(user);
236 responseString = "235 Authentication Successful";
237 if (getLogger().isDebugEnabled()) {
238
239 getLogger().debug("AUTH method LOGIN succeeded");
240 }
241 } else {
242 responseString = "535 Authentication Failed";
243
244 getLogger().error("AUTH method LOGIN failed");
245 }
246 session.writeResponse(responseString);
247 return;
248 }
249
250 /***
251 * Handles the case of an unrecognized auth type.
252 *
253 * @param session SMTP session object
254 * @param authType the unknown auth type
255 * @param initialResponse the initial response line passed in with the AUTH command
256 */
257 private void doUnknownAuth(SMTPSession session, String authType, String initialResponse) {
258 String responseString = "504 Unrecognized Authentication Type";
259 session.writeResponse(responseString);
260 if (getLogger().isErrorEnabled()) {
261 StringBuffer errorBuffer =
262 new StringBuffer(128)
263 .append("AUTH method ")
264 .append(authType)
265 .append(" is an unrecognized authentication type");
266 getLogger().error(errorBuffer.toString());
267 }
268 return;
269 }
270
271
272 }