| /* |
| * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* |
| * (C) Copyright IBM Corp. 1999 All Rights Reserved. |
| * Copyright 1997 The Open Group Research Institute. All rights reserved. |
| */ |
| |
| package sun.security.krb5.internal.tools; |
| |
| import java.io.File; |
| import sun.security.krb5.*; |
| import sun.security.krb5.internal.*; |
| import sun.security.krb5.internal.ccache.*; |
| import java.io.IOException; |
| import java.util.Arrays; |
| import sun.security.util.Password; |
| import javax.security.auth.kerberos.KeyTab; |
| |
| /** |
| * Kinit tool for obtaining Kerberos v5 tickets. |
| * |
| * @author Yanni Zhang |
| * @author Ram Marti |
| */ |
| public class Kinit { |
| |
| private KinitOptions options; |
| private static final boolean DEBUG = Krb5.DEBUG; |
| |
| /** |
| * The main method is used to accept user command line input for ticket |
| * request. Read {@link KinitOptions#printHelp} for usages or call |
| * java sun.security.krb5.internal.tools.Kinit -help |
| * to bring up help menu. |
| * <p> |
| * We currently support only file-based credentials cache to |
| * store the tickets obtained from the KDC. |
| * By default, for all Unix platforms a cache file named |
| * /tmp/krb5cc_<uid> will be generated. The <uid> is the |
| * numeric user identifier. |
| * For all other platforms, a cache file named |
| * <USER_HOME>/krb5cc_<USER_NAME> would be generated. |
| * <p> |
| * <USER_HOME> is obtained from <code>java.lang.System</code> |
| * property <i>user.home</i>. |
| * <USER_NAME> is obtained from <code>java.lang.System</code> |
| * property <i>user.name</i>. |
| * If <USER_HOME> is null the cache file would be stored in |
| * the current directory that the program is running from. |
| * <USER_NAME> is operating system's login username. |
| * It could be different from user's principal name. |
| *</p> |
| *<p> |
| * For instance, on Windows NT, it could be |
| * c:\winnt\profiles\duke\krb5cc_duke, in |
| * which duke is the <USER_NAME>, and c:\winnt\profile\duke is the |
| * <USER_HOME>. |
| *<p> |
| * A single user could have multiple principal names, |
| * but the primary principal of the credentials cache could only be one, |
| * which means one cache file could only store tickets for one |
| * specific user principal. If the user switches |
| * the principal name at the next Kinit, the cache file generated for the |
| * new ticket would overwrite the old cache file by default. |
| * To avoid overwriting, you need to specify |
| * a different cache file name when you request a |
| * new ticket. |
| *</p> |
| *<p> |
| * You can specify the location of the cache file by using the -c option |
| * |
| */ |
| |
| public static void main(String[] args) { |
| try { |
| Kinit self = new Kinit(args); |
| } |
| catch (Exception e) { |
| String msg = null; |
| if (e instanceof KrbException) { |
| msg = ((KrbException)e).krbErrorMessage() + " " + |
| ((KrbException)e).returnCodeMessage(); |
| } else { |
| msg = e.getMessage(); |
| } |
| if (msg != null) { |
| System.err.println("Exception: " + msg); |
| } else { |
| System.out.println("Exception: " + e); |
| } |
| e.printStackTrace(); |
| System.exit(-1); |
| } |
| return; |
| } |
| |
| /** |
| * Constructs a new Kinit object. |
| * @param args array of ticket request options. |
| * Avaiable options are: -f, -p, -c, principal, password. |
| * @exception IOException if an I/O error occurs. |
| * @exception RealmException if the Realm could not be instantiated. |
| * @exception KrbException if error occurs during Kerberos operation. |
| */ |
| private Kinit(String[] args) |
| throws IOException, RealmException, KrbException { |
| if (args == null || args.length == 0) { |
| options = new KinitOptions(); |
| } else { |
| options = new KinitOptions(args); |
| } |
| switch (options.action) { |
| case 1: |
| acquire(); |
| break; |
| case 2: |
| renew(); |
| break; |
| default: |
| throw new KrbException("kinit does not support action " |
| + options.action); |
| } |
| } |
| |
| private void renew() |
| throws IOException, RealmException, KrbException { |
| |
| PrincipalName principal = options.getPrincipal(); |
| String realm = principal.getRealmAsString(); |
| CredentialsCache cache = CredentialsCache.getInstance(options.cachename); |
| |
| if (cache == null) { |
| throw new IOException("Unable to find existing cache file " + |
| options.cachename); |
| } |
| sun.security.krb5.internal.ccache.Credentials credentials = |
| cache.getCreds(PrincipalName.tgsService(realm, realm)); |
| |
| credentials = credentials.setKrbCreds() |
| .renew() |
| .toCCacheCreds(); |
| |
| cache = CredentialsCache.create(principal, options.cachename); |
| if (cache == null) { |
| throw new IOException("Unable to create the cache file " + |
| options.cachename); |
| } |
| cache.update(credentials); |
| cache.save(); |
| } |
| |
| private void acquire() |
| throws IOException, RealmException, KrbException { |
| |
| String princName = null; |
| PrincipalName principal = options.getPrincipal(); |
| if (principal != null) { |
| princName = principal.toString(); |
| } |
| KrbAsReqBuilder builder; |
| if (DEBUG) { |
| System.out.println("Principal is " + principal); |
| } |
| char[] psswd = options.password; |
| boolean useKeytab = options.useKeytabFile(); |
| if (!useKeytab) { |
| if (princName == null) { |
| throw new IllegalArgumentException |
| (" Can not obtain principal name"); |
| } |
| if (psswd == null) { |
| System.out.print("Password for " + princName + ":"); |
| System.out.flush(); |
| psswd = Password.readPassword(System.in); |
| if (DEBUG) { |
| System.out.println(">>> Kinit console input " + |
| new String(psswd)); |
| } |
| } |
| builder = new KrbAsReqBuilder(principal, psswd); |
| } else { |
| if (DEBUG) { |
| System.out.println(">>> Kinit using keytab"); |
| } |
| if (princName == null) { |
| throw new IllegalArgumentException |
| ("Principal name must be specified."); |
| } |
| String ktabName = options.keytabFileName(); |
| if (ktabName != null) { |
| if (DEBUG) { |
| System.out.println( |
| ">>> Kinit keytab file name: " + ktabName); |
| } |
| } |
| |
| builder = new KrbAsReqBuilder(principal, ktabName == null |
| ? KeyTab.getInstance() |
| : KeyTab.getInstance(new File(ktabName))); |
| } |
| |
| KDCOptions opt = new KDCOptions(); |
| setOptions(KDCOptions.FORWARDABLE, options.forwardable, opt); |
| setOptions(KDCOptions.PROXIABLE, options.proxiable, opt); |
| builder.setOptions(opt); |
| String realm = options.getKDCRealm(); |
| if (realm == null) { |
| realm = Config.getInstance().getDefaultRealm(); |
| } |
| |
| if (DEBUG) { |
| System.out.println(">>> Kinit realm name is " + realm); |
| } |
| |
| PrincipalName sname = PrincipalName.tgsService(realm, realm); |
| builder.setTarget(sname); |
| |
| if (DEBUG) { |
| System.out.println(">>> Creating KrbAsReq"); |
| } |
| |
| if (options.getAddressOption()) |
| builder.setAddresses(HostAddresses.getLocalAddresses()); |
| |
| builder.setTill(options.lifetime); |
| builder.setRTime(options.renewable_lifetime); |
| |
| builder.action(); |
| |
| sun.security.krb5.internal.ccache.Credentials credentials = |
| builder.getCCreds(); |
| builder.destroy(); |
| |
| // we always create a new cache and store the ticket we get |
| CredentialsCache cache = |
| CredentialsCache.create(principal, options.cachename); |
| if (cache == null) { |
| throw new IOException("Unable to create the cache file " + |
| options.cachename); |
| } |
| cache.update(credentials); |
| cache.save(); |
| |
| if (options.password == null) { |
| // Assume we're running interactively |
| System.out.println("New ticket is stored in cache file " + |
| options.cachename); |
| } else { |
| Arrays.fill(options.password, '0'); |
| } |
| |
| // clear the password |
| if (psswd != null) { |
| Arrays.fill(psswd, '0'); |
| } |
| options = null; // release reference to options |
| } |
| |
| private static void setOptions(int flag, int option, KDCOptions opt) { |
| switch (option) { |
| case 0: |
| break; |
| case -1: |
| opt.set(flag, false); |
| break; |
| case 1: |
| opt.set(flag, true); |
| } |
| } |
| } |