001/** 002 * 003 * Copyright © 2014-2019 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smack.sasl.javax; 018 019import java.io.IOException; 020import java.util.HashMap; 021import java.util.Map; 022 023import javax.security.auth.callback.Callback; 024import javax.security.auth.callback.CallbackHandler; 025import javax.security.auth.callback.NameCallback; 026import javax.security.auth.callback.PasswordCallback; 027import javax.security.auth.callback.UnsupportedCallbackException; 028import javax.security.sasl.RealmCallback; 029import javax.security.sasl.RealmChoiceCallback; 030import javax.security.sasl.Sasl; 031import javax.security.sasl.SaslClient; 032import javax.security.sasl.SaslException; 033 034import org.jivesoftware.smack.SmackException.SmackSaslException; 035import org.jivesoftware.smack.sasl.SASLMechanism; 036 037public abstract class SASLJavaXMechanism extends SASLMechanism { 038 039 protected SaslClient sc; 040 041 @Override 042 public abstract String getName(); 043 044 @Override 045 public final void checkIfSuccessfulOrThrow() throws SmackSaslException { 046 if (!sc.isComplete()) { 047 throw new SmackSaslException(getName() + " was not completed"); 048 } 049 } 050 051 @Override 052 protected void authenticateInternal() 053 throws SmackJavaxSaslException { 054 String[] mechanisms = { getName() }; 055 Map<String, String> props = getSaslProps(); 056 String authzid = null; 057 if (authorizationId != null) { 058 authzid = authorizationId.toString(); 059 } 060 try { 061 sc = Sasl.createSaslClient(mechanisms, authzid, "xmpp", getServerName().toString(), props, 062 new CallbackHandler() { 063 @Override 064 public void handle(Callback[] callbacks) throws IOException, 065 UnsupportedCallbackException { 066 for (int i = 0; i < callbacks.length; i++) { 067 if (callbacks[i] instanceof NameCallback) { 068 NameCallback ncb = (NameCallback) callbacks[i]; 069 ncb.setName(authenticationId); 070 } 071 else if (callbacks[i] instanceof PasswordCallback) { 072 PasswordCallback pcb = (PasswordCallback) callbacks[i]; 073 pcb.setPassword(password.toCharArray()); 074 } 075 else if (callbacks[i] instanceof RealmCallback) { 076 RealmCallback rcb = (RealmCallback) callbacks[i]; 077 // Retrieve the REALM from the challenge response that 078 // the server returned when the client initiated the 079 // authentication exchange. If this value is not null or 080 // empty, *this value* has to be sent back to the server 081 // in the client's response to the server's challenge 082 String text = rcb.getDefaultText(); 083 // The SASL client (sc) created in smack uses 084 // rcb.getText when creating the negotiatedRealm to send 085 // it back to the server. Make sure that this value 086 // matches the server's realm 087 rcb.setText(text); 088 } 089 else if (callbacks[i] instanceof RealmChoiceCallback) { 090 // unused, prevents UnsupportedCallbackException 091 // RealmChoiceCallback rccb = 092 // (RealmChoiceCallback)callbacks[i]; 093 } 094 else { 095 throw new UnsupportedCallbackException(callbacks[i]); 096 } 097 } 098 } 099 100 }); 101 } 102 catch (SaslException e) { 103 throw new SmackJavaxSaslException(e); 104 } 105 } 106 107 @Override 108 protected void authenticateInternal(CallbackHandler cbh) 109 throws SmackJavaxSaslException { 110 String[] mechanisms = { getName() }; 111 Map<String, String> props = getSaslProps(); 112 try { 113 sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, cbh); 114 } 115 catch (SaslException e) { 116 throw new SmackJavaxSaslException(e); 117 } 118 } 119 120 @Override 121 protected byte[] getAuthenticationText() throws SmackJavaxSaslException { 122 if (sc.hasInitialResponse()) { 123 try { 124 return sc.evaluateChallenge(new byte[0]); 125 } 126 catch (SaslException e) { 127 throw new SmackJavaxSaslException(e); 128 } 129 } 130 return null; 131 } 132 133 @Override 134 protected byte[] evaluateChallenge(byte[] challenge) throws SmackJavaxSaslException { 135 try { 136 if (challenge != null) { 137 return sc.evaluateChallenge(challenge); 138 } 139 else { 140 return sc.evaluateChallenge(new byte[0]); 141 } 142 } 143 catch (SaslException e) { 144 throw new SmackJavaxSaslException(e); 145 } 146 } 147 148 protected Map<String, String> getSaslProps() { 149 return new HashMap<>(); 150 } 151 152 protected String getServerName() { 153 return serviceName.toString(); 154 } 155}