1 /* 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 */ 21 package org.apache.qpid.management.common.sasl; 22 23 import org.apache.harmony.javax.security.auth.callback.Callback; 24 import org.apache.harmony.javax.security.auth.callback.CallbackHandler; 25 import org.apache.harmony.javax.security.auth.callback.NameCallback; 26 import org.apache.harmony.javax.security.auth.callback.PasswordCallback; 27 import org.apache.harmony.javax.security.auth.callback.UnsupportedCallbackException; 28 import de.measite.smack.Sasl; 29 import org.apache.harmony.javax.security.sasl.SaslClient; 30 import org.apache.harmony.javax.security.sasl.SaslException; 31 import java.io.IOException; 32 import java.io.UnsupportedEncodingException; 33 34 public class PlainSaslClient implements SaslClient 35 { 36 37 private boolean completed; 38 private CallbackHandler cbh; 39 private String authorizationID; 40 private String authenticationID; 41 private byte password[]; 42 private static byte SEPARATOR = 0; 43 44 public PlainSaslClient(String authorizationID, CallbackHandler cbh) throws SaslException 45 { 46 completed = false; 47 this.cbh = cbh; 48 Object[] userInfo = getUserInfo(); 49 this.authorizationID = authorizationID; 50 this.authenticationID = (String) userInfo[0]; 51 this.password = (byte[]) userInfo[1]; 52 if (authenticationID == null || password == null) 53 { 54 throw new SaslException("PLAIN: authenticationID and password must be specified"); 55 } 56 } 57 58 public byte[] evaluateChallenge(byte[] challenge) throws SaslException 59 { 60 if (completed) 61 { 62 throw new IllegalStateException("PLAIN: authentication already " + 63 "completed"); 64 } 65 completed = true; 66 try 67 { 68 byte authzid[] = 69 authorizationID == null ? null : authorizationID.getBytes("UTF8"); 70 byte authnid[] = authenticationID.getBytes("UTF8"); 71 byte response[] = 72 new byte[ 73 password.length + 74 authnid.length + 75 2 + // SEPARATOR 76 (authzid != null ? authzid.length : 0) 77 ]; 78 int size = 0; 79 if (authzid != null) { 80 System.arraycopy(authzid, 0, response, 0, authzid.length); 81 size = authzid.length; 82 } 83 response[size++] = SEPARATOR; 84 System.arraycopy(authnid, 0, response, size, authnid.length); 85 size += authnid.length; 86 response[size++] = SEPARATOR; 87 System.arraycopy(password, 0, response, size, password.length); 88 clearPassword(); 89 return response; 90 } catch (UnsupportedEncodingException e) { 91 throw new SaslException("PLAIN: Cannot get UTF-8 encoding of ids", 92 e); 93 } 94 } 95 96 public String getMechanismName() 97 { 98 return "PLAIN"; 99 } 100 101 public boolean hasInitialResponse() 102 { 103 return true; 104 } 105 106 public boolean isComplete() 107 { 108 return completed; 109 } 110 111 public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException 112 { 113 if (completed) { 114 throw new IllegalStateException("PLAIN: this mechanism supports " + 115 "neither integrity nor privacy"); 116 } else { 117 throw new IllegalStateException("PLAIN: authentication not " + 118 "completed"); 119 } 120 } 121 122 public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException 123 { 124 if (completed) 125 { 126 throw new IllegalStateException("PLAIN: this mechanism supports " + 127 "neither integrity nor privacy"); 128 } 129 else 130 { 131 throw new IllegalStateException("PLAIN: authentication not " + 132 "completed"); 133 } 134 } 135 136 public Object getNegotiatedProperty(String propName) 137 { 138 if (completed) 139 { 140 if (propName.equals(Sasl.QOP)) 141 { 142 return "auth"; 143 } 144 else 145 { 146 return null; 147 } 148 } 149 else 150 { 151 throw new IllegalStateException("PLAIN: authentication not " + 152 "completed"); 153 } 154 } 155 156 private void clearPassword() 157 { 158 if (password != null) 159 { 160 for (int i = 0 ; i < password.length ; i++) 161 { 162 password[i] = 0; 163 } 164 password = null; 165 } 166 } 167 168 public void dispose() throws SaslException 169 { 170 clearPassword(); 171 } 172 173 protected void finalize() 174 { 175 clearPassword(); 176 } 177 178 private Object[] getUserInfo() throws SaslException 179 { 180 try 181 { 182 final String userPrompt = "PLAIN authentication id: "; 183 final String pwPrompt = "PLAIN password: "; 184 NameCallback nameCb = new NameCallback(userPrompt); 185 PasswordCallback passwordCb = new PasswordCallback(pwPrompt, false); 186 cbh.handle(new Callback[] { nameCb, passwordCb }); 187 String userid = nameCb.getName(); 188 char pwchars[] = passwordCb.getPassword(); 189 byte pwbytes[]; 190 if (pwchars != null) 191 { 192 pwbytes = (new String(pwchars)).getBytes("UTF8"); 193 passwordCb.clearPassword(); 194 } 195 else 196 { 197 pwbytes = null; 198 } 199 return (new Object[] { userid, pwbytes }); 200 } 201 catch (IOException e) 202 { 203 throw new SaslException("Cannot get password", e); 204 } 205 catch (UnsupportedCallbackException e) 206 { 207 throw new SaslException("Cannot get userid/password", e); 208 } 209 } 210 } 211