1 /* 2 * Copyright (c) 2008-2009, Motorola, Inc. 3 * 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * - Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 12 * - Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * - Neither the name of the Motorola, Inc. nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 package javax.obex; 34 35 import java.io.IOException; 36 37 /** 38 * The <code>ObexSession</code> interface characterizes the term 39 * "OBEX Connection" as defined in the IrDA Object Exchange Protocol v1.2, which 40 * could be the server-side view of an OBEX connection, or the client-side view 41 * of the same connection, which is established by server's accepting of a 42 * client issued "CONNECT". 43 * <P> 44 * This interface serves as the common super class for 45 * <CODE>ClientSession</CODE> and <CODE>ServerSession</CODE>. 46 * @hide 47 */ 48 public class ObexSession { 49 50 protected Authenticator mAuthenticator; 51 52 protected byte[] mChallengeDigest; 53 54 /** 55 * Called when the server received an authentication challenge header. This 56 * will cause the authenticator to handle the authentication challenge. 57 * @param header the header with the authentication challenge 58 * @return <code>true</code> if the last request should be resent; 59 * <code>false</code> if the last request should not be resent 60 * @throws IOException 61 */ 62 public boolean handleAuthChall(HeaderSet header) throws IOException { 63 if (mAuthenticator == null) { 64 return false; 65 } 66 67 /* 68 * An authentication challenge is made up of one required and two 69 * optional tag length value triplets. The tag 0x00 is required to be in 70 * the authentication challenge and it represents the challenge digest 71 * that was received. The tag 0x01 is the options tag. This tag tracks 72 * if user ID is required and if full access will be granted. The tag 73 * 0x02 is the realm, which provides a description of which user name 74 * and password to use. 75 */ 76 byte[] challenge = ObexHelper.getTagValue((byte)0x00, header.mAuthChall); 77 byte[] option = ObexHelper.getTagValue((byte)0x01, header.mAuthChall); 78 byte[] description = ObexHelper.getTagValue((byte)0x02, header.mAuthChall); 79 80 String realm = null; 81 if (description != null) { 82 byte[] realmString = new byte[description.length - 1]; 83 System.arraycopy(description, 1, realmString, 0, realmString.length); 84 85 switch (description[0] & 0xFF) { 86 87 case ObexHelper.OBEX_AUTH_REALM_CHARSET_ASCII: 88 // ASCII encoding 89 // Fall through 90 case ObexHelper.OBEX_AUTH_REALM_CHARSET_ISO_8859_1: 91 // ISO-8859-1 encoding 92 try { 93 realm = new String(realmString, "ISO8859_1"); 94 } catch (Exception e) { 95 throw new IOException("Unsupported Encoding Scheme"); 96 } 97 break; 98 99 case ObexHelper.OBEX_AUTH_REALM_CHARSET_UNICODE: 100 // UNICODE Encoding 101 realm = ObexHelper.convertToUnicode(realmString, false); 102 break; 103 104 default: 105 throw new IOException("Unsupported Encoding Scheme"); 106 } 107 } 108 109 boolean isUserIDRequired = false; 110 boolean isFullAccess = true; 111 if (option != null) { 112 if ((option[0] & 0x01) != 0) { 113 isUserIDRequired = true; 114 } 115 116 if ((option[0] & 0x02) != 0) { 117 isFullAccess = false; 118 } 119 } 120 121 PasswordAuthentication result = null; 122 header.mAuthChall = null; 123 124 try { 125 result = mAuthenticator 126 .onAuthenticationChallenge(realm, isUserIDRequired, isFullAccess); 127 } catch (Exception e) { 128 return false; 129 } 130 131 /* 132 * If no password is provided then we not resent the request 133 */ 134 if (result == null) { 135 return false; 136 } 137 138 byte[] password = result.getPassword(); 139 if (password == null) { 140 return false; 141 } 142 143 byte[] userName = result.getUserName(); 144 145 /* 146 * Create the authentication response header. It includes 1 required and 147 * 2 option tag length value triples. The required triple has a tag of 148 * 0x00 and is the response digest. The first optional tag is 0x01 and 149 * represents the user ID. If no user ID is provided, then no user ID 150 * will be sent. The second optional tag is 0x02 and is the challenge 151 * that was received. This will always be sent 152 */ 153 if (userName != null) { 154 header.mAuthResp = new byte[38 + userName.length]; 155 header.mAuthResp[36] = (byte)0x01; 156 header.mAuthResp[37] = (byte)userName.length; 157 System.arraycopy(userName, 0, header.mAuthResp, 38, userName.length); 158 } else { 159 header.mAuthResp = new byte[36]; 160 } 161 162 // Create the secret String 163 byte[] digest = new byte[challenge.length + password.length + 1]; 164 System.arraycopy(challenge, 0, digest, 0, challenge.length); 165 // Insert colon between challenge and password 166 digest[challenge.length] = (byte)0x3A; 167 System.arraycopy(password, 0, digest, challenge.length + 1, password.length); 168 169 // Add the Response Digest 170 header.mAuthResp[0] = (byte)0x00; 171 header.mAuthResp[1] = (byte)0x10; 172 173 System.arraycopy(ObexHelper.computeMd5Hash(digest), 0, header.mAuthResp, 2, 16); 174 175 // Add the challenge 176 header.mAuthResp[18] = (byte)0x02; 177 header.mAuthResp[19] = (byte)0x10; 178 System.arraycopy(challenge, 0, header.mAuthResp, 20, 16); 179 180 return true; 181 } 182 183 /** 184 * Called when the server received an authentication response header. This 185 * will cause the authenticator to handle the authentication response. 186 * @param authResp the authentication response 187 * @return <code>true</code> if the response passed; <code>false</code> if 188 * the response failed 189 */ 190 public boolean handleAuthResp(byte[] authResp) { 191 if (mAuthenticator == null) { 192 return false; 193 } 194 // get the correct password from the application 195 byte[] correctPassword = mAuthenticator.onAuthenticationResponse(ObexHelper.getTagValue( 196 (byte)0x01, authResp)); 197 if (correctPassword == null) { 198 return false; 199 } 200 201 byte[] temp = new byte[correctPassword.length + 16]; 202 203 System.arraycopy(mChallengeDigest, 0, temp, 0, 16); 204 System.arraycopy(correctPassword, 0, temp, 16, correctPassword.length); 205 206 byte[] correctResponse = ObexHelper.computeMd5Hash(temp); 207 byte[] actualResponse = ObexHelper.getTagValue((byte)0x00, authResp); 208 209 // compare the MD5 hash array . 210 for (int i = 0; i < 16; i++) { 211 if (correctResponse[i] != actualResponse[i]) { 212 return false; 213 } 214 } 215 216 return true; 217 } 218 } 219