Пример #1
0
  /**
   * @param props The {@link Properties} holding the server's configuration - ignored if {@code
   *     null}/empty
   * @param options The {@link LinkOption}s to use when checking files existence
   * @return A {@link Map} of the found identities where key=the identity type (case
   *     <U>insensitive</U>) and value=the {@link Path} of the file holding the specific type key
   * @throws IOException If failed to access the file system
   * @see #getIdentityType(String)
   * @see #HOST_KEY_CONFIG_PROP
   * @see org.apache.sshd.common.config.SshConfigFileReader#readConfigFile(File)
   */
  public static Map<String, Path> findIdentities(Properties props, LinkOption... options)
      throws IOException {
    if (GenericUtils.isEmpty(props)) {
      return Collections.emptyMap();
    }

    String keyList = props.getProperty(HOST_KEY_CONFIG_PROP);
    String[] paths = GenericUtils.split(keyList, ',');
    if (GenericUtils.isEmpty(paths)) {
      return Collections.emptyMap();
    }

    Map<String, Path> ids = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
    for (String p : paths) {
      File file = new File(p);
      Path path = file.toPath();
      if (!Files.exists(path, options)) {
        continue;
      }

      String type = getIdentityType(path.getFileName().toString());
      if (GenericUtils.isEmpty(type)) {
        type = p; // just in case the file name does not adhere to the standard naming convention
      }
      Path prev = ids.put(type, path);
      ValidateUtils.checkTrue(prev == null, "Multiple mappings for type=%s", type);
    }

    return ids;
  }
Пример #2
0
  protected Collection<Option> addTargetIsDirectory(Collection<Option> options) {
    if (GenericUtils.isEmpty(options) || (!options.contains(Option.TargetIsDirectory))) {
      // create a copy in case the original collection is un-modifiable
      options =
          GenericUtils.isEmpty(options) ? EnumSet.noneOf(Option.class) : GenericUtils.of(options);
      options.add(Option.TargetIsDirectory);
    }

    return options;
  }
Пример #3
0
  public Collection<KeyPair> loadKeyPairs(
      String resourceKey,
      String pubData,
      String prvData,
      String prvEncryption,
      FilePasswordProvider passwordProvider)
      throws IOException, GeneralSecurityException {
    Decoder b64Decoder = Base64.getDecoder();
    byte[] pubBytes = b64Decoder.decode(pubData);
    byte[] prvBytes = b64Decoder.decode(prvData);
    String password = null;
    if ((GenericUtils.length(prvEncryption) > 0)
        && (!NO_PRIVATE_KEY_ENCRYPTION_VALUE.equalsIgnoreCase(prvEncryption))) {
      password = passwordProvider.getPassword(resourceKey);
    }

    if (GenericUtils.isEmpty(prvEncryption)
        || NO_PRIVATE_KEY_ENCRYPTION_VALUE.equalsIgnoreCase(prvEncryption)
        || GenericUtils.isEmpty(password)) {
      return loadKeyPairs(resourceKey, pubBytes, prvBytes);
    }

    // format is "<cipher><bits>-<mode>" - e.g., "aes256-cbc"
    int pos = prvEncryption.indexOf('-');
    if (pos <= 0) {
      throw new StreamCorruptedException("Missing private key encryption mode in " + prvEncryption);
    }

    String mode = prvEncryption.substring(pos + 1).toUpperCase();
    String algName = null;
    int numBits = 0;
    for (int index = 0; index < pos; index++) {
      char ch = prvEncryption.charAt(index);
      if ((ch >= '0') && (ch <= '9')) {
        algName = prvEncryption.substring(0, index).toUpperCase();
        numBits = Integer.parseInt(prvEncryption.substring(index, pos));
        break;
      }
    }

    if (GenericUtils.isEmpty(algName) || (numBits <= 0)) {
      throw new StreamCorruptedException(
          "Missing private key encryption algorithm details in " + prvEncryption);
    }

    prvBytes =
        PuttyKeyPairResourceParser.decodePrivateKeyBytes(
            prvBytes, algName, numBits, mode, password);
    return loadKeyPairs(resourceKey, pubBytes, prvBytes);
  }
Пример #4
0
 @Override
 public void upload(Path[] local, String remote, Option... options) throws IOException {
   upload(
       local,
       remote,
       GenericUtils.isEmpty(options) ? Collections.<Option>emptySet() : GenericUtils.of(options));
 }
