1 /*
   2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   3  *
   4  * This code is free software; you can redistribute it and/or modify it
   5  * under the terms of the GNU General Public License version 2 only, as
   6  * published by the Free Software Foundation.  Oracle designates this
   7  * particular file as subject to the "Classpath" exception as provided
   8  * by Oracle in the LICENSE file that accompanied this code.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  */
  24 
  25 /*
  26  *
  27  *  (C) Copyright IBM Corp. 1999 All Rights Reserved.
  28  *  Copyright 1997 The Open Group Research Institute.  All rights reserved.
  29  */
  30 
  31 package sun.security.krb5;
  32 
  33 import sun.security.krb5.internal.*;
  34 import sun.security.krb5.internal.crypto.KeyUsage;
  35 import sun.security.util.DerInputStream;
  36 
  37 abstract class KrbKdcRep {
  38 
  39     static void check(
  40                       boolean isAsReq,
  41                       KDCReq req,
  42                       KDCRep rep,
  43                       EncryptionKey replyKey
  44                       ) throws KrbApErrException {
  45 
  46         // cname change in AS-REP is allowed only if the client
  47         // sent CANONICALIZE and the server supports RFC 6806 - Section 11
  48         // FAST scheme (ENC-PA-REP flag).
  49         if (isAsReq && !req.reqBody.cname.equals(rep.cname) &&
  50                 (!req.reqBody.kdcOptions.get(KDCOptions.CANONICALIZE) ||
  51                  !rep.encKDCRepPart.flags.get(Krb5.TKT_OPTS_ENC_PA_REP))) {
  52             rep.encKDCRepPart.key.destroy();
  53             throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
  54         }
  55 
  56         // sname change in TGS-REP is allowed only if client
  57         // sent CANONICALIZE and new sname is a referral of
  58         // the form krbtgt/TO-REALM.COM@FROM-REALM.COM.
  59         if (!req.reqBody.sname.equals(rep.encKDCRepPart.sname)) {
  60             String[] snameStrings = rep.encKDCRepPart.sname.getNameStrings();
  61             if (isAsReq || !req.reqBody.kdcOptions.get(KDCOptions.CANONICALIZE) ||
  62                     snameStrings == null || snameStrings.length != 2 ||
  63                     !snameStrings[0].equals(PrincipalName.TGS_DEFAULT_SRV_NAME) ||
  64                     !rep.encKDCRepPart.sname.getRealmString().equals(
  65                             req.reqBody.sname.getRealmString())) {
  66                 rep.encKDCRepPart.key.destroy();
  67                 throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
  68             }
  69         }
  70 
  71         if (req.reqBody.getNonce() != rep.encKDCRepPart.nonce) {
  72             rep.encKDCRepPart.key.destroy();
  73             throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
  74         }
  75 
  76         if (
  77             ((req.reqBody.addresses != null && rep.encKDCRepPart.caddr != null) &&
  78              !req.reqBody.addresses.equals(rep.encKDCRepPart.caddr))) {
  79             rep.encKDCRepPart.key.destroy();
  80             throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
  81         }
  82 
  83         // We allow KDC to return a non-forwardable ticket if request has -f
  84         for (int i = 2; i < 6; i++) {
  85             if (req.reqBody.kdcOptions.get(i) !=
  86                    rep.encKDCRepPart.flags.get(i)) {
  87                 if (Krb5.DEBUG) {
  88                     System.out.println("> KrbKdcRep.check: at #" + i
  89                             + ". request for " + req.reqBody.kdcOptions.get(i)
  90                             + ", received " + rep.encKDCRepPart.flags.get(i));
  91                 }
  92                 throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
  93             }
  94         }
  95 
  96         // Reply to a renewable request should be renewable, but if request does
  97         // not contain renewable, KDC is free to issue a renewable ticket (for
  98         // example, if ticket_lifetime is too big).
  99         if (req.reqBody.kdcOptions.get(KDCOptions.RENEWABLE) &&
 100                 !rep.encKDCRepPart.flags.get(KDCOptions.RENEWABLE)) {
 101             throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
 102         }
 103 
 104         if ((req.reqBody.from == null) || req.reqBody.from.isZero()) {
 105             // verify this is allowed
 106             if ((rep.encKDCRepPart.starttime != null) &&
 107                     !rep.encKDCRepPart.starttime.inClockSkew()) {
 108                 rep.encKDCRepPart.key.destroy();
 109                 throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW);
 110             }
 111         }
 112 
 113         if ((req.reqBody.from != null) && !req.reqBody.from.isZero()) {
 114             // verify this is allowed
 115             if ((rep.encKDCRepPart.starttime != null) &&
 116                     !req.reqBody.from.equals(rep.encKDCRepPart.starttime)) {
 117                 rep.encKDCRepPart.key.destroy();
 118                 throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
 119             }
 120         }
 121 
 122         if (!req.reqBody.till.isZero() &&
 123                 rep.encKDCRepPart.endtime.greaterThan(req.reqBody.till)) {
 124             rep.encKDCRepPart.key.destroy();
 125             throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
 126         }
 127 
 128         if (req.reqBody.kdcOptions.get(KDCOptions.RENEWABLE)) {
 129             if (req.reqBody.rtime != null && !req.reqBody.rtime.isZero()) {
 130                 // verify this is required
 131                 if ((rep.encKDCRepPart.renewTill == null) ||
 132                         rep.encKDCRepPart.renewTill.greaterThan(req.reqBody.rtime)
 133                         ) {
 134                     rep.encKDCRepPart.key.destroy();
 135                     throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
 136                 }
 137             }
 138         }
 139 
 140         // RFC 6806 - Section 11 mechanism check
 141         if (rep.encKDCRepPart.flags.get(Krb5.TKT_OPTS_ENC_PA_REP) &&
 142                 req.reqBody.kdcOptions.get(KDCOptions.CANONICALIZE)) {
 143             boolean reqPaReqEncPaRep = false;
 144             boolean repPaReqEncPaRepValid = false;
 145 
 146             // PA_REQ_ENC_PA_REP only required for AS requests
 147             for (PAData pa : req.pAData) {
 148                 if (pa.getType() == Krb5.PA_REQ_ENC_PA_REP) {
 149                     reqPaReqEncPaRep = true;
 150                     break;
 151                 }
 152             }
 153 
 154             for (PAData pa : rep.encKDCRepPart.pAData) {
 155                 if (pa.getType() == Krb5.PA_REQ_ENC_PA_REP) {
 156                     try {
 157                         Checksum repCksum = new Checksum(
 158                                 new DerInputStream(pa.getValue()).getDerValue());
 159                         repPaReqEncPaRepValid = repCksum.verifyKeyedChecksum(
 160                                 req.asn1Encode(), replyKey, KeyUsage.KU_AS_REQ);
 161                     } catch (Exception e) {
 162                         if (Krb5.DEBUG) {
 163                             e.printStackTrace();
 164                         }
 165                     }
 166                     break;
 167                 }
 168             }
 169 
 170             if (reqPaReqEncPaRep && !repPaReqEncPaRepValid) {
 171                 throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
 172             }
 173         }
 174     }
 175 }