1 /*
   2  * Copyright (c) 2001, 2013, 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.*;
  35 import java.io.IOException;
  36 
  37 /**
  38  * This class is a utility that contains much of the TGS-Exchange
  39  * protocol. It is used by ../Credentials.java for service ticket
  40  * acquisition in both the normal and the x-realm case.
  41  */
  42 public class CredentialsUtil {
  43 
  44     private static boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG;
  45 
  46     /**
  47      * Used by a middle server to acquire credentials on behalf of a
  48      * client to itself using the S4U2self extension.
  49      * @param client the client to impersonate
  50      * @param ccreds the TGT of the middle service
  51      * @return the new creds (cname=client, sname=middle)
  52      */
  53     public static Credentials acquireS4U2selfCreds(PrincipalName client,
  54             Credentials ccreds) throws KrbException, IOException {
  55         String uRealm = client.getRealmString();
  56         String localRealm = ccreds.getClient().getRealmString();
  57         if (!uRealm.equals(localRealm)) {
  58             // TODO: we do not support kerberos referral now
  59             throw new KrbException("Cross realm impersonation not supported");
  60         }
  61         if (!ccreds.isForwardable()) {
  62             throw new KrbException("S4U2self needs a FORWARDABLE ticket");
  63         }
  64         KrbTgsReq req = new KrbTgsReq(
  65                 ccreds,
  66                 ccreds.getClient(),
  67                 new PAData(Krb5.PA_FOR_USER,
  68                     new PAForUserEnc(client,
  69                         ccreds.getSessionKey()).asn1Encode()));
  70         Credentials creds = req.sendAndGetCreds();
  71         if (!creds.getClient().equals(client)) {
  72             throw new KrbException("S4U2self request not honored by KDC");
  73         }
  74         if (!creds.isForwardable()) {
  75             throw new KrbException("S4U2self ticket must be FORWARDABLE");
  76         }
  77         return creds;
  78     }
  79 
  80     /**
  81      * Used by a middle server to acquire a service ticket to a backend
  82      * server using the S4U2proxy extension.
  83      * @param backend the name of the backend service
  84      * @param second the client's service ticket to the middle server
  85      * @param ccreds the TGT of the middle server
  86      * @return the creds (cname=client, sname=backend)
  87      */
  88     public static Credentials acquireS4U2proxyCreds(
  89                 String backend, Ticket second,
  90                 PrincipalName client, Credentials ccreds)
  91             throws KrbException, IOException {
  92         KrbTgsReq req = new KrbTgsReq(
  93                 ccreds,
  94                 second,
  95                 new PrincipalName(backend));
  96         Credentials creds = req.sendAndGetCreds();
  97         if (!creds.getClient().equals(client)) {
  98             throw new KrbException("S4U2proxy request not honored by KDC");
  99         }
 100         return creds;
 101     }
 102 
 103     /**
 104      * Acquires credentials for a specified service using initial
 105      * credential. When the service has a different realm from the initial
 106      * credential, we do cross-realm authentication - first, we use the
 107      * current credential to get a cross-realm credential from the local KDC,
 108      * then use that cross-realm credential to request service credential
 109      * from the foreign KDC.
 110      *
 111      * @param service the name of service principal
 112      * @param ccreds client's initial credential
 113      */
 114     public static Credentials acquireServiceCreds(
 115                 String service, Credentials ccreds)
 116             throws KrbException, IOException {
 117         PrincipalName sname = new PrincipalName(service);
 118         String serviceRealm = sname.getRealmString();
 119         String localRealm = ccreds.getClient().getRealmString();
 120 
 121         if (localRealm.equals(serviceRealm)) {
 122             if (DEBUG) {
 123                 System.out.println(
 124                         ">>> Credentials acquireServiceCreds: same realm");
 125             }
 126             return serviceCreds(sname, ccreds);
 127         }
 128         Credentials theCreds = null;
 129 
 130         boolean[] okAsDelegate = new boolean[1];
 131         Credentials theTgt = getTGTforRealm(localRealm, serviceRealm,
 132                 ccreds, okAsDelegate);
 133         if (theTgt != null) {
 134             if (DEBUG) {
 135                 System.out.println(">>> Credentials acquireServiceCreds: "
 136                         + "got right tgt");
 137                 System.out.println(">>> Credentials acquireServiceCreds: "
 138                         + "obtaining service creds for " + sname);
 139             }
 140 
 141             try {
 142                 theCreds = serviceCreds(sname, theTgt);
 143             } catch (Exception exc) {
 144                 if (DEBUG) {
 145                     System.out.println(exc);
 146                 }
 147                 theCreds = null;
 148             }
 149         }
 150 
 151         if (theCreds != null) {
 152             if (DEBUG) {
 153                 System.out.println(">>> Credentials acquireServiceCreds: "
 154                         + "returning creds:");
 155                 Credentials.printDebug(theCreds);
 156             }
 157             if (!okAsDelegate[0]) {
 158                 theCreds.resetDelegate();
 159             }
 160             return theCreds;
 161         }
 162         throw new KrbApErrException(Krb5.KRB_AP_ERR_GEN_CRED,
 163                                     "No service creds");
 164     }
 165 
 166     /**
 167      * Gets a TGT to another realm
 168      * @param localRealm this realm
 169      * @param serviceRealm the other realm, cannot equals to localRealm
 170      * @param ccreds TGT in this realm
 171      * @param okAsDelegate an [out] argument to receive the okAsDelegate
 172      * property. True only if all realms allow delegation.
 173      * @return the TGT for the other realm, null if cannot find a path
 174      * @throws KrbException if something goes wrong
 175      */
 176     private static Credentials getTGTforRealm(String localRealm,
 177             String serviceRealm, Credentials ccreds, boolean[] okAsDelegate)
 178             throws KrbException {
 179 
 180         // Get a list of realms to traverse
 181         String[] realms = Realm.getRealmsList(localRealm, serviceRealm);
 182 
 183         int i = 0, k = 0;
 184         Credentials cTgt = null, newTgt = null, theTgt = null;
 185         PrincipalName tempService = null;
 186         String newTgtRealm = null;
 187 
 188         okAsDelegate[0] = true;
 189         for (cTgt = ccreds, i = 0; i < realms.length;) {
 190             tempService = PrincipalName.tgsService(serviceRealm, realms[i]);
 191 
 192             if (DEBUG) {
 193                 System.out.println(
 194                         ">>> Credentials acquireServiceCreds: main loop: ["
 195                         + i +"] tempService=" + tempService);
 196             }
 197 
 198             try {
 199                 newTgt = serviceCreds(tempService, cTgt);
 200             } catch (Exception exc) {
 201                 newTgt = null;
 202             }
 203 
 204             if (newTgt == null) {
 205                 if (DEBUG) {
 206                     System.out.println(">>> Credentials acquireServiceCreds: "
 207                             + "no tgt; searching thru capath");
 208                 }
 209 
 210                 /*
 211                  * No tgt found. Let's go thru the realms list one by one.
 212                  */
 213                 for (newTgt = null, k = i+1;
 214                         newTgt == null && k < realms.length; k++) {
 215                     tempService = PrincipalName.tgsService(realms[k], realms[i]);
 216                     if (DEBUG) {
 217                         System.out.println(
 218                                 ">>> Credentials acquireServiceCreds: "
 219                                 + "inner loop: [" + k
 220                                 + "] tempService=" + tempService);
 221                     }
 222                     try {
 223                         newTgt = serviceCreds(tempService, cTgt);
 224                     } catch (Exception exc) {
 225                         newTgt = null;
 226                     }
 227                 }
 228             } // Ends 'if (newTgt == null)'
 229 
 230             if (newTgt == null) {
 231                 if (DEBUG) {
 232                     System.out.println(">>> Credentials acquireServiceCreds: "
 233                             + "no tgt; cannot get creds");
 234                 }
 235                 break;
 236             }
 237 
 238             /*
 239              * We have a tgt. It may or may not be for the target.
 240              * If it's for the target realm, we're done looking for a tgt.
 241              */
 242             newTgtRealm = newTgt.getServer().getInstanceComponent();
 243             if (okAsDelegate[0] && !newTgt.checkDelegate()) {
 244                 if (DEBUG) {
 245                     System.out.println(">>> Credentials acquireServiceCreds: " +
 246                             "global OK-AS-DELEGATE turned off at " +
 247                             newTgt.getServer());
 248                 }
 249                 okAsDelegate[0] = false;
 250             }
 251 
 252             if (DEBUG) {
 253                 System.out.println(">>> Credentials acquireServiceCreds: "
 254                         + "got tgt");
 255             }
 256 
 257             if (newTgtRealm.equals(serviceRealm)) {
 258                 /* We got the right tgt */
 259                 theTgt = newTgt;
 260                 break;
 261             }
 262 
 263             /*
 264              * The new tgt is not for the target realm.
 265              * See if the realm of the new tgt is in the list of realms
 266              * and continue looking from there.
 267              */
 268             for (k = i+1; k < realms.length; k++) {
 269                 if (newTgtRealm.equals(realms[k])) {
 270                     break;
 271                 }
 272             }
 273 
 274             if (k < realms.length) {
 275                 /*
 276                  * (re)set the counter so we start looking
 277                  * from the realm we just obtained a tgt for.
 278                  */
 279                 i = k;
 280                 cTgt = newTgt;
 281 
 282                 if (DEBUG) {
 283                     System.out.println(">>> Credentials acquireServiceCreds: "
 284                             + "continuing with main loop counter reset to " + i);
 285                 }
 286                 continue;
 287             }
 288             else {
 289                 /*
 290                  * The new tgt's realm is not in the hierarchy of realms.
 291                  * It's probably not safe to get a tgt from
 292                  * a tgs that is outside the known list of realms.
 293                  * Give up now.
 294                  */
 295                 break;
 296             }
 297         } // Ends outermost/main 'for' loop
 298 
 299         return theTgt;
 300     }
 301 
 302    /*
 303     * This method does the real job to request the service credential.
 304     */
 305     private static Credentials serviceCreds(
 306             PrincipalName service, Credentials ccreds)
 307             throws KrbException, IOException {
 308         return new KrbTgsReq(ccreds, service).sendAndGetCreds();
 309     }
 310 }