Пример #5
0
 @Override
 public void upload(Path local, String remote, Collection<Option> options) throws IOException {
   upload(
       new Path[] {ValidateUtils.checkNotNull(local, "Invalid local argument: %s", local)},
       remote,
       GenericUtils.isEmpty(options) ? Collections.<Option>emptySet() : GenericUtils.of(options));
 }
Пример #6
0
  /**
   * @param <E> The generic entry type
   * @param entry The {@link PublicKeyEntry} whose contents are to be updated - ignored if {@code
   *     null}
   * @param data Assumed to contain at least {@code key-type base64-data} (anything beyond the
   *     BASE64 data is ignored) - ignored if {@code null}/empty
   * @return The updated entry instance
   * @throws IllegalArgumentException if bad format found
   */
  public static final <E extends PublicKeyEntry> E parsePublicKeyEntry(E entry, String data)
      throws IllegalArgumentException {
    if (GenericUtils.isEmpty(data) || (entry == null)) {
      return entry;
    }

    int startPos = data.indexOf(' ');
    if (startPos <= 0) {
      throw new IllegalArgumentException("Bad format (no key data delimiter): " + data);
    }

    int endPos = data.indexOf(' ', startPos + 1);
    if (endPos <= startPos) { // OK if no continuation beyond the BASE64 encoded data
      endPos = data.length();
    }

    String keyType = data.substring(0, startPos);
    String b64Data = data.substring(startPos + 1, endPos).trim();
    byte[] keyData = Base64.decodeString(b64Data);
    if (NumberUtils.isEmpty(keyData)) {
      throw new IllegalArgumentException("Bad format (no BASE64 key data): " + data);
    }

    entry.setKeyType(keyType);
    entry.setKeyData(keyData);
    return entry;
  }
Пример #7
0
 @Override
 public void download(String[] remote, Path local, Option... options) throws IOException {
   download(
       remote,
       local,
       GenericUtils.isEmpty(options) ? Collections.<Option>emptySet() : GenericUtils.of(options));
 }
Пример #8
0
  public SshdSocketAddress(String hostName, int port) {
    ValidateUtils.checkNotNull(hostName, "Host name may not be null");
    this.hostName = GenericUtils.isEmpty(hostName) ? "0.0.0.0" : hostName;

    ValidateUtils.checkTrue(port >= 0, "Port must be >= 0", Integer.valueOf(port));
    this.port = port;
  }
Пример #9
0
  @Override
  public T getPath(String first, String... more) {
    StringBuilder sb = new StringBuilder();
    if (!GenericUtils.isEmpty(first)) {
      appendDedupSep(sb, first.replace('\\', '/')); // in case we are running on Windows
    }

    if (GenericUtils.length(more) > 0) {
      for (String segment : more) {
        if ((sb.length() > 0) && (sb.charAt(sb.length() - 1) != '/')) {
          sb.append('/');
        }
        // in case we are running on Windows
        appendDedupSep(sb, segment.replace('\\', '/'));
      }
    }

    if ((sb.length() > 1) && (sb.charAt(sb.length() - 1) == '/')) {
      sb.setLength(sb.length() - 1);
    }

    String path = sb.toString();
    String root = null;
    if (path.startsWith("/")) {
      root = "/";
      path = path.substring(1);
    }

    String[] names = GenericUtils.split(path, '/');
    return create(root, names);
  }
Пример #10
0
    /**
     * @param <R> The generic resource type
     * @param name Name of the resource - ignored if {@code null}/empty
     * @param c The {@link Comparator} to decide whether the {@link NamedResource#getName()} matches
     *     the <tt>name</tt> parameter
     * @param resources The {@link NamedResource} to check - ignored if {@code null}/empty
     * @return The <U>first</U> resource whose name matches the parameter (by invoking {@link
     *     Comparator#compare(Object, Object)} - {@code null} if no match found
     */
    public static <R extends NamedResource> R findByName(
        String name, Comparator<? super String> c, Collection<? extends R> resources) {
      if (GenericUtils.isEmpty(name) || GenericUtils.isEmpty(resources)) {
        return null;
      }

      for (R r : resources) {
        String n = r.getName();
        int nRes = c.compare(name, n);
        if (nRes == 0) {
          return r;
        }
      }

      return null;
    }
Пример #11
0
 /**
  * @param data Assumed to contain at least {@code key-type base64-data} (anything beyond the
  *     BASE64 data is ignored) - ignored if {@code null}/empty
  * @return A {@link PublicKeyEntry} or {@code null} if no data
  * @throws IllegalArgumentException if bad format found
  * @see #parsePublicKeyEntry(PublicKeyEntry, String)
  */
 public static final PublicKeyEntry parsePublicKeyEntry(String data)
     throws IllegalArgumentException {
   if (GenericUtils.isEmpty(data)) {
     return null;
   } else {
     return parsePublicKeyEntry(new PublicKeyEntry(), data);
   }
 }
