aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit.ssh.apache
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit.ssh.apache')
-rw-r--r--org.eclipse.jgit.ssh.apache/.classpath6
-rw-r--r--org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.core.prefs129
-rw-r--r--org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.ui.prefs2
-rw-r--r--org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF33
-rw-r--r--org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.ssh.apache/README.md61
-rw-r--r--org.eclipse.jgit.ssh.apache/pom.xml20
-rw-r--r--org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties5
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java6
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java49
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java26
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java7
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java14
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/ConnectorFactoryProvider.java61
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/JGitSshAgentFactory.java72
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/SshAgentClient.java246
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java4
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AbstractClientProxyConnector.java4
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java7
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java19
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java2
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProvider.java6
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java89
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java40
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactoryBuilder.java55
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/AbstractConnector.java116
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/Connector.java62
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/ConnectorFactory.java173
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/package-info.java6
-rw-r--r--org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/package-info.java6
30 files changed, 1212 insertions, 118 deletions
diff --git a/org.eclipse.jgit.ssh.apache/.classpath b/org.eclipse.jgit.ssh.apache/.classpath
index 110168ffa1..1fde318a04 100644
--- a/org.eclipse.jgit.ssh.apache/.classpath
+++ b/org.eclipse.jgit.ssh.apache/.classpath
@@ -1,6 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
+ <attributes>
+ <attribute name="module" value="true"/>
+ </attributes>
+ </classpathentry>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="resources"/>
diff --git a/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.core.prefs
index 15ef2aad5d..d1f54bbe65 100644
--- a/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.core.prefs
@@ -7,9 +7,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jgit.annotations.N
org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.compliance=11
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -24,6 +24,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
@@ -80,6 +81,7 @@ org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warn
org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
@@ -112,34 +114,66 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.8
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=11
+org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false
+org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false
+org.eclipse.jdt.core.formatter.align_with_spaces=false
+org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant=0
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field=49
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable=49
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method=49
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package=49
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter=0
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type=49
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assertion_message=0
org.eclipse.jdt.core.formatter.alignment_for_assignment=0
org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16
org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0
+org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16
org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_module_statements=16
org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_record_components=16
+org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0
org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0
+org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16
org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_type_annotations=0
+org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0
+org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0
org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration=0
org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method=1
org.eclipse.jdt.core.formatter.blank_lines_before_field=1
org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
@@ -148,6 +182,7 @@ org.eclipse.jdt.core.formatter.blank_lines_before_method=1
org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
org.eclipse.jdt.core.formatter.blank_lines_before_package=0
org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch=0
org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
@@ -157,12 +192,18 @@ org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_record_constructor=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_record_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=false
+org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false
org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false
org.eclipse.jdt.core.formatter.comment.format_block_comments=true
org.eclipse.jdt.core.formatter.comment.format_comments=true
org.eclipse.jdt.core.formatter.comment.format_header=false
@@ -172,7 +213,9 @@ org.eclipse.jdt.core.formatter.comment.format_line_comments=true
org.eclipse.jdt.core.formatter.comment.format_source_code=true
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.indent_tag_description=false
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags=do not insert
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
org.eclipse.jdt.core.formatter.comment.line_length=80
org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
@@ -184,10 +227,11 @@ org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
-org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_empty_lines=false
@@ -197,15 +241,17 @@ org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
org.eclipse.jdt.core.formatter.indentation.size=4
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
@@ -219,11 +265,15 @@ org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default=insert
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
@@ -249,10 +299,16 @@ org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arg
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_not_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
@@ -269,6 +325,7 @@ org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not ins
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
@@ -277,13 +334,20 @@ org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert
org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case=insert
+org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default=insert
org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
@@ -300,6 +364,7 @@ org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not in
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
@@ -326,10 +391,15 @@ org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_ar
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
@@ -341,6 +411,8 @@ org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
@@ -356,6 +428,7 @@ org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
@@ -366,9 +439,12 @@ org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not inser
org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
@@ -380,20 +456,63 @@ org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_decla
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.join_lines_in_comments=true
org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never
org.eclipse.jdt.core.formatter.lineSplit=80
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block=0
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block=0
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
org.eclipse.jdt.core.formatter.tabulation.char=tab
org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.text_block_indentation=0
org.eclipse.jdt.core.formatter.use_on_off_tags=true
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true
org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.ui.prefs
index fef3713825..5cfb8b6ac6 100644
--- a/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.ui.prefs
+++ b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.ui.prefs
@@ -1,7 +1,7 @@
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
formatter_profile=_JGit Format
-formatter_settings_version=12
+formatter_settings_version=21
org.eclipse.jdt.ui.ignorelowercasenames=true
org.eclipse.jdt.ui.importorder=java;javax;org;com;
org.eclipse.jdt.ui.ondemandthreshold=99
diff --git a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
index e8bb662812..81e01ccadc 100644
--- a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
@@ -6,9 +6,9 @@ Bundle-SymbolicName: org.eclipse.jgit.ssh.apache
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-ActivationPolicy: lazy
-Bundle-Version: 5.13.3.202401111512-r
-Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Export-Package: org.eclipse.jgit.internal.transport.sshd;version="5.13.3";x-internal:=true;
+Bundle-Version: 6.0.1.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-11
+Export-Package: org.eclipse.jgit.internal.transport.sshd;version="6.0.1";x-internal:=true;
uses:="org.apache.sshd.client,
org.apache.sshd.client.auth,
org.apache.sshd.client.auth.keyboard,
@@ -23,15 +23,17 @@ Export-Package: org.eclipse.jgit.internal.transport.sshd;version="5.13.3";x-inte
org.apache.sshd.common.signature,
org.apache.sshd.common.util.buffer,
org.eclipse.jgit.transport",
- org.eclipse.jgit.internal.transport.sshd.auth;version="5.13.3";x-internal:=true,
- org.eclipse.jgit.internal.transport.sshd.proxy;version="5.13.3";x-friends:="org.eclipse.jgit.ssh.apache.test",
- org.eclipse.jgit.transport.sshd;version="5.13.3";
+ org.eclipse.jgit.internal.transport.sshd.agent;version="6.0.1";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.auth;version="6.0.1";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.proxy;version="6.0.1";x-friends:="org.eclipse.jgit.ssh.apache.test",
+ org.eclipse.jgit.transport.sshd;version="6.0.1";
uses:="org.eclipse.jgit.transport,
org.apache.sshd.client.config.hosts,
org.apache.sshd.common.keyprovider,
org.eclipse.jgit.util,
org.apache.sshd.client.session,
- org.apache.sshd.client.keyverifier"
+ org.apache.sshd.client.keyverifier",
+ org.eclipse.jgit.transport.sshd.agent;version="6.0.1"
Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)",
org.apache.sshd.agent;version="[2.7.0,2.8.0)",
org.apache.sshd.client;version="[2.7.0,2.8.0)",
@@ -71,6 +73,7 @@ Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)",
org.apache.sshd.common.util.buffer;version="[2.7.0,2.8.0)",
org.apache.sshd.common.util.closeable;version="[2.7.0,2.8.0)",
org.apache.sshd.common.util.io;version="[2.7.0,2.8.0)",
+ org.apache.sshd.common.util.io.functors;version="[2.7.0,2.8.0)",
org.apache.sshd.common.util.io.resource;version="[2.7.0,2.8.0)",
org.apache.sshd.common.util.logging;version="[2.7.0,2.8.0)",
org.apache.sshd.common.util.net;version="[2.7.0,2.8.0)",
@@ -80,12 +83,12 @@ Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)",
org.apache.sshd.sftp;version="[2.7.0,2.8.0)",
org.apache.sshd.sftp.client;version="[2.7.0,2.8.0)",
org.apache.sshd.sftp.common;version="[2.7.0,2.8.0)",
- org.eclipse.jgit.annotations;version="[5.13.3,5.14.0)",
- org.eclipse.jgit.errors;version="[5.13.3,5.14.0)",
- org.eclipse.jgit.fnmatch;version="[5.13.3,5.14.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.13.3,5.14.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[5.13.3,5.14.0)",
- org.eclipse.jgit.nls;version="[5.13.3,5.14.0)",
- org.eclipse.jgit.transport;version="[5.13.3,5.14.0)",
- org.eclipse.jgit.util;version="[5.13.3,5.14.0)",
+ org.eclipse.jgit.annotations;version="[6.0.1,6.1.0)",
+ org.eclipse.jgit.errors;version="[6.0.1,6.1.0)",
+ org.eclipse.jgit.fnmatch;version="[6.0.1,6.1.0)",
+ org.eclipse.jgit.internal.storage.file;version="[6.0.1,6.1.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[6.0.1,6.1.0)",
+ org.eclipse.jgit.nls;version="[6.0.1,6.1.0)",
+ org.eclipse.jgit.transport;version="[6.0.1,6.1.0)",
+ org.eclipse.jgit.util;version="[6.0.1,6.1.0)",
org.slf4j;version="[1.7.0,2.0.0)"
diff --git a/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF
index c64f658c16..b770b90344 100644
--- a/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.ssh.apache - Sources
Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.13.3.202401111512-r
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="5.13.3.202401111512-r";roots="."
+Bundle-Version: 6.0.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="6.0.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache/README.md b/org.eclipse.jgit.ssh.apache/README.md
new file mode 100644
index 0000000000..cba87ac9cc
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/README.md
@@ -0,0 +1,61 @@
+# JGit SSH support via Apache MINA sshd
+
+This bundle provides an implementation of git transport over SSH implemented via
+[Apache MINA sshd](https://mina.apache.org/sshd-project/).
+
+## Service registration
+
+This bundle declares a service for the `java.util.ServiceLoader` for interface
+`org.eclipse.jgit.transport.ssh.SshSessionFactory`. The core JGit bundle uses the service
+loader to pick up an implementation of that interface.
+
+Note that JGit simply uses the first `SshSessionFactory` provided by the `ServiceLoader`.
+
+If the service loader cannot find the session factory, either ensure that the service
+declaration is on the Classpath of bundle `org.eclipse.jgit`, or set the factory explicitly
+(see below).
+
+In an OSGi environment, one might need a service loader bridge, or have a little OSGi
+fragment for bundle `org.eclipse.jgit` that puts the right service declaration onto the
+Classpath of that bundle. (OSGi fragments become part of the Classpath of their host
+bundle.)
+
+## Configuring an SSH implementation for JGit
+
+The simplest way to set an SSH implementation for JGit is to install it globally via
+`SshSessionFactory.setInstance()`. This instance will be used by JGit for all SSH
+connections by default.
+
+It is also possible to set the SSH implementation individually for any git command
+that needs a transport (`TransportCommand`) via a `org.eclipse.jgit.api.TransportConfigCallback`.
+
+To do so, set the wanted `SshSessionFactory` on the SSH transport, like:
+
+```java
+SshSessionFactory customFactory = ...; // Get it from wherever
+FetchCommand fetch = git.fetch()
+ .setTransportConfigCallback(transport -> {
+ if (transport instanceof SshTransport) {
+ ((SshTransport) transport).setSshSessionFactory(customFactory);
+ }
+ })
+ ...
+ .call();
+```
+
+## Using a different SSH implementation
+
+To use a different SSH implementation:
+
+* Do not include this bundle in your product.
+* Include the bundle of the alternate implementation.
+ * If the service loader finds the alternate implementation, nothing more is needed.
+ * Otherwise ensure the service declaration from the other bundle is on the Classpath of bundle `org.eclipse.jgit`,
+ * or set the `SshSessionFactory` for JGit explicitly (see above).
+
+## Using an external SSH executable
+
+JGit has built-in support for not using any Java SSH implementation but an external SSH
+executable. To use an external SSH executable, set environment variable **GIT_SSH** to
+the path of the executable. JGit will create a sub-process to run the executable and
+communicate with this sub-process to perform the git operation.
diff --git a/org.eclipse.jgit.ssh.apache/pom.xml b/org.eclipse.jgit.ssh.apache/pom.xml
index 074998a203..340cca158d 100644
--- a/org.eclipse.jgit.ssh.apache/pom.xml
+++ b/org.eclipse.jgit.ssh.apache/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.13.3.202401111512-r</version>
+ <version>6.0.1-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ssh.apache</artifactId>
@@ -105,15 +105,15 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-source-plugin</artifactId>
- <inherited>true</inherited>
- <executions>
- <execution>
- <id>attach-sources</id>
- <phase>process-classes</phase>
- <goals>
- <goal>jar</goal>
- </goals>
+ <artifactId>maven-source-plugin</artifactId>
+ <inherited>true</inherited>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <phase>process-classes</phase>
+ <goals>
+ <goal>jar</goal>
+ </goals>
<configuration>
<archive>
<manifestFile>${source-bundle-manifest}</manifestFile>
diff --git a/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties b/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
index defcbdcfc1..2bba736aad 100644
--- a/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
+++ b/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
@@ -19,6 +19,7 @@ identityFileNoKey=No keys found in identity {0}
identityFileMultipleKeys=Multiple key pairs found in identity {0}
identityFileNotFound=Skipping identity ''{0}'': file not found
identityFileUnsupportedFormat=Unsupported format in identity {0}
+invalidSignatureAlgorithm=Signature algorithm ''{0}'' is not valid for a key of type ''{1}''
kexServerKeyInvalid=Server key did not validate
keyEncryptedMsg=Key ''{0}'' is encrypted. Enter the passphrase to decrypt it.
keyEncryptedPrompt=Passphrase
@@ -84,6 +85,10 @@ serverIdTooLong=Server identification is longer than 255 characters (including l
serverIdWithNul=Server identification contains a NUL character: {0}
sessionCloseFailed=Closing the session failed
sessionWithoutUsername=SSH session created without user name; cannot authenticate
+sshAgentReplyLengthError=Invalid SSH agent reply message length {0} after command {1}
+sshAgentReplyUnexpected=Unexpected reply from ssh-agent: {0}
+sshAgentShortReadBuffer=Short read from SSH agent
+sshAgentWrongNumberOfKeys=Invalid number of SSH agent keys: {0}
sshClosingDown=Apache MINA sshd session factory is closing down; cannot create new ssh sessions on this factory
sshCommandTimeout={0} timed out after {1} seconds while opening the channel
sshProcessStillRunning={0} is not yet completed, cannot get exit code
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java
index f7b37d7816..e2dbb4c466 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java
@@ -71,9 +71,9 @@ public class JGitClientSession extends ClientSessionImpl {
/**
* Default setting for the maximum number of bytes to read in the initial
- * protocol version exchange. 64kb is what OpenSSH < 8.0 read; OpenSSH 8.0
- * changed it to 8Mb, but that seems excessive for the purpose stated in RFC
- * 4253. The Apache MINA sshd default in
+ * protocol version exchange. 64kb is what OpenSSH &lt; 8.0 read; OpenSSH
+ * 8.0 changed it to 8Mb, but that seems excessive for the purpose stated in
+ * RFC 4253. The Apache MINA sshd default in
* {@link org.apache.sshd.core.CoreModuleProperties#MAX_IDENTIFICATION_SIZE}
* is 16kb.
*/
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
index 08da18f5aa..c082a9a963 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
@@ -12,8 +12,12 @@ package org.eclipse.jgit.internal.transport.sshd;
import static java.text.MessageFormat.format;
import static org.eclipse.jgit.transport.SshConstants.PUBKEY_ACCEPTED_ALGORITHMS;
+import java.util.Iterator;
import java.util.List;
+import java.util.NoSuchElementException;
+import org.apache.sshd.client.auth.pubkey.KeyAgentIdentity;
+import org.apache.sshd.client.auth.pubkey.PublicKeyIdentity;
import org.apache.sshd.client.auth.pubkey.UserAuthPublicKey;
import org.apache.sshd.client.config.hosts.HostConfigEntry;
import org.apache.sshd.client.session.ClientSession;
@@ -38,7 +42,7 @@ public class JGitPublicKeyAuthentication extends UserAuthPublicKey {
throw new IllegalStateException("Wrong session type: " //$NON-NLS-1$
+ rawSession.getClass().getCanonicalName());
}
- JGitClientSession session = ((JGitClientSession) rawSession);
+ JGitClientSession session = (JGitClientSession) rawSession;
HostConfigEntry hostConfig = session.getHostConfigEntry();
// Set signature algorithms for public key authentication
String pubkeyAlgos = hostConfig.getProperty(PUBKEY_ACCEPTED_ALGORITHMS);
@@ -60,5 +64,48 @@ public class JGitPublicKeyAuthentication extends UserAuthPublicKey {
// If we don't set signature factories here, the default ones from the
// session will be used.
super.init(session, service);
+ // In sshd 2.7.0, we end up now with a key iterator that uses keys
+ // provided by an ssh-agent even if IdentitiesOnly is true. So if
+ // needed, filter out any KeyAgentIdentity.
+ if (hostConfig.isIdentitiesOnly()) {
+ Iterator<PublicKeyIdentity> original = keys;
+ // The original iterator will already have gotten the identities
+ // from the agent. Unfortunately there's nothing we can do about
+ // that; it'll have to be fixed upstream. (As will, ultimately,
+ // respecting isIdentitiesOnly().) At least we can simply not
+ // use the keys the agent provided.
+ //
+ // See https://issues.apache.org/jira/browse/SSHD-1218
+ keys = new Iterator<>() {
+
+ private PublicKeyIdentity value;
+
+ @Override
+ public boolean hasNext() {
+ if (value != null) {
+ return true;
+ }
+ PublicKeyIdentity next = null;
+ while (original.hasNext()) {
+ next = original.next();
+ if (!(next instanceof KeyAgentIdentity)) {
+ value = next;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public PublicKeyIdentity next() {
+ if (hasNext()) {
+ PublicKeyIdentity result = value;
+ value = null;
+ return result;
+ }
+ throw new NoSuchElementException();
+ }
+ };
+ }
}
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
index ae12c2028d..71e8e61585 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
@@ -32,8 +32,10 @@ import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
+import org.apache.sshd.agent.SshAgentFactory;
import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.config.hosts.HostConfigEntry;
import org.apache.sshd.client.future.ConnectFuture;
@@ -100,6 +102,8 @@ public class JGitSshClient extends SshClient {
private ProxyDataFactory proxyDatabase;
+ private Supplier<SshAgentFactory> agentFactorySupplier = () -> null;
+
@Override
protected SessionFactory createSessionFactory() {
// Override the parent's default
@@ -223,7 +227,7 @@ public class JGitSshClient extends SshClient {
private SshFutureListener<IoConnectFuture> createConnectCompletionListener(
ConnectFuture connectFuture, String username,
InetSocketAddress address, HostConfigEntry hostConfig) {
- return new SshFutureListener<IoConnectFuture>() {
+ return new SshFutureListener<>() {
@Override
public void operationComplete(IoConnectFuture future) {
@@ -368,6 +372,22 @@ public class JGitSshClient extends SshClient {
return credentialsProvider;
}
+ @Override
+ public SshAgentFactory getAgentFactory() {
+ return agentFactorySupplier.get();
+ }
+
+ @Override
+ protected void checkConfig() {
+ // The super class requires channel factories for agent forwarding if a
+ // factory for an SSH agent is set. We haven't implemented this yet, and
+ // we don't do SSH agent forwarding for now. Unfortunately, there is no
+ // way to bypass this check in the super class except making
+ // getAgentFactory() return null until after the check.
+ super.checkConfig();
+ agentFactorySupplier = super::getAgentFactory;
+ }
+
/**
* A {@link SessionFactory} to create our own specialized
* {@link JGitClientSession}s.
@@ -406,7 +426,7 @@ public class JGitSshClient extends SshClient {
@Override
public Iterable<KeyPair> loadKeys(SessionContext context) {
- return () -> new Iterator<KeyPair>() {
+ return () -> new Iterator<>() {
private Iterator<KeyIdentityProvider> factories = providers
.iterator();
@@ -439,7 +459,7 @@ public class JGitSshClient extends SshClient {
@Override
public KeyPair next() {
- if (hasElement == null && !hasNext()
+ if ((hasElement == null && !hasNext())
|| !hasElement.booleanValue()) {
throw new NoSuchElementException();
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java
index 85e406f422..d8bf449acf 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java
@@ -34,6 +34,7 @@ import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Random;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
@@ -138,6 +139,8 @@ public class OpenSshServerKeyDatabase
private final List<HostKeyFile> defaultFiles = new ArrayList<>();
+ private Random prng;
+
/**
* Creates a new {@link OpenSshServerKeyDatabase}.
*
@@ -680,7 +683,9 @@ public class OpenSshServerKeyDatabase
// or to Apache MINA sshd.
NamedFactory<Mac> digester = KnownHostDigest.SHA1;
Mac mac = digester.create();
- SecureRandom prng = new SecureRandom();
+ if (prng == null) {
+ prng = new SecureRandom();
+ }
byte[] salt = new byte[mac.getDefaultBlockSize()];
for (SshdSocketAddress address : patterns) {
if (result.length() > 0) {
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
index c0f5719629..00ee62d6dd 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
@@ -1,3 +1,12 @@
+/*
+ * Copyright (C) 2018, 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
package org.eclipse.jgit.internal.transport.sshd;
import org.eclipse.jgit.nls.NLS;
@@ -39,6 +48,7 @@ public final class SshdText extends TranslationBundle {
/***/ public String identityFileMultipleKeys;
/***/ public String identityFileNotFound;
/***/ public String identityFileUnsupportedFormat;
+ /***/ public String invalidSignatureAlgorithm;
/***/ public String kexServerKeyInvalid;
/***/ public String keyEncryptedMsg;
/***/ public String keyEncryptedPrompt;
@@ -96,6 +106,10 @@ public final class SshdText extends TranslationBundle {
/***/ public String serverIdWithNul;
/***/ public String sessionCloseFailed;
/***/ public String sessionWithoutUsername;
+ /***/ public String sshAgentReplyLengthError;
+ /***/ public String sshAgentReplyUnexpected;
+ /***/ public String sshAgentShortReadBuffer;
+ /***/ public String sshAgentWrongNumberOfKeys;
/***/ public String sshClosingDown;
/***/ public String sshCommandTimeout;
/***/ public String sshProcessStillRunning;
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/ConnectorFactoryProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/ConnectorFactoryProvider.java
new file mode 100644
index 0000000000..aba7a76459
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/ConnectorFactoryProvider.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd.agent;
+
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
+
+/**
+ * Provides a {@link ConnectorFactory} obtained via the {@link ServiceLoader}.
+ */
+public final class ConnectorFactoryProvider {
+
+ private static volatile ConnectorFactory INSTANCE = loadDefaultFactory();
+
+ private static ConnectorFactory loadDefaultFactory() {
+ ServiceLoader<ConnectorFactory> loader = ServiceLoader
+ .load(ConnectorFactory.class);
+ Iterator<ConnectorFactory> iter = loader.iterator();
+ while (iter.hasNext()) {
+ ConnectorFactory candidate = iter.next();
+ if (candidate.isSupported()) {
+ return candidate;
+ }
+ }
+ return null;
+
+ }
+
+ /**
+ * Retrieves the currently set default {@link ConnectorFactory}.
+ *
+ * @return the {@link ConnectorFactory}, or {@code null} if none.
+ */
+ public static ConnectorFactory getDefaultFactory() {
+ return INSTANCE;
+ }
+
+ /**
+ * Sets the default {@link ConnectorFactory}.
+ *
+ * @param factory
+ * {@link ConnectorFactory} to use, or {@code null} to use the
+ * factory discovered via the {@link ServiceLoader}.
+ */
+ public static void setDefaultFactory(ConnectorFactory factory) {
+ INSTANCE = factory == null ? loadDefaultFactory() : factory;
+ }
+
+ private ConnectorFactoryProvider() {
+ // No instantiation
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/JGitSshAgentFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/JGitSshAgentFactory.java
new file mode 100644
index 0000000000..1ed2ab9d78
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/JGitSshAgentFactory.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd.agent;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.agent.SshAgent;
+import org.apache.sshd.agent.SshAgentFactory;
+import org.apache.sshd.agent.SshAgentServer;
+import org.apache.sshd.common.FactoryManager;
+import org.apache.sshd.common.channel.ChannelFactory;
+import org.apache.sshd.common.session.ConnectionService;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
+
+/**
+ * A factory for creating {@link SshAgentClient}s.
+ */
+public class JGitSshAgentFactory implements SshAgentFactory {
+
+ private final @NonNull ConnectorFactory factory;
+
+ private final File homeDir;
+
+ /**
+ * Creates a new {@link JGitSshAgentFactory}.
+ *
+ * @param factory
+ * {@link JGitSshAgentFactory} to wrap
+ * @param homeDir
+ * for obtaining the current local user's home directory
+ */
+ public JGitSshAgentFactory(@NonNull ConnectorFactory factory,
+ File homeDir) {
+ this.factory = factory;
+ this.homeDir = homeDir;
+ }
+
+ @Override
+ public List<ChannelFactory> getChannelForwardingFactories(
+ FactoryManager manager) {
+ // No agent forwarding supported yet.
+ return Collections.emptyList();
+ }
+
+ @Override
+ public SshAgent createClient(FactoryManager manager) throws IOException {
+ // sshd 2.8.0 will pass us the session here. At that point, we can get
+ // the HostConfigEntry and extract and handle the IdentityAgent setting.
+ // For now, pass null to let the ConnectorFactory do its default
+ // behavior (Pageant on Windows, SSH_AUTH_SOCK on Unixes with the
+ // jgit-builtin factory).
+ return new SshAgentClient(factory.create(null, homeDir));
+ }
+
+ @Override
+ public SshAgentServer createServer(ConnectionService service)
+ throws IOException {
+ // This should be called in a server only.
+ return null;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/SshAgentClient.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/SshAgentClient.java
new file mode 100644
index 0000000000..08483e4c20
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/SshAgentClient.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd.agent;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.text.MessageFormat;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.sshd.agent.SshAgent;
+import org.apache.sshd.agent.SshAgentConstants;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.session.SessionContext;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.BufferException;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.transport.sshd.agent.Connector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A client for an SSH2 agent. This client supports only querying identities and
+ * signature requests.
+ *
+ * @see <a href="https://tools.ietf.org/html/draft-miller-ssh-agent-04">SSH
+ * Agent Protocol, RFC draft</a>
+ */
+public class SshAgentClient implements SshAgent {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(SshAgentClient.class);
+
+ // OpenSSH limit
+ private static final int MAX_NUMBER_OF_KEYS = 2048;
+
+ private final AtomicBoolean closed = new AtomicBoolean();
+
+ private final Connector connector;
+
+ /**
+ * Creates a new {@link SshAgentClient} implementing the SSH2 ssh agent
+ * protocol, using the given {@link Connector} to connect to the SSH agent
+ * and to exchange messages.
+ *
+ * @param connector
+ * {@link Connector} to use
+ */
+ public SshAgentClient(Connector connector) {
+ this.connector = connector;
+ }
+
+ private boolean open(boolean debugging) throws IOException {
+ if (closed.get()) {
+ if (debugging) {
+ LOG.debug("SSH agent connection already closed"); //$NON-NLS-1$
+ }
+ return false;
+ }
+ boolean connected = connector != null && connector.connect();
+ if (!connected) {
+ if (debugging) {
+ LOG.debug("No SSH agent (SSH_AUTH_SOCK not set)"); //$NON-NLS-1$
+ }
+ }
+ return connected;
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (!closed.getAndSet(true) && connector != null) {
+ connector.close();
+ }
+ }
+
+ @Override
+ public Iterable<? extends Map.Entry<PublicKey, String>> getIdentities()
+ throws IOException {
+ boolean debugging = LOG.isDebugEnabled();
+ if (!open(debugging)) {
+ return Collections.emptyList();
+ }
+ if (debugging) {
+ LOG.debug("Requesting identities from SSH agent"); //$NON-NLS-1$
+ }
+ try {
+ Buffer reply = rpc(
+ SshAgentConstants.SSH2_AGENTC_REQUEST_IDENTITIES);
+ byte cmd = reply.getByte();
+ if (cmd != SshAgentConstants.SSH2_AGENT_IDENTITIES_ANSWER) {
+ throw new SshException(MessageFormat.format(
+ SshdText.get().sshAgentReplyUnexpected,
+ SshAgentConstants.getCommandMessageName(cmd)));
+ }
+ int numberOfKeys = reply.getInt();
+ if (numberOfKeys < 0 || numberOfKeys > MAX_NUMBER_OF_KEYS) {
+ throw new SshException(MessageFormat.format(
+ SshdText.get().sshAgentWrongNumberOfKeys,
+ Integer.toString(numberOfKeys)));
+ }
+ if (numberOfKeys == 0) {
+ if (debugging) {
+ LOG.debug("SSH agent has no keys"); //$NON-NLS-1$
+ }
+ return Collections.emptyList();
+ }
+ if (debugging) {
+ LOG.debug("Got {} key(s) from the SSH agent", //$NON-NLS-1$
+ Integer.toString(numberOfKeys));
+ }
+ boolean tracing = LOG.isTraceEnabled();
+ List<Map.Entry<PublicKey, String>> keys = new ArrayList<>(
+ numberOfKeys);
+ for (int i = 0; i < numberOfKeys; i++) {
+ PublicKey key = reply.getPublicKey();
+ String comment = reply.getString();
+ if (tracing) {
+ LOG.trace("Got SSH agent {} key: {} {}", //$NON-NLS-1$
+ KeyUtils.getKeyType(key),
+ KeyUtils.getFingerPrint(key), comment);
+ }
+ keys.add(new AbstractMap.SimpleImmutableEntry<>(key, comment));
+ }
+ return keys;
+ } catch (BufferException e) {
+ throw new SshException(SshdText.get().sshAgentShortReadBuffer, e);
+ }
+ }
+
+ @Override
+ public Map.Entry<String, byte[]> sign(SessionContext session, PublicKey key,
+ String algorithm, byte[] data) throws IOException {
+ boolean debugging = LOG.isDebugEnabled();
+ String keyType = KeyUtils.getKeyType(key);
+ String signatureAlgorithm;
+ if (algorithm != null) {
+ if (!KeyUtils.getCanonicalKeyType(algorithm).equals(keyType)) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ SshdText.get().invalidSignatureAlgorithm, algorithm,
+ keyType));
+ }
+ signatureAlgorithm = algorithm;
+ } else {
+ signatureAlgorithm = keyType;
+ }
+ if (!open(debugging)) {
+ return null;
+ }
+ int flags = 0;
+ switch (signatureAlgorithm) {
+ case KeyUtils.RSA_SHA512_KEY_TYPE_ALIAS:
+ case KeyUtils.RSA_SHA512_CERT_TYPE_ALIAS:
+ flags = 4;
+ break;
+ case KeyUtils.RSA_SHA256_KEY_TYPE_ALIAS:
+ case KeyUtils.RSA_SHA256_CERT_TYPE_ALIAS:
+ flags = 2;
+ break;
+ default:
+ break;
+ }
+ ByteArrayBuffer msg = new ByteArrayBuffer();
+ msg.putInt(0);
+ msg.putByte(SshAgentConstants.SSH2_AGENTC_SIGN_REQUEST);
+ msg.putPublicKey(key);
+ msg.putBytes(data);
+ msg.putInt(flags);
+ if (debugging) {
+ LOG.debug(
+ "sign({}): signing request to SSH agent for {} key, {} signature; flags={}", //$NON-NLS-1$
+ session, keyType, signatureAlgorithm,
+ Integer.toString(flags));
+ }
+ Buffer reply = rpc(SshAgentConstants.SSH2_AGENTC_SIGN_REQUEST,
+ msg.getCompactData());
+ byte cmd = reply.getByte();
+ if (cmd != SshAgentConstants.SSH2_AGENT_SIGN_RESPONSE) {
+ throw new SshException(
+ MessageFormat.format(SshdText.get().sshAgentReplyUnexpected,
+ SshAgentConstants.getCommandMessageName(cmd)));
+ }
+ try {
+ Buffer signatureReply = new ByteArrayBuffer(reply.getBytes());
+ String actualAlgorithm = signatureReply.getString();
+ byte[] signature = signatureReply.getBytes();
+ if (LOG.isTraceEnabled()) {
+ LOG.trace(
+ "sign({}): signature reply from SSH agent for {} key: {} signature={}", //$NON-NLS-1$
+ session, keyType, actualAlgorithm,
+ BufferUtils.toHex(':', signature));
+
+ } else if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "sign({}): signature reply from SSH agent for {} key, {} signature", //$NON-NLS-1$
+ session, keyType, actualAlgorithm);
+ }
+ return new AbstractMap.SimpleImmutableEntry<>(actualAlgorithm,
+ signature);
+ } catch (BufferException e) {
+ throw new SshException(SshdText.get().sshAgentShortReadBuffer, e);
+ }
+ }
+
+ private Buffer rpc(byte command, byte[] message) throws IOException {
+ return new ByteArrayBuffer(connector.rpc(command, message));
+ }
+
+ private Buffer rpc(byte command) throws IOException {
+ return new ByteArrayBuffer(connector.rpc(command));
+ }
+
+ @Override
+ public boolean isOpen() {
+ return !closed.get();
+ }
+
+ @Override
+ public void addIdentity(KeyPair key, String comment) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeIdentity(PublicKey key) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeAllIdentities() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java
index eae0d75355..e5f884e299 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java
@@ -95,8 +95,8 @@ public abstract class BasicAuthentication<ParameterType, TokenType>
@Override
public final void start() throws Exception {
- if (user != null && !user.isEmpty()
- || password != null && password.length > 0) {
+ if ((user != null && !user.isEmpty())
+ || (password != null && password.length > 0)) {
return;
}
askCredentials();
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AbstractClientProxyConnector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AbstractClientProxyConnector.java
index 54e2cbcebf..a8e33af35e 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AbstractClientProxyConnector.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AbstractClientProxyConnector.java
@@ -31,7 +31,7 @@ public abstract class AbstractClientProxyConnector
.toMillis(30L);
/** Guards {@link #done} and {@link #bufferedCommands}. */
- private Object lock = new Object();
+ private final Object lock = new Object();
private boolean done;
@@ -105,7 +105,7 @@ public abstract class AbstractClientProxyConnector
/**
* Obtains the timeout for the whole rest of the proxy connection protocol.
*
- * @return the timeout in milliseconds, always > 0L
+ * @return the timeout in milliseconds, always &gt; 0L
*/
protected long getTimeout() {
long last = lastProxyOperationTime;
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java
index e5d1e80f74..b7deb29dc4 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java
@@ -113,8 +113,8 @@ public class HttpClientConnector extends AbstractClientProxyConnector {
IoSession session = sshSession.getIoSession();
session.addCloseFutureListener(f -> close());
StringBuilder msg = connect();
- if (proxyUser != null && !proxyUser.isEmpty()
- || proxyPassword != null && proxyPassword.length > 0) {
+ if ((proxyUser != null && !proxyUser.isEmpty())
+ || (proxyPassword != null && proxyPassword.length > 0)) {
authenticator = basic;
basic.setParams(null);
basic.start();
@@ -232,7 +232,8 @@ public class HttpClientConnector extends AbstractClientProxyConnector {
} catch (HttpParser.ParseException e) {
throw new IOException(
format(SshdText.get().proxyHttpUnexpectedReply,
- proxyAddress, reply.get(0)));
+ proxyAddress, reply.get(0)),
+ e);
}
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java
index 0500a63428..ece22af1ce 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java
@@ -31,6 +31,23 @@ public final class HttpParser {
private static final long serialVersionUID = -1634090143702048640L;
+ /**
+ * Creates a new {@link ParseException} without cause.
+ */
+ public ParseException() {
+ super();
+ }
+
+ /**
+ * Creates a new {@link ParseException} with the given {@code cause}.
+ *
+ * @param cause
+ * {@link Throwable} that caused this exception, or
+ * {@code null} if none
+ */
+ public ParseException(Throwable cause) {
+ super(cause);
+ }
}
private HttpParser() {
@@ -64,7 +81,7 @@ public final class HttpParser {
resultCode = Integer.parseUnsignedInt(
line.substring(firstBlank + 1, secondBlank));
} catch (NumberFormatException e) {
- throw new ParseException();
+ throw new ParseException(e);
}
// Again, accept even if the reason is missing
String reason = ""; //$NON-NLS-1$
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java
index 8844efa6b7..bb227bbac8 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java
@@ -94,7 +94,7 @@ public class Socks5ClientConnector extends AbstractClientProxyConnector {
// JSON(9),
NONE_ACCEPTABLE(0xFF);
- private byte value;
+ private final byte value;
SocksAuthenticationMethod(int value) {
this.value = (byte) value;
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProvider.java
index acc11cefb1..ed9fe37d5c 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProvider.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProvider.java
@@ -31,7 +31,7 @@ public interface KeyPasswordProvider {
* identifying the key resource that is being attempted to be
* loaded
* @param attempt
- * the number of previous attempts to get a passphrase; >= 0
+ * the number of previous attempts to get a passphrase; &gt;= 0
* @return the passphrase
* @throws IOException
* if no password can be obtained
@@ -44,7 +44,7 @@ public interface KeyPasswordProvider {
*
* @param maxNumberOfAttempts
* number of times to ask for a passphrase;
- * {@link IllegalArgumentException} may be thrown if <= 0
+ * {@link IllegalArgumentException} may be thrown if &lt;= 0
*/
void setAttempts(int maxNumberOfAttempts);
@@ -53,7 +53,7 @@ public interface KeyPasswordProvider {
* attempted for one identity resource through this provider. The default
* return 1.
*
- * @return the number of times to ask for a passphrase; should be >= 1.
+ * @return the number of times to ask for a passphrase; should be &gt;= 1.
*/
default int getAttempts() {
return 1;
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java
index 33b234b1f1..c270b44956 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2018, 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -28,7 +28,6 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.regex.Pattern;
@@ -44,9 +43,9 @@ import org.apache.sshd.common.SshException;
import org.apache.sshd.common.future.CloseFuture;
import org.apache.sshd.common.future.SshFutureListener;
import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.io.functors.IOFunction;
import org.apache.sshd.common.util.net.SshdSocketAddress;
import org.apache.sshd.sftp.client.SftpClient;
-import org.apache.sshd.sftp.client.SftpClient.CloseableHandle;
import org.apache.sshd.sftp.client.SftpClient.CopyMode;
import org.apache.sshd.sftp.client.SftpClientFactory;
import org.apache.sshd.sftp.common.SftpException;
@@ -220,7 +219,8 @@ public class SshdSession implements RemoteSession2 {
HostConfigEntry hostConfig, String host) throws IOException {
if (currentHops.isEmpty()) {
String jumpHosts = hostConfig.getProperty(SshConstants.PROXY_JUMP);
- if (!StringUtils.isEmptyOrNull(jumpHosts)) {
+ if (!StringUtils.isEmptyOrNull(jumpHosts)
+ && !SshConstants.NONE.equals(jumpHosts)) {
try {
return parseProxyJump(jumpHosts);
} catch (URISyntaxException e) {
@@ -416,20 +416,6 @@ public class SshdSession implements RemoteSession2 {
}
}
- /**
- * Helper interface like {@link Supplier}, but possibly raising an
- * {@link IOException}.
- *
- * @param <T>
- * return type
- */
- @FunctionalInterface
- private interface FtpOperation<T> {
-
- T call() throws IOException;
-
- }
-
private class SshdFtpChannel implements FtpChannel {
private SftpClient ftp;
@@ -485,9 +471,9 @@ public class SshdSession implements RemoteSession2 {
return path;
}
- private <T> T map(FtpOperation<T> op) throws IOException {
+ private <T> T map(IOFunction<Void, T> op) throws IOException {
try {
- return op.call();
+ return op.apply(null);
} catch (IOException e) {
if (e instanceof SftpException) {
throw new FtpChannel.FtpException(e.getLocalizedMessage(),
@@ -499,7 +485,7 @@ public class SshdSession implements RemoteSession2 {
@Override
public void cd(String path) throws IOException {
- cwd = map(() -> ftp.canonicalPath(absolute(path)));
+ cwd = map(x -> ftp.canonicalPath(absolute(path)));
if (cwd.isEmpty()) {
cwd += '/';
}
@@ -512,39 +498,28 @@ public class SshdSession implements RemoteSession2 {
@Override
public Collection<DirEntry> ls(String path) throws IOException {
- return map(() -> {
+ return map(x -> {
List<DirEntry> result = new ArrayList<>();
- try (CloseableHandle handle = ftp.openDir(absolute(path))) {
- AtomicReference<Boolean> atEnd = new AtomicReference<>(
- Boolean.FALSE);
- while (!atEnd.get().booleanValue()) {
- List<SftpClient.DirEntry> chunk = ftp.readDir(handle,
- atEnd);
- if (chunk == null) {
- break;
+ for (SftpClient.DirEntry remote : ftp.readDir(absolute(path))) {
+ result.add(new DirEntry() {
+
+ @Override
+ public String getFilename() {
+ return remote.getFilename();
}
- for (SftpClient.DirEntry remote : chunk) {
- result.add(new DirEntry() {
-
- @Override
- public String getFilename() {
- return remote.getFilename();
- }
-
- @Override
- public long getModifiedTime() {
- return remote.getAttributes()
- .getModifyTime().toMillis();
- }
-
- @Override
- public boolean isDirectory() {
- return remote.getAttributes().isDirectory();
- }
-
- });
+
+ @Override
+ public long getModifiedTime() {
+ return remote.getAttributes().getModifyTime()
+ .toMillis();
}
- }
+
+ @Override
+ public boolean isDirectory() {
+ return remote.getAttributes().isDirectory();
+ }
+
+ });
}
return result;
});
@@ -552,7 +527,7 @@ public class SshdSession implements RemoteSession2 {
@Override
public void rmdir(String path) throws IOException {
- map(() -> {
+ map(x -> {
ftp.rmdir(absolute(path));
return null;
});
@@ -561,7 +536,7 @@ public class SshdSession implements RemoteSession2 {
@Override
public void mkdir(String path) throws IOException {
- map(() -> {
+ map(x -> {
ftp.mkdir(absolute(path));
return null;
});
@@ -569,17 +544,17 @@ public class SshdSession implements RemoteSession2 {
@Override
public InputStream get(String path) throws IOException {
- return map(() -> ftp.read(absolute(path)));
+ return map(x -> ftp.read(absolute(path)));
}
@Override
public OutputStream put(String path) throws IOException {
- return map(() -> ftp.write(absolute(path)));
+ return map(x -> ftp.write(absolute(path)));
}
@Override
public void rm(String path) throws IOException {
- map(() -> {
+ map(x -> {
ftp.remove(absolute(path));
return null;
});
@@ -587,7 +562,7 @@ public class SshdSession implements RemoteSession2 {
@Override
public void rename(String from, String to) throws IOException {
- map(() -> {
+ map(x -> {
String src = absolute(from);
String dest = absolute(to);
try {
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
index cad959c904..58cf8e1ddd 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2018, 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -56,11 +56,14 @@ import org.eclipse.jgit.internal.transport.sshd.JGitUserInteraction;
import org.eclipse.jgit.internal.transport.sshd.OpenSshServerKeyDatabase;
import org.eclipse.jgit.internal.transport.sshd.PasswordProviderWrapper;
import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.internal.transport.sshd.agent.JGitSshAgentFactory;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.SshConfigStore;
import org.eclipse.jgit.transport.SshConstants;
import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.transport.sshd.agent.Connector;
+import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
import org.eclipse.jgit.util.FS;
/**
@@ -102,9 +105,9 @@ public class SshdSessionFactory extends SshSessionFactory implements Closeable {
/**
* Creates a new {@link SshdSessionFactory} using the given {@link KeyCache}
- * and {@link ProxyDataFactory}. The {@code keyCache} is used for all sessions
- * created through this session factory; cached keys are destroyed when the
- * session factory is {@link #close() closed}.
+ * and {@link ProxyDataFactory}. The {@code keyCache} is used for all
+ * sessions created through this session factory; cached keys are destroyed
+ * when the session factory is {@link #close() closed}.
* <p>
* Caching ssh keys in memory for an extended period of time is generally
* considered bad practice, but there may be circumstances where using a
@@ -114,13 +117,21 @@ public class SshdSessionFactory extends SshSessionFactory implements Closeable {
* to use a {@link #createKeyPasswordProvider(CredentialsProvider)
* KeyPasswordProvider} that has access to some secure storage and can save
* and retrieve passwords from there without user interaction. Another
- * approach is to use an ssh agent.
+ * approach is to use an SSH agent.
* </p>
* <p>
* Note that the underlying ssh library (Apache MINA sshd) may or may not
* keep ssh keys in memory for unspecified periods of time irrespective of
* the use of a {@link KeyCache}.
* </p>
+ * <p>
+ * By default, the factory uses the {@link java.util.ServiceLoader} to find
+ * a {@link ConnectorFactory} for creating a {@link Connector} to connect to
+ * a running SSH agent. If it finds one, the SSH agent is used in publickey
+ * authentication. If there is none, no SSH agent will ever be contacted.
+ * Note that one can define {@code IdentitiesOnly yes} for a host entry in
+ * the {@code ~/.ssh/config} file to bypass the SSH agent in any case.
+ * </p>
*
* @param keyCache
* {@link KeyCache} to use for caching ssh keys, or {@code null}
@@ -216,6 +227,11 @@ public class SshdSessionFactory extends SshSessionFactory implements Closeable {
new JGitUserInteraction(credentialsProvider));
client.setUserAuthFactories(getUserAuthFactories());
client.setKeyIdentityProvider(defaultKeysProvider);
+ ConnectorFactory connectors = getConnectorFactory();
+ if (connectors != null) {
+ client.setAgentFactory(
+ new JGitSshAgentFactory(connectors, home));
+ }
// JGit-specific things:
JGitSshClient jgitClient = (JGitSshClient) client;
jgitClient.setKeyCache(getKeyCache());
@@ -437,6 +453,20 @@ public class SshdSessionFactory extends SshSessionFactory implements Closeable {
}
/**
+ * Gets a {@link ConnectorFactory}. If this returns {@code null}, SSH agents
+ * are not supported.
+ * <p>
+ * The default implementation uses {@link ConnectorFactory#getDefault()}
+ * </p>
+ *
+ * @return the factory, or {@code null} if no SSH agent support is desired
+ * @since 6.0
+ */
+ protected ConnectorFactory getConnectorFactory() {
+ return ConnectorFactory.getDefault();
+ }
+
+ /**
* Gets the list of default user known hosts files. The default returns
* ~/.ssh/known_hosts and ~/.ssh/known_hosts2. The ssh config
* {@code UserKnownHostsFile} overrides this default.
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactoryBuilder.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactoryBuilder.java
index 2147c2bd58..7ed9b5ea3b 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactoryBuilder.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactoryBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2020, 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -20,6 +20,7 @@ import java.util.function.Function;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.SshConfigStore;
+import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
import org.eclipse.jgit.util.StringUtils;
/**
@@ -114,7 +115,7 @@ public final class SshdSessionFactoryBuilder {
}
/**
- * A factory interface for creating a @link SshConfigStore}.
+ * A factory interface for creating a {@link SshConfigStore}.
*/
@FunctionalInterface
public interface ConfigStoreFactory {
@@ -233,6 +234,41 @@ public final class SshdSessionFactoryBuilder {
}
/**
+ * Sets an explicit {@link ConnectorFactory}. If {@code null}, there will be
+ * no support for SSH agents.
+ * <p>
+ * If not set, the created {@link SshdSessionFactory} will use the
+ * {@link java.util.ServiceLoader} to find an {@link ConnectorFactory}.
+ * </p>
+ *
+ * @param factory
+ * {@link ConnectorFactory} to use
+ * @return this {@link SshdSessionFactoryBuilder}
+ * @since 6.0
+ */
+ public SshdSessionFactoryBuilder setConnectorFactory(
+ ConnectorFactory factory) {
+ this.state.connectorFactory = factory;
+ this.state.connectorFactorySet = true;
+ return this;
+ }
+
+ /**
+ * Removes a previously set {@link ConnectorFactory}. The created
+ * {@link SshdSessionFactory} will use the {@link java.util.ServiceLoader}
+ * to find an {@link ConnectorFactory}. This is also the default if
+ * {@link #setConnectorFactory(ConnectorFactory)} isn't called at all.
+ *
+ * @return this {@link SshdSessionFactoryBuilder}
+ * @since 6.0
+ */
+ public SshdSessionFactoryBuilder withDefaultConnectorFactory() {
+ this.state.connectorFactory = null;
+ this.state.connectorFactorySet = false;
+ return this;
+ }
+
+ /**
* Builds a {@link SshdSessionFactory} as configured, using the given
* {@link KeyCache} for caching keys.
* <p>
@@ -277,6 +313,10 @@ public final class SshdSessionFactoryBuilder {
BiFunction<File, File, ServerKeyDatabase> serverKeyDatabaseCreator;
+ ConnectorFactory connectorFactory;
+
+ boolean connectorFactorySet;
+
State copy() {
State c = new State();
c.proxyDataFactory = proxyDataFactory;
@@ -290,6 +330,8 @@ public final class SshdSessionFactoryBuilder {
c.defaultKeyFileFinder = defaultKeyFileFinder;
c.defaultKeysProvider = defaultKeysProvider;
c.serverKeyDatabaseCreator = serverKeyDatabaseCreator;
+ c.connectorFactory = connectorFactory;
+ c.connectorFactorySet = connectorFactorySet;
return c;
}
@@ -388,6 +430,15 @@ public final class SshdSessionFactoryBuilder {
return super.createSshConfigStore(homeDir, configFile,
localUserName);
}
+
+ @Override
+ protected ConnectorFactory getConnectorFactory() {
+ if (connectorFactorySet) {
+ return connectorFactory;
+ }
+ // Use default via ServiceLoader
+ return super.getConnectorFactory();
+ }
}
}
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/AbstractConnector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/AbstractConnector.java
new file mode 100644
index 0000000000..71ddc3b003
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/AbstractConnector.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.transport.sshd.agent;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.Objects;
+
+import org.apache.sshd.agent.SshAgentConstants;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+
+/**
+ * Provides some utility methods for implementing {@link Connector}s.
+ *
+ * @since 6.0
+ */
+public abstract class AbstractConnector implements Connector {
+
+ // A somewhat sane lower bound for the maximum reply length
+ private static final int MIN_REPLY_LENGTH = 8 * 1024;
+
+ /**
+ * Default maximum reply length. 256kB is the OpenSSH limit.
+ */
+ protected static final int DEFAULT_MAX_REPLY_LENGTH = 256 * 1024;
+
+ private final int maxReplyLength;
+
+ /**
+ * Creates a new instance using the {@link #DEFAULT_MAX_REPLY_LENGTH}.
+ */
+ protected AbstractConnector() {
+ this(DEFAULT_MAX_REPLY_LENGTH);
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param maxReplyLength
+ * maximum number of payload bytes we're ready to accept
+ */
+ protected AbstractConnector(int maxReplyLength) {
+ if (maxReplyLength < MIN_REPLY_LENGTH) {
+ throw new IllegalArgumentException(
+ "Maximum payload length too small"); //$NON-NLS-1$
+ }
+ this.maxReplyLength = maxReplyLength;
+ }
+
+ /**
+ * Retrieves the maximum message length this {@link AbstractConnector} is
+ * configured for.
+ *
+ * @return the maximum message length
+ */
+ protected int getMaximumMessageLength() {
+ return this.maxReplyLength;
+ }
+
+ /**
+ * Prepares a message for sending by inserting the command and message
+ * length.
+ *
+ * @param command
+ * SSH agent command the request is for
+ * @param message
+ * about to be sent, including the 5 spare bytes at the front
+ * @throws IllegalArgumentException
+ * if {@code message} has less than 5 bytes
+ */
+ protected void prepareMessage(byte command, byte[] message)
+ throws IllegalArgumentException {
+ Objects.requireNonNull(message);
+ if (message.length < 5) {
+ // No translation; internal error
+ throw new IllegalArgumentException("Message buffer for " //$NON-NLS-1$
+ + SshAgentConstants.getCommandMessageName(command)
+ + " must have at least 5 bytes; have only " //$NON-NLS-1$
+ + message.length);
+ }
+ BufferUtils.putUInt(message.length - 4, message);
+ message[4] = command;
+ }
+
+ /**
+ * Checks the received length of a reply.
+ *
+ * @param command
+ * SSH agent command the reply is for
+ * @param length
+ * length as received: number of payload bytes
+ * @return the length as an {@code int}
+ * @throws IOException
+ * if the length is invalid
+ */
+ protected int toLength(byte command, byte[] length)
+ throws IOException {
+ long l = BufferUtils.getUInt(length);
+ if (l <= 0 || l > maxReplyLength - 4) {
+ throw new SshException(MessageFormat.format(
+ SshdText.get().sshAgentReplyLengthError,
+ Long.toString(l),
+ SshAgentConstants.getCommandMessageName(command)));
+ }
+ return (int) l;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/Connector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/Connector.java
new file mode 100644
index 0000000000..d8dfbfc94d
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/Connector.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.transport.sshd.agent;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * Simple interface for connecting to something and making RPC-style
+ * request-reply calls.
+ *
+ * @see ConnectorFactory
+ * @since 6.0
+ */
+public interface Connector extends Closeable {
+
+ /**
+ * Connects to an SSH agent if there is one running. If called when already
+ * connected just returns {@code true}.
+ *
+ * @return {@code true} if an SSH agent is available and connected,
+ * {@false} if no SSH agent is available
+ * @throws IOException
+ * if connecting to the SSH agent failed
+ */
+ boolean connect() throws IOException;
+
+ /**
+ * Performs a remote call to the SSH agent and returns the result.
+ *
+ * @param command
+ * to send
+ * @param message
+ * to send; must have at least 5 bytes, and must have 5 unused
+ * bytes at the front.
+ * @return the result received
+ * @throws IOException
+ * if an error occurs
+ */
+ byte[] rpc(byte command, byte[] message) throws IOException;
+
+ /**
+ * Performs a remote call sending only a command without any parameters to
+ * the SSH agent and returns the result.
+ *
+ * @param command
+ * to send
+ * @return the result received
+ * @throws IOException
+ * if an error occurs
+ */
+ default byte[] rpc(byte command) throws IOException {
+ return rpc(command, new byte[5]);
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/ConnectorFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/ConnectorFactory.java
new file mode 100644
index 0000000000..da98ea7fe0
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/ConnectorFactory.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.transport.sshd.agent;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.transport.sshd.agent.ConnectorFactoryProvider;
+
+/**
+ * A factory for creating {@link Connector}s. This is a service provider
+ * interface; implementations are discovered via the
+ * {@link java.util.ServiceLoader}, or can be set explicitly on a
+ * {@link org.eclipse.jgit.transport.sshd.SshdSessionFactory}.
+ *
+ * @since 6.0
+ */
+public interface ConnectorFactory {
+
+ /**
+ * Retrieves the currently set default {@link ConnectorFactory}. This is the
+ * factory that is used unless overridden by the
+ * {@link org.eclipse.jgit.transport.sshd.SshdSessionFactory}.
+ *
+ * @return the current default factory; may be {@code null} if none is set
+ * and the {@link java.util.ServiceLoader} cannot find any suitable
+ * implementation
+ */
+ static ConnectorFactory getDefault() {
+ return ConnectorFactoryProvider.getDefaultFactory();
+ }
+
+ /**
+ * Sets a default {@link ConnectorFactory}. This is the factory that is used
+ * unless overridden by the
+ * {@link org.eclipse.jgit.transport.sshd.SshdSessionFactory}.
+ * <p>
+ * If no default factory is set programmatically, an implementation is
+ * discovered via the {@link java.util.ServiceLoader}.
+ * </p>
+ *
+ * @param factory
+ * {@link ConnectorFactory} to set, or {@code null} to revert to
+ * the default behavior of using the
+ * {@link java.util.ServiceLoader}.
+ */
+ static void setDefault(ConnectorFactory factory) {
+ ConnectorFactoryProvider.setDefaultFactory(factory);
+ }
+
+ /**
+ * Creates a new {@link Connector}.
+ *
+ * @param identityAgent
+ * identifies the wanted agent connection; if {@code null}, the
+ * factory is free to provide a {@link Connector} to a default
+ * agent. The value will typically come from the
+ * {@code IdentityAgent} setting in {@code ~/.ssh/config}.
+ * @param homeDir
+ * the current local user's home directory as configured in the
+ * {@link org.eclipse.jgit.transport.sshd.SshdSessionFactory}
+ * @return a new {@link Connector}
+ * @throws IOException
+ * if no connector can be created
+ */
+ @NonNull
+ Connector create(String identityAgent, File homeDir)
+ throws IOException;
+
+ /**
+ * Tells whether this {@link ConnectorFactory} is applicable on the
+ * currently running platform.
+ *
+ * @return {@code true} if the factory can be used, {@code false} otherwise
+ */
+ boolean isSupported();
+
+ /**
+ * Retrieves a name for this factory.
+ *
+ * @return the name
+ */
+ String getName();
+
+ /**
+ * {@link ConnectorDescriptor}s describe available {@link Connector}s a
+ * {@link ConnectorFactory} may provide.
+ * <p>
+ * A {@link ConnectorFactory} may support connecting to different SSH
+ * agents. Agents are identified by name; a user can choose a specific agent
+ * for instance via the {@code IdentityAgent} setting in
+ * {@code ~/.ssh/config}.
+ * </p>
+ * <p>
+ * OpenSSH knows two built-in names: "none" for not using any agent, and
+ * "SSH_AUTH_SOCK" for using an agent that communicates over a Unix domain
+ * socket given by the value of environment variable {@code SSH_AUTH_SOCK}.
+ * Other agents can be specified in OpenSSH by specifying the socket file
+ * directly. (The "standard" OpenBSD OpenSSH knows only this communication
+ * mechanism.) "SSH_AUTH_SOCK" is also the default in OpenBSD OpenSSH if
+ * nothing is configured.
+ * </p>
+ * <p>
+ * A particular {@link ConnectorFactory} may support more communication
+ * mechanisms or different agents. For instance, a factory on Windows might
+ * support Pageant, Win32-OpenSSH, or even git bash ssh-agent, and might
+ * accept internal names like "pageant", "openssh", "SSH_AUTH_SOCK" in
+ * {@link ConnectorFactory#create(String, File)} to choose among them.
+ * </p>
+ * The {@link ConnectorDescriptor} interface and the
+ * {@link ConnectorFactory#getSupportedConnectors()} and
+ * {@link ConnectorFactory#getDefaultConnector()} methods provide a way for
+ * code using a {@link ConnectorFactory} to learn what the factory supports
+ * and thus implement some way by which a user can influence the default
+ * behavior if {@code IdentityAgent} is not set or
+ * {@link ConnectorFactory#create(String, File)} is called with
+ * {@code identityAgent == null}.
+ */
+ interface ConnectorDescriptor {
+
+ /**
+ * Retrieves the internal name of a supported {@link Connector}. The
+ * internal name is the one a user can specify for instance in the
+ * {@code IdentityAgent} setting in {@code ~/.ssh/config} to select the
+ * connector.
+ *
+ * @return the internal name; not empty
+ */
+ @NonNull
+ String getIdentityAgent();
+
+ /**
+ * Retrieves a display name for a {@link Connector}, suitable for
+ * showing in a UI.
+ *
+ * @return the display name; properly localized and not empty
+ */
+ @NonNull
+ String getDisplayName();
+ }
+
+ /**
+ * Tells which kinds of SSH agents this {@link ConnectorFactory} supports.
+ * <p>
+ * An implementation of this method should document the possible values it
+ * returns.
+ * </p>
+ *
+ * @return an immutable collection of {@link ConnectorDescriptor}s,
+ * including {@link #getDefaultConnector()} and not including a
+ * descriptor for internal name "none"
+ */
+ @NonNull
+ Collection<ConnectorDescriptor> getSupportedConnectors();
+
+ /**
+ * Tells what kind of {@link Connector} this {@link ConnectorFactory}
+ * creates if {@link ConnectorFactory#create(String, File)} is called with
+ * {@code identityAgent == null}.
+ *
+ * @return a {@link ConnectorDescriptor} for the default connector
+ */
+ ConnectorDescriptor getDefaultConnector();
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/package-info.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/package-info.java
new file mode 100644
index 0000000000..71ca43f3d5
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * Service provider interfaces for connecting to an SSH agent. Implementations
+ * are discovered via the {@link java.util.ServiceLoader}, or can be set
+ * explicitly on a {@link org.eclipse.jgit.transport.sshd.SshdSessionFactory}.
+ */
+package org.eclipse.jgit.transport.sshd.agent;
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/package-info.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/package-info.java
new file mode 100644
index 0000000000..926234a3bd
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * Provides a JGit {@link org.eclipse.jgit.transport.SshSessionFactory}
+ * implemented via <a href="https://mina.apache.org/sshd-project/">Apache MINA
+ * sshd</a>.
+ */
+package org.eclipse.jgit.transport.sshd;