1 /* 2 * Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.security.sasl; 27 28 import javax.security.sasl.*; 29 import com.sun.security.sasl.util.PolicyUtils; 30 31 import java.util.Map; 32 import java.io.IOException; 33 import javax.security.auth.callback.Callback; 34 import javax.security.auth.callback.CallbackHandler; 35 import javax.security.auth.callback.NameCallback; 36 import javax.security.auth.callback.PasswordCallback; 37 import javax.security.auth.callback.UnsupportedCallbackException; 38 39 /** 40 * Client factory for EXTERNAL, CRAM-MD5, PLAIN. 41 * 42 * Requires the following callbacks to be satisfied by callback handler 43 * when using CRAM-MD5 or PLAIN. 44 * - NameCallback (to get username) 45 * - PasswordCallback (to get password) 46 * 47 * @author Rosanna Lee 48 */ 49 final public class ClientFactoryImpl implements SaslClientFactory { 50 private static final String[] myMechs = { 51 "EXTERNAL", 52 "CRAM-MD5", 53 "PLAIN", 54 }; 55 56 private static final int[] mechPolicies = { 57 // %%% RL: Policies should actually depend on the external channel 58 PolicyUtils.NOPLAINTEXT|PolicyUtils.NOACTIVE|PolicyUtils.NODICTIONARY, 59 PolicyUtils.NOPLAINTEXT|PolicyUtils.NOANONYMOUS, // CRAM-MD5 60 PolicyUtils.NOANONYMOUS, // PLAIN 61 }; 62 63 private static final int EXTERNAL = 0; 64 private static final int CRAMMD5 = 1; 65 private static final int PLAIN = 2; 66 ClientFactoryImpl()67 public ClientFactoryImpl() { 68 } 69 createSaslClient(String[] mechs, String authorizationId, String protocol, String serverName, Map<String,?> props, CallbackHandler cbh)70 public SaslClient createSaslClient(String[] mechs, 71 String authorizationId, 72 String protocol, 73 String serverName, 74 Map<String,?> props, 75 CallbackHandler cbh) throws SaslException { 76 77 for (int i = 0; i < mechs.length; i++) { 78 if (mechs[i].equals(myMechs[EXTERNAL]) 79 && PolicyUtils.checkPolicy(mechPolicies[EXTERNAL], props)) { 80 return new ExternalClient(authorizationId); 81 82 } else if (mechs[i].equals(myMechs[CRAMMD5]) 83 && PolicyUtils.checkPolicy(mechPolicies[CRAMMD5], props)) { 84 85 Object[] uinfo = getUserInfo("CRAM-MD5", authorizationId, cbh); 86 87 // Callee responsible for clearing bytepw 88 return new CramMD5Client((String) uinfo[0], 89 (byte []) uinfo[1]); 90 91 } else if (mechs[i].equals(myMechs[PLAIN]) 92 && PolicyUtils.checkPolicy(mechPolicies[PLAIN], props)) { 93 94 Object[] uinfo = getUserInfo("PLAIN", authorizationId, cbh); 95 96 // Callee responsible for clearing bytepw 97 return new PlainClient(authorizationId, 98 (String) uinfo[0], (byte []) uinfo[1]); 99 } 100 } 101 return null; 102 }; 103 getMechanismNames(Map<String,?> props)104 public String[] getMechanismNames(Map<String,?> props) { 105 return PolicyUtils.filterMechs(myMechs, mechPolicies, props); 106 } 107 108 /** 109 * Gets the authentication id and password. The 110 * password is converted to bytes using UTF-8 and stored in bytepw. 111 * The authentication id is stored in authId. 112 * 113 * @param prefix The non-null prefix to use for the prompt (e.g., mechanism 114 * name) 115 * @param authorizationId The possibly null authorization id. This is used 116 * as a default for the NameCallback. If null, it is not used in prompt. 117 * @param cbh The non-null callback handler to use. 118 * @return an {authid, passwd} pair 119 */ getUserInfo(String prefix, String authorizationId, CallbackHandler cbh)120 private Object[] getUserInfo(String prefix, String authorizationId, 121 CallbackHandler cbh) throws SaslException { 122 if (cbh == null) { 123 throw new SaslException( 124 "Callback handler to get username/password required"); 125 } 126 try { 127 String userPrompt = prefix + " authentication id: "; 128 String passwdPrompt = prefix + " password: "; 129 130 NameCallback ncb = authorizationId == null? 131 new NameCallback(userPrompt) : 132 new NameCallback(userPrompt, authorizationId); 133 134 PasswordCallback pcb = new PasswordCallback(passwdPrompt, false); 135 136 cbh.handle(new Callback[]{ncb,pcb}); 137 138 char[] pw = pcb.getPassword(); 139 140 byte[] bytepw; 141 String authId; 142 143 if (pw != null) { 144 bytepw = new String(pw).getBytes("UTF8"); 145 pcb.clearPassword(); 146 } else { 147 bytepw = null; 148 } 149 150 authId = ncb.getName(); 151 152 return new Object[]{authId, bytepw}; 153 154 } catch (IOException e) { 155 throw new SaslException("Cannot get password", e); 156 } catch (UnsupportedCallbackException e) { 157 throw new SaslException("Cannot get userid/password", e); 158 } 159 } 160 } 161