Пример #12
0
  /**
   * Unregisters specified extension
   *
   * @param name The factory name - ignored if {@code null}/empty
   * @return The registered extension - {@code null} if not found
   */
  public static SignatureFactory unregisterExtension(String name) {
    if (GenericUtils.isEmpty(name)) {
      return null;
    }

    synchronized (EXTENSIONS) {
      return EXTENSIONS.remove(name);
    }
  }
Пример #13
0
 /**
  * @param name The file name - ignored if {@code null}/empty
  * @return The identity type - {@code null} if cannot determine it - e.g., does not start/end with
  *     the {@link #ID_FILE_PREFIX}/{@link #ID_FILE_SUFFIX}
  */
 public static String getIdentityType(String name) {
   if (GenericUtils.isEmpty(name)
       || (name.length() <= (ID_FILE_PREFIX.length() + ID_FILE_SUFFIX.length()))
       || (!name.startsWith(ID_FILE_PREFIX))
       || (!name.endsWith(ID_FILE_SUFFIX))) {
     return null;
   } else {
     return name.substring(ID_FILE_PREFIX.length(), name.length() - ID_FILE_SUFFIX.length());
   }
 }
Пример #14
0
  /**
   * Checks if a path has strict permissions
   *
   * <UL>
   *   <LI>
   *       <p>(For {@code Unix}) The path may not have group or others write permissions
   *   <LI>
   *       <p>The path must be owned by current user.
   *   <LI>
   *       <p>(For {@code Unix}) The path may be owned by root.
   * </UL>
   *
   * @param path The {@link Path} to be checked - ignored if {@code null} or does not exist
   * @param options The {@link LinkOption}s to use to query the file's permissions
   * @return The violated permission as {@link Pair} where {@link Pair#getClass()} is a loggable
   *     message and {@link Pair#getSecond()} is the offending object - e.g., {@link
   *     PosixFilePermission} or {@link String} for owner. Return value is {@code null} if no
   *     violations detected
   * @throws IOException If failed to retrieve the permissions
   * @see #STRICTLY_PROHIBITED_FILE_PERMISSION
   */
  public static Pair<String, Object> validateStrictConfigFilePermissions(
      Path path, LinkOption... options) throws IOException {
    if ((path == null) || (!Files.exists(path, options))) {
      return null;
    }

    Collection<PosixFilePermission> perms = IoUtils.getPermissions(path, options);
    if (GenericUtils.isEmpty(perms)) {
      return null;
    }

    if (OsUtils.isUNIX()) {
      PosixFilePermission p =
          IoUtils.validateExcludedPermissions(perms, STRICTLY_PROHIBITED_FILE_PERMISSION);
      if (p != null) {
        return new Pair<String, Object>(String.format("Permissions violation (%s)", p), p);
      }
    }

    String owner = IoUtils.getFileOwner(path, options);
    if (GenericUtils.isEmpty(owner)) {
      // we cannot get owner
      // general issue: jvm does not support permissions
      // security issue: specific filesystem does not support permissions
      return null;
    }

    String current = OsUtils.getCurrentUser();
    Set<String> expected = new HashSet<>();
    expected.add(current);
    if (OsUtils.isUNIX()) {
      // Windows "Administrator" was considered however in Windows most likely a group is used.
      expected.add(OsUtils.ROOT_USER);
    }

    if (!expected.contains(owner)) {
      return new Pair<String, Object>(String.format("Owner violation (%s)", owner), owner);
    }

    return null;
  }
Пример #15
0
    /**
     * @param resources The named resources
     * @return A {@link List} of all the factories names - in same order as they appear in the input
     *     collection
     */
    public static List<String> getNameList(Collection<? extends NamedResource> resources) {
      if (GenericUtils.isEmpty(resources)) {
        return Collections.emptyList();
      }

      List<String> names = new ArrayList<>(resources.size());
      for (NamedResource r : resources) {
        names.add(r.getName());
      }

      return names;
    }
Пример #16
0
  /**
   * @param s The {@link Enum}'s name - ignored if {@code null}/empty
   * @return The matching {@link org.apache.sshd.common.signature.BuiltinSignatures} whose {@link
   *     Enum#name()} matches (case <U>insensitive</U>) the provided argument - {@code null} if no
   *     match
   */
  public static BuiltinSignatures fromString(String s) {
    if (GenericUtils.isEmpty(s)) {
      return null;
    }

    for (BuiltinSignatures c : VALUES) {
      if (s.equalsIgnoreCase(c.name())) {
        return c;
      }
    }

    return null;
  }
