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 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 StringBuilder sb = new StringBuilder("\""); 168 sb.append(delegateTo.getName()).append('\"'); 169 String realm = delegateTo.getRealmAsString(); 170 sb.append(" \"krbtgt/").append(realm).append('@'); 171 sb.append(realm).append('\"'); 172 SecurityManager sm = System.getSecurityManager(); 173 if (sm != null) { 174 DelegationPermission perm = 175 new DelegationPermission(sb.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 message 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, Checksum checksum, 214 EncryptionKey key, EncryptionKey subKey) 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 KrbCred cred; 281 try { 282 cred = new KrbCred(credBytes, key); 283 } catch (KrbException ke) { 284 if (subKey != null) { 285 cred = new KrbCred(credBytes, subKey); 286 } else { 287 throw ke; 288 } 289 } 290 delegCreds = cred.getDelegatedCreds()[0]; 291 } 292 } 293 294 // check if KRB-CRED message should use NULL_KEY for encryption 295 private boolean useNullKey(CipherHelper ch) { 296 boolean flag = true; 297 // for "newer" etypes and RC4-HMAC do not use NULL KEY 298 if ((ch.getProto() == 1) || ch.isArcFour()) { 299 flag = false; 300 } 301 return flag; 302 } 303 304 public Checksum getChecksum() throws KrbException { 305 return new Checksum(checksumBytes, CHECKSUM_TYPE); 306 } 307 308 public Credentials getDelegatedCreds() { 309 return delegCreds; 310 } 311 312 // Only called by acceptor 313 public void setContextFlags(Krb5Context context) { 314 // default for cred delegation is false 315 if ((flags & CHECKSUM_DELEG_FLAG) > 0) 316 context.setCredDelegState(true); 317 // default for the following are true 318 if ((flags & CHECKSUM_MUTUAL_FLAG) == 0) { 319 context.setMutualAuthState(false); 320 } 321 if ((flags & CHECKSUM_REPLAY_FLAG) == 0) { 322 context.setReplayDetState(false); 323 } 324 if ((flags & CHECKSUM_SEQUENCE_FLAG) == 0) { 325 context.setSequenceDetState(false); 326 } 327 if ((flags & CHECKSUM_CONF_FLAG) == 0) { 328 context.setConfState(false); 329 } 330 if ((flags & CHECKSUM_INTEG_FLAG) == 0) { 331 context.setIntegState(false); 332 } 333 } 334 } 335 336 private int getAddrType(InetAddress addr) { 337 int addressType = CHANNEL_BINDING_AF_NULL_ADDR; 338 339 if (addr instanceof Inet4Address) 340 addressType = CHANNEL_BINDING_AF_INET; 341 else if (addr instanceof Inet6Address) 342 addressType = CHANNEL_BINDING_AF_INET6; 343 return (addressType); 344 } 345 346 private byte[] getAddrBytes(InetAddress addr) throws GSSException { 347 int addressType = getAddrType(addr); 348 byte[] addressBytes = addr.getAddress(); 349 if (addressBytes != null) { 350 switch (addressType) { 351 case CHANNEL_BINDING_AF_INET: 352 if (addressBytes.length != Inet4_ADDRSZ) { 353 throw new GSSException(GSSException.FAILURE, -1, 354 "Incorrect AF-INET address length in ChannelBinding."); 355 } 356 return (addressBytes); 357 case CHANNEL_BINDING_AF_INET6: 358 if (addressBytes.length != Inet6_ADDRSZ) { 359 throw new GSSException(GSSException.FAILURE, -1, 360 "Incorrect AF-INET6 address length in ChannelBinding."); 361 } 362 return (addressBytes); 363 default: 364 throw new GSSException(GSSException.FAILURE, -1, 365 "Cannot handle non AF-INET addresses in ChannelBinding."); 366 } 367 } 368 return null; 369 } 370 371 private byte[] computeChannelBinding(ChannelBinding channelBinding) 372 throws GSSException { 373 374 InetAddress initiatorAddress = channelBinding.getInitiatorAddress(); 375 InetAddress acceptorAddress = channelBinding.getAcceptorAddress(); 376 int size = 5*4; 377 378 int initiatorAddressType = getAddrType(initiatorAddress); 379 int acceptorAddressType = getAddrType(acceptorAddress); 380 381 byte[] initiatorAddressBytes = null; 382 if (initiatorAddress != null) { 383 initiatorAddressBytes = getAddrBytes(initiatorAddress); 384 size += initiatorAddressBytes.length; 385 } 386 387 byte[] acceptorAddressBytes = null; 388 if (acceptorAddress != null) { 389 acceptorAddressBytes = getAddrBytes(acceptorAddress); 390 size += acceptorAddressBytes.length; 391 } 392 393 byte[] appDataBytes = channelBinding.getApplicationData(); 394 if (appDataBytes != null) { 395 size += appDataBytes.length; 396 } 397 398 byte[] data = new byte[size]; 399 400 int pos = 0; 401 402 writeLittleEndian(initiatorAddressType, data, pos); 403 pos += 4; 404 405 if (initiatorAddressBytes != null) { 406 writeLittleEndian(initiatorAddressBytes.length, data, pos); 407 pos += 4; 408 System.arraycopy(initiatorAddressBytes, 0, 409 data, pos, initiatorAddressBytes.length); 410 pos += initiatorAddressBytes.length; 411 } else { 412 // Write length 0 413 pos += 4; 414 } 415 416 writeLittleEndian(acceptorAddressType, data, pos); 417 pos += 4; 418 419 if (acceptorAddressBytes != null) { 420 writeLittleEndian(acceptorAddressBytes.length, data, pos); 421 pos += 4; 422 System.arraycopy(acceptorAddressBytes, 0, 423 data, pos, acceptorAddressBytes.length); 424 pos += acceptorAddressBytes.length; 425 } else { 426 // Write length 0 427 pos += 4; 428 } 429 430 if (appDataBytes != null) { 431 writeLittleEndian(appDataBytes.length, data, pos); 432 pos += 4; 433 System.arraycopy(appDataBytes, 0, data, pos, 434 appDataBytes.length); 435 pos += appDataBytes.length; 436 } else { 437 // Write 0 438 pos += 4; 439 } 440 441 try { 442 MessageDigest md5 = MessageDigest.getInstance("MD5"); 443 return md5.digest(data); 444 } catch (NoSuchAlgorithmException e) { 445 throw new GSSException(GSSException.FAILURE, -1, 446 "Could not get MD5 Message Digest - " 447 + e.getMessage()); 448 } 449 } 450 451 public abstract byte[] encode() throws IOException; 452 453 }