1 /* 2 * Copyright (c) 2000, 2009, 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 sun.security.jgss.krb5; 27 28 import org.ietf.jgss.*; 29 import javax.security.auth.kerberos.DelegationPermission; 30 import java.io.IOException; 31 import java.net.InetAddress; 32 import java.net.Inet4Address; 33 import java.net.Inet6Address; 34 import java.security.MessageDigest; 35 import java.security.NoSuchAlgorithmException; 36 import java.util.Arrays; 37 import sun.security.krb5.*; 38 import sun.security.krb5.internal.Krb5; 39 40 abstract class InitialToken extends Krb5Token { 41 42 private static final int CHECKSUM_TYPE = 0x8003; 43 44 private static final int CHECKSUM_LENGTH_SIZE = 4; 45 private static final int CHECKSUM_BINDINGS_SIZE = 16; 46 private static final int CHECKSUM_FLAGS_SIZE = 4; 47 private static final int CHECKSUM_DELEG_OPT_SIZE = 2; 48 private static final int CHECKSUM_DELEG_LGTH_SIZE = 2; 49 50 private static final int CHECKSUM_DELEG_FLAG = 1; 51 private static final int CHECKSUM_MUTUAL_FLAG = 2; 52 private static final int CHECKSUM_REPLAY_FLAG = 4; 53 private static final int CHECKSUM_SEQUENCE_FLAG = 8; 54 private static final int CHECKSUM_CONF_FLAG = 16; 55 private static final int CHECKSUM_INTEG_FLAG = 32; 56 57 private final byte[] CHECKSUM_FIRST_BYTES = 58 {(byte)0x10, (byte)0x00, (byte)0x00, (byte)0x00}; 59 60 private static final int CHANNEL_BINDING_AF_INET = 2; 61 private static final int CHANNEL_BINDING_AF_INET6 = 24; 62 private static final int CHANNEL_BINDING_AF_NULL_ADDR = 255; 63 64 private static final int Inet4_ADDRSZ = 4; 65 private static final int Inet6_ADDRSZ = 16; 66 67 protected class OverloadedChecksum { 68 69 private byte[] checksumBytes = null; 70 private Credentials delegCreds = null; 71 private int flags = 0; 72 73 /** 74 * Called on the initiator side when creating the 75 * InitSecContextToken. 76 */ 77 public OverloadedChecksum(Krb5Context context, 78 Credentials tgt, 79 Credentials serviceTicket) 80 throws KrbException, IOException, GSSException { 81 82 byte[] krbCredMessage = null; 83 int pos = 0; 84 int size = CHECKSUM_LENGTH_SIZE + CHECKSUM_BINDINGS_SIZE + 85 CHECKSUM_FLAGS_SIZE; 86 87 if (!tgt.isForwardable()) { 88 context.setCredDelegState(false); 89 context.setDelegPolicyState(false); 90 } else if (context.getCredDelegState()) { 91 if (context.getDelegPolicyState()) { 92 if (!serviceTicket.checkDelegate()) { 93 // delegation not permitted by server policy, mark it 94 context.setDelegPolicyState(false); 95 } 96 } 97 } else if (context.getDelegPolicyState()) { 98 if (serviceTicket.checkDelegate()) { 99 context.setCredDelegState(true); 100 } else { 101 context.setDelegPolicyState(false); 102 } 103 } 104 105 if (context.getCredDelegState()) { 106 KrbCred krbCred = null; 107 CipherHelper cipherHelper = 108 context.getCipherHelper(serviceTicket.getSessionKey()); 109 if (useNullKey(cipherHelper)) { 110 krbCred = new KrbCred(tgt, serviceTicket, 111 EncryptionKey.NULL_KEY); 112 } else { 113 krbCred = new KrbCred(tgt, serviceTicket, 114 serviceTicket.getSessionKey()); 115 } 116 krbCredMessage = krbCred.getMessage(); 117 size += CHECKSUM_DELEG_OPT_SIZE + 118 CHECKSUM_DELEG_LGTH_SIZE + 119 krbCredMessage.length; 120 } 121 122 checksumBytes = new byte[size]; 123 124 checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[0]; 125 checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[1]; 126 checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[2]; 127 checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[3]; 128 129 ChannelBinding localBindings = context.getChannelBinding(); 130 if (localBindings != null) { 131 byte[] localBindingsBytes = 132 computeChannelBinding(context.getChannelBinding()); 133 System.arraycopy(localBindingsBytes, 0, 134 checksumBytes, pos, localBindingsBytes.length); 135 // System.out.println("ChannelBinding hash: " 136 // + getHexBytes(localBindingsBytes)); 137 } 138 139 pos += CHECKSUM_BINDINGS_SIZE; 140 141 if (context.getCredDelegState()) 142 flags |= CHECKSUM_DELEG_FLAG; 143 if (context.getMutualAuthState()) 144 flags |= CHECKSUM_MUTUAL_FLAG; 145 if (context.getReplayDetState()) 146 flags |= CHECKSUM_REPLAY_FLAG; 147 if (context.getSequenceDetState()) 148 flags |= CHECKSUM_SEQUENCE_FLAG; 149 if (context.getIntegState()) 150 flags |= CHECKSUM_INTEG_FLAG; 151 if (context.getConfState()) 152 flags |= CHECKSUM_CONF_FLAG; 153 154 byte[] temp = new byte[4]; 155 writeLittleEndian(flags, temp); 156 checksumBytes[pos++] = temp[0]; 157 checksumBytes[pos++] = temp[1]; 158 checksumBytes[pos++] = temp[2]; 159 checksumBytes[pos++] = temp[3]; 160 161 if (context.getCredDelegState()) { 162 163 PrincipalName delegateTo = 164 serviceTicket.getServer(); 165 // Cannot use '\"' instead of "\"" in constructor because 166 // it is interpreted as suggested length! 167 StringBuffer buf = new StringBuffer("\""); 168 buf.append(delegateTo.getName()).append('\"'); 169 String realm = delegateTo.getRealmAsString(); 170 buf.append(" \"krbtgt/").append(realm).append('@'); 171 buf.append(realm).append('\"'); 172 SecurityManager sm = System.getSecurityManager(); 173 if (sm != null) { 174 DelegationPermission perm = 175 new DelegationPermission(buf.toString()); 176 sm.checkPermission(perm); 177 } 178 179 180 /* 181 * Write 1 in little endian but in two bytes 182 * for DlgOpt 183 */ 184 185 checksumBytes[pos++] = (byte)0x01; 186 checksumBytes[pos++] = (byte)0x00; 187 188 /* 189 * Write the length of the delegated credential in little 190 * endian but in two bytes for Dlgth 191 */ 192 193 if (krbCredMessage.length > 0x0000ffff) 194 throw new GSSException(GSSException.FAILURE, -1, 195 "Incorrect messsage length"); 196 197 writeLittleEndian(krbCredMessage.length, temp); 198 checksumBytes[pos++] = temp[0]; 199 checksumBytes[pos++] = temp[1]; 200 System.arraycopy(krbCredMessage, 0, 201 checksumBytes, pos, krbCredMessage.length); 202 } 203 204 } 205 206 /** 207 * Called on the acceptor side when reading an InitSecContextToken. 208 */ 209 // XXX Passing in Checksum is not required. byte[] can 210 // be passed in if this checksum type denotes a 211 // raw_checksum. In that case, make Checksum class krb5 212 // internal. 213 public OverloadedChecksum(Krb5Context context, 214 Checksum checksum, EncryptionKey key) 215 throws GSSException, KrbException, IOException { 216 217 int pos = 0; 218 219 if (checksum == null) { 220 GSSException ge = new GSSException(GSSException.FAILURE, -1, 221 "No cksum in AP_REQ's authenticator"); 222 ge.initCause(new KrbException(Krb5.KRB_AP_ERR_INAPP_CKSUM)); 223 throw ge; 224 } 225 checksumBytes = checksum.getBytes(); 226 227 if ((checksumBytes[0] != CHECKSUM_FIRST_BYTES[0]) || 228 (checksumBytes[1] != CHECKSUM_FIRST_BYTES[1]) || 229 (checksumBytes[2] != CHECKSUM_FIRST_BYTES[2]) || 230 (checksumBytes[3] != CHECKSUM_FIRST_BYTES[3])) { 231 throw new GSSException(GSSException.FAILURE, -1, 232 "Incorrect checksum"); 233 } 234 235 ChannelBinding localBindings = context.getChannelBinding(); 236 237 // Ignore remote channel binding info when not requested at 238 // local side (RFC 4121 4.1.1.2: the acceptor MAY ignore...). 239 // 240 // All major krb5 implementors implement this "MAY", 241 // and some applications depend on it as a workaround 242 // for not having a way to negotiate the use of channel 243 // binding -- the initiator application always uses CB 244 // and hopes the acceptor will ignore the CB if the 245 // acceptor doesn't support CB. 246 if (localBindings != null) { 247 byte[] remoteBindingBytes = new byte[CHECKSUM_BINDINGS_SIZE]; 248 System.arraycopy(checksumBytes, 4, remoteBindingBytes, 0, 249 CHECKSUM_BINDINGS_SIZE); 250 251 byte[] noBindings = new byte[CHECKSUM_BINDINGS_SIZE]; 252 if (!Arrays.equals(noBindings, remoteBindingBytes)) { 253 byte[] localBindingsBytes = 254 computeChannelBinding(localBindings); 255 if (!Arrays.equals(localBindingsBytes, 256 remoteBindingBytes)) { 257 throw new GSSException(GSSException.BAD_BINDINGS, -1, 258 "Bytes mismatch!"); 259 } 260 } else { 261 throw new GSSException(GSSException.BAD_BINDINGS, -1, 262 "Token missing ChannelBinding!"); 263 } 264 } 265 266 flags = readLittleEndian(checksumBytes, 20, 4); 267 268 if ((flags & CHECKSUM_DELEG_FLAG) > 0) { 269 270 /* 271 * XXX 272 * if ((checksumBytes[24] != (byte)0x01) && 273 * (checksumBytes[25] != (byte)0x00)) 274 */ 275 276 int credLen = readLittleEndian(checksumBytes, 26, 2); 277 byte[] credBytes = new byte[credLen]; 278 System.arraycopy(checksumBytes, 28, credBytes, 0, credLen); 279 280 CipherHelper cipherHelper = context.getCipherHelper(key); 281 if (useNullKey(cipherHelper)) { 282 delegCreds = 283 new KrbCred(credBytes, EncryptionKey.NULL_KEY). 284 getDelegatedCreds()[0]; 285 } else { 286 delegCreds = 287 new KrbCred(credBytes, key). 288 getDelegatedCreds()[0]; 289 } 290 } 291 } 292 293 // check if KRB-CRED message should use NULL_KEY for encryption 294 private boolean useNullKey(CipherHelper ch) { 295 boolean flag = true; 296 // for "newer" etypes and RC4-HMAC do not use NULL KEY 297 if ((ch.getProto() == 1) || ch.isArcFour()) { 298 flag = false; 299 } 300 return flag; 301 } 302 303 public Checksum getChecksum() throws KrbException { 304 return new Checksum(checksumBytes, CHECKSUM_TYPE); 305 } 306 307 public Credentials getDelegatedCreds() { 308 return delegCreds; 309 } 310 311 // Only called by acceptor 312 public void setContextFlags(Krb5Context context) { 313 // default for cred delegation is false 314 if ((flags & CHECKSUM_DELEG_FLAG) > 0) 315 context.setCredDelegState(true); 316 // default for the following are true 317 if ((flags & CHECKSUM_MUTUAL_FLAG) == 0) { 318 context.setMutualAuthState(false); 319 } 320 if ((flags & CHECKSUM_REPLAY_FLAG) == 0) { 321 context.setReplayDetState(false); 322 } 323 if ((flags & CHECKSUM_SEQUENCE_FLAG) == 0) { 324 context.setSequenceDetState(false); 325 } 326 if ((flags & CHECKSUM_CONF_FLAG) == 0) { 327 context.setConfState(false); 328 } 329 if ((flags & CHECKSUM_INTEG_FLAG) == 0) { 330 context.setIntegState(false); 331 } 332 } 333 } 334 335 private int getAddrType(InetAddress addr) { 336 int addressType = CHANNEL_BINDING_AF_NULL_ADDR; 337 338 if (addr instanceof Inet4Address) 339 addressType = CHANNEL_BINDING_AF_INET; 340 else if (addr instanceof Inet6Address) 341 addressType = CHANNEL_BINDING_AF_INET6; 342 return (addressType); 343 } 344 345 private byte[] getAddrBytes(InetAddress addr) throws GSSException { 346 int addressType = getAddrType(addr); 347 byte[] addressBytes = addr.getAddress(); 348 if (addressBytes != null) { 349 switch (addressType) { 350 case CHANNEL_BINDING_AF_INET: 351 if (addressBytes.length != Inet4_ADDRSZ) { 352 throw new GSSException(GSSException.FAILURE, -1, 353 "Incorrect AF-INET address length in ChannelBinding."); 354 } 355 return (addressBytes); 356 case CHANNEL_BINDING_AF_INET6: 357 if (addressBytes.length != Inet6_ADDRSZ) { 358 throw new GSSException(GSSException.FAILURE, -1, 359 "Incorrect AF-INET6 address length in ChannelBinding."); 360 } 361 return (addressBytes); 362 default: 363 throw new GSSException(GSSException.FAILURE, -1, 364 "Cannot handle non AF-INET addresses in ChannelBinding."); 365 } 366 } 367 return null; 368 } 369 370 private byte[] computeChannelBinding(ChannelBinding channelBinding) 371 throws GSSException { 372 373 InetAddress initiatorAddress = channelBinding.getInitiatorAddress(); 374 InetAddress acceptorAddress = channelBinding.getAcceptorAddress(); 375 int size = 5*4; 376 377 int initiatorAddressType = getAddrType(initiatorAddress); 378 int acceptorAddressType = getAddrType(acceptorAddress); 379 380 byte[] initiatorAddressBytes = null; 381 if (initiatorAddress != null) { 382 initiatorAddressBytes = getAddrBytes(initiatorAddress); 383 size += initiatorAddressBytes.length; 384 } 385 386 byte[] acceptorAddressBytes = null; 387 if (acceptorAddress != null) { 388 acceptorAddressBytes = getAddrBytes(acceptorAddress); 389 size += acceptorAddressBytes.length; 390 } 391 392 byte[] appDataBytes = channelBinding.getApplicationData(); 393 if (appDataBytes != null) { 394 size += appDataBytes.length; 395 } 396 397 byte[] data = new byte[size]; 398 399 int pos = 0; 400 401 writeLittleEndian(initiatorAddressType, data, pos); 402 pos += 4; 403 404 if (initiatorAddressBytes != null) { 405 writeLittleEndian(initiatorAddressBytes.length, data, pos); 406 pos += 4; 407 System.arraycopy(initiatorAddressBytes, 0, 408 data, pos, initiatorAddressBytes.length); 409 pos += initiatorAddressBytes.length; 410 } else { 411 // Write length 0 412 pos += 4; 413 } 414 415 writeLittleEndian(acceptorAddressType, data, pos); 416 pos += 4; 417 418 if (acceptorAddressBytes != null) { 419 writeLittleEndian(acceptorAddressBytes.length, data, pos); 420 pos += 4; 421 System.arraycopy(acceptorAddressBytes, 0, 422 data, pos, acceptorAddressBytes.length); 423 pos += acceptorAddressBytes.length; 424 } else { 425 // Write length 0 426 pos += 4; 427 } 428 429 if (appDataBytes != null) { 430 writeLittleEndian(appDataBytes.length, data, pos); 431 pos += 4; 432 System.arraycopy(appDataBytes, 0, data, pos, 433 appDataBytes.length); 434 pos += appDataBytes.length; 435 } else { 436 // Write 0 437 pos += 4; 438 } 439 440 try { 441 MessageDigest md5 = MessageDigest.getInstance("MD5"); 442 return md5.digest(data); 443 } catch (NoSuchAlgorithmException e) { 444 throw new GSSException(GSSException.FAILURE, -1, 445 "Could not get MD5 Message Digest - " 446 + e.getMessage()); 447 } 448 } 449 450 public abstract byte[] encode() throws IOException; 451 452 }