Пример #17
0
    /**
     * @param name The result name - ignored if {@code null}/empty
     * @return The matching {@link Result} value (case <U>insensitive</U>) or {@code null} if no
     *     match found
     */
    public static Result fromName(String name) {
      if (GenericUtils.isEmpty(name)) {
        return null;
      }

      for (Result r : VALUES) {
        if (name.equalsIgnoreCase(r.name())) {
          return r;
        }
      }

      return null;
    }
Пример #18
0
  protected <T> void setOption(
      NetworkChannel socket, String property, SocketOption<T> option, T defaultValue)
      throws IOException {
    PropertyResolver manager = getFactoryManager();
    String valStr = PropertyResolverUtils.getString(manager, property);
    T val = defaultValue;
    if (!GenericUtils.isEmpty(valStr)) {
      Class<T> type = option.type();
      if (type == Integer.class) {
        val = type.cast(Integer.valueOf(valStr));
      } else if (type == Boolean.class) {
        val = type.cast(Boolean.valueOf(valStr));
      } else {
        throw new IllegalStateException("Unsupported socket option type " + type);
      }
    }

    if (val != null) {
      Collection<? extends SocketOption<?>> supported = socket.supportedOptions();
      if ((GenericUtils.size(supported) <= 0) || (!supported.contains(option))) {
        log.warn(
            "Unsupported socket option ("
                + option
                + ") to set using property '"
                + property
                + "' value="
                + val);
        return;
      }

      try {
        socket.setOption(option, val);
        if (log.isDebugEnabled()) {
          log.debug("setOption({})[{}] from property={}", option, val, property);
        }
      } catch (IOException | RuntimeException e) {
        log.warn(
            "Unable ("
                + e.getClass().getSimpleName()
                + ")"
                + " to set socket option "
                + option
                + " using property '"
                + property
                + "' value="
                + val
                + ": "
                + e.getMessage());
      }
    }
  }
Пример #19
0
  /**
   * @param address The request bind address
   * @param handlerFactory A {@link Factory} to create an {@link IoHandler} if necessary
   * @return The {@link InetSocketAddress} to which the binding occurred
   * @throws IOException If failed to bind
   */
  private InetSocketAddress doBind(
      SshdSocketAddress address, Factory<? extends IoHandler> handlerFactory) throws IOException {
    if (acceptor == null) {
      FactoryManager manager = session.getFactoryManager();
      IoServiceFactory factory = manager.getIoServiceFactory();
      IoHandler handler = handlerFactory.create();
      acceptor = factory.createAcceptor(handler);
    }

    // TODO find a better way to determine the resulting bind address - what if multi-threaded
    // calls...
    Set<SocketAddress> before = acceptor.getBoundAddresses();
    try {
      InetSocketAddress bindAddress = address.toInetSocketAddress();
      acceptor.bind(bindAddress);

      Set<SocketAddress> after = acceptor.getBoundAddresses();
      if (GenericUtils.size(after) > 0) {
        after.removeAll(before);
      }
      if (GenericUtils.isEmpty(after)) {
        throw new IOException(
            "Error binding to " + address + "[" + bindAddress + "]: no local addresses bound");
      }

      if (after.size() > 1) {
        throw new IOException(
            "Multiple local addresses have been bound for " + address + "[" + bindAddress + "]");
      }
      return (InetSocketAddress) after.iterator().next();
    } catch (IOException bindErr) {
      Set<SocketAddress> after = acceptor.getBoundAddresses();
      if (GenericUtils.isEmpty(after)) {
        close();
      }
      throw bindErr;
    }
  }
Пример #20
0
  /**
   * Reads configuration entries
   *
   * @param rdr The {@link BufferedReader} to use
   * @return The {@link List} of read {@link KnownHostEntry}-ies
   * @throws IOException If failed to parse the read configuration
   */
  public static List<KnownHostEntry> readKnownHostEntries(BufferedReader rdr) throws IOException {
    List<KnownHostEntry> entries = null;

    int lineNumber = 1;
    for (String line = rdr.readLine(); line != null; line = rdr.readLine(), lineNumber++) {
      line = GenericUtils.trimToEmpty(line);
      if (GenericUtils.isEmpty(line)) {
        continue;
      }

      int pos = line.indexOf(SshConfigFileReader.COMMENT_CHAR);
      if (pos == 0) {
        continue;
      }

      if (pos > 0) {
        line = line.substring(0, pos);
        line = line.trim();
      }

      try {
        KnownHostEntry entry = parseKnownHostEntry(line);
        if (entry == null) {
          continue;
        }

        if (entries == null) {
          entries = new ArrayList<>();
        }
        entries.add(entry);
      } catch (RuntimeException | Error e) { // TODO consider consulting a user callback
        throw new StreamCorruptedException(
            "Failed ("
                + e.getClass().getSimpleName()
                + ")"
                + " to parse line #"
                + lineNumber
                + " '"
                + line
                + "': "
                + e.getMessage());
      }
    }

    if (entries == null) {
      return Collections.emptyList();
    } else {
      return entries;
    }
  }
