jeanouii commented on a change in pull request #1: URL: https://github.com/apache/geronimo-javamail/pull/1#discussion_r497744411
########## File path: geronimo-javamail_1.6/NOTICE ########## @@ -0,0 +1,8 @@ + +Geronimo JavaMail 1.5 Review comment: 1.6 instead of 1.5 ########## File path: geronimo-javamail_1.6/geronimo-javamail_1.6_provider/pom.xml ########## @@ -0,0 +1,353 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor + license agreements. See the NOTICE file distributed with this work for additional + information regarding copyright ownership. The ASF licenses this file to + you under the Apache License, Version 2.0 (the "License"); you may not use + this file except in compliance with the License. You may obtain a copy of + the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required + by applicable law or agreed to in writing, software distributed under the + License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS + OF ANY KIND, either express or implied. See the License for the specific + language governing permissions and limitations under the License. --> + +<!-- $Rev$ $Date: 2014-07-20 09:36:35 +0200 (So, 20. Jul 2014) Review comment: Yank this ########## File path: geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/SASLAuthenticator.java ########## @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geronimo.javamail.authentication; + +import java.io.UnsupportedEncodingException ; +import java.util.Map; +import java.util.Properties; + +import javax.mail.MessagingException; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; +import javax.security.sasl.RealmCallback; +import javax.security.sasl.RealmChoiceCallback; + +public class SASLAuthenticator implements ClientAuthenticator, CallbackHandler { + // The realm we're authenticating within + protected String realm; + // the user we're authenticating + protected String username; + // the user's password (the "shared secret") + protected String password; + // the authenticator we're proxying + protected SaslClient authenticator; + + protected boolean complete = false; + + /** + * Main constructor. + * + * @param username + * The login user name. + * @param password + * The login password. + */ + public SASLAuthenticator(String[] mechanisms, Properties properties, String protocol, String host, String realm, + String authorizationID, String username, String password) throws MessagingException { + this.realm = realm; + this.username = username; + this.password = password; + try { + authenticator = Sasl.createSaslClient(mechanisms, authorizationID, protocol, host, (Map)properties, + this); + } catch (SaslException e) { Review comment: If we shallow the exception, authenticator will be null later bellow ########## File path: geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/handlers/RFC822MessageHandler.java ########## @@ -0,0 +1,120 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.geronimo.javamail.handlers; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Properties; + +import javax.activation.ActivationDataFlavor; +import javax.activation.DataContentHandler; +import javax.activation.DataSource; +import javax.mail.Message; +import javax.mail.MessageAware; +import javax.mail.MessageContext; +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.mail.internet.MimeMessage; + +/** + * Content handler for RFC-822 compliant messages. + * @version $Rev$ $Date$ + */ +public class RFC822MessageHandler implements DataContentHandler { + // the data flavor defines what this looks like, and is fixed once the + // handler is instantiated + protected final DataFlavor flavour; + + public RFC822MessageHandler() { + flavour = new ActivationDataFlavor(Message.class, "message/rfc822", "Message"); + } + + /** + * Return all of the flavors processed by this handler. This + * is just the singleton flavor. + * + * @return An array of the transfer flavors supported by this handler. + */ + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] { flavour }; + } + + /** + * Retrieve the transfer data from the data source, but + * only if the requested flavor matches what we support. + * + * @param df The requested data flavor. + * @param ds The source DataSource. + * + * @return The extracted content object, or null if there is a + * mismatch of flavors. + * @exception UnsupportedFlavorException + * @exception IOException + */ + public Object getTransferData(DataFlavor df, DataSource ds) throws UnsupportedFlavorException, IOException { + return flavour.equals(df) ? getContent(ds) : null; + } + + /** + * Extract the RFC822 Message content from a DataSource. + * + * @param ds The source data source. + * + * @return An extracted MimeMessage object. + * @exception IOException + */ + public Object getContent(DataSource ds) throws IOException { + try { + // creating a MimeMessage instance requires a session. If the DataSource + // is a MessageAware one, we can get the session information from the MessageContext. + // This is generally the case, but if it is not available, then just retrieve + // the default instance and use it. + if (ds instanceof MessageAware) { + MessageContext context = ((MessageAware)ds).getMessageContext(); + return new MimeMessage(context.getSession(), ds.getInputStream()); + } + else { + return new MimeMessage(Session.getDefaultInstance(new Properties(), null), ds.getInputStream()); + } + } catch (MessagingException e) { + throw (IOException) new IOException(e.getMessage()).initCause(e); Review comment: concerned by the cast without test, isn't it? ########## File path: geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/XOAUTH2Authenticator.java ########## @@ -0,0 +1,97 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.geronimo.javamail.authentication; + +import javax.mail.MessagingException; +import java.nio.charset.StandardCharsets; +import java.util.Properties; + +public class XOAUTH2Authenticator implements ClientAuthenticator { + + //The user we're authenticating + protected String username; + //The user's password (the "shared secret") + protected String password; + protected boolean complete = false; + + /** + * Main constructor. + * + * @param username The login user name. + * @param password The Oauth2 token. + */ + public XOAUTH2Authenticator(String[] mechanisms, Properties properties, String protocol, String host, String realm, + String authorizationID, String username, String password) throws MessagingException { + this.username = username; + this.password = password; + } + + /** + * Respond to the hasInitialResponse query. + * + * @return The SaslClient response to the same query. + */ + public boolean hasInitialResponse() { + return true; + } + + /** + * Indicate whether the challenge/response process is complete. + * + * @return True if the last challenge has been processed, false otherwise. + */ + public boolean isComplete() { + return complete; + } + + /** + * Retrieve the authenticator mechanism name. + * + * @return Will returns the string "XOAUTH2" + */ + public String getMechanismName() { + return "XOAUTH2"; + } + + /** + * Evaluate a login challenge, returning the a result string that + * should satisfy the challenge. This use the CallBackHandler to retrieve the + * information it needs for the given protocol. + * + * @param challenge The decoded challenge data, as byte array. + * @return A formatted challenge response, as an array of bytes. + * @throws MessagingException + */ + public byte[] evaluateChallenge(final byte[] challenge) { + if (complete) { + return new byte[0]; + } + + final String response = new StringBuilder() + .append("user=") + .append(this.username) + .append("\001auth=Bearer ") + .append(this.password) + .append("\001\001") + .toString(); + + + complete = true; + return response.getBytes(StandardCharsets.UTF_8); Review comment: another charset again? Probably not a problem, just asking in case ########## File path: geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java ########## @@ -0,0 +1,628 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geronimo.javamail.authentication; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.ArrayList; + +import javax.mail.AuthenticationFailedException; +import javax.mail.MessagingException; + +import org.apache.geronimo.mail.util.Base64; +import org.apache.geronimo.mail.util.Hex; + +/** + * Process a DIGEST-MD5 authentication, using the challenge/response mechanisms. + */ +public class DigestMD5Authenticator implements ClientAuthenticator { + + protected static final int AUTHENTICATE_CLIENT = 0; + + protected static final int AUTHENTICATE_SERVER = 1; + + protected static final int AUTHENTICATION_COMPLETE = 2; + + // the host server name + protected String host; + + // the user we're authenticating + protected String username; + + // the user's password (the "shared secret") + protected String password; + + // the target login realm + protected String realm; + + // our message digest for processing the challenges. + MessageDigest digest; Review comment: package? Why not private or protected? ########## File path: geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java ########## @@ -0,0 +1,628 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geronimo.javamail.authentication; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.ArrayList; + +import javax.mail.AuthenticationFailedException; +import javax.mail.MessagingException; + +import org.apache.geronimo.mail.util.Base64; +import org.apache.geronimo.mail.util.Hex; + +/** + * Process a DIGEST-MD5 authentication, using the challenge/response mechanisms. + */ +public class DigestMD5Authenticator implements ClientAuthenticator { + + protected static final int AUTHENTICATE_CLIENT = 0; + + protected static final int AUTHENTICATE_SERVER = 1; + + protected static final int AUTHENTICATION_COMPLETE = 2; + + // the host server name + protected String host; + + // the user we're authenticating + protected String username; + + // the user's password (the "shared secret") + protected String password; + + // the target login realm + protected String realm; + + // our message digest for processing the challenges. + MessageDigest digest; + + // the string we send to the server on the first challenge. + protected String clientResponse; + + // the response back from an authentication challenge. + protected String authenticationResponse = null; + + // our list of realms received from the server (normally just one). + protected ArrayList realms; + + // the nonce value sent from the server + protected String nonce; + + // indicates whether we've gone through the entire challenge process. + protected int stage = AUTHENTICATE_CLIENT; + + /** + * Main constructor. + * + * @param host + * The server host name. + * @param username + * The login user name. + * @param password + * The login password. + * @param realm + * The target login realm (can be null). + */ + public DigestMD5Authenticator(String host, String username, String password, String realm) { + this.host = host; + this.username = username; + this.password = password; + this.realm = realm; + } + + /** + * Respond to the hasInitialResponse query. This mechanism does not have an + * initial response. + * + * @return Always returns false. + */ + public boolean hasInitialResponse() { + return false; + } + + /** + * Indicate whether the challenge/response process is complete. + * + * @return True if the last challenge has been processed, false otherwise. + */ + public boolean isComplete() { + return stage == AUTHENTICATION_COMPLETE; + } + + /** + * Retrieve the authenticator mechanism name. + * + * @return Always returns the string "DIGEST-MD5" + */ + public String getMechanismName() { + return "DIGEST-MD5"; + } + + /** + * Evaluate a DIGEST-MD5 login challenge, returning the a result string that + * should satisfy the clallenge. + * + * @param challenge + * The decoded challenge data, as a string. + * + * @return A formatted challege response, as an array of bytes. + * @exception MessagingException + */ + public byte[] evaluateChallenge(byte[] challenge) throws MessagingException { + + // DIGEST-MD5 authentication goes in two stages. First state involves us + // validating with the + // server, the second stage is the server validating with us, using the + // shared secret. + switch (stage) { + // stage one of the process. + case AUTHENTICATE_CLIENT: { + // get the response and advance the processing stage. + byte[] response = authenticateClient(challenge); + stage = AUTHENTICATE_SERVER; + return response; + } + + // stage two of the process. + case AUTHENTICATE_SERVER: { + // get the response and advance the processing stage to completed. + byte[] response = authenticateServer(challenge); + stage = AUTHENTICATION_COMPLETE; + return response; + } + + // should never happen. + default: + throw new MessagingException("Invalid LOGIN challenge"); + } + } + + /** + * Evaluate a DIGEST-MD5 login server authentication challenge, returning + * the a result string that should satisfy the clallenge. + * + * @param challenge + * The decoded challenge data, as a string. + * + * @return A formatted challege response, as an array of bytes. + * @exception MessagingException + */ + public byte[] authenticateServer(byte[] challenge) throws MessagingException { + // parse the challenge string and validate. + if (!parseChallenge(challenge)) { + return null; + } + + try { + // like all of the client validation steps, the following is order + // critical. + // first add in the URI information. + digest.update((":smtp/" + host).getBytes("US-ASCII")); + // now mix in the response we sent originally + String responseString = clientResponse + new String(Hex.encode(digest.digest()), "US-ASCII"); + digest.update(responseString.getBytes("US-ASCII")); + + // now convert that into a hex encoded string. + String validationText = new String(Hex.encode(digest.digest()), "US-ASCII"); + + // if everything went well, this calculated value should match what + // we got back from the server. + // our response back is just a null string.... + if (validationText.equals(authenticationResponse)) { + return new byte[0]; + } + throw new AuthenticationFailedException("Invalid DIGEST-MD5 response from server"); + } catch (UnsupportedEncodingException e) { + throw new MessagingException("Invalid character encodings"); + } + + } + + /** + * Evaluate a DIGEST-MD5 login client authentication challenge, returning + * the a result string that should satisfy the clallenge. + * + * @param challenge + * The decoded challenge data, as a string. + * + * @return A formatted challege response, as an array of bytes. + * @exception MessagingException + */ + public byte[] authenticateClient(byte[] challenge) throws MessagingException { + // parse the challenge string and validate. + if (!parseChallenge(challenge)) { + return null; + } + + SecureRandom randomGenerator; + // before doing anything, make sure we can get the required crypto Review comment: Shouldn't that be done in the constructor? So we have a field and we don't create it every time ########## File path: geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/authentication/DigestMD5Authenticator.java ########## @@ -0,0 +1,628 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geronimo.javamail.authentication; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.ArrayList; + +import javax.mail.AuthenticationFailedException; +import javax.mail.MessagingException; + +import org.apache.geronimo.mail.util.Base64; +import org.apache.geronimo.mail.util.Hex; + +/** + * Process a DIGEST-MD5 authentication, using the challenge/response mechanisms. + */ +public class DigestMD5Authenticator implements ClientAuthenticator { + + protected static final int AUTHENTICATE_CLIENT = 0; + + protected static final int AUTHENTICATE_SERVER = 1; + + protected static final int AUTHENTICATION_COMPLETE = 2; + + // the host server name + protected String host; + + // the user we're authenticating + protected String username; + + // the user's password (the "shared secret") + protected String password; + + // the target login realm + protected String realm; + + // our message digest for processing the challenges. + MessageDigest digest; + + // the string we send to the server on the first challenge. + protected String clientResponse; + + // the response back from an authentication challenge. + protected String authenticationResponse = null; + + // our list of realms received from the server (normally just one). + protected ArrayList realms; + + // the nonce value sent from the server + protected String nonce; + + // indicates whether we've gone through the entire challenge process. + protected int stage = AUTHENTICATE_CLIENT; + + /** + * Main constructor. + * + * @param host + * The server host name. + * @param username + * The login user name. + * @param password + * The login password. + * @param realm + * The target login realm (can be null). + */ + public DigestMD5Authenticator(String host, String username, String password, String realm) { + this.host = host; + this.username = username; + this.password = password; + this.realm = realm; + } + + /** + * Respond to the hasInitialResponse query. This mechanism does not have an + * initial response. + * + * @return Always returns false. + */ + public boolean hasInitialResponse() { + return false; + } + + /** + * Indicate whether the challenge/response process is complete. + * + * @return True if the last challenge has been processed, false otherwise. + */ + public boolean isComplete() { + return stage == AUTHENTICATION_COMPLETE; + } + + /** + * Retrieve the authenticator mechanism name. + * + * @return Always returns the string "DIGEST-MD5" + */ + public String getMechanismName() { + return "DIGEST-MD5"; + } + + /** + * Evaluate a DIGEST-MD5 login challenge, returning the a result string that + * should satisfy the clallenge. + * + * @param challenge + * The decoded challenge data, as a string. + * + * @return A formatted challege response, as an array of bytes. + * @exception MessagingException + */ + public byte[] evaluateChallenge(byte[] challenge) throws MessagingException { + + // DIGEST-MD5 authentication goes in two stages. First state involves us + // validating with the + // server, the second stage is the server validating with us, using the + // shared secret. + switch (stage) { + // stage one of the process. + case AUTHENTICATE_CLIENT: { + // get the response and advance the processing stage. + byte[] response = authenticateClient(challenge); + stage = AUTHENTICATE_SERVER; + return response; + } + + // stage two of the process. + case AUTHENTICATE_SERVER: { + // get the response and advance the processing stage to completed. + byte[] response = authenticateServer(challenge); + stage = AUTHENTICATION_COMPLETE; + return response; + } + + // should never happen. + default: + throw new MessagingException("Invalid LOGIN challenge"); + } + } + + /** + * Evaluate a DIGEST-MD5 login server authentication challenge, returning + * the a result string that should satisfy the clallenge. + * + * @param challenge + * The decoded challenge data, as a string. + * + * @return A formatted challege response, as an array of bytes. + * @exception MessagingException + */ + public byte[] authenticateServer(byte[] challenge) throws MessagingException { + // parse the challenge string and validate. + if (!parseChallenge(challenge)) { + return null; + } + + try { + // like all of the client validation steps, the following is order + // critical. + // first add in the URI information. + digest.update((":smtp/" + host).getBytes("US-ASCII")); + // now mix in the response we sent originally + String responseString = clientResponse + new String(Hex.encode(digest.digest()), "US-ASCII"); + digest.update(responseString.getBytes("US-ASCII")); + + // now convert that into a hex encoded string. + String validationText = new String(Hex.encode(digest.digest()), "US-ASCII"); + + // if everything went well, this calculated value should match what + // we got back from the server. + // our response back is just a null string.... + if (validationText.equals(authenticationResponse)) { + return new byte[0]; + } + throw new AuthenticationFailedException("Invalid DIGEST-MD5 response from server"); + } catch (UnsupportedEncodingException e) { + throw new MessagingException("Invalid character encodings"); + } + + } + + /** + * Evaluate a DIGEST-MD5 login client authentication challenge, returning + * the a result string that should satisfy the clallenge. + * + * @param challenge + * The decoded challenge data, as a string. + * + * @return A formatted challege response, as an array of bytes. + * @exception MessagingException + */ + public byte[] authenticateClient(byte[] challenge) throws MessagingException { + // parse the challenge string and validate. + if (!parseChallenge(challenge)) { + return null; + } + + SecureRandom randomGenerator; + // before doing anything, make sure we can get the required crypto + // support. + try { + randomGenerator = new SecureRandom(); + digest = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + throw new MessagingException("Unable to access cryptography libraries"); + } + + // if not configured for a realm, take the first realm from the list, if + // any + if (realm == null) { + // if not handed any realms, just use the host name. + if (realms.isEmpty()) { + realm = host; + } else { + // pretty arbitrary at this point, so just use the first one. + realm = (String) realms.get(0); + } + } + + // use secure random to generate a collection of bytes. that is our + // cnonce value. + byte[] cnonceBytes = new byte[32]; + + randomGenerator.nextBytes(cnonceBytes); + + try { + // and get this as a base64 encoded string. + String cnonce = new String(Base64.encode(cnonceBytes), "US-ASCII"); Review comment: I saw some ISO-8859-1 previously. Not sure if that is intended or per spec, so it's a question. ########## File path: geronimo-javamail_1.6/NOTICE ########## @@ -0,0 +1,8 @@ + +Geronimo JavaMail 1.5 +Copyright 2003-2016 The Apache Software Foundation Review comment: Update headers if possible ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org