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