001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    package org.apache.hadoop.security;
019    
020    import static org.apache.hadoop.fs.CommonConfigurationKeys.HADOOP_SECURITY_AUTHENTICATION;
021    
022    import java.io.File;
023    import java.io.IOException;
024    import java.lang.reflect.UndeclaredThrowableException;
025    import java.security.AccessControlContext;
026    import java.security.AccessController;
027    import java.security.Principal;
028    import java.security.PrivilegedAction;
029    import java.security.PrivilegedActionException;
030    import java.security.PrivilegedExceptionAction;
031    import java.util.ArrayList;
032    import java.util.Arrays;
033    import java.util.Collection;
034    import java.util.Collections;
035    import java.util.HashMap;
036    import java.util.Iterator;
037    import java.util.List;
038    import java.util.Map;
039    import java.util.Set;
040    
041    import javax.security.auth.Subject;
042    import javax.security.auth.callback.CallbackHandler;
043    import javax.security.auth.kerberos.KerberosKey;
044    import javax.security.auth.kerberos.KerberosPrincipal;
045    import javax.security.auth.kerberos.KerberosTicket;
046    import javax.security.auth.login.AppConfigurationEntry;
047    import javax.security.auth.login.LoginContext;
048    import javax.security.auth.login.LoginException;
049    import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
050    import javax.security.auth.spi.LoginModule;
051    
052    import org.apache.commons.logging.Log;
053    import org.apache.commons.logging.LogFactory;
054    import org.apache.hadoop.classification.InterfaceAudience;
055    import org.apache.hadoop.classification.InterfaceStability;
056    import org.apache.hadoop.conf.Configuration;
057    import org.apache.hadoop.fs.Path;
058    import org.apache.hadoop.metrics2.annotation.Metric;
059    import org.apache.hadoop.metrics2.annotation.Metrics;
060    import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
061    import org.apache.hadoop.metrics2.lib.MutableRate;
062    import org.apache.hadoop.security.authentication.util.KerberosName;
063    import org.apache.hadoop.security.authentication.util.KerberosUtil;
064    import org.apache.hadoop.security.token.Token;
065    import org.apache.hadoop.security.token.TokenIdentifier;
066    import org.apache.hadoop.util.Shell;
067    import org.apache.hadoop.util.Time;
068    
069    /**
070     * User and group information for Hadoop.
071     * This class wraps around a JAAS Subject and provides methods to determine the
072     * user's username and groups. It supports both the Windows, Unix and Kerberos 
073     * login modules.
074     */
075    @InterfaceAudience.LimitedPrivate({"HDFS", "MapReduce", "HBase", "Hive", "Oozie"})
076    @InterfaceStability.Evolving
077    public class UserGroupInformation {
078      private static final Log LOG =  LogFactory.getLog(UserGroupInformation.class);
079      /**
080       * Percentage of the ticket window to use before we renew ticket.
081       */
082      private static final float TICKET_RENEW_WINDOW = 0.80f;
083      static final String HADOOP_USER_NAME = "HADOOP_USER_NAME";
084      
085      /** 
086       * UgiMetrics maintains UGI activity statistics
087       * and publishes them through the metrics interfaces.
088       */
089      @Metrics(about="User and group related metrics", context="ugi")
090      static class UgiMetrics {
091        @Metric("Rate of successful kerberos logins and latency (milliseconds)")
092        MutableRate loginSuccess;
093        @Metric("Rate of failed kerberos logins and latency (milliseconds)")
094        MutableRate loginFailure;
095    
096        static UgiMetrics create() {
097          return DefaultMetricsSystem.instance().register(new UgiMetrics());
098        }
099      }
100      
101      /**
102       * A login module that looks at the Kerberos, Unix, or Windows principal and
103       * adds the corresponding UserName.
104       */
105      @InterfaceAudience.Private
106      public static class HadoopLoginModule implements LoginModule {
107        private Subject subject;
108    
109        @Override
110        public boolean abort() throws LoginException {
111          return true;
112        }
113    
114        private <T extends Principal> T getCanonicalUser(Class<T> cls) {
115          for(T user: subject.getPrincipals(cls)) {
116            return user;
117          }
118          return null;
119        }
120    
121        @Override
122        public boolean commit() throws LoginException {
123          if (LOG.isDebugEnabled()) {
124            LOG.debug("hadoop login commit");
125          }
126          // if we already have a user, we are done.
127          if (!subject.getPrincipals(User.class).isEmpty()) {
128            if (LOG.isDebugEnabled()) {
129              LOG.debug("using existing subject:"+subject.getPrincipals());
130            }
131            return true;
132          }
133          Principal user = null;
134          // if we are using kerberos, try it out
135          if (useKerberos) {
136            user = getCanonicalUser(KerberosPrincipal.class);
137            if (LOG.isDebugEnabled()) {
138              LOG.debug("using kerberos user:"+user);
139            }
140          }
141          //If we don't have a kerberos user and security is disabled, check
142          //if user is specified in the environment or properties
143          if (!isSecurityEnabled() && (user == null)) {
144            String envUser = System.getenv(HADOOP_USER_NAME);
145            if (envUser == null) {
146              envUser = System.getProperty(HADOOP_USER_NAME);
147            }
148            user = envUser == null ? null : new User(envUser);
149          }
150          // use the OS user
151          if (user == null) {
152            user = getCanonicalUser(OS_PRINCIPAL_CLASS);
153            if (LOG.isDebugEnabled()) {
154              LOG.debug("using local user:"+user);
155            }
156          }
157          // if we found the user, add our principal
158          if (user != null) {
159            subject.getPrincipals().add(new User(user.getName()));
160            return true;
161          }
162          LOG.error("Can't find user in " + subject);
163          throw new LoginException("Can't find user name");
164        }
165    
166        @Override
167        public void initialize(Subject subject, CallbackHandler callbackHandler,
168                               Map<String, ?> sharedState, Map<String, ?> options) {
169          this.subject = subject;
170        }
171    
172        @Override
173        public boolean login() throws LoginException {
174          if (LOG.isDebugEnabled()) {
175            LOG.debug("hadoop login");
176          }
177          return true;
178        }
179    
180        @Override
181        public boolean logout() throws LoginException {
182          if (LOG.isDebugEnabled()) {
183            LOG.debug("hadoop logout");
184          }
185          return true;
186        }
187      }
188    
189      /** Metrics to track UGI activity */
190      static UgiMetrics metrics = UgiMetrics.create();
191      /** Are the static variables that depend on configuration initialized? */
192      private static boolean isInitialized = false;
193      /** Should we use Kerberos configuration? */
194      private static boolean useKerberos;
195      /** Server-side groups fetching service */
196      private static Groups groups;
197      /** The configuration to use */
198      private static Configuration conf;
199    
200      
201      /** Leave 10 minutes between relogin attempts. */
202      private static final long MIN_TIME_BEFORE_RELOGIN = 10 * 60 * 1000L;
203      
204      /**Environment variable pointing to the token cache file*/
205      public static final String HADOOP_TOKEN_FILE_LOCATION = 
206        "HADOOP_TOKEN_FILE_LOCATION";
207      
208      /** 
209       * A method to initialize the fields that depend on a configuration.
210       * Must be called before useKerberos or groups is used.
211       */
212      private static synchronized void ensureInitialized() {
213        if (!isInitialized) {
214            initialize(new Configuration(), KerberosName.hasRulesBeenSet());
215        }
216      }
217    
218      /**
219       * Initialize UGI and related classes.
220       * @param conf the configuration to use
221       */
222      private static synchronized void initialize(Configuration conf, boolean skipRulesSetting) {
223        initUGI(conf);
224        // give the configuration on how to translate Kerberos names
225        try {
226          if (!skipRulesSetting) {
227            HadoopKerberosName.setConfiguration(conf);
228          }
229        } catch (IOException ioe) {
230          throw new RuntimeException("Problem with Kerberos auth_to_local name " +
231              "configuration", ioe);
232        }
233      }
234      
235      /**
236       * Set the configuration values for UGI.
237       * @param conf the configuration to use
238       */
239      private static synchronized void initUGI(Configuration conf) {
240        String value = conf.get(HADOOP_SECURITY_AUTHENTICATION);
241        if (value == null || "simple".equals(value)) {
242          useKerberos = false;
243        } else if ("kerberos".equals(value)) {
244          useKerberos = true;
245        } else {
246          throw new IllegalArgumentException("Invalid attribute value for " +
247                                             HADOOP_SECURITY_AUTHENTICATION + 
248                                             " of " + value);
249        }
250        // If we haven't set up testing groups, use the configuration to find it
251        if (!(groups instanceof TestingGroups)) {
252          groups = Groups.getUserToGroupsMappingService(conf);
253        }
254        isInitialized = true;
255        UserGroupInformation.conf = conf;
256      }
257    
258      /**
259       * Set the static configuration for UGI.
260       * In particular, set the security authentication mechanism and the
261       * group look up service.
262       * @param conf the configuration to use
263       */
264      @InterfaceAudience.Public
265      @InterfaceStability.Evolving
266      public static void setConfiguration(Configuration conf) {
267        initialize(conf, false);
268      }
269      
270      /**
271       * Determine if UserGroupInformation is using Kerberos to determine
272       * user identities or is relying on simple authentication
273       * 
274       * @return true if UGI is working in a secure environment
275       */
276      public static boolean isSecurityEnabled() {
277        ensureInitialized();
278        return useKerberos;
279      }
280      
281      /**
282       * Information about the logged in user.
283       */
284      private static UserGroupInformation loginUser = null;
285      private static String keytabPrincipal = null;
286      private static String keytabFile = null;
287    
288      private final Subject subject;
289      // All non-static fields must be read-only caches that come from the subject.
290      private final User user;
291      private final boolean isKeytab;
292      private final boolean isKrbTkt;
293      
294      private static String OS_LOGIN_MODULE_NAME;
295      private static Class<? extends Principal> OS_PRINCIPAL_CLASS;
296      private static final boolean windows = 
297                               System.getProperty("os.name").startsWith("Windows");
298      /* Return the OS login module class name */
299      private static String getOSLoginModuleName() {
300        if (System.getProperty("java.vendor").contains("IBM")) {
301          return windows ? "com.ibm.security.auth.module.NTLoginModule"
302           : "com.ibm.security.auth.module.LinuxLoginModule";
303        } else {
304          return windows ? "com.sun.security.auth.module.NTLoginModule"
305            : "com.sun.security.auth.module.UnixLoginModule";
306        }
307      }
308    
309      /* Return the OS principal class */
310      @SuppressWarnings("unchecked")
311      private static Class<? extends Principal> getOsPrincipalClass() {
312        ClassLoader cl = ClassLoader.getSystemClassLoader();
313        try {
314          if (System.getProperty("java.vendor").contains("IBM")) {
315            if (windows) {
316              return (Class<? extends Principal>)
317                cl.loadClass("com.ibm.security.auth.UsernamePrincipal");
318            } else {
319              return (Class<? extends Principal>)
320                (System.getProperty("os.arch").contains("64")
321                 ? cl.loadClass("com.ibm.security.auth.UsernamePrincipal")
322                 : cl.loadClass("com.ibm.security.auth.LinuxPrincipal"));
323            }
324          } else {
325            return (Class<? extends Principal>) (windows
326               ? cl.loadClass("com.sun.security.auth.NTUserPrincipal")
327               : cl.loadClass("com.sun.security.auth.UnixPrincipal"));
328          }
329        } catch (ClassNotFoundException e) {
330          LOG.error("Unable to find JAAS classes:" + e.getMessage());
331        }
332        return null;
333      }
334      static {
335        OS_LOGIN_MODULE_NAME = getOSLoginModuleName();
336        OS_PRINCIPAL_CLASS = getOsPrincipalClass();
337      }
338    
339      private static class RealUser implements Principal {
340        private final UserGroupInformation realUser;
341        
342        RealUser(UserGroupInformation realUser) {
343          this.realUser = realUser;
344        }
345        
346        public String getName() {
347          return realUser.getUserName();
348        }
349        
350        public UserGroupInformation getRealUser() {
351          return realUser;
352        }
353        
354        @Override
355        public boolean equals(Object o) {
356          if (this == o) {
357            return true;
358          } else if (o == null || getClass() != o.getClass()) {
359            return false;
360          } else {
361            return realUser.equals(((RealUser) o).realUser);
362          }
363        }
364        
365        @Override
366        public int hashCode() {
367          return realUser.hashCode();
368        }
369        
370        @Override
371        public String toString() {
372          return realUser.toString();
373        }
374      }
375      
376      /**
377       * A JAAS configuration that defines the login modules that we want
378       * to use for login.
379       */
380      private static class HadoopConfiguration 
381          extends javax.security.auth.login.Configuration {
382        private static final String SIMPLE_CONFIG_NAME = "hadoop-simple";
383        private static final String USER_KERBEROS_CONFIG_NAME = 
384          "hadoop-user-kerberos";
385        private static final String KEYTAB_KERBEROS_CONFIG_NAME = 
386          "hadoop-keytab-kerberos";
387    
388        private static final Map<String, String> BASIC_JAAS_OPTIONS =
389          new HashMap<String,String>();
390        static {
391          String jaasEnvVar = System.getenv("HADOOP_JAAS_DEBUG");
392          if (jaasEnvVar != null && "true".equalsIgnoreCase(jaasEnvVar)) {
393            BASIC_JAAS_OPTIONS.put("debug", "true");
394          }
395        }
396        
397        private static final AppConfigurationEntry OS_SPECIFIC_LOGIN =
398          new AppConfigurationEntry(OS_LOGIN_MODULE_NAME,
399                                    LoginModuleControlFlag.REQUIRED,
400                                    BASIC_JAAS_OPTIONS);
401        private static final AppConfigurationEntry HADOOP_LOGIN =
402          new AppConfigurationEntry(HadoopLoginModule.class.getName(),
403                                    LoginModuleControlFlag.REQUIRED,
404                                    BASIC_JAAS_OPTIONS);
405        private static final Map<String,String> USER_KERBEROS_OPTIONS = 
406          new HashMap<String,String>();
407        static {
408          USER_KERBEROS_OPTIONS.put("doNotPrompt", "true");
409          USER_KERBEROS_OPTIONS.put("useTicketCache", "true");
410          USER_KERBEROS_OPTIONS.put("renewTGT", "true");
411          String ticketCache = System.getenv("KRB5CCNAME");
412          if (ticketCache != null) {
413            USER_KERBEROS_OPTIONS.put("ticketCache", ticketCache);
414          }
415          USER_KERBEROS_OPTIONS.putAll(BASIC_JAAS_OPTIONS);
416        }
417        private static final AppConfigurationEntry USER_KERBEROS_LOGIN =
418          new AppConfigurationEntry(KerberosUtil.getKrb5LoginModuleName(),
419                                    LoginModuleControlFlag.OPTIONAL,
420                                    USER_KERBEROS_OPTIONS);
421        private static final Map<String,String> KEYTAB_KERBEROS_OPTIONS = 
422          new HashMap<String,String>();
423        static {
424          KEYTAB_KERBEROS_OPTIONS.put("doNotPrompt", "true");
425          KEYTAB_KERBEROS_OPTIONS.put("useKeyTab", "true");
426          KEYTAB_KERBEROS_OPTIONS.put("storeKey", "true");
427          KEYTAB_KERBEROS_OPTIONS.put("refreshKrb5Config", "true");
428          KEYTAB_KERBEROS_OPTIONS.putAll(BASIC_JAAS_OPTIONS);      
429        }
430        private static final AppConfigurationEntry KEYTAB_KERBEROS_LOGIN =
431          new AppConfigurationEntry(KerberosUtil.getKrb5LoginModuleName(),
432                                    LoginModuleControlFlag.REQUIRED,
433                                    KEYTAB_KERBEROS_OPTIONS);
434        
435        private static final AppConfigurationEntry[] SIMPLE_CONF = 
436          new AppConfigurationEntry[]{OS_SPECIFIC_LOGIN, HADOOP_LOGIN};
437    
438        private static final AppConfigurationEntry[] USER_KERBEROS_CONF =
439          new AppConfigurationEntry[]{OS_SPECIFIC_LOGIN, USER_KERBEROS_LOGIN,
440                                      HADOOP_LOGIN};
441    
442        private static final AppConfigurationEntry[] KEYTAB_KERBEROS_CONF =
443          new AppConfigurationEntry[]{KEYTAB_KERBEROS_LOGIN, HADOOP_LOGIN};
444    
445        @Override
446        public AppConfigurationEntry[] getAppConfigurationEntry(String appName) {
447          if (SIMPLE_CONFIG_NAME.equals(appName)) {
448            return SIMPLE_CONF;
449          } else if (USER_KERBEROS_CONFIG_NAME.equals(appName)) {
450            return USER_KERBEROS_CONF;
451          } else if (KEYTAB_KERBEROS_CONFIG_NAME.equals(appName)) {
452            KEYTAB_KERBEROS_OPTIONS.put("keyTab", keytabFile);
453            KEYTAB_KERBEROS_OPTIONS.put("principal", keytabPrincipal);
454            return KEYTAB_KERBEROS_CONF;
455          }
456          return null;
457        }
458      }
459    
460      /**
461       * Represents a javax.security configuration that is created at runtime.
462       */
463      private static class DynamicConfiguration
464          extends javax.security.auth.login.Configuration {
465        private AppConfigurationEntry[] ace;
466        
467        DynamicConfiguration(AppConfigurationEntry[] ace) {
468          this.ace = ace;
469        }
470        
471        @Override
472        public AppConfigurationEntry[] getAppConfigurationEntry(String appName) {
473          return ace;
474        }
475      }
476    
477      private static LoginContext
478      newLoginContext(String appName, Subject subject,
479        javax.security.auth.login.Configuration loginConf)
480          throws LoginException {
481        // Temporarily switch the thread's ContextClassLoader to match this
482        // class's classloader, so that we can properly load HadoopLoginModule
483        // from the JAAS libraries.
484        Thread t = Thread.currentThread();
485        ClassLoader oldCCL = t.getContextClassLoader();
486        t.setContextClassLoader(HadoopLoginModule.class.getClassLoader());
487        try {
488          return new LoginContext(appName, subject, null, loginConf);
489        } finally {
490          t.setContextClassLoader(oldCCL);
491        }
492      }
493    
494      private LoginContext getLogin() {
495        return user.getLogin();
496      }
497      
498      private void setLogin(LoginContext login) {
499        user.setLogin(login);
500      }
501    
502      /**
503       * Create a UserGroupInformation for the given subject.
504       * This does not change the subject or acquire new credentials.
505       * @param subject the user's subject
506       */
507      UserGroupInformation(Subject subject) {
508        this.subject = subject;
509        this.user = subject.getPrincipals(User.class).iterator().next();
510        this.isKeytab = !subject.getPrivateCredentials(KerberosKey.class).isEmpty();
511        this.isKrbTkt = !subject.getPrivateCredentials(KerberosTicket.class).isEmpty();
512      }
513      
514      /**
515       * checks if logged in using kerberos
516       * @return true if the subject logged via keytab or has a Kerberos TGT
517       */
518      public boolean hasKerberosCredentials() {
519        return isKeytab || isKrbTkt;
520      }
521    
522      /**
523       * Return the current user, including any doAs in the current stack.
524       * @return the current user
525       * @throws IOException if login fails
526       */
527      @InterfaceAudience.Public
528      @InterfaceStability.Evolving
529      public synchronized
530      static UserGroupInformation getCurrentUser() throws IOException {
531        AccessControlContext context = AccessController.getContext();
532        Subject subject = Subject.getSubject(context);
533        if (subject == null || subject.getPrincipals(User.class).isEmpty()) {
534          return getLoginUser();
535        } else {
536          return new UserGroupInformation(subject);
537        }
538      }
539    
540      /**
541       * Find the most appropriate UserGroupInformation to use
542       *
543       * @param ticketCachePath    The Kerberos ticket cache path, or NULL
544       *                           if none is specfied
545       * @param user               The user name, or NULL if none is specified.
546       *
547       * @return                   The most appropriate UserGroupInformation
548       */ 
549      public static UserGroupInformation getBestUGI(
550          String ticketCachePath, String user) throws IOException {
551        if (ticketCachePath != null) {
552          return getUGIFromTicketCache(ticketCachePath, user);
553        } else if (user == null) {
554          return getCurrentUser();
555        } else {
556          return createRemoteUser(user);
557        }    
558      }
559    
560      /**
561       * Create a UserGroupInformation from a Kerberos ticket cache.
562       * 
563       * @param user                The principal name to load from the ticket
564       *                            cache
565       * @param ticketCachePath     the path to the ticket cache file
566       *
567       * @throws IOException        if the kerberos login fails
568       */
569      @InterfaceAudience.Public
570      @InterfaceStability.Evolving
571      public static UserGroupInformation getUGIFromTicketCache(
572                String ticketCache, String user) throws IOException {
573        if (!isSecurityEnabled()) {
574          return getBestUGI(null, user);
575        }
576        try {
577          Map<String,String> krbOptions = new HashMap<String,String>();
578          krbOptions.put("doNotPrompt", "true");
579          krbOptions.put("useTicketCache", "true");
580          krbOptions.put("useKeyTab", "false");
581          krbOptions.put("renewTGT", "false");
582          krbOptions.put("ticketCache", ticketCache);
583          krbOptions.putAll(HadoopConfiguration.BASIC_JAAS_OPTIONS);
584          AppConfigurationEntry ace = new AppConfigurationEntry(
585              KerberosUtil.getKrb5LoginModuleName(),
586              LoginModuleControlFlag.REQUIRED,
587              krbOptions);
588          DynamicConfiguration dynConf =
589              new DynamicConfiguration(new AppConfigurationEntry[]{ ace });
590          LoginContext login = newLoginContext(
591              HadoopConfiguration.USER_KERBEROS_CONFIG_NAME, null, dynConf);
592          login.login();
593    
594          Subject loginSubject = login.getSubject();
595          Set<Principal> loginPrincipals = loginSubject.getPrincipals();
596          if (loginPrincipals.isEmpty()) {
597            throw new RuntimeException("No login principals found!");
598          }
599          if (loginPrincipals.size() != 1) {
600            LOG.warn("found more than one principal in the ticket cache file " +
601              ticketCache);
602          }
603          User ugiUser = new User(loginPrincipals.iterator().next().getName(),
604              AuthenticationMethod.KERBEROS, login);
605          loginSubject.getPrincipals().add(ugiUser);
606          UserGroupInformation ugi = new UserGroupInformation(loginSubject);
607          ugi.setLogin(login);
608          ugi.setAuthenticationMethod(AuthenticationMethod.KERBEROS);
609          return ugi;
610        } catch (LoginException le) {
611          throw new IOException("failure to login using ticket cache file " +
612              ticketCache, le);
613        }
614      }
615    
616      /**
617       * Get the currently logged in user.
618       * @return the logged in user
619       * @throws IOException if login fails
620       */
621      @InterfaceAudience.Public
622      @InterfaceStability.Evolving
623      public synchronized 
624      static UserGroupInformation getLoginUser() throws IOException {
625        if (loginUser == null) {
626          try {
627            Subject subject = new Subject();
628            LoginContext login;
629            if (isSecurityEnabled()) {
630              login = newLoginContext(HadoopConfiguration.USER_KERBEROS_CONFIG_NAME,
631                  subject, new HadoopConfiguration());
632            } else {
633              login = newLoginContext(HadoopConfiguration.SIMPLE_CONFIG_NAME, 
634                  subject, new HadoopConfiguration());
635            }
636            login.login();
637            loginUser = new UserGroupInformation(subject);
638            loginUser.setLogin(login);
639            loginUser.setAuthenticationMethod(isSecurityEnabled() ?
640                                              AuthenticationMethod.KERBEROS :
641                                              AuthenticationMethod.SIMPLE);
642            loginUser = new UserGroupInformation(login.getSubject());
643            String fileLocation = System.getenv(HADOOP_TOKEN_FILE_LOCATION);
644            if (fileLocation != null && isSecurityEnabled()) {
645              // load the token storage file and put all of the tokens into the
646              // user.
647              Credentials cred = Credentials.readTokenStorageFile(
648                  new Path("file:///" + fileLocation), conf);
649              for (Token<?> token: cred.getAllTokens()) {
650                loginUser.addToken(token);
651              }
652            }
653            loginUser.spawnAutoRenewalThreadForUserCreds();
654          } catch (LoginException le) {
655            throw new IOException("failure to login", le);
656          }
657          if (LOG.isDebugEnabled()) {
658            LOG.debug("UGI loginUser:"+loginUser);
659          }
660        }
661        return loginUser;
662      }
663    
664      /**
665       * Is this user logged in from a keytab file?
666       * @return true if the credentials are from a keytab file.
667       */
668      public boolean isFromKeytab() {
669        return isKeytab;
670      }
671      
672      /**
673       * Get the Kerberos TGT
674       * @return the user's TGT or null if none was found
675       */
676      private synchronized KerberosTicket getTGT() {
677        Set<KerberosTicket> tickets = subject
678            .getPrivateCredentials(KerberosTicket.class);
679        for (KerberosTicket ticket : tickets) {
680          if (SecurityUtil.isOriginalTGT(ticket)) {
681            if (LOG.isDebugEnabled()) {
682              LOG.debug("Found tgt " + ticket);
683            }
684            return ticket;
685          }
686        }
687        return null;
688      }
689      
690      private long getRefreshTime(KerberosTicket tgt) {
691        long start = tgt.getStartTime().getTime();
692        long end = tgt.getEndTime().getTime();
693        return start + (long) ((end - start) * TICKET_RENEW_WINDOW);
694      }
695    
696      /**Spawn a thread to do periodic renewals of kerberos credentials*/
697      private void spawnAutoRenewalThreadForUserCreds() {
698        if (isSecurityEnabled()) {
699          //spawn thread only if we have kerb credentials
700          if (user.getAuthenticationMethod() == AuthenticationMethod.KERBEROS &&
701              !isKeytab) {
702            Thread t = new Thread(new Runnable() {
703              
704              public void run() {
705                String cmd = conf.get("hadoop.kerberos.kinit.command",
706                                      "kinit");
707                KerberosTicket tgt = getTGT();
708                if (tgt == null) {
709                  return;
710                }
711                long nextRefresh = getRefreshTime(tgt);
712                while (true) {
713                  try {
714                    long now = Time.now();
715                    if(LOG.isDebugEnabled()) {
716                      LOG.debug("Current time is " + now);
717                      LOG.debug("Next refresh is " + nextRefresh);
718                    }
719                    if (now < nextRefresh) {
720                      Thread.sleep(nextRefresh - now);
721                    }
722                    Shell.execCommand(cmd, "-R");
723                    if(LOG.isDebugEnabled()) {
724                      LOG.debug("renewed ticket");
725                    }
726                    reloginFromTicketCache();
727                    tgt = getTGT();
728                    if (tgt == null) {
729                      LOG.warn("No TGT after renewal. Aborting renew thread for " +
730                               getUserName());
731                      return;
732                    }
733                    nextRefresh = Math.max(getRefreshTime(tgt),
734                                           now + MIN_TIME_BEFORE_RELOGIN);
735                  } catch (InterruptedException ie) {
736                    LOG.warn("Terminating renewal thread");
737                    return;
738                  } catch (IOException ie) {
739                    LOG.warn("Exception encountered while running the" +
740                        " renewal command. Aborting renew thread. " + ie);
741                    return;
742                  }
743                }
744              }
745            });
746            t.setDaemon(true);
747            t.setName("TGT Renewer for " + getUserName());
748            t.start();
749          }
750        }
751      }
752      /**
753       * Log a user in from a keytab file. Loads a user identity from a keytab
754       * file and logs them in. They become the currently logged-in user.
755       * @param user the principal name to load from the keytab
756       * @param path the path to the keytab file
757       * @throws IOException if the keytab file can't be read
758       */
759      @InterfaceAudience.Public
760      @InterfaceStability.Evolving
761      public synchronized
762      static void loginUserFromKeytab(String user,
763                                      String path
764                                      ) throws IOException {
765        if (!isSecurityEnabled())
766          return;
767    
768        keytabFile = path;
769        keytabPrincipal = user;
770        Subject subject = new Subject();
771        LoginContext login; 
772        long start = 0;
773        try {
774          login = newLoginContext(HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME,
775                subject, new HadoopConfiguration());
776          start = Time.now();
777          login.login();
778          metrics.loginSuccess.add(Time.now() - start);
779          loginUser = new UserGroupInformation(subject);
780          loginUser.setLogin(login);
781          loginUser.setAuthenticationMethod(AuthenticationMethod.KERBEROS);
782        } catch (LoginException le) {
783          if (start > 0) {
784            metrics.loginFailure.add(Time.now() - start);
785          }
786          throw new IOException("Login failure for " + user + " from keytab " + 
787                                path, le);
788        }
789        LOG.info("Login successful for user " + keytabPrincipal
790            + " using keytab file " + keytabFile);
791      }
792      
793      /**
794       * Re-login a user from keytab if TGT is expired or is close to expiry.
795       * 
796       * @throws IOException
797       */
798      public synchronized void checkTGTAndReloginFromKeytab() throws IOException {
799        if (!isSecurityEnabled()
800            || user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS
801            || !isKeytab)
802          return;
803        KerberosTicket tgt = getTGT();
804        if (tgt != null && Time.now() < getRefreshTime(tgt)) {
805          return;
806        }
807        reloginFromKeytab();
808      }
809    
810      /**
811       * Re-Login a user in from a keytab file. Loads a user identity from a keytab
812       * file and logs them in. They become the currently logged-in user. This
813       * method assumes that {@link #loginUserFromKeytab(String, String)} had 
814       * happened already.
815       * The Subject field of this UserGroupInformation object is updated to have
816       * the new credentials.
817       * @throws IOException on a failure
818       */
819      @InterfaceAudience.Public
820      @InterfaceStability.Evolving
821      public synchronized void reloginFromKeytab()
822      throws IOException {
823        if (!isSecurityEnabled() ||
824             user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS ||
825             !isKeytab)
826          return;
827        
828        long now = Time.now();
829        if (!hasSufficientTimeElapsed(now)) {
830          return;
831        }
832    
833        KerberosTicket tgt = getTGT();
834        //Return if TGT is valid and is not going to expire soon.
835        if (tgt != null && now < getRefreshTime(tgt)) {
836          return;
837        }
838        
839        LoginContext login = getLogin();
840        if (login == null || keytabFile == null) {
841          throw new IOException("loginUserFromKeyTab must be done first");
842        }
843        
844        long start = 0;
845        // register most recent relogin attempt
846        user.setLastLogin(now);
847        try {
848          LOG.info("Initiating logout for " + getUserName());
849          synchronized (UserGroupInformation.class) {
850            // clear up the kerberos state. But the tokens are not cleared! As per
851            // the Java kerberos login module code, only the kerberos credentials
852            // are cleared
853            login.logout();
854            // login and also update the subject field of this instance to
855            // have the new credentials (pass it to the LoginContext constructor)
856            login = newLoginContext(
857                HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME, getSubject(),
858                new HadoopConfiguration());
859            LOG.info("Initiating re-login for " + keytabPrincipal);
860            start = Time.now();
861            login.login();
862            metrics.loginSuccess.add(Time.now() - start);
863            setLogin(login);
864          }
865        } catch (LoginException le) {
866          if (start > 0) {
867            metrics.loginFailure.add(Time.now() - start);
868          }
869          throw new IOException("Login failure for " + keytabPrincipal + 
870              " from keytab " + keytabFile, le);
871        } 
872      }
873    
874      /**
875       * Re-Login a user in from the ticket cache.  This
876       * method assumes that login had happened already.
877       * The Subject field of this UserGroupInformation object is updated to have
878       * the new credentials.
879       * @throws IOException on a failure
880       */
881      @InterfaceAudience.Public
882      @InterfaceStability.Evolving
883      public synchronized void reloginFromTicketCache()
884      throws IOException {
885        if (!isSecurityEnabled() || 
886            user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS ||
887            !isKrbTkt)
888          return;
889        LoginContext login = getLogin();
890        if (login == null) {
891          throw new IOException("login must be done first");
892        }
893        long now = Time.now();
894        if (!hasSufficientTimeElapsed(now)) {
895          return;
896        }
897        // register most recent relogin attempt
898        user.setLastLogin(now);
899        try {
900          LOG.info("Initiating logout for " + getUserName());
901          //clear up the kerberos state. But the tokens are not cleared! As per 
902          //the Java kerberos login module code, only the kerberos credentials
903          //are cleared
904          login.logout();
905          //login and also update the subject field of this instance to 
906          //have the new credentials (pass it to the LoginContext constructor)
907          login = 
908            newLoginContext(HadoopConfiguration.USER_KERBEROS_CONFIG_NAME, 
909                getSubject(), new HadoopConfiguration());
910          LOG.info("Initiating re-login for " + getUserName());
911          login.login();
912          setLogin(login);
913        } catch (LoginException le) {
914          throw new IOException("Login failure for " + getUserName(), le);
915        } 
916      }
917    
918    
919      /**
920       * Log a user in from a keytab file. Loads a user identity from a keytab
921       * file and login them in. This new user does not affect the currently
922       * logged-in user.
923       * @param user the principal name to load from the keytab
924       * @param path the path to the keytab file
925       * @throws IOException if the keytab file can't be read
926       */
927      public synchronized
928      static UserGroupInformation loginUserFromKeytabAndReturnUGI(String user,
929                                      String path
930                                      ) throws IOException {
931        if (!isSecurityEnabled())
932          return UserGroupInformation.getCurrentUser();
933        String oldKeytabFile = null;
934        String oldKeytabPrincipal = null;
935    
936        long start = 0;
937        try {
938          oldKeytabFile = keytabFile;
939          oldKeytabPrincipal = keytabPrincipal;
940          keytabFile = path;
941          keytabPrincipal = user;
942          Subject subject = new Subject();
943          
944          LoginContext login = newLoginContext(
945              HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME, subject,
946              new HadoopConfiguration());
947           
948          start = Time.now();
949          login.login();
950          metrics.loginSuccess.add(Time.now() - start);
951          UserGroupInformation newLoginUser = new UserGroupInformation(subject);
952          newLoginUser.setLogin(login);
953          newLoginUser.setAuthenticationMethod(AuthenticationMethod.KERBEROS);
954          
955          return newLoginUser;
956        } catch (LoginException le) {
957          if (start > 0) {
958            metrics.loginFailure.add(Time.now() - start);
959          }
960          throw new IOException("Login failure for " + user + " from keytab " + 
961                                path, le);
962        } finally {
963          if(oldKeytabFile != null) keytabFile = oldKeytabFile;
964          if(oldKeytabPrincipal != null) keytabPrincipal = oldKeytabPrincipal;
965        }
966      }
967    
968      private boolean hasSufficientTimeElapsed(long now) {
969        if (now - user.getLastLogin() < MIN_TIME_BEFORE_RELOGIN ) {
970          LOG.warn("Not attempting to re-login since the last re-login was " +
971              "attempted less than " + (MIN_TIME_BEFORE_RELOGIN/1000) + " seconds"+
972              " before.");
973          return false;
974        }
975        return true;
976      }
977      
978      /**
979       * Did the login happen via keytab
980       * @return true or false
981       */
982      @InterfaceAudience.Public
983      @InterfaceStability.Evolving
984      public synchronized static boolean isLoginKeytabBased() throws IOException {
985        return getLoginUser().isKeytab;
986      }
987    
988      /**
989       * Create a user from a login name. It is intended to be used for remote
990       * users in RPC, since it won't have any credentials.
991       * @param user the full user principal name, must not be empty or null
992       * @return the UserGroupInformation for the remote user.
993       */
994      @InterfaceAudience.Public
995      @InterfaceStability.Evolving
996      public static UserGroupInformation createRemoteUser(String user) {
997        if (user == null || "".equals(user)) {
998          throw new IllegalArgumentException("Null user");
999        }
1000        Subject subject = new Subject();
1001        subject.getPrincipals().add(new User(user));
1002        UserGroupInformation result = new UserGroupInformation(subject);
1003        result.setAuthenticationMethod(AuthenticationMethod.SIMPLE);
1004        return result;
1005      }
1006    
1007      /**
1008       * existing types of authentications' methods
1009       */
1010      @InterfaceAudience.Public
1011      @InterfaceStability.Evolving
1012      public static enum AuthenticationMethod {
1013        SIMPLE,
1014        KERBEROS,
1015        TOKEN,
1016        CERTIFICATE,
1017        KERBEROS_SSL,
1018        PROXY;
1019      }
1020    
1021      /**
1022       * Create a proxy user using username of the effective user and the ugi of the
1023       * real user.
1024       * @param user
1025       * @param realUser
1026       * @return proxyUser ugi
1027       */
1028      @InterfaceAudience.Public
1029      @InterfaceStability.Evolving
1030      public static UserGroupInformation createProxyUser(String user,
1031          UserGroupInformation realUser) {
1032        if (user == null || "".equals(user)) {
1033          throw new IllegalArgumentException("Null user");
1034        }
1035        if (realUser == null) {
1036          throw new IllegalArgumentException("Null real user");
1037        }
1038        Subject subject = new Subject();
1039        Set<Principal> principals = subject.getPrincipals();
1040        principals.add(new User(user));
1041        principals.add(new RealUser(realUser));
1042        UserGroupInformation result =new UserGroupInformation(subject);
1043        result.setAuthenticationMethod(AuthenticationMethod.PROXY);
1044        return result;
1045      }
1046    
1047      /**
1048       * get RealUser (vs. EffectiveUser)
1049       * @return realUser running over proxy user
1050       */
1051      @InterfaceAudience.Public
1052      @InterfaceStability.Evolving
1053      public UserGroupInformation getRealUser() {
1054        for (RealUser p: subject.getPrincipals(RealUser.class)) {
1055          return p.getRealUser();
1056        }
1057        return null;
1058      }
1059    
1060    
1061      
1062      /**
1063       * This class is used for storing the groups for testing. It stores a local
1064       * map that has the translation of usernames to groups.
1065       */
1066      private static class TestingGroups extends Groups {
1067        private final Map<String, List<String>> userToGroupsMapping = 
1068          new HashMap<String,List<String>>();
1069        private Groups underlyingImplementation;
1070        
1071        private TestingGroups(Groups underlyingImplementation) {
1072          super(new org.apache.hadoop.conf.Configuration());
1073          this.underlyingImplementation = underlyingImplementation;
1074        }
1075        
1076        @Override
1077        public List<String> getGroups(String user) throws IOException {
1078          List<String> result = userToGroupsMapping.get(user);
1079          
1080          if (result == null) {
1081            result = underlyingImplementation.getGroups(user);
1082          }
1083    
1084          return result;
1085        }
1086    
1087        private void setUserGroups(String user, String[] groups) {
1088          userToGroupsMapping.put(user, Arrays.asList(groups));
1089        }
1090      }
1091    
1092      /**
1093       * Create a UGI for testing HDFS and MapReduce
1094       * @param user the full user principal name
1095       * @param userGroups the names of the groups that the user belongs to
1096       * @return a fake user for running unit tests
1097       */
1098      @InterfaceAudience.Public
1099      @InterfaceStability.Evolving
1100      public static UserGroupInformation createUserForTesting(String user, 
1101                                                              String[] userGroups) {
1102        ensureInitialized();
1103        UserGroupInformation ugi = createRemoteUser(user);
1104        // make sure that the testing object is setup
1105        if (!(groups instanceof TestingGroups)) {
1106          groups = new TestingGroups(groups);
1107        }
1108        // add the user groups
1109        ((TestingGroups) groups).setUserGroups(ugi.getShortUserName(), userGroups);
1110        return ugi;
1111      }
1112    
1113    
1114      /**
1115       * Create a proxy user UGI for testing HDFS and MapReduce
1116       * 
1117       * @param user
1118       *          the full user principal name for effective user
1119       * @param realUser
1120       *          UGI of the real user
1121       * @param userGroups
1122       *          the names of the groups that the user belongs to
1123       * @return a fake user for running unit tests
1124       */
1125      public static UserGroupInformation createProxyUserForTesting(String user,
1126          UserGroupInformation realUser, String[] userGroups) {
1127        ensureInitialized();
1128        UserGroupInformation ugi = createProxyUser(user, realUser);
1129        // make sure that the testing object is setup
1130        if (!(groups instanceof TestingGroups)) {
1131          groups = new TestingGroups(groups);
1132        }
1133        // add the user groups
1134        ((TestingGroups) groups).setUserGroups(ugi.getShortUserName(), userGroups);
1135        return ugi;
1136      }
1137      
1138      /**
1139       * Get the user's login name.
1140       * @return the user's name up to the first '/' or '@'.
1141       */
1142      public String getShortUserName() {
1143        for (User p: subject.getPrincipals(User.class)) {
1144          return p.getShortName();
1145        }
1146        return null;
1147      }
1148    
1149      /**
1150       * Get the user's full principal name.
1151       * @return the user's full principal name.
1152       */
1153      @InterfaceAudience.Public
1154      @InterfaceStability.Evolving
1155      public String getUserName() {
1156        return user.getName();
1157      }
1158    
1159      /**
1160       * Add a TokenIdentifier to this UGI. The TokenIdentifier has typically been
1161       * authenticated by the RPC layer as belonging to the user represented by this
1162       * UGI.
1163       * 
1164       * @param tokenId
1165       *          tokenIdentifier to be added
1166       * @return true on successful add of new tokenIdentifier
1167       */
1168      public synchronized boolean addTokenIdentifier(TokenIdentifier tokenId) {
1169        return subject.getPublicCredentials().add(tokenId);
1170      }
1171    
1172      /**
1173       * Get the set of TokenIdentifiers belonging to this UGI
1174       * 
1175       * @return the set of TokenIdentifiers belonging to this UGI
1176       */
1177      public synchronized Set<TokenIdentifier> getTokenIdentifiers() {
1178        return subject.getPublicCredentials(TokenIdentifier.class);
1179      }
1180      
1181      /**
1182       * Add a token to this UGI
1183       * 
1184       * @param token Token to be added
1185       * @return true on successful add of new token
1186       */
1187      public synchronized boolean addToken(Token<? extends TokenIdentifier> token) {
1188        return subject.getPrivateCredentials().add(token);
1189      }
1190      
1191      /**
1192       * Obtain the collection of tokens associated with this user.
1193       * 
1194       * @return an unmodifiable collection of tokens associated with user
1195       */
1196      public synchronized
1197      Collection<Token<? extends TokenIdentifier>> getTokens() {
1198        Set<Object> creds = subject.getPrivateCredentials();
1199        List<Token<?>> result = new ArrayList<Token<?>>(creds.size());
1200        for(Object o: creds) {
1201          if (o instanceof Token<?>) {
1202            result.add((Token<?>) o);
1203          }
1204        }
1205        return Collections.unmodifiableList(result);
1206      }
1207    
1208      /**
1209       * Get the group names for this user.
1210       * @return the list of users with the primary group first. If the command
1211       *    fails, it returns an empty list.
1212       */
1213      public synchronized String[] getGroupNames() {
1214        ensureInitialized();
1215        try {
1216          List<String> result = groups.getGroups(getShortUserName());
1217          return result.toArray(new String[result.size()]);
1218        } catch (IOException ie) {
1219          LOG.warn("No groups available for user " + getShortUserName());
1220          return new String[0];
1221        }
1222      }
1223      
1224      /**
1225       * Return the username.
1226       */
1227      @Override
1228      public String toString() {
1229        StringBuilder sb = new StringBuilder(getUserName());
1230        sb.append(" (auth:"+getAuthenticationMethod()+")");
1231        if (getRealUser() != null) {
1232          sb.append(" via ").append(getRealUser().toString());
1233        }
1234        return sb.toString();
1235      }
1236    
1237      /**
1238       * Sets the authentication method in the subject
1239       * 
1240       * @param authMethod
1241       */
1242      public synchronized 
1243      void setAuthenticationMethod(AuthenticationMethod authMethod) {
1244        user.setAuthenticationMethod(authMethod);
1245      }
1246    
1247      /**
1248       * Get the authentication method from the subject
1249       * 
1250       * @return AuthenticationMethod in the subject, null if not present.
1251       */
1252      public synchronized AuthenticationMethod getAuthenticationMethod() {
1253        return user.getAuthenticationMethod();
1254      }
1255      
1256      /**
1257       * Returns the authentication method of a ugi. If the authentication method is
1258       * PROXY, returns the authentication method of the real user.
1259       * 
1260       * @param ugi
1261       * @return AuthenticationMethod
1262       */
1263      public static AuthenticationMethod getRealAuthenticationMethod(
1264          UserGroupInformation ugi) {
1265        AuthenticationMethod authMethod = ugi.getAuthenticationMethod();
1266        if (authMethod == AuthenticationMethod.PROXY) {
1267          authMethod = ugi.getRealUser().getAuthenticationMethod();
1268        }
1269        return authMethod;
1270      }
1271    
1272      /**
1273       * Compare the subjects to see if they are equal to each other.
1274       */
1275      @Override
1276      public boolean equals(Object o) {
1277        if (o == this) {
1278          return true;
1279        } else if (o == null || getClass() != o.getClass()) {
1280          return false;
1281        } else {
1282          return subject == ((UserGroupInformation) o).subject;
1283        }
1284      }
1285    
1286      /**
1287       * Return the hash of the subject.
1288       */
1289      @Override
1290      public int hashCode() {
1291        return System.identityHashCode(subject);
1292      }
1293    
1294      /**
1295       * Get the underlying subject from this ugi.
1296       * @return the subject that represents this user.
1297       */
1298      protected Subject getSubject() {
1299        return subject;
1300      }
1301    
1302      /**
1303       * Run the given action as the user.
1304       * @param <T> the return type of the run method
1305       * @param action the method to execute
1306       * @return the value from the run method
1307       */
1308      @InterfaceAudience.Public
1309      @InterfaceStability.Evolving
1310      public <T> T doAs(PrivilegedAction<T> action) {
1311        logPrivilegedAction(subject, action);
1312        return Subject.doAs(subject, action);
1313      }
1314      
1315      /**
1316       * Run the given action as the user, potentially throwing an exception.
1317       * @param <T> the return type of the run method
1318       * @param action the method to execute
1319       * @return the value from the run method
1320       * @throws IOException if the action throws an IOException
1321       * @throws Error if the action throws an Error
1322       * @throws RuntimeException if the action throws a RuntimeException
1323       * @throws InterruptedException if the action throws an InterruptedException
1324       * @throws UndeclaredThrowableException if the action throws something else
1325       */
1326      @InterfaceAudience.Public
1327      @InterfaceStability.Evolving
1328      public <T> T doAs(PrivilegedExceptionAction<T> action
1329                        ) throws IOException, InterruptedException {
1330        try {
1331          logPrivilegedAction(subject, action);
1332          return Subject.doAs(subject, action);
1333        } catch (PrivilegedActionException pae) {
1334          Throwable cause = pae.getCause();
1335          LOG.error("PriviledgedActionException as:"+this+" cause:"+cause);
1336          if (cause instanceof IOException) {
1337            throw (IOException) cause;
1338          } else if (cause instanceof Error) {
1339            throw (Error) cause;
1340          } else if (cause instanceof RuntimeException) {
1341            throw (RuntimeException) cause;
1342          } else if (cause instanceof InterruptedException) {
1343            throw (InterruptedException) cause;
1344          } else {
1345            throw new UndeclaredThrowableException(pae,"Unknown exception in doAs");
1346          }
1347        }
1348      }
1349    
1350      private void logPrivilegedAction(Subject subject, Object action) {
1351        if (LOG.isDebugEnabled()) {
1352          // would be nice if action included a descriptive toString()
1353          String where = new Throwable().getStackTrace()[2].toString();
1354          LOG.debug("PrivilegedAction as:"+this+" from:"+where);
1355        }
1356      }
1357    
1358      private void print() throws IOException {
1359        System.out.println("User: " + getUserName());
1360        System.out.print("Group Ids: ");
1361        System.out.println();
1362        String[] groups = getGroupNames();
1363        System.out.print("Groups: ");
1364        for(int i=0; i < groups.length; i++) {
1365          System.out.print(groups[i] + " ");
1366        }
1367        System.out.println();    
1368      }
1369    
1370      /**
1371       * A test method to print out the current user's UGI.
1372       * @param args if there are two arguments, read the user from the keytab
1373       * and print it out.
1374       * @throws Exception
1375       */
1376      public static void main(String [] args) throws Exception {
1377      System.out.println("Getting UGI for current user");
1378        UserGroupInformation ugi = getCurrentUser();
1379        ugi.print();
1380        System.out.println("UGI: " + ugi);
1381        System.out.println("Auth method " + ugi.user.getAuthenticationMethod());
1382        System.out.println("Keytab " + ugi.isKeytab);
1383        System.out.println("============================================================");
1384        
1385        if (args.length == 2) {
1386          System.out.println("Getting UGI from keytab....");
1387          loginUserFromKeytab(args[0], args[1]);
1388          getCurrentUser().print();
1389          System.out.println("Keytab: " + ugi);
1390          System.out.println("Auth method " + loginUser.user.getAuthenticationMethod());
1391          System.out.println("Keytab " + loginUser.isKeytab);
1392        }
1393      }
1394    
1395    }