1 /*
   2  * Copyright (c) 1998, 2018, 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 com.sun.crypto.provider;
  27 
  28 import jdk.internal.misc.SharedSecrets;
  29 
  30 import java.io.*;
  31 import java.security.*;
  32 import javax.crypto.*;
  33 
  34 final class SealedObjectForKeyProtector extends SealedObject {
  35 
  36     static final long serialVersionUID = -3650226485480866989L;
  37 
  38     /**
  39      * The InputStreamFilter for a Key object inside this SealedObject. It can
  40      * be either provided as a {@link Security} property or a system property
  41      * (when provided as latter, it shadows the former). If the result of this
  42      * filter is {@link java.io.ObjectInputFilter.Status.UNDECIDED}, the system
  43      * level filter defined by jdk.serialFilter will be consulted. The value
  44      * of this property uses the same format of jdk.serialFilter.
  45      */
  46     private static final String KEY_SERIAL_FILTER = "jceks.key.serialFilter";
  47 
  48     SealedObjectForKeyProtector(Serializable object, Cipher c)
  49             throws IOException, IllegalBlockSizeException {
  50         super(object, c);
  51     }
  52 
  53     SealedObjectForKeyProtector(SealedObject so) {
  54         super(so);
  55     }
  56 
  57     AlgorithmParameters getParameters() {
  58         AlgorithmParameters params = null;
  59         if (super.encodedParams != null) {
  60             try {
  61                 params = AlgorithmParameters.getInstance("PBE",
  62                     SunJCE.getInstance());
  63                 params.init(super.encodedParams);
  64             } catch (NoSuchAlgorithmException nsae) {
  65                 throw new RuntimeException(
  66                     "SunJCE provider is not configured properly");
  67             } catch (IOException io) {
  68                 throw new RuntimeException("Parameter failure: "+
  69                     io.getMessage());
  70             }
  71         }
  72         return params;
  73     }
  74 
  75     final Key getKey(Cipher c, int maxLength)
  76             throws IOException, ClassNotFoundException, IllegalBlockSizeException,
  77             BadPaddingException {
  78 
  79         try (ObjectInputStream ois = SharedSecrets.getJavaxCryptoSealedObjectAccess()
  80                 .getExtObjectInputStream(this, c)) {
  81             AccessController.doPrivileged(
  82                     (PrivilegedAction<Void>) () -> {
  83                         ois.setObjectInputFilter(new DeserializationChecker(maxLength));
  84                         return null;
  85                     });
  86             try {
  87                 @SuppressWarnings("unchecked")
  88                 Key t = (Key) ois.readObject();
  89                 return t;
  90             } catch (InvalidClassException ice) {
  91                 String msg = ice.getMessage();
  92                 if (msg.contains("REJECTED")) {
  93                     throw new IOException("Rejected by the"
  94                             + " jceks.key.serialFilter or jdk.serialFilter"
  95                             + " property", ice);
  96                 } else {
  97                     throw ice;
  98                 }
  99             }
 100         }
 101     }
 102 
 103     /**
 104      * The filter for the content of a SealedObjectForKeyProtector.
 105      *
 106      * First, the jceks.key.serialFilter will be consulted. If the result
 107      * is UNDECIDED, the system level jdk.serialFilter will be consulted.
 108      */
 109     private static class DeserializationChecker implements ObjectInputFilter {
 110 
 111         private static final ObjectInputFilter OWN_FILTER;
 112 
 113         static {
 114             String prop = AccessController.doPrivileged(
 115                     (PrivilegedAction<String>) () -> {
 116                         String tmp = System.getProperty(KEY_SERIAL_FILTER);
 117                         if (tmp != null) {
 118                             return tmp;
 119                         } else {
 120                             return Security.getProperty(KEY_SERIAL_FILTER);
 121                         }
 122                     });
 123             OWN_FILTER = prop == null
 124                     ? null
 125                     : ObjectInputFilter.Config.createFilter(prop);
 126         }
 127 
 128         // Maximum possible length of anything inside
 129         private final int maxLength;
 130 
 131         private DeserializationChecker(int maxLength) {
 132             this.maxLength = maxLength;
 133         }
 134 
 135         @Override
 136         public ObjectInputFilter.Status checkInput(
 137                 ObjectInputFilter.FilterInfo info) {
 138 
 139             if (info.arrayLength() > maxLength) {
 140                 return Status.REJECTED;
 141             }
 142 
 143             if (info.serialClass() == Object.class) {
 144                 return Status.UNDECIDED;
 145             }
 146 
 147             if (OWN_FILTER != null) {
 148                 Status result = OWN_FILTER.checkInput(info);
 149                 if (result != Status.UNDECIDED) {
 150                     return result;
 151                 }
 152             }
 153 
 154             ObjectInputFilter defaultFilter =
 155                     ObjectInputFilter.Config.getSerialFilter();
 156             if (defaultFilter != null) {
 157                 return defaultFilter.checkInput(info);
 158             }
 159 
 160             return Status.UNDECIDED;
 161         }
 162     }
 163 }