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