Пример #21
0
  /**
   * @param name The factory name
   * @return The factory or {@code null} if it is neither a built-in one or a registered extension
   */
  public static SignatureFactory resolveFactory(String name) {
    if (GenericUtils.isEmpty(name)) {
      return null;
    }

    SignatureFactory s = fromFactoryName(name);
    if (s != null) {
      return s;
    }

    synchronized (EXTENSIONS) {
      return EXTENSIONS.get(name);
    }
  }
Пример #22
0
  public static <E extends KnownHostEntry> E parseKnownHostEntry(E entry, String data) {
    String line = data;
    if (GenericUtils.isEmpty(line) || (line.charAt(0) == PublicKeyEntry.COMMENT_CHAR)) {
      return entry;
    }

    entry.setConfigLine(line);

    if (line.charAt(0) == MARKER_INDICATOR) {
      int pos = line.indexOf(' ');
      ValidateUtils.checkTrue(pos > 0, "Missing marker name end delimiter in line=%s", data);
      ValidateUtils.checkTrue(pos > 1, "No marker name after indicator in line=%s", data);
      entry.setMarker(line.substring(1, pos));
      line = line.substring(pos + 1).trim();
    } else {
      entry.setMarker(null);
    }

    int pos = line.indexOf(' ');
    ValidateUtils.checkTrue(pos > 0, "Missing host patterns end delimiter in line=%s", data);
    String hostPattern = line.substring(0, pos);
    line = line.substring(pos + 1).trim();

    if (hostPattern.charAt(0) == KnownHostHashValue.HASHED_HOST_DELIMITER) {
      KnownHostHashValue hash =
          ValidateUtils.checkNotNull(
              KnownHostHashValue.parse(hostPattern),
              "Failed to extract host hash value from line=%s",
              data);
      entry.setHashedEntry(hash);
      entry.setPatterns(null);
    } else {
      entry.setHashedEntry(null);
      entry.setPatterns(parsePatterns(GenericUtils.split(hostPattern, ',')));
    }

    AuthorizedKeyEntry key =
        ValidateUtils.checkNotNull(
            AuthorizedKeyEntry.parseAuthorizedKeyEntry(line),
            "No valid key entry recovered from line=%s",
            data);
    entry.setKeyEntry(key);
    return entry;
  }
Пример #23
0
  public static ParseResult parseSignatureList(Collection<String> sigs) {
    if (GenericUtils.isEmpty(sigs)) {
      return ParseResult.EMPTY;
    }

    List<SignatureFactory> factories = new ArrayList<>(sigs.size());
    List<String> unknown = Collections.emptyList();
    for (String name : sigs) {
      SignatureFactory s = resolveFactory(name);
      if (s != null) {
        factories.add(s);
      } else {
        // replace the (unmodifiable) empty list with a real one
        if (unknown.isEmpty()) {
          unknown = new ArrayList<>();
        }
        unknown.add(name);
      }
    }

    return new ParseResult(factories, unknown);
  }
Пример #24
0
 public static ParseResult parseSignatureList(String... sigs) {
   return parseSignatureList(
       GenericUtils.isEmpty((Object[]) sigs) ? Collections.emptyList() : Arrays.asList(sigs));
 }
Пример #25
0
 public static KnownHostEntry parseKnownHostEntry(String line) {
   return parseKnownHostEntry(GenericUtils.isEmpty(line) ? null : new KnownHostEntry(), line);
 }
 protected AbstractSftpClientExtension(
     String name, SftpClient client, RawSftpClient raw, Map<String, byte[]> extensions) {
   this(
       name, client, raw, GenericUtils.isEmpty(extensions) ? false : extensions.containsKey(name));
 }
 protected AbstractSftpClientExtension(
     String name, SftpClient client, RawSftpClient raw, Collection<String> extras) {
   this(name, client, raw, GenericUtils.isEmpty(extras) ? false : extras.contains(name));
 }