1 /* 2 * Copyright (c) 2017, 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 /* 27 * 28 * (C) Copyright IBM Corp. 1999 All Rights Reserved. 29 * Copyright 1997 The Open Group Research Institute. All rights reserved. 30 */ 31 32 package sun.security.krb5.internal; 33 34 import sun.security.krb5.internal.crypto.EType; 35 import sun.security.util.*; 36 import sun.security.krb5.Asn1Exception; 37 import java.io.IOException; 38 import sun.security.krb5.internal.util.KerberosString; 39 40 /** 41 * Implements the ASN.1 PA-DATA type. 42 * 43 * <pre>{@code 44 * PA-DATA ::= SEQUENCE { 45 * -- NOTE: first tag is [1], not [0] 46 * padata-type [1] Int32, 47 * padata-value [2] OCTET STRING -- might be encoded AP-REQ 48 * } 49 * }</pre> 50 * 51 * <p> 52 * This definition reflects the Network Working Group RFC 4120 53 * specification available at 54 * <a href="http://www.ietf.org/rfc/rfc4120.txt"> 55 * http://www.ietf.org/rfc/rfc4120.txt</a>. 56 */ 57 58 public class PAData { 59 private int pADataType; 60 private byte[] pADataValue = null; 61 private static final byte TAG_PATYPE = 1; 62 private static final byte TAG_PAVALUE = 2; 63 64 private PAData() { 65 } 66 67 public PAData(int new_pADataType, byte[] new_pADataValue) { 68 pADataType = new_pADataType; 69 if (new_pADataValue != null) { 70 pADataValue = new_pADataValue.clone(); 71 } 72 } 73 74 public Object clone() { 75 PAData new_pAData = new PAData(); 76 new_pAData.pADataType = pADataType; 77 if (pADataValue != null) { 78 new_pAData.pADataValue = new byte[pADataValue.length]; 79 System.arraycopy(pADataValue, 0, new_pAData.pADataValue, 80 0, pADataValue.length); 81 } 82 return new_pAData; 83 } 84 85 /** 86 * Constructs a PAData object. 87 * @param encoding a Der-encoded data. 88 * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. 89 * @exception IOException if an I/O error occurs while reading encoded data. 90 */ 91 public PAData(DerValue encoding) throws Asn1Exception, IOException { 92 DerValue der = null; 93 if (encoding.getTag() != DerValue.tag_Sequence) { 94 throw new Asn1Exception(Krb5.ASN1_BAD_ID); 95 } 96 der = encoding.getData().getDerValue(); 97 if ((der.getTag() & 0x1F) == 0x01) { 98 this.pADataType = der.getData().getBigInteger().intValue(); 99 } 100 else 101 throw new Asn1Exception(Krb5.ASN1_BAD_ID); 102 der = encoding.getData().getDerValue(); 103 if ((der.getTag() & 0x1F) == 0x02) { 104 this.pADataValue = der.getData().getOctetString(); 105 } 106 if (encoding.getData().available() > 0) 107 throw new Asn1Exception(Krb5.ASN1_BAD_ID); 108 } 109 110 /** 111 * Encodes this object to an OutputStream. 112 * 113 * @return byte array of the encoded data. 114 * @exception IOException if an I/O error occurs while reading encoded data. 115 * @exception Asn1Exception on encoding errors. 116 */ 117 public byte[] asn1Encode() throws Asn1Exception, IOException { 118 119 DerOutputStream bytes = new DerOutputStream(); 120 DerOutputStream temp = new DerOutputStream(); 121 122 temp.putInteger(pADataType); 123 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_PATYPE), temp); 124 temp = new DerOutputStream(); 125 temp.putOctetString(pADataValue); 126 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_PAVALUE), temp); 127 128 temp = new DerOutputStream(); 129 temp.write(DerValue.tag_Sequence, bytes); 130 return temp.toByteArray(); 131 } 132 133 // accessor methods 134 public int getType() { 135 return pADataType; 136 } 137 138 public byte[] getValue() { 139 return ((pADataValue == null) ? null : pADataValue.clone()); 140 } 141 142 /** 143 * Gets the preferred etype from the PAData array. 144 * <ol> 145 * <li>ETYPE-INFO2-ENTRY with unknown s2kparams ignored</li> 146 * <li>ETYPE-INFO2 preferred to ETYPE-INFO</li> 147 * <li>Multiple entries for same etype in one PA-DATA, use the first one.</li> 148 * <li>Multiple PA-DATA with same type, choose the last one.</li> 149 * </ol> 150 * (This is useful when PA-DATAs from KRB-ERROR and AS-REP are combined). 151 * 152 * @return the etype, or defaultEType if not enough info 153 * @throws Asn1Exception|IOException if there is an encoding error 154 */ 155 public static int getPreferredEType(PAData[] pas, int defaultEType) 156 throws IOException, Asn1Exception { 157 158 if (pas == null) return defaultEType; 159 160 DerValue d = null, d2 = null; 161 for (PAData p: pas) { 162 if (p.getValue() == null) continue; 163 switch (p.getType()) { 164 case Krb5.PA_ETYPE_INFO: 165 d = new DerValue(p.getValue()); 166 break; 167 case Krb5.PA_ETYPE_INFO2: 168 d2 = new DerValue(p.getValue()); 169 break; 170 } 171 } 172 if (d2 != null) { 173 while (d2.data.available() > 0) { 174 DerValue value = d2.data.getDerValue(); 175 ETypeInfo2 tmp = new ETypeInfo2(value); 176 if (EType.isNewer(tmp.getEType()) || tmp.getParams() == null) { 177 // we don't support non-null s2kparams for old etypes 178 return tmp.getEType(); 179 } 180 } 181 } 182 if (d != null) { 183 while (d.data.available() > 0) { 184 DerValue value = d.data.getDerValue(); 185 ETypeInfo tmp = new ETypeInfo(value); 186 return tmp.getEType(); 187 } 188 } 189 return defaultEType; 190 } 191 192 /** 193 * A place to store a pair of salt and s2kparams. 194 * An empty salt is changed to null, to be interoperable 195 * with Windows 2000 server. This is in fact not correct. 196 */ 197 public static class SaltAndParams { 198 public final String salt; 199 public final byte[] params; 200 public SaltAndParams(String s, byte[] p) { 201 if (s != null && s.isEmpty()) s = null; 202 this.salt = s; 203 this.params = p; 204 } 205 } 206 207 /** 208 * Fetches salt and s2kparams value for eType in a series of PA-DATAs. 209 * 1. ETYPE-INFO2-ENTRY with unknown s2kparams ignored 210 * 2. PA-ETYPE-INFO2 preferred to PA-ETYPE-INFO preferred to PA-PW-SALT. 211 * 3. multiple entries for same etype in one PA-DATA, use the first one. 212 * 4. Multiple PA-DATA with same type, choose the last one 213 * (This is useful when PA-DATAs from KRB-ERROR and AS-REP are combined). 214 * @return salt and s2kparams. can be null if not found 215 */ 216 public static SaltAndParams getSaltAndParams(int eType, PAData[] pas) 217 throws Asn1Exception, IOException { 218 219 if (pas == null) return null; 220 221 DerValue d = null, d2 = null; 222 String paPwSalt = null; 223 224 for (PAData p: pas) { 225 if (p.getValue() == null) continue; 226 switch (p.getType()) { 227 case Krb5.PA_PW_SALT: 228 paPwSalt = new String(p.getValue(), 229 KerberosString.MSNAME?"UTF8":"8859_1"); 230 break; 231 case Krb5.PA_ETYPE_INFO: 232 d = new DerValue(p.getValue()); 233 break; 234 case Krb5.PA_ETYPE_INFO2: 235 d2 = new DerValue(p.getValue()); 236 break; 237 } 238 } 239 if (d2 != null) { 240 while (d2.data.available() > 0) { 241 DerValue value = d2.data.getDerValue(); 242 ETypeInfo2 tmp = new ETypeInfo2(value); 243 if (tmp.getEType() == eType && 244 (EType.isNewer(eType) || tmp.getParams() == null)) { 245 // we don't support non-null s2kparams for old etypes 246 return new SaltAndParams(tmp.getSalt(), tmp.getParams()); 247 } 248 } 249 } 250 if (d != null) { 251 while (d.data.available() > 0) { 252 DerValue value = d.data.getDerValue(); 253 ETypeInfo tmp = new ETypeInfo(value); 254 if (tmp.getEType() == eType) { 255 return new SaltAndParams(tmp.getSalt(), null); 256 } 257 } 258 } 259 if (paPwSalt != null) { 260 return new SaltAndParams(paPwSalt, null); 261 } 262 return null; 263 } 264 265 @Override 266 public String toString(){ 267 StringBuilder sb = new StringBuilder(); 268 sb.append(">>>Pre-Authentication Data:\n\t PA-DATA type = ") 269 .append(pADataType).append('\n'); 270 271 switch(pADataType) { 272 case Krb5.PA_ENC_TIMESTAMP: 273 sb.append("\t PA-ENC-TIMESTAMP"); 274 break; 275 case Krb5.PA_ETYPE_INFO: 276 if (pADataValue != null) { 277 try { 278 DerValue der = new DerValue(pADataValue); 279 while (der.data.available() > 0) { 280 DerValue value = der.data.getDerValue(); 281 ETypeInfo info = new ETypeInfo(value); 282 sb.append("\t PA-ETYPE-INFO etype = ") 283 .append(info.getEType()) 284 .append(", salt = ") 285 .append(info.getSalt()) 286 .append('\n'); 287 } 288 } catch (IOException|Asn1Exception e) { 289 sb.append("\t <Unparseable PA-ETYPE-INFO>\n"); 290 } 291 } 292 break; 293 case Krb5.PA_ETYPE_INFO2: 294 if (pADataValue != null) { 295 try { 296 DerValue der = new DerValue(pADataValue); 297 while (der.data.available() > 0) { 298 DerValue value = der.data.getDerValue(); 299 ETypeInfo2 info2 = new ETypeInfo2(value); 300 sb.append("\t PA-ETYPE-INFO2 etype = ") 301 .append(info2.getEType()) 302 .append(", salt = ") 303 .append(info2.getSalt()) 304 .append(", s2kparams = "); 305 byte[] s2kparams = info2.getParams(); 306 if (s2kparams == null) { 307 sb.append("null\n"); 308 } else if (s2kparams.length == 0) { 309 sb.append("empty\n"); 310 } else { 311 sb.append(new sun.security.util.HexDumpEncoder() 312 .encodeBuffer(s2kparams)); 313 } 314 } 315 } catch (IOException|Asn1Exception e) { 316 sb.append("\t <Unparseable PA-ETYPE-INFO>\n"); 317 } 318 } 319 break; 320 case Krb5.PA_FOR_USER: 321 sb.append("\t PA-FOR-USER\n"); 322 break; 323 default: 324 // Unknown Pre-auth type 325 break; 326 } 327 return sb.toString(); 328 } 329 }