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