1 /*
   2  * Copyright (c) 2000, 2012, 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  *  (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.internal.tools;
  32 
  33 import sun.security.krb5.*;
  34 import sun.security.krb5.internal.*;
  35 import sun.security.krb5.internal.ccache.*;
  36 import java.io.IOException;
  37 import java.time.Instant;
  38 import java.io.FileInputStream;
  39 
  40 /**
  41  * Maintains user-specific options or default settings when the user requests
  42  * a KDC ticket using Kinit.
  43  *
  44  * @author Yanni Zhang
  45  * @author Ram Marti
  46  */
  47 class KinitOptions {
  48 
  49     // 1. acquire, 2. renew, 3. validate
  50     public int action = 1;
  51 
  52     // forwardable and proxiable flags have two states:
  53     // -1 - flag set to be not forwardable or proxiable;
  54     // 1 - flag set to be forwardable or proxiable.
  55     public short forwardable = 0;
  56     public short proxiable = 0;
  57     public KerberosTime lifetime;
  58     public KerberosTime renewable_lifetime;
  59     public String target_service;
  60     public String keytab_file;
  61     public String cachename;
  62     private PrincipalName principal;
  63     public String realm;
  64     char[] password = null;
  65     public boolean keytab;
  66     private boolean DEBUG = Krb5.DEBUG;
  67     private boolean includeAddresses = true; // default.
  68     private boolean useKeytab = false; // default = false.
  69     private String ktabName; // keytab file name
  70 
  71     public KinitOptions() throws RuntimeException, RealmException {
  72         // no args were specified in the command line;
  73         // use default values
  74         cachename = FileCredentialsCache.getDefaultCacheName();
  75         if (cachename == null) {
  76             throw new RuntimeException("default cache name error");
  77         }
  78         principal = getDefaultPrincipal();
  79     }
  80 
  81     public void setKDCRealm(String r) throws RealmException {
  82         realm = r;
  83     }
  84 
  85     public String getKDCRealm() {
  86         if (realm == null) {
  87             if (principal != null) {
  88                 return principal.getRealmString();
  89             }
  90         }
  91         return null;
  92     }
  93 
  94     public KinitOptions(String[] args)
  95         throws KrbException, RuntimeException, IOException {
  96         // currently we provide support for -f -p -c principal options
  97         String p = null; // store principal
  98 
  99         for (int i = 0; i < args.length; i++) {
 100             if (args[i].equals("-f")) {
 101                 forwardable = 1;
 102             } else if (args[i].equals("-p")) {
 103                 proxiable = 1;
 104             } else if (args[i].equals("-c")) {
 105 
 106                 if (args[i + 1].startsWith("-")) {
 107                     throw new IllegalArgumentException("input format " +
 108                                                        " not correct: " +
 109                                                        " -c  option " +
 110                                                        "must be followed " +
 111                                                        "by the cache name");
 112                 }
 113                 cachename = args[++i];
 114                 if ((cachename.length() >= 5) &&
 115                     cachename.substring(0, 5).equalsIgnoreCase("FILE:")) {
 116                     cachename = cachename.substring(5);
 117                 };
 118             } else if (args[i].equals("-A")) {
 119                 includeAddresses = false;
 120             } else if (args[i].equals("-k")) {
 121                 useKeytab = true;
 122             } else if (args[i].equals("-t")) {
 123                 if (ktabName != null) {
 124                     throw new IllegalArgumentException
 125                         ("-t option/keytab file name repeated");
 126                 } else if (i + 1 < args.length) {
 127                     ktabName = args[++i];
 128                 } else {
 129                     throw new IllegalArgumentException
 130                         ("-t option requires keytab file name");
 131                 }
 132 
 133                 useKeytab = true;
 134             } else if (args[i].equals("-R")) {
 135                 action = 2;
 136             } else if (args[i].equals("-l")) {
 137                 lifetime = getTime(Config.duration(args[++i]));
 138             } else if (args[i].equals("-r")) {
 139                 renewable_lifetime = getTime(Config.duration(args[++i]));
 140             } else if (args[i].equalsIgnoreCase("-help")) {
 141                 printHelp();
 142                 System.exit(0);
 143             } else if (p == null) { // Haven't yet processed a "principal"
 144                 p = args[i];
 145                 try {
 146                     principal = new PrincipalName(p);
 147                 } catch (Exception e) {
 148                     throw new IllegalArgumentException("invalid " +
 149                                                        "Principal name: " + p +
 150                                                        e.getMessage());
 151                 }
 152             } else if (this.password == null) {
 153                 // Have already processed a Principal, this must be a password
 154                 password = args[i].toCharArray();
 155             } else {
 156                 throw new IllegalArgumentException("too many parameters");
 157             }
 158         }
 159         // we should get cache name before getting the default principal name
 160         if (cachename == null) {
 161             cachename = FileCredentialsCache.getDefaultCacheName();
 162             if (cachename == null) {
 163                 throw new RuntimeException("default cache name error");
 164             }
 165         }
 166         if (principal == null) {
 167             principal = getDefaultPrincipal();
 168         }
 169     }
 170 
 171     PrincipalName getDefaultPrincipal() {
 172 
 173         // get default principal name from the cachename if it is
 174         // available.
 175 
 176         try {
 177             CCacheInputStream cis =
 178                 new CCacheInputStream(new FileInputStream(cachename));
 179             int version;
 180             if ((version = cis.readVersion()) ==
 181                     FileCCacheConstants.KRB5_FCC_FVNO_4) {
 182                 cis.readTag();
 183             } else {
 184                 if (version == FileCCacheConstants.KRB5_FCC_FVNO_1 ||
 185                         version == FileCCacheConstants.KRB5_FCC_FVNO_2) {
 186                     cis.setNativeByteOrder();
 187                 }
 188             }
 189             PrincipalName p = cis.readPrincipal(version);
 190             cis.close();
 191             if (DEBUG) {
 192                 System.out.println(">>>KinitOptions principal name from "+
 193                                    "the cache is :" + p);
 194             }
 195             return p;
 196         } catch (IOException e) {
 197             // ignore any exceptions; we will use the user name as the
 198             // principal name
 199             if (DEBUG) {
 200                 e.printStackTrace();
 201             }
 202         } catch (RealmException e) {
 203             if (DEBUG) {
 204                 e.printStackTrace();
 205             }
 206         }
 207 
 208         String username = System.getProperty("user.name");
 209         if (DEBUG) {
 210             System.out.println(">>>KinitOptions default username is :"
 211                                + username);
 212         }
 213         try {
 214             PrincipalName p = new PrincipalName(username);
 215             return p;
 216         } catch (RealmException e) {
 217             // ignore exception , return null
 218             if (DEBUG) {
 219                 System.out.println ("Exception in getting principal " +
 220                                     "name " + e.getMessage());
 221                 e.printStackTrace();
 222             }
 223         }
 224         return null;
 225     }
 226 
 227 
 228     void printHelp() {
 229         System.out.println("Usage:\n\n1. Initial ticket request:\n" +
 230                 "    kinit [-A] [-f] [-p] [-c cachename] " +
 231                 "[-l lifetime] [-r renewable_time]\n" +
 232                 "          [[-k [-t keytab_file_name]] [principal] " +
 233                            "[password]");
 234         System.out.println("2. Renew a ticket:\n" +
 235                 "    kinit -R [-c cachename] [principal]");
 236         System.out.println("\nAvailable options to " +
 237                            "Kerberos 5 ticket request:");
 238         System.out.println("\t-A   do not include addresses");
 239         System.out.println("\t-f   forwardable");
 240         System.out.println("\t-p   proxiable");
 241         System.out.println("\t-c   cache name " +
 242                 "(i.e., FILE:\\d:\\myProfiles\\mykrb5cache)");
 243         System.out.println("\t-l   lifetime");
 244         System.out.println("\t-r   renewable time " +
 245                 "(total lifetime a ticket can be renewed)");
 246         System.out.println("\t-k   use keytab");
 247         System.out.println("\t-t   keytab file name");
 248         System.out.println("\tprincipal   the principal name "+
 249                 "(i.e., qweadf@ATHENA.MIT.EDU qweadf)");
 250         System.out.println("\tpassword    the principal's Kerberos password");
 251     }
 252 
 253     public boolean getAddressOption() {
 254         return includeAddresses;
 255     }
 256 
 257     public boolean useKeytabFile() {
 258         return useKeytab;
 259     }
 260 
 261     public String keytabFileName() {
 262         return ktabName;
 263     }
 264 
 265     public PrincipalName getPrincipal() {
 266         return principal;
 267     }
 268 
 269     private KerberosTime getTime(int s) {
 270         return new KerberosTime(Instant.now().plusSeconds(s));
 271     }
 272 }