001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with this
004 * work for additional information regarding copyright ownership. The ASF
005 * licenses this file to you under the Apache License, Version 2.0 (the
006 * "License"); you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 * 
009 * http://www.apache.org/licenses/LICENSE-2.0
010 * 
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014 * License for the specific language governing permissions and limitations under
015 * the License.
016 */
017package org.apache.hadoop.security;
018
019import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION;
020import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_DNS_INTERFACE_KEY;
021import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_DNS_NAMESERVER_KEY;
022
023import java.io.IOException;
024import java.net.InetAddress;
025import java.net.InetSocketAddress;
026import java.net.URI;
027import java.net.UnknownHostException;
028import java.security.PrivilegedAction;
029import java.security.PrivilegedExceptionAction;
030import java.util.Arrays;
031import java.util.List;
032import java.util.ServiceLoader;
033import java.util.concurrent.TimeUnit;
034
035import javax.annotation.Nullable;
036import javax.security.auth.kerberos.KerberosPrincipal;
037import javax.security.auth.kerberos.KerberosTicket;
038
039import org.apache.commons.logging.Log;
040import org.apache.commons.logging.LogFactory;
041import org.apache.hadoop.classification.InterfaceAudience;
042import org.apache.hadoop.classification.InterfaceStability;
043import org.apache.hadoop.conf.Configuration;
044import org.apache.hadoop.fs.CommonConfigurationKeys;
045import org.apache.hadoop.io.Text;
046import org.apache.hadoop.net.DNS;
047import org.apache.hadoop.net.NetUtils;
048import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
049import org.apache.hadoop.security.token.Token;
050import org.apache.hadoop.security.token.TokenInfo;
051import org.apache.hadoop.util.StopWatch;
052import org.apache.hadoop.util.StringUtils;
053
054
055//this will need to be replaced someday when there is a suitable replacement
056import sun.net.dns.ResolverConfiguration;
057import sun.net.util.IPAddressUtil;
058
059import com.google.common.annotations.VisibleForTesting;
060
061/**
062 * Security Utils.
063 */
064@InterfaceAudience.Public
065@InterfaceStability.Evolving
066public final class SecurityUtil {
067  public static final Log LOG = LogFactory.getLog(SecurityUtil.class);
068  public static final String HOSTNAME_PATTERN = "_HOST";
069  public static final String FAILED_TO_GET_UGI_MSG_HEADER = 
070      "Failed to obtain user group information:";
071
072  private SecurityUtil() {
073  }
074
075  // controls whether buildTokenService will use an ip or host/ip as given
076  // by the user
077  @VisibleForTesting
078  static boolean useIpForTokenService;
079  @VisibleForTesting
080  static HostResolver hostResolver;
081
082  static {
083    Configuration conf = new Configuration();
084    boolean useIp = conf.getBoolean(
085        CommonConfigurationKeys.HADOOP_SECURITY_TOKEN_SERVICE_USE_IP,
086        CommonConfigurationKeys.HADOOP_SECURITY_TOKEN_SERVICE_USE_IP_DEFAULT);
087    setTokenServiceUseIp(useIp);
088  }
089
090  private static boolean logSlowLookups = getLogSlowLookupsEnabled();
091  private static int slowLookupThresholdMs = getSlowLookupThresholdMs();
092
093  /**
094   * For use only by tests and initialization
095   */
096  @InterfaceAudience.Private
097  @VisibleForTesting
098  public static void setTokenServiceUseIp(boolean flag) {
099    useIpForTokenService = flag;
100    hostResolver = !useIpForTokenService
101        ? new QualifiedHostResolver()
102        : new StandardHostResolver();
103  }
104  
105  /**
106   * TGS must have the server principal of the form "krbtgt/FOO@FOO".
107   * @param principal
108   * @return true or false
109   */
110  static boolean 
111  isTGSPrincipal(KerberosPrincipal principal) {
112    if (principal == null)
113      return false;
114    if (principal.getName().equals("krbtgt/" + principal.getRealm() + 
115        "@" + principal.getRealm())) {
116      return true;
117    }
118    return false;
119  }
120  
121  /**
122   * Check whether the server principal is the TGS's principal
123   * @param ticket the original TGT (the ticket that is obtained when a 
124   * kinit is done)
125   * @return true or false
126   */
127  protected static boolean isOriginalTGT(KerberosTicket ticket) {
128    return isTGSPrincipal(ticket.getServer());
129  }
130
131  /**
132   * Convert Kerberos principal name pattern to valid Kerberos principal
133   * names. It replaces hostname pattern with hostname, which should be
134   * fully-qualified domain name. If hostname is null or "0.0.0.0", it uses
135   * dynamically looked-up fqdn of the current host instead.
136   * 
137   * @param principalConfig
138   *          the Kerberos principal name conf value to convert
139   * @param hostname
140   *          the fully-qualified domain name used for substitution
141   * @return converted Kerberos principal name
142   * @throws IOException if the client address cannot be determined
143   */
144  @InterfaceAudience.Public
145  @InterfaceStability.Evolving
146  public static String getServerPrincipal(String principalConfig,
147      String hostname) throws IOException {
148    String[] components = getComponents(principalConfig);
149    if (components == null || components.length != 3
150        || !components[1].equals(HOSTNAME_PATTERN)) {
151      return principalConfig;
152    } else {
153      return replacePattern(components, hostname);
154    }
155  }
156  
157  /**
158   * Convert Kerberos principal name pattern to valid Kerberos principal names.
159   * This method is similar to {@link #getServerPrincipal(String, String)},
160   * except 1) the reverse DNS lookup from addr to hostname is done only when
161   * necessary, 2) param addr can't be null (no default behavior of using local
162   * hostname when addr is null).
163   * 
164   * @param principalConfig
165   *          Kerberos principal name pattern to convert
166   * @param addr
167   *          InetAddress of the host used for substitution
168   * @return converted Kerberos principal name
169   * @throws IOException if the client address cannot be determined
170   */
171  @InterfaceAudience.Public
172  @InterfaceStability.Evolving
173  public static String getServerPrincipal(String principalConfig,
174      InetAddress addr) throws IOException {
175    String[] components = getComponents(principalConfig);
176    if (components == null || components.length != 3
177        || !components[1].equals(HOSTNAME_PATTERN)) {
178      return principalConfig;
179    } else {
180      if (addr == null) {
181        throw new IOException("Can't replace " + HOSTNAME_PATTERN
182            + " pattern since client address is null");
183      }
184      return replacePattern(components, addr.getCanonicalHostName());
185    }
186  }
187  
188  private static String[] getComponents(String principalConfig) {
189    if (principalConfig == null)
190      return null;
191    return principalConfig.split("[/@]");
192  }
193  
194  private static String replacePattern(String[] components, String hostname)
195      throws IOException {
196    String fqdn = hostname;
197    if (fqdn == null || fqdn.isEmpty() || fqdn.equals("0.0.0.0")) {
198      fqdn = getLocalHostName(null);
199    }
200    return components[0] + "/" +
201        StringUtils.toLowerCase(fqdn) + "@" + components[2];
202  }
203
204  /**
205   * Retrieve the name of the current host. Multihomed hosts may restrict the
206   * hostname lookup to a specific interface and nameserver with {@link
207   * org.apache.hadoop.fs.CommonConfigurationKeysPublic#HADOOP_SECURITY_DNS_INTERFACE_KEY}
208   * and {@link org.apache.hadoop.fs.CommonConfigurationKeysPublic#HADOOP_SECURITY_DNS_NAMESERVER_KEY}
209   *
210   * @param conf Configuration object. May be null.
211   * @return
212   * @throws UnknownHostException
213   */
214  static String getLocalHostName(@Nullable Configuration conf)
215      throws UnknownHostException {
216    if (conf != null) {
217      String dnsInterface = conf.get(HADOOP_SECURITY_DNS_INTERFACE_KEY);
218      String nameServer = conf.get(HADOOP_SECURITY_DNS_NAMESERVER_KEY);
219
220      if (dnsInterface != null) {
221        return DNS.getDefaultHost(dnsInterface, nameServer, true);
222      } else if (nameServer != null) {
223        throw new IllegalArgumentException(HADOOP_SECURITY_DNS_NAMESERVER_KEY +
224            " requires " + HADOOP_SECURITY_DNS_INTERFACE_KEY + ". Check your" +
225            "configuration.");
226      }
227    }
228
229    // Fallback to querying the default hostname as we did before.
230    return InetAddress.getLocalHost().getCanonicalHostName();
231  }
232
233  /**
234   * Login as a principal specified in config. Substitute $host in
235   * user's Kerberos principal name with a dynamically looked-up fully-qualified
236   * domain name of the current host.
237   * 
238   * @param conf
239   *          conf to use
240   * @param keytabFileKey
241   *          the key to look for keytab file in conf
242   * @param userNameKey
243   *          the key to look for user's Kerberos principal name in conf
244   * @throws IOException if login fails
245   */
246  @InterfaceAudience.Public
247  @InterfaceStability.Evolving
248  public static void login(final Configuration conf,
249      final String keytabFileKey, final String userNameKey) throws IOException {
250    login(conf, keytabFileKey, userNameKey, getLocalHostName(conf));
251  }
252
253  /**
254   * Login as a principal specified in config. Substitute $host in user's Kerberos principal 
255   * name with hostname. If non-secure mode - return. If no keytab available -
256   * bail out with an exception
257   * 
258   * @param conf
259   *          conf to use
260   * @param keytabFileKey
261   *          the key to look for keytab file in conf
262   * @param userNameKey
263   *          the key to look for user's Kerberos principal name in conf
264   * @param hostname
265   *          hostname to use for substitution
266   * @throws IOException if the config doesn't specify a keytab
267   */
268  @InterfaceAudience.Public
269  @InterfaceStability.Evolving
270  public static void login(final Configuration conf,
271      final String keytabFileKey, final String userNameKey, String hostname)
272      throws IOException {
273    
274    if(! UserGroupInformation.isSecurityEnabled()) 
275      return;
276    
277    String keytabFilename = conf.get(keytabFileKey);
278    if (keytabFilename == null || keytabFilename.length() == 0) {
279      throw new IOException("Running in secure mode, but config doesn't have a keytab");
280    }
281
282    String principalConfig = conf.get(userNameKey, System
283        .getProperty("user.name"));
284    String principalName = SecurityUtil.getServerPrincipal(principalConfig,
285        hostname);
286    UserGroupInformation.loginUserFromKeytab(principalName, keytabFilename);
287  }
288
289  /**
290   * create the service name for a Delegation token
291   * @param uri of the service
292   * @param defPort is used if the uri lacks a port
293   * @return the token service, or null if no authority
294   * @see #buildTokenService(InetSocketAddress)
295   */
296  public static String buildDTServiceName(URI uri, int defPort) {
297    String authority = uri.getAuthority();
298    if (authority == null) {
299      return null;
300    }
301    InetSocketAddress addr = NetUtils.createSocketAddr(authority, defPort);
302    return buildTokenService(addr).toString();
303   }
304  
305  /**
306   * Get the host name from the principal name of format <service>/host@realm.
307   * @param principalName principal name of format as described above
308   * @return host name if the the string conforms to the above format, else null
309   */
310  public static String getHostFromPrincipal(String principalName) {
311    return new HadoopKerberosName(principalName).getHostName();
312  }
313
314  private static ServiceLoader<SecurityInfo> securityInfoProviders = 
315    ServiceLoader.load(SecurityInfo.class);
316  private static SecurityInfo[] testProviders = new SecurityInfo[0];
317
318  /**
319   * Test setup method to register additional providers.
320   * @param providers a list of high priority providers to use
321   */
322  @InterfaceAudience.Private
323  public static void setSecurityInfoProviders(SecurityInfo... providers) {
324    testProviders = providers;
325  }
326  
327  /**
328   * Look up the KerberosInfo for a given protocol. It searches all known
329   * SecurityInfo providers.
330   * @param protocol the protocol class to get the information for
331   * @param conf configuration object
332   * @return the KerberosInfo or null if it has no KerberosInfo defined
333   */
334  public static KerberosInfo 
335  getKerberosInfo(Class<?> protocol, Configuration conf) {
336    for(SecurityInfo provider: testProviders) {
337      KerberosInfo result = provider.getKerberosInfo(protocol, conf);
338      if (result != null) {
339        return result;
340      }
341    }
342    
343    synchronized (securityInfoProviders) {
344      for(SecurityInfo provider: securityInfoProviders) {
345        KerberosInfo result = provider.getKerberosInfo(protocol, conf);
346        if (result != null) {
347          return result;
348        }
349      }
350    }
351    return null;
352  }
353 
354  /**
355   * Look up the TokenInfo for a given protocol. It searches all known
356   * SecurityInfo providers.
357   * @param protocol The protocol class to get the information for.
358   * @param conf Configuration object
359   * @return the TokenInfo or null if it has no KerberosInfo defined
360   */
361  public static TokenInfo getTokenInfo(Class<?> protocol, Configuration conf) {
362    for(SecurityInfo provider: testProviders) {
363      TokenInfo result = provider.getTokenInfo(protocol, conf);
364      if (result != null) {
365        return result;
366      }      
367    }
368    
369    synchronized (securityInfoProviders) {
370      for(SecurityInfo provider: securityInfoProviders) {
371        TokenInfo result = provider.getTokenInfo(protocol, conf);
372        if (result != null) {
373          return result;
374        }
375      } 
376    }
377    
378    return null;
379  }
380
381  /**
382   * Decode the given token's service field into an InetAddress
383   * @param token from which to obtain the service
384   * @return InetAddress for the service
385   */
386  public static InetSocketAddress getTokenServiceAddr(Token<?> token) {
387    return NetUtils.createSocketAddr(token.getService().toString());
388  }
389
390  /**
391   * Set the given token's service to the format expected by the RPC client 
392   * @param token a delegation token
393   * @param addr the socket for the rpc connection
394   */
395  public static void setTokenService(Token<?> token, InetSocketAddress addr) {
396    Text service = buildTokenService(addr);
397    if (token != null) {
398      token.setService(service);
399      if (LOG.isDebugEnabled()) {
400        LOG.debug("Acquired token "+token);  // Token#toString() prints service
401      }
402    } else {
403      LOG.warn("Failed to get token for service "+service);
404    }
405  }
406  
407  /**
408   * Construct the service key for a token
409   * @param addr InetSocketAddress of remote connection with a token
410   * @return "ip:port" or "host:port" depending on the value of
411   *          hadoop.security.token.service.use_ip
412   */
413  public static Text buildTokenService(InetSocketAddress addr) {
414    String host = null;
415    if (useIpForTokenService) {
416      if (addr.isUnresolved()) { // host has no ip address
417        throw new IllegalArgumentException(
418            new UnknownHostException(addr.getHostName())
419        );
420      }
421      host = addr.getAddress().getHostAddress();
422    } else {
423      host = StringUtils.toLowerCase(addr.getHostName());
424    }
425    return new Text(host + ":" + addr.getPort());
426  }
427
428  /**
429   * Construct the service key for a token
430   * @param uri of remote connection with a token
431   * @return "ip:port" or "host:port" depending on the value of
432   *          hadoop.security.token.service.use_ip
433   */
434  public static Text buildTokenService(URI uri) {
435    return buildTokenService(NetUtils.createSocketAddr(uri.getAuthority()));
436  }
437  
438  /**
439   * Perform the given action as the daemon's login user. If the login
440   * user cannot be determined, this will log a FATAL error and exit
441   * the whole JVM.
442   */
443  public static <T> T doAsLoginUserOrFatal(PrivilegedAction<T> action) { 
444    if (UserGroupInformation.isSecurityEnabled()) {
445      UserGroupInformation ugi = null;
446      try { 
447        ugi = UserGroupInformation.getLoginUser();
448      } catch (IOException e) {
449        LOG.fatal("Exception while getting login user", e);
450        e.printStackTrace();
451        Runtime.getRuntime().exit(-1);
452      }
453      return ugi.doAs(action);
454    } else {
455      return action.run();
456    }
457  }
458  
459  /**
460   * Perform the given action as the daemon's login user. If an
461   * InterruptedException is thrown, it is converted to an IOException.
462   *
463   * @param action the action to perform
464   * @return the result of the action
465   * @throws IOException in the event of error
466   */
467  public static <T> T doAsLoginUser(PrivilegedExceptionAction<T> action)
468      throws IOException {
469    return doAsUser(UserGroupInformation.getLoginUser(), action);
470  }
471
472  /**
473   * Perform the given action as the daemon's current user. If an
474   * InterruptedException is thrown, it is converted to an IOException.
475   *
476   * @param action the action to perform
477   * @return the result of the action
478   * @throws IOException in the event of error
479   */
480  public static <T> T doAsCurrentUser(PrivilegedExceptionAction<T> action)
481      throws IOException {
482    return doAsUser(UserGroupInformation.getCurrentUser(), action);
483  }
484
485  private static <T> T doAsUser(UserGroupInformation ugi,
486      PrivilegedExceptionAction<T> action) throws IOException {
487    try {
488      return ugi.doAs(action);
489    } catch (InterruptedException ie) {
490      throw new IOException(ie);
491    }
492  }
493
494  private static boolean getLogSlowLookupsEnabled() {
495    Configuration conf = new Configuration();
496
497    return conf.getBoolean(CommonConfigurationKeys
498            .HADOOP_SECURITY_DNS_LOG_SLOW_LOOKUPS_ENABLED_KEY,
499        CommonConfigurationKeys
500            .HADOOP_SECURITY_DNS_LOG_SLOW_LOOKUPS_ENABLED_DEFAULT);
501  }
502
503  private static int getSlowLookupThresholdMs() {
504    Configuration conf = new Configuration();
505
506    return conf.getInt(CommonConfigurationKeys
507            .HADOOP_SECURITY_DNS_LOG_SLOW_LOOKUPS_THRESHOLD_MS_KEY,
508        CommonConfigurationKeys
509            .HADOOP_SECURITY_DNS_LOG_SLOW_LOOKUPS_THRESHOLD_MS_DEFAULT);
510  }
511
512  /**
513   * Resolves a host subject to the security requirements determined by
514   * hadoop.security.token.service.use_ip. Optionally logs slow resolutions.
515   * 
516   * @param hostname host or ip to resolve
517   * @return a resolved host
518   * @throws UnknownHostException if the host doesn't exist
519   */
520  @InterfaceAudience.Private
521  public static
522  InetAddress getByName(String hostname) throws UnknownHostException {
523    if (logSlowLookups || LOG.isTraceEnabled()) {
524      StopWatch lookupTimer = new StopWatch().start();
525      InetAddress result = hostResolver.getByName(hostname);
526      long elapsedMs = lookupTimer.stop().now(TimeUnit.MILLISECONDS);
527
528      if (elapsedMs >= slowLookupThresholdMs) {
529        LOG.warn("Slow name lookup for " + hostname + ". Took " + elapsedMs +
530            " ms.");
531      } else if (LOG.isTraceEnabled()) {
532        LOG.trace("Name lookup for " + hostname + " took " + elapsedMs +
533            " ms.");
534      }
535      return result;
536    } else {
537      return hostResolver.getByName(hostname);
538    }
539  }
540  
541  interface HostResolver {
542    InetAddress getByName(String host) throws UnknownHostException;    
543  }
544  
545  /**
546   * Uses standard java host resolution
547   */
548  static class StandardHostResolver implements HostResolver {
549    @Override
550    public InetAddress getByName(String host) throws UnknownHostException {
551      return InetAddress.getByName(host);
552    }
553  }
554  
555  /**
556   * This an alternate resolver with important properties that the standard
557   * java resolver lacks:
558   * 1) The hostname is fully qualified.  This avoids security issues if not
559   *    all hosts in the cluster do not share the same search domains.  It
560   *    also prevents other hosts from performing unnecessary dns searches.
561   *    In contrast, InetAddress simply returns the host as given.
562   * 2) The InetAddress is instantiated with an exact host and IP to prevent
563   *    further unnecessary lookups.  InetAddress may perform an unnecessary
564   *    reverse lookup for an IP.
565   * 3) A call to getHostName() will always return the qualified hostname, or
566   *    more importantly, the IP if instantiated with an IP.  This avoids
567   *    unnecessary dns timeouts if the host is not resolvable.
568   * 4) Point 3 also ensures that if the host is re-resolved, ex. during a
569   *    connection re-attempt, that a reverse lookup to host and forward
570   *    lookup to IP is not performed since the reverse/forward mappings may
571   *    not always return the same IP.  If the client initiated a connection
572   *    with an IP, then that IP is all that should ever be contacted.
573   *    
574   * NOTE: this resolver is only used if:
575   *       hadoop.security.token.service.use_ip=false 
576   */
577  protected static class QualifiedHostResolver implements HostResolver {
578    @SuppressWarnings("unchecked")
579    private List<String> searchDomains =
580        ResolverConfiguration.open().searchlist();
581    
582    /**
583     * Create an InetAddress with a fully qualified hostname of the given
584     * hostname.  InetAddress does not qualify an incomplete hostname that
585     * is resolved via the domain search list.
586     * {@link InetAddress#getCanonicalHostName()} will fully qualify the
587     * hostname, but it always return the A record whereas the given hostname
588     * may be a CNAME.
589     * 
590     * @param host a hostname or ip address
591     * @return InetAddress with the fully qualified hostname or ip
592     * @throws UnknownHostException if host does not exist
593     */
594    @Override
595    public InetAddress getByName(String host) throws UnknownHostException {
596      InetAddress addr = null;
597
598      if (IPAddressUtil.isIPv4LiteralAddress(host)) {
599        // use ipv4 address as-is
600        byte[] ip = IPAddressUtil.textToNumericFormatV4(host);
601        addr = InetAddress.getByAddress(host, ip);
602      } else if (IPAddressUtil.isIPv6LiteralAddress(host)) {
603        // use ipv6 address as-is
604        byte[] ip = IPAddressUtil.textToNumericFormatV6(host);
605        addr = InetAddress.getByAddress(host, ip);
606      } else if (host.endsWith(".")) {
607        // a rooted host ends with a dot, ex. "host."
608        // rooted hosts never use the search path, so only try an exact lookup
609        addr = getByExactName(host);
610      } else if (host.contains(".")) {
611        // the host contains a dot (domain), ex. "host.domain"
612        // try an exact host lookup, then fallback to search list
613        addr = getByExactName(host);
614        if (addr == null) {
615          addr = getByNameWithSearch(host);
616        }
617      } else {
618        // it's a simple host with no dots, ex. "host"
619        // try the search list, then fallback to exact host
620        InetAddress loopback = InetAddress.getByName(null);
621        if (host.equalsIgnoreCase(loopback.getHostName())) {
622          addr = InetAddress.getByAddress(host, loopback.getAddress());
623        } else {
624          addr = getByNameWithSearch(host);
625          if (addr == null) {
626            addr = getByExactName(host);
627          }
628        }
629      }
630      // unresolvable!
631      if (addr == null) {
632        throw new UnknownHostException(host);
633      }
634      return addr;
635    }
636
637    InetAddress getByExactName(String host) {
638      InetAddress addr = null;
639      // InetAddress will use the search list unless the host is rooted
640      // with a trailing dot.  The trailing dot will disable any use of the
641      // search path in a lower level resolver.  See RFC 1535.
642      String fqHost = host;
643      if (!fqHost.endsWith(".")) fqHost += ".";
644      try {
645        addr = getInetAddressByName(fqHost);
646        // can't leave the hostname as rooted or other parts of the system
647        // malfunction, ex. kerberos principals are lacking proper host
648        // equivalence for rooted/non-rooted hostnames
649        addr = InetAddress.getByAddress(host, addr.getAddress());
650      } catch (UnknownHostException e) {
651        // ignore, caller will throw if necessary
652      }
653      return addr;
654    }
655
656    InetAddress getByNameWithSearch(String host) {
657      InetAddress addr = null;
658      if (host.endsWith(".")) { // already qualified?
659        addr = getByExactName(host); 
660      } else {
661        for (String domain : searchDomains) {
662          String dot = !domain.startsWith(".") ? "." : "";
663          addr = getByExactName(host + dot + domain);
664          if (addr != null) break;
665        }
666      }
667      return addr;
668    }
669
670    // implemented as a separate method to facilitate unit testing
671    InetAddress getInetAddressByName(String host) throws UnknownHostException {
672      return InetAddress.getByName(host);
673    }
674
675    void setSearchDomains(String ... domains) {
676      searchDomains = Arrays.asList(domains);
677    }
678  }
679
680  public static AuthenticationMethod getAuthenticationMethod(Configuration conf) {
681    String value = conf.get(HADOOP_SECURITY_AUTHENTICATION, "simple");
682    try {
683      return Enum.valueOf(AuthenticationMethod.class,
684          StringUtils.toUpperCase(value));
685    } catch (IllegalArgumentException iae) {
686      throw new IllegalArgumentException("Invalid attribute value for " +
687          HADOOP_SECURITY_AUTHENTICATION + " of " + value);
688    }
689  }
690
691  public static void setAuthenticationMethod(
692      AuthenticationMethod authenticationMethod, Configuration conf) {
693    if (authenticationMethod == null) {
694      authenticationMethod = AuthenticationMethod.SIMPLE;
695    }
696    conf.set(HADOOP_SECURITY_AUTHENTICATION,
697        StringUtils.toLowerCase(authenticationMethod.toString()));
698  }
699
700  /*
701   * Check if a given port is privileged.
702   * The ports with number smaller than 1024 are treated as privileged ports in
703   * unix/linux system. For other operating systems, use this method with care.
704   * For example, Windows doesn't have the concept of privileged ports.
705   * However, it may be used at Windows client to check port of linux server.
706   * 
707   * @param port the port number
708   * @return true for privileged ports, false otherwise
709   * 
710   */
711  public static boolean isPrivilegedPort(final int port) {
712    return port < 1024;
713  }
714}