use of org.jivesoftware.openfire.Connection in project Openfire by igniterealtime.
the class ExternalClientSaslServer method evaluateResponse.
@Override
public byte[] evaluateResponse(byte[] response) throws SaslException {
if (isComplete()) {
throw new IllegalStateException("Authentication exchange already completed.");
}
// There will be no further steps. Either authentication succeeds or fails, but in any case, we're done.
complete = true;
final Connection connection = session.getConnection();
Certificate[] peerCertificates = connection.getPeerCertificates();
if (peerCertificates == null || peerCertificates.length < 1) {
throw new SaslException("No peer certificates.");
}
final X509Certificate trusted;
if (SASLAuthentication.SKIP_PEER_CERT_REVALIDATION_CLIENT.getValue()) {
// Trust that the peer certificate has been validated when TLS got established.
trusted = (X509Certificate) peerCertificates[0];
} else {
// Re-evaluate the validity of the peer certificate.
final TrustStore trustStore = connection.getConfiguration().getTrustStore();
trusted = trustStore.getEndEntityCertificate(peerCertificates);
}
if (trusted == null) {
throw new SaslException("Certificate chain of peer is not trusted.");
}
// Process client identities / principals.
final ArrayList<String> principals = new ArrayList<>();
principals.addAll(CertificateManager.getClientIdentities(trusted));
String principal;
switch(principals.size()) {
case 0:
principal = "";
break;
default:
Log.debug("More than one principal found, using the first one.");
// intended fall-through;
case 1:
principal = principals.get(0);
break;
}
// Process requested user name.
String username;
if (response != null && response.length > 0) {
username = new String(response, StandardCharsets.UTF_8);
if (PROPERTY_SASL_EXTERNAL_CLIENT_SUPPRESS_MATCHING_REALMNAME.getValue() && username.contains("@")) {
String userUser = username.substring(0, username.lastIndexOf("@"));
String userRealm = username.substring((username.lastIndexOf("@") + 1));
if (XMPPServer.getInstance().getServerInfo().getXMPPDomain().equals(userRealm)) {
username = userUser;
}
}
} else {
username = null;
}
if (username == null || username.length() == 0) {
// cause an authorization failure.
for (String princ : principals) {
final String mappedUsername = AuthorizationManager.map(princ);
if (!mappedUsername.equals(princ)) {
username = mappedUsername;
principal = princ;
break;
}
}
if (username == null || username.length() == 0) {
// Still no username. Punt.
username = principal;
}
Log.debug("No username requested, using: {}", username);
}
// Its possible that either/both username and principal are null here. The providers should not allow a null authorization
if (AuthorizationManager.authorize(username, principal)) {
Log.debug("Principal {} authorized to username {}", principal, username);
authorizationID = username;
// Success!
return null;
}
throw new SaslException();
}
use of org.jivesoftware.openfire.Connection in project Openfire by igniterealtime.
the class SASLAuthentication method getSASLMechanismsElement.
public static Element getSASLMechanismsElement(LocalIncomingServerSession session) {
final Element result = DocumentHelper.createElement(new QName("mechanisms", new Namespace("", SASL_NAMESPACE)));
if (session.isSecure()) {
final Connection connection = session.getConnection();
final TrustStore trustStore = connection.getConfiguration().getTrustStore();
final X509Certificate trusted = trustStore.getEndEntityCertificate(session.getConnection().getPeerCertificates());
boolean haveTrustedCertificate = trusted != null;
if (trusted != null && session.getDefaultIdentity() != null) {
haveTrustedCertificate = verifyCertificate(trusted, session.getDefaultIdentity());
}
if (haveTrustedCertificate) {
// Offer SASL EXTERNAL only if TLS has already been negotiated and the peer has a trusted cert.
final Element mechanism = result.addElement("mechanism");
mechanism.setText("EXTERNAL");
}
}
// OF-2072: Return null instead of an empty element, if so configured.
if (JiveGlobals.getBooleanProperty("sasl.server.suppressEmpty", false) && result.elements().isEmpty()) {
return null;
}
return result;
}
use of org.jivesoftware.openfire.Connection in project Openfire by igniterealtime.
the class StreamManager method startResume.
private void startResume(String namespace, String previd, long h) {
Log.debug("Attempting resumption for {}, h={}", previd, h);
this.namespace = namespace;
// Ensure that resource binding has NOT occurred.
if (!allowResume()) {
Log.debug("Unable to process session resumption attempt, as session {} is in a state where session resumption is not allowed.", session);
sendUnexpectedError();
return;
}
if (session.getStatus() == Session.STATUS_AUTHENTICATED) {
Log.debug("Unable to process session resumption attempt, as session {} is not authenticated.", session);
sendUnexpectedError();
return;
}
AuthToken authToken = null;
// Ensure that resource binding has occurred.
if (session instanceof ClientSession) {
authToken = ((LocalClientSession) session).getAuthToken();
}
if (authToken == null) {
Log.debug("Unable to process session resumption attempt, as session {} does not provide any auth context.", session);
sendUnexpectedError();
return;
}
// Decode previd.
String resource;
String streamId;
try {
StringTokenizer toks = new StringTokenizer(new String(StringUtils.decodeBase64(previd), StandardCharsets.UTF_8), "\0");
resource = toks.nextToken();
streamId = toks.nextToken();
} catch (Exception e) {
Log.debug("Exception from previd decode:", e);
sendUnexpectedError();
return;
}
final JID fullJid;
if (authToken.isAnonymous()) {
fullJid = new JID(resource, session.getServerName(), resource, true);
} else {
fullJid = new JID(authToken.getUsername(), session.getServerName(), resource, true);
}
Log.debug("Resuming session for '{}'. Current session: {}", fullJid, session.getStreamID());
// Locate existing session.
final ClientSession route = XMPPServer.getInstance().getRoutingTable().getClientRoute(fullJid);
if (route == null) {
sendError(new PacketError(PacketError.Condition.item_not_found));
return;
}
if (!(route instanceof LocalClientSession)) {
Log.debug("Not allowing a client of '{}' to resume a session on this cluster node. The session can only be resumed on the Openfire cluster node where the original session was connected.", fullJid);
sendError(new PacketError(PacketError.Condition.unexpected_request));
return;
}
final LocalClientSession otherSession = (LocalClientSession) route;
if (!otherSession.getStreamID().getID().equals(streamId)) {
sendError(new PacketError(PacketError.Condition.item_not_found));
return;
}
Log.debug("Found existing session for '{}', checking status", fullJid);
// Previd identifies proper session. Now check SM status
if (!otherSession.getStreamManager().resume) {
Log.debug("Not allowing a client of '{}' to resume a session, the session to be resumed does not have the stream management resumption feature enabled.", fullJid);
sendError(new PacketError(PacketError.Condition.unexpected_request));
return;
}
if (otherSession.getStreamManager().namespace == null) {
Log.debug("Not allowing a client of '{}' to resume a session, the session to be resumed disabled SM functionality as a response to an earlier error.", fullJid);
sendError(new PacketError(PacketError.Condition.unexpected_request));
return;
}
if (!otherSession.getStreamManager().namespace.equals(namespace)) {
Log.debug("Not allowing a client of '{}' to resume a session, the session to be resumed used a different version ({}) of the session management resumption feature as compared to the version that's requested now: {}.", fullJid, otherSession.getStreamManager().namespace, namespace);
sendError(new PacketError(PacketError.Condition.unexpected_request));
return;
}
if (!otherSession.getStreamManager().validateClientAcknowledgement(h)) {
Log.debug("Not allowing a client of '{}' to resume a session, as it reports it received more stanzas from us than that we've send it.", fullJid);
sendError(new PacketError(PacketError.Condition.unexpected_request));
return;
}
if (!otherSession.isDetached()) {
Log.debug("Existing session {} of '{}' is not detached; detaching.", otherSession.getStreamID(), fullJid);
Connection oldConnection = otherSession.getConnection();
otherSession.setDetached();
oldConnection.close();
}
Log.debug("Attaching to other session '{}' of '{}'.", otherSession.getStreamID(), fullJid);
// If we're all happy, re-attach the connection from the pre-existing session to the new session, discarding the old session.
otherSession.reattach(session, h);
Log.debug("Perform resumption of session {} for '{}', using connection from session {}", otherSession.getStreamID(), fullJid, session.getStreamID());
}
use of org.jivesoftware.openfire.Connection in project Openfire by igniterealtime.
the class LocalSession method deliverRawText.
@Override
public void deliverRawText(String text) {
Connection connection = conn;
if (connection == null) {
Log.debug("Unable to deliver raw text in session with address {} and streamID {}, as its connection is null. Dropping: {}", this.address, this.streamID, text);
return;
}
connection.deliverRawText(text);
}
use of org.jivesoftware.openfire.Connection in project Openfire by igniterealtime.
the class LocalIncomingServerSession method createSession.
/**
* Creates a new session that will receive packets. The new session will be authenticated
* before being returned. If the authentication process fails then the answer will be
* <tt>null</tt>.<p>
*
* @param serverName hostname of this server.
* @param reader reader on the new established connection with the remote server.
* @param connection the new established connection with the remote server.
* @return a new session that will receive packets or null if a problem occured while
* authenticating the remote server or when acting as the Authoritative Server during
* a Server Dialback authentication process.
* @throws org.xmlpull.v1.XmlPullParserException if an error occurs while parsing the XML.
* @throws java.io.IOException if an input/output error occurs while using the connection.
*/
public static LocalIncomingServerSession createSession(String serverName, XMPPPacketReader reader, SocketConnection connection) throws XmlPullParserException, IOException {
XmlPullParser xpp = reader.getXPPParser();
String version = xpp.getAttributeValue("", "version");
String fromDomain = xpp.getAttributeValue("", "from");
String toDomain = xpp.getAttributeValue("", "to");
int[] serverVersion = version != null ? decodeVersion(version) : new int[] { 0, 0 };
if (toDomain == null) {
toDomain = serverName;
}
try {
// Get the stream ID for the new session
StreamID streamID = SessionManager.getInstance().nextStreamID();
// Create a server Session for the remote server
LocalIncomingServerSession session = SessionManager.getInstance().createIncomingServerSession(connection, streamID, fromDomain);
// Send the stream header
StringBuilder openingStream = new StringBuilder();
openingStream.append("<stream:stream");
openingStream.append(" xmlns:db=\"jabber:server:dialback\"");
openingStream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\"");
openingStream.append(" xmlns=\"jabber:server\"");
openingStream.append(" from=\"").append(toDomain).append("\"");
if (fromDomain != null) {
openingStream.append(" to=\"").append(fromDomain).append("\"");
}
openingStream.append(" id=\"").append(streamID).append("\"");
// implementations appears to reduce connection issues with those domains (patch by Marcin Cieślak).
if (serverVersion[0] >= 1) {
openingStream.append(" version=\"1.0\">");
} else {
openingStream.append('>');
}
connection.deliverRawText(openingStream.toString());
if (serverVersion[0] >= 1) {
// Remote server is XMPP 1.0 compliant so offer TLS and SASL to establish the connection (and server dialback)
// Indicate the TLS policy to use for this connection
Connection.TLSPolicy tlsPolicy = connection.getTlsPolicy();
boolean hasCertificates = false;
try {
hasCertificates = XMPPServer.getInstance().getCertificateStoreManager().getIdentityStore(ConnectionType.SOCKET_S2S).getStore().size() > 0;
} catch (Exception e) {
Log.error(e.getMessage(), e);
}
if (Connection.TLSPolicy.required == tlsPolicy && !hasCertificates) {
Log.error("Server session rejected. TLS is required but no certificates " + "were created.");
return null;
}
connection.setTlsPolicy(hasCertificates ? tlsPolicy : Connection.TLSPolicy.disabled);
}
// Indicate the compression policy to use for this connection
connection.setCompressionPolicy(connection.getConfiguration().getCompressionPolicy());
StringBuilder sb = new StringBuilder();
if (serverVersion[0] >= 1) {
// Remote server is XMPP 1.0 compliant so offer TLS and SASL to establish the connection (and server dialback)
// Don't offer stream-features to pre-1.0 servers, as it confuses them. Sending features to Openfire < 3.7.1 confuses it too - OF-443)
sb.append("<stream:features>");
if (JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_ENABLED, true)) {
sb.append("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">");
if (!ServerDialback.isEnabled()) {
// Server dialback is disabled so TLS is required
sb.append("<required/>");
}
sb.append("</starttls>");
}
// Include available SASL Mechanisms
sb.append(SASLAuthentication.getSASLMechanisms(session));
if (ServerDialback.isEnabled()) {
// Also offer server dialback (when TLS is not required). Server dialback may be offered
// after TLS has been negotiated and a self-signed certificate is being used
sb.append("<dialback xmlns=\"urn:xmpp:features:dialback\"><errors/></dialback>");
}
sb.append("</stream:features>");
}
connection.deliverRawText(sb.toString());
// Set the domain or subdomain of the local server targeted by the remote server
session.setLocalDomain(serverName);
return session;
} catch (Exception e) {
Log.error("Error establishing connection from remote server:" + connection, e);
connection.close();
return null;
}
}
Aggregations