XMPPCallbackHandler.java 6.43 KB
Newer Older
1
/**
Matt Tucker's avatar
Matt Tucker committed
2 3
 * $Revision$
 * $Date$
4
 *
5
 * Copyright (C) 2005-2008 Jive Software. All rights reserved.
6
 *
7 8 9 10 11 12 13 14 15 16 17
 * Licensed 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.
18 19
 */

20
package org.jivesoftware.openfire.net;
21

22 23 24 25 26 27 28 29 30 31
import java.io.IOException;

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.auth.callback.UnsupportedCallbackException;
import javax.security.sasl.AuthorizeCallback;
import javax.security.sasl.RealmCallback;

32
import org.jivesoftware.openfire.auth.AuthFactory;
33
import org.jivesoftware.openfire.auth.AuthToken;
34
import org.jivesoftware.openfire.auth.AuthorizationManager;
35
import org.jivesoftware.openfire.sasl.VerifyPasswordCallback;
36
import org.jivesoftware.openfire.user.UserNotFoundException;
37 38
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
39 40

/**
Matt Tucker's avatar
Matt Tucker committed
41
 * Callback handler that may be used when doing SASL authentication. A CallbackHandler
42
 * may be required depending on the SASL mechanism being used.<p>
43
 *
Matt Tucker's avatar
Matt Tucker committed
44 45 46 47 48
 * Mechanisms that use a digest don't include a password so the server needs to use the
 * stored password of the user to compare it (somehow) with the specified digest. This
 * operation requires that the UserProvider being used supports passwords retrival.
 * {@link SASLAuthentication} should not offer these kind of SASL mechanisms if the user
 * provider being in use does not support passwords retrieval.
49 50 51 52 53
 *
 * @author Hao Chen
 */
public class XMPPCallbackHandler implements CallbackHandler {

54 55
	private static final Logger Log = LoggerFactory.getLogger(XMPPCallbackHandler.class);

Matt Tucker's avatar
Matt Tucker committed
56 57
    public XMPPCallbackHandler() {
    }
58

59
    @Override
Matt Tucker's avatar
Matt Tucker committed
60 61
    public void handle(final Callback[] callbacks)
            throws IOException, UnsupportedCallbackException {
62

63

64
        String realm;
Matt Tucker's avatar
Matt Tucker committed
65
        String name = null;
66

67 68 69
        for (Callback callback : callbacks) {
            if (callback instanceof RealmCallback) {
                realm = ((RealmCallback) callback).getText();
Matt Tucker's avatar
Matt Tucker committed
70
                if (realm == null) {
71
                    realm = ((RealmCallback) callback).getDefaultText();
Matt Tucker's avatar
Matt Tucker committed
72
                }
73
                //Log.debug("XMPPCallbackHandler: RealmCallback: " + realm);
Matt Tucker's avatar
Matt Tucker committed
74
            }
75 76
            else if (callback instanceof NameCallback) {
                name = ((NameCallback) callback).getName();
Matt Tucker's avatar
Matt Tucker committed
77
                if (name == null) {
78
                    name = ((NameCallback) callback).getDefaultName();
Matt Tucker's avatar
Matt Tucker committed
79
                }
80
                //Log.debug("XMPPCallbackHandler: NameCallback: " + name);
Matt Tucker's avatar
Matt Tucker committed
81
            }
82
            else if (callback instanceof PasswordCallback) {
Matt Tucker's avatar
Matt Tucker committed
83
                try {
84 85
                    // Get the password from the UserProvider. Some UserProviders may not support
                    // this operation
86
                    ((PasswordCallback) callback)
87 88
                            .setPassword(AuthFactory.getPassword(name).toCharArray());

89
                    //Log.debug("XMPPCallbackHandler: PasswordCallback");
Matt Tucker's avatar
Matt Tucker committed
90
                }
91
                catch (UserNotFoundException | UnsupportedOperationException e) {
Matt Tucker's avatar
Matt Tucker committed
92 93
                    throw new IOException(e.toString());
                }
94 95

            }
96
            else if (callback instanceof VerifyPasswordCallback) {
97
                //Log.debug("XMPPCallbackHandler: VerifyPasswordCallback");
98
                VerifyPasswordCallback vpcb = (VerifyPasswordCallback) callback;
99
                try {
100 101
                    AuthToken at = AuthFactory.authenticate(name, new String(vpcb.getPassword()));
                    vpcb.setVerified((at != null));
102
                }
103
                catch (Exception e) {
104 105
                    vpcb.setVerified(false);
                }
Matt Tucker's avatar
Matt Tucker committed
106
            }
107
            else if (callback instanceof AuthorizeCallback) {
108
                //Log.debug("XMPPCallbackHandler: AuthorizeCallback");
109 110 111 112 113 114 115 116 117 118 119
                AuthorizeCallback authCallback = ((AuthorizeCallback) callback);
                // Principal that authenticated
                String principal = authCallback.getAuthenticationID();
                // Username requested (not full JID)
                String username = authCallback.getAuthorizationID();
                // Remove any REALM from the username. This is optional in the spec and it may cause
                // a lot of users to fail to log in if their clients is sending an incorrect value
                if (username != null && username.contains("@")) {
                    username = username.substring(0, username.lastIndexOf("@"));
                }
                if (principal.equals(username)) {
120 121
                    //client perhaps made no request, get default username
                    username = AuthorizationManager.map(principal);
122
                    if (Log.isDebugEnabled()) {
123
                        //Log.debug("XMPPCallbackHandler: no username requested, using " + username);
124
                    }
125 126
                }
                if (AuthorizationManager.authorize(username, principal)) {
127
                    if (Log.isDebugEnabled()) {
128
                        //Log.debug("XMPPCallbackHandler: " + principal + " authorized to " + username);
129
                    }
Matt Tucker's avatar
Matt Tucker committed
130
                    authCallback.setAuthorized(true);
131
                    authCallback.setAuthorizedID(username);
132 133
                }
                else {
134
                    if (Log.isDebugEnabled()) {
135
                        //Log.debug("XMPPCallbackHandler: " + principal + " not authorized to " + username);
136
                    }
137
                    authCallback.setAuthorized(false);
Matt Tucker's avatar
Matt Tucker committed
138 139 140
                }
            }
            else {
141
                if (Log.isDebugEnabled()) {
142
                    //Log.debug("XMPPCallbackHandler: Callback: " + callback.getClass().getSimpleName());
143 144
                }
                throw new UnsupportedCallbackException(callback, "Unrecognized Callback");
Matt Tucker's avatar
Matt Tucker committed
145 146 147 148
            }
        }
    }
}