diff --git a/davmail-setup.nsi b/davmail-setup.nsi index 2329f713..0cb9825e 100644 --- a/davmail-setup.nsi +++ b/davmail-setup.nsi @@ -92,6 +92,7 @@ Section "MainSection" SEC01 File "dist\lib\commons-logging-1.0.4.jar" File "dist\lib\htmlcleaner-2.1.jar" File "dist\lib\jackrabbit-webdav-1.4.jar" + File "dist\lib\jcifs-1.3.14.jar" File "dist\lib\jdom-1.0.jar" File "dist\lib\log4j-1.2.15.jar" File "dist\lib\mail-1.4.1.jar" @@ -162,6 +163,7 @@ no_quest: Delete "$INSTDIR\lib\commons-logging-1.0.4.jar" Delete "$INSTDIR\lib\htmlcleaner-2.1.jar" Delete "$INSTDIR\lib\jackrabbit-webdav-1.4.jar" + Delete "$INSTDIR\lib\jcifs-1.3.14.jar" Delete "$INSTDIR\lib\jdom-1.0.jar" Delete "$INSTDIR\lib\log4j-1.2.15.jar" Delete "$INSTDIR\lib\mail-1.4.1.jar" diff --git a/davmail.jsmooth b/davmail.jsmooth index 1b1a471c..83967a1c 100644 --- a/davmail.jsmooth +++ b/davmail.jsmooth @@ -15,6 +15,7 @@ dist\lib\commons-logging-1.0.4.jar dist\lib\htmlcleaner-2.1.jar dist\lib\jackrabbit-webdav-1.4.jar +dist\lib\jcifs-1.3.14.jar dist\lib\jdom-1.0.jar dist\lib\log4j-1.2.15.jar dist\lib\mail-1.4.1.jar diff --git a/davmailconsole.jsmooth b/davmailconsole.jsmooth index d9cc0a2f..72b6367e 100644 --- a/davmailconsole.jsmooth +++ b/davmailconsole.jsmooth @@ -8,22 +8,23 @@ jview dist\davmail.jar -dist\lib\activation-1.1.jar -dist\lib\commons-codec-1.3.jar -dist\lib\commons-collections-3.1.jar -dist\lib\commons-httpclient-3.1.jar -dist\lib\commons-logging-1.0.4.jar -dist\lib\htmlcleaner-2.1.jar -dist\lib\jackrabbit-webdav-1.4.jar -dist\lib\jdom-1.0.jar -dist\lib\log4j-1.2.15.jar -dist\lib\mail-1.4.1.jar -dist\lib\slf4j-api-1.3.1.jar -dist\lib\slf4j-log4j12-1.3.1.jar -dist\lib\stax-api-1.0.1.jar -dist\lib\swt-3.5-win32-x86.jar -dist\lib\wstx-asl-3.2.7.jar -dist\lib\xercesImpl-2.8.1.jar +dist\lib\activation-1.1.jar +dist\lib\commons-codec-1.3.jar +dist\lib\commons-collections-3.1.jar +dist\lib\commons-httpclient-3.1.jar +dist\lib\commons-logging-1.0.4.jar +dist\lib\htmlcleaner-2.1.jar +dist\lib\jackrabbit-webdav-1.4.jar +dist\lib\jcifs-1.3.14.jar +dist\lib\jdom-1.0.jar +dist\lib\log4j-1.2.15.jar +dist\lib\mail-1.4.1.jar +dist\lib\slf4j-api-1.3.1.jar +dist\lib\slf4j-log4j12-1.3.1.jar +dist\lib\stax-api-1.0.1.jar +dist\lib\swt-3.5-win32-x86.jar +dist\lib\wstx-asl-3.2.7.jar +dist\lib\xercesImpl-2.8.1.jar false dist\davmailconsole.exe src\java\tray32.png diff --git a/davmailservice.jsmooth b/davmailservice.jsmooth index 4ac06801..f587cafa 100644 --- a/davmailservice.jsmooth +++ b/davmailservice.jsmooth @@ -15,6 +15,7 @@ dist\lib\commons-logging-1.0.4.jar dist\lib\htmlcleaner-2.1.jar dist\lib\jackrabbit-webdav-1.4.jar +dist\lib\jcifs-1.3.14.jar dist\lib\jdom-1.0.jar dist\lib\log4j-1.2.15.jar dist\lib\mail-1.4.1.jar diff --git a/lib/jcifs-1.3.14.jar b/lib/jcifs-1.3.14.jar new file mode 100644 index 00000000..c3d7f4e9 Binary files /dev/null and b/lib/jcifs-1.3.14.jar differ diff --git a/pom.xml b/pom.xml index 64d4317b..463e85c9 100644 --- a/pom.xml +++ b/pom.xml @@ -242,6 +242,11 @@ wstx-asl 3.2.7 + + org.samba.jcifs + jcifs + 1.3.14 + diff --git a/src/java/davmail/http/DavGatewayHttpClientFacade.java b/src/java/davmail/http/DavGatewayHttpClientFacade.java index ea0aed08..e7169d0b 100644 --- a/src/java/davmail/http/DavGatewayHttpClientFacade.java +++ b/src/java/davmail/http/DavGatewayHttpClientFacade.java @@ -410,6 +410,9 @@ public final class DavGatewayHttpClientFacade { * @param httpClient HttpClient instance */ public static void addNTLM(HttpClient httpClient) { + // register the jcifs based NTLMv2 implementation + AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, NTLMv2Scheme.class); + ArrayList authPrefs = new ArrayList(); authPrefs.add(AuthPolicy.NTLM); authPrefs.add(AuthPolicy.DIGEST); diff --git a/src/java/davmail/http/NTLMv2Scheme.java b/src/java/davmail/http/NTLMv2Scheme.java new file mode 100644 index 00000000..b4c17f8a --- /dev/null +++ b/src/java/davmail/http/NTLMv2Scheme.java @@ -0,0 +1,198 @@ +/* + * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway + * Copyright (C) 2010 Mickael Guessant + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package davmail.http; + +import jcifs.ntlmssp.NtlmFlags; +import jcifs.ntlmssp.Type1Message; +import jcifs.ntlmssp.Type2Message; +import jcifs.ntlmssp.Type3Message; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.httpclient.Credentials; +import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.NTCredentials; +import org.apache.commons.httpclient.auth.*; +import org.apache.commons.httpclient.util.EncodingUtil; + +import java.io.IOException; + +/** + * NTLMv2 scheme implementation. + */ +public class NTLMv2Scheme implements AuthScheme { + private static final int UNINITIATED = 0; + private static final int INITIATED = 1; + private static final int TYPE1_MSG_GENERATED = 2; + private static final int TYPE2_MSG_RECEIVED = 3; + private static final int TYPE3_MSG_GENERATED = 4; + private static final int FAILED = Integer.MAX_VALUE; + + private Type2Message type2Message; + /** + * Authentication process state + */ + private int state; + + /** + * Processes the NTLM challenge. + * + * @param challenge the challenge string + * @throws MalformedChallengeException is thrown if the authentication challenge + * is malformed + */ + public void processChallenge(final String challenge) throws MalformedChallengeException { + String authScheme = AuthChallengeParser.extractScheme(challenge); + if (!authScheme.equalsIgnoreCase(getSchemeName())) { + throw new MalformedChallengeException("Invalid NTLM challenge: " + challenge); + } + int spaceIndex = challenge.indexOf(' '); + if (spaceIndex != -1) { + try { + type2Message = new Type2Message(Base64.decodeBase64(EncodingUtil.getBytes( + challenge.substring(spaceIndex, challenge.length()).trim(), "ASCII"))); + } catch (IOException e) { + throw new MalformedChallengeException("Invalid NTLM challenge: " + challenge, e); + } + this.state = TYPE2_MSG_RECEIVED; + } else { + this.type2Message = null; + if (this.state == UNINITIATED) { + this.state = INITIATED; + } else { + this.state = FAILED; + } + } + } + + + /** + * Returns textual designation of the NTLM authentication scheme. + * + * @return ntlm + */ + public String getSchemeName() { + return "ntlm"; + } + + /** + * Not used with NTLM. + * + * @return null + */ + public String getParameter(String s) { + return null; + } + + /** + * Not used with NTLM. + * + * @return null + */ + public String getRealm() { + return null; + } + + /** + * Deprecated. + * + * @deprecated + */ + public String getID() { + throw new UnsupportedOperationException(); + } + + /** + * NTLM is connection based. + * + * @return true + */ + public boolean isConnectionBased() { + return true; + } + + /** + * Tests if the NTLM authentication process has been completed. + * + * @return true if authorization has been processed + */ + public boolean isComplete() { + return state == TYPE3_MSG_GENERATED || state == FAILED; + } + + /** + * Not implemented. + * + * @param credentials user credentials + * @param method method name + * @param uri URI + * @return an NTLM authorization string + * @throws InvalidCredentialsException if authentication credentials + * are not valid or not applicable for this authentication scheme + * @throws AuthenticationException if authorization string cannot + * be generated due to an authentication failure + * @deprecated + */ + public String authenticate(final Credentials credentials, String method, String uri) throws AuthenticationException { + throw new UnsupportedOperationException(); + } + + /** + * Produces NTLM authorization string for the given set of + * {@link Credentials}. + * + * @param credentials The set of credentials to be used for authentication + * @param httpMethod The method being authenticated + * @return an NTLM authorization string + * @throws InvalidCredentialsException if authentication credentials + * are not valid or not applicable for this authentication scheme + * @throws AuthenticationException if authorization string cannot + * be generated due to an authentication failure + */ + public String authenticate(Credentials credentials, HttpMethod httpMethod) throws AuthenticationException { + if (this.state == UNINITIATED) { + throw new IllegalStateException("NTLM authentication process has not been initiated"); + } + + NTCredentials ntcredentials; + try { + ntcredentials = (NTCredentials) credentials; + } catch (ClassCastException e) { + throw new InvalidCredentialsException( + "Credentials cannot be used for NTLM authentication: " + + credentials.getClass().getName()); + } + String response; + if (this.state == INITIATED || this.state == FAILED) { + int flags = NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2 | NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN | + NtlmFlags.NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED | NtlmFlags.NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED | + NtlmFlags.NTLMSSP_NEGOTIATE_NTLM | NtlmFlags.NTLMSSP_REQUEST_TARGET | + NtlmFlags.NTLMSSP_NEGOTIATE_OEM | NtlmFlags.NTLMSSP_NEGOTIATE_UNICODE; + Type1Message type1Message = new Type1Message(flags, ntcredentials.getDomain(), ntcredentials.getHost()); + response = EncodingUtil.getAsciiString(Base64.encodeBase64(type1Message.toByteArray())); + this.state = TYPE1_MSG_GENERATED; + } else { + Type3Message type3Message = new Type3Message(type2Message, ntcredentials.getPassword(), + ntcredentials.getDomain(), ntcredentials.getUserName(), ntcredentials.getHost(), 0); + response = EncodingUtil.getAsciiString(Base64.encodeBase64(type3Message.toByteArray())); + this.state = TYPE3_MSG_GENERATED; + } + return "NTLM " + response; + } + + +}