Skip to content

Package: SMTPSaslAuthenticator$1

SMTPSaslAuthenticator$1

nameinstructionbranchcomplexitylinemethod
handle(Callback[])
M: 144 C: 0
0%
M: 22 C: 0
0%
M: 12 C: 0
0%
M: 25 C: 0
0%
M: 1 C: 0
0%
{...}
M: 15 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*
2: * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
3: *
4: * This program and the accompanying materials are made available under the
5: * terms of the Eclipse Public License v. 2.0, which is available at
6: * http://www.eclipse.org/legal/epl-2.0.
7: *
8: * This Source Code may also be made available under the following Secondary
9: * Licenses when the conditions for such availability set forth in the
10: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11: * version 2 with the GNU Classpath Exception, which is available at
12: * https://www.gnu.org/software/classpath/license.html.
13: *
14: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15: */
16:
17: package org.eclipse.angus.mail.smtp;
18:
19: import jakarta.mail.MessagingException;
20: import org.eclipse.angus.mail.auth.OAuth2SaslClientFactory;
21: import org.eclipse.angus.mail.util.ASCIIUtility;
22: import org.eclipse.angus.mail.util.MailLogger;
23:
24: import javax.security.auth.callback.Callback;
25: import javax.security.auth.callback.CallbackHandler;
26: import javax.security.auth.callback.NameCallback;
27: import javax.security.auth.callback.PasswordCallback;
28: import javax.security.sasl.RealmCallback;
29: import javax.security.sasl.RealmChoiceCallback;
30: import javax.security.sasl.Sasl;
31: import javax.security.sasl.SaslClient;
32: import javax.security.sasl.SaslException;
33: import java.util.Base64;
34: import java.util.Map;
35: import java.util.Properties;
36: import java.util.logging.Level;
37:
38: /**
39: * This class contains a single method that does authentication using
40: * SASL. This is in a separate class so that it can be compiled with
41: * J2SE 1.5. Eventually it should be merged into SMTPTransport.java.
42: */
43:
44: public class SMTPSaslAuthenticator implements SaslAuthenticator {
45:
46: private SMTPTransport pr;
47: private String name;
48: private Properties props;
49: private MailLogger logger;
50: private String host;
51:
52: /*
53: * This is a hack to initialize the OAUTH SASL provider just before,
54: * and only if, we might need it. This avoids the need for the user
55: * to initialize it explicitly, or manually configure the security
56: * providers file.
57: */
58: static {
59: try {
60: OAuth2SaslClientFactory.init();
61: } catch (Throwable t) {
62: }
63: }
64:
65: public SMTPSaslAuthenticator(SMTPTransport pr, String name,
66: Properties props, MailLogger logger, String host) {
67: this.pr = pr;
68: this.name = name;
69: this.props = props;
70: this.logger = logger;
71: this.host = host;
72: }
73:
74: @Override
75: public boolean authenticate(String[] mechs, final String realm,
76: final String authzid, final String u,
77: final String p) throws MessagingException {
78:
79: boolean done = false;
80: if (logger.isLoggable(Level.FINE)) {
81: logger.fine("SASL Mechanisms:");
82: for (int i = 0; i < mechs.length; i++)
83: logger.fine(" " + mechs[i]);
84: logger.fine("");
85: }
86:
87: SaslClient sc;
88: CallbackHandler cbh = new CallbackHandler() {
89: @Override
90: public void handle(Callback[] callbacks) {
91:• if (logger.isLoggable(Level.FINE))
92: logger.fine("SASL callback length: " + callbacks.length);
93:• for (int i = 0; i < callbacks.length; i++) {
94:• if (logger.isLoggable(Level.FINE))
95: logger.fine("SASL callback " + i + ": " + callbacks[i]);
96:• if (callbacks[i] instanceof NameCallback) {
97: NameCallback ncb = (NameCallback) callbacks[i];
98: ncb.setName(u);
99:• } else if (callbacks[i] instanceof PasswordCallback) {
100: PasswordCallback pcb = (PasswordCallback) callbacks[i];
101: pcb.setPassword(p.toCharArray());
102:• } else if (callbacks[i] instanceof RealmCallback) {
103: RealmCallback rcb = (RealmCallback) callbacks[i];
104:• rcb.setText(realm != null ?
105: realm : rcb.getDefaultText());
106:• } else if (callbacks[i] instanceof RealmChoiceCallback) {
107: RealmChoiceCallback rcb =
108: (RealmChoiceCallback) callbacks[i];
109:• if (realm == null)
110: rcb.setSelectedIndex(rcb.getDefaultChoice());
111: else {
112: // need to find specified realm in list
113: String[] choices = rcb.getChoices();
114:• for (int k = 0; k < choices.length; k++) {
115:• if (choices[k].equals(realm)) {
116: rcb.setSelectedIndex(k);
117: break;
118: }
119: }
120: }
121: }
122: }
123: }
124: };
125:
126: try {
127: @SuppressWarnings("unchecked")
128: Map<String, ?> propsMap = (Map) props;
129: sc = Sasl.createSaslClient(mechs, authzid, name, host,
130: propsMap, cbh);
131: } catch (SaslException sex) {
132: logger.log(Level.FINE, "Failed to create SASL client", sex);
133: throw new UnsupportedOperationException(sex.getMessage(), sex);
134: }
135: if (sc == null) {
136: logger.fine("No SASL support");
137: throw new UnsupportedOperationException("No SASL support");
138: }
139: if (logger.isLoggable(Level.FINE))
140: logger.fine("SASL client " + sc.getMechanismName());
141:
142: int resp;
143: try {
144: String mech = sc.getMechanismName();
145: String ir = null;
146: if (sc.hasInitialResponse()) {
147: byte[] ba = sc.evaluateChallenge(new byte[0]);
148: if (ba.length > 0) {
149: ba = Base64.getEncoder().encode(ba);
150: ir = ASCIIUtility.toString(ba, 0, ba.length);
151: } else
152: ir = "=";
153: }
154: if (ir != null)
155: resp = pr.simpleCommand("AUTH " + mech + " " + ir);
156: else
157: resp = pr.simpleCommand("AUTH " + mech);
158:
159: /*
160: * A 530 response indicates that the server wants us to
161: * issue a STARTTLS command first. Do that and try again.
162: */
163: if (resp == 530) {
164: pr.startTLS();
165: if (ir != null)
166: resp = pr.simpleCommand("AUTH " + mech + " " + ir);
167: else
168: resp = pr.simpleCommand("AUTH " + mech);
169: }
170:
171: if (resp == 235)
172: return true; // success already!
173:
174: if (resp != 334)
175: return false;
176: } catch (Exception ex) {
177: logger.log(Level.FINE, "SASL AUTHENTICATE Exception", ex);
178: return false;
179: }
180:
181: while (!done) { // loop till we are done
182: try {
183: if (resp == 334) {
184: byte[] ba = null;
185: if (!sc.isComplete()) {
186: ba = ASCIIUtility.getBytes(responseText(pr));
187: if (ba.length > 0)
188: ba = Base64.getDecoder().decode(ba);
189: if (logger.isLoggable(Level.FINE))
190: logger.fine("SASL challenge: " +
191: ASCIIUtility.toString(ba, 0, ba.length) + " :");
192: ba = sc.evaluateChallenge(ba);
193: }
194: if (ba == null) {
195: logger.fine("SASL: no response");
196: resp = pr.simpleCommand("");
197: } else {
198: if (logger.isLoggable(Level.FINE))
199: logger.fine("SASL response: " +
200: ASCIIUtility.toString(ba, 0, ba.length) + " :");
201: ba = Base64.getEncoder().encode(ba);
202: resp = pr.simpleCommand(ba);
203: }
204: } else
205: done = true;
206: } catch (Exception ioex) {
207: logger.log(Level.FINE, "SASL Exception", ioex);
208: done = true;
209: // XXX - ultimately return true???
210: }
211: }
212: if (resp != 235)
213: return false;
214:
215: if (sc.isComplete() /*&& res.status == SUCCESS*/) {
216: String qop = (String) sc.getNegotiatedProperty(Sasl.QOP);
217: if (qop != null && (qop.equalsIgnoreCase("auth-int") ||
218: qop.equalsIgnoreCase("auth-conf"))) {
219: // XXX - NOT SUPPORTED!!!
220: logger.fine(
221: "SASL Mechanism requires integrity or confidentiality");
222: return false;
223: }
224: }
225:
226: return true;
227: }
228:
229: private static final String responseText(SMTPTransport pr) {
230: String resp = pr.getLastServerResponse().trim();
231: if (resp.length() > 4)
232: return resp.substring(4);
233: else
234: return "";
235: }
236: }