1 /*
   2  * Copyright (c) 2000, 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;
  33 
  34 import sun.security.krb5.internal.*;
  35 import sun.security.krb5.internal.crypto.KeyUsage;
  36 import sun.security.krb5.internal.crypto.EType;
  37 import sun.security.util.*;
  38 import java.io.IOException;
  39 import java.util.Objects;
  40 import javax.security.auth.kerberos.KeyTab;
  41 import sun.security.jgss.krb5.Krb5Util;
  42 
  43 /**
  44  * This class encapsulates a AS-REP message that the KDC sends to the
  45  * client.
  46  */
  47 class KrbAsRep extends KrbKdcRep {
  48 
  49     private ASRep rep;  // The AS-REP message
  50     private Credentials creds;  // The Credentials provide by the AS-REP
  51                                 // message, created by initiator after calling
  52                                 // the decrypt() method
  53 
  54     private boolean DEBUG = Krb5.DEBUG;
  55 
  56     KrbAsRep(byte[] ibuf) throws
  57             KrbException, Asn1Exception, IOException {
  58         DerValue encoding = new DerValue(ibuf);
  59         try {
  60             rep = new ASRep(encoding);
  61         } catch (Asn1Exception e) {
  62             rep = null;
  63             KRBError err = new KRBError(encoding);
  64             String errStr = err.getErrorString();
  65             String eText = null; // pick up text sent by the server (if any)
  66 
  67             if (errStr != null && errStr.length() > 0) {
  68                 if (errStr.charAt(errStr.length() - 1) == 0)
  69                     eText = errStr.substring(0, errStr.length() - 1);
  70                 else
  71                     eText = errStr;
  72             }
  73             KrbException ke;
  74             if (eText == null) {
  75                 // no text sent from server
  76                 ke = new KrbException(err);
  77             } else {
  78                 if (DEBUG) {
  79                     System.out.println("KRBError received: " + eText);
  80                 }
  81                 // override default text with server text
  82                 ke = new KrbException(err, eText);
  83             }
  84             ke.initCause(e);
  85             throw ke;
  86         }
  87     }
  88 
  89     // KrbAsReqBuilder need to read back the PA for key generation
  90     PAData[] getPA() {
  91         return rep.pAData;
  92     }
  93 
  94     /**
  95      * Called by KrbAsReqBuilder to resolve a AS-REP message using a keytab.
  96      * @param ktab the keytab, not null
  97      * @param asReq the original AS-REQ sent, used to validate AS-REP
  98      * @param cname the user principal name, used to locate keys in ktab
  99      */
 100     void decryptUsingKeyTab(KeyTab ktab, KrbAsReq asReq, PrincipalName cname)
 101             throws KrbException, Asn1Exception, IOException {
 102         EncryptionKey dkey = null;
 103         int encPartKeyType = rep.encPart.getEType();
 104         Integer encPartKvno = rep.encPart.kvno;
 105             try {
 106                 dkey = EncryptionKey.findKey(encPartKeyType, encPartKvno,
 107                         Krb5Util.keysFromJavaxKeyTab(ktab, cname));
 108             } catch (KrbException ke) {
 109                 if (ke.returnCode() == Krb5.KRB_AP_ERR_BADKEYVER) {
 110                     // Fallback to no kvno. In some cases, keytab is generated
 111                     // not by sysadmin but Java's ktab command
 112                     dkey = EncryptionKey.findKey(encPartKeyType,
 113                             Krb5Util.keysFromJavaxKeyTab(ktab, cname));
 114                 }
 115             }
 116             if (dkey == null) {
 117                 throw new KrbException(Krb5.API_INVALID_ARG,
 118                     "Cannot find key for type/kvno to decrypt AS REP - " +
 119                     EType.toString(encPartKeyType) + "/" + encPartKvno);
 120             }
 121         decrypt(dkey, asReq);
 122     }
 123 
 124     /**
 125      * Called by KrbAsReqBuilder to resolve a AS-REP message using a password.
 126      * @param password user provided password. not null
 127      * @param asReq the original AS-REQ sent, used to validate AS-REP
 128      * @param cname the user principal name, used to provide salt
 129      */
 130     void decryptUsingPassword(char[] password,
 131             KrbAsReq asReq, PrincipalName cname)
 132             throws KrbException, Asn1Exception, IOException {
 133         int encPartKeyType = rep.encPart.getEType();
 134         EncryptionKey dkey = EncryptionKey.acquireSecretKey(
 135                 cname,
 136                 password,
 137                 encPartKeyType,
 138                 PAData.getSaltAndParams(encPartKeyType, rep.pAData));
 139         decrypt(dkey, asReq);
 140     }
 141 
 142     /**
 143      * Decrypts encrypted content inside AS-REP. Called by initiator.
 144      * @param dkey the decryption key to use
 145      * @param asReq the original AS-REQ sent, used to validate AS-REP
 146      */
 147     private void decrypt(EncryptionKey dkey, KrbAsReq asReq)
 148             throws KrbException, Asn1Exception, IOException {
 149         byte[] enc_as_rep_bytes = rep.encPart.decrypt(dkey,
 150             KeyUsage.KU_ENC_AS_REP_PART);
 151         byte[] enc_as_rep_part = rep.encPart.reset(enc_as_rep_bytes);
 152 
 153         DerValue encoding = new DerValue(enc_as_rep_part);
 154         EncASRepPart enc_part = new EncASRepPart(encoding);
 155         rep.encKDCRepPart = enc_part;
 156 
 157         ASReq req = asReq.getMessage();
 158         check(true, req, rep, dkey);
 159 
 160         creds = new Credentials(
 161                                 rep.ticket,
 162                                 rep.cname,
 163                                 enc_part.sname,
 164                                 enc_part.key,
 165                                 enc_part.flags,
 166                                 enc_part.authtime,
 167                                 enc_part.starttime,
 168                                 enc_part.endtime,
 169                                 enc_part.renewTill,
 170                                 enc_part.caddr);
 171         if (DEBUG) {
 172             System.out.println(">>> KrbAsRep cons in KrbAsReq.getReply " +
 173                                req.reqBody.cname.getNameString());
 174         }
 175     }
 176 
 177     Credentials getCreds() {
 178         return Objects.requireNonNull(creds, "Creds not available yet.");
 179     }
 180 
 181     sun.security.krb5.internal.ccache.Credentials getCCreds() {
 182         return new sun.security.krb5.internal.ccache.Credentials(rep);
 183     }
 184 }