diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/src/main.rs | 97 |
1 files changed, 56 insertions, 41 deletions
diff --git a/server/src/main.rs b/server/src/main.rs index 4a88dcd..e615d19 100644 --- a/server/src/main.rs +++ b/server/src/main.rs | |||
@@ -762,28 +762,46 @@ fn authorize_batch( | |||
762 | specific_claims: common::SpecificClaims::BatchApi(operation), | 762 | specific_claims: common::SpecificClaims::BatchApi(operation), |
763 | repo_path, | 763 | repo_path, |
764 | }; | 764 | }; |
765 | if verify_claims(conf, &claims, headers)? { | 765 | if !verify_claims(conf, &claims, headers)? { |
766 | return Ok(Trusted(true)); | 766 | return authorize_batch_unauthenticated(conf, public, operation, headers); |
767 | } | 767 | } |
768 | return Ok(Trusted(true)); | ||
769 | } | ||
768 | 770 | ||
771 | fn authorize_batch_unauthenticated( | ||
772 | conf: &AuthorizationConfig, | ||
773 | public: bool, | ||
774 | operation: common::Operation, | ||
775 | headers: &HeaderMap, | ||
776 | ) -> Result<Trusted, GitLfsErrorResponse<'static>> { | ||
769 | let trusted = forwarded_from_trusted_host(headers, &conf.trusted_forwarded_hosts)?; | 777 | let trusted = forwarded_from_trusted_host(headers, &conf.trusted_forwarded_hosts)?; |
770 | if operation != common::Operation::Download { | 778 | match operation { |
771 | if trusted { | 779 | common::Operation::Upload => { |
780 | // Trusted users can clone all repositories (by virtue of accessing the server via a | ||
781 | // trusted network). However, they can not push without proper authentication. Untrusted | ||
782 | // users who are also not authenticated should not need to know which repositories exists. | ||
783 | // Therefore, we tell untrusted && unauthenticated users that the repo doesn't exist, but | ||
784 | // tell trusted users that they need to authenticate. | ||
785 | if !trusted { | ||
786 | return Err(REPO_NOT_FOUND); | ||
787 | } | ||
772 | return Err(make_error_resp( | 788 | return Err(make_error_resp( |
773 | StatusCode::FORBIDDEN, | 789 | StatusCode::FORBIDDEN, |
774 | "Authentication required to upload", | 790 | "Authentication required to upload", |
775 | )); | 791 | )); |
792 | }, | ||
793 | common::Operation::Download => { | ||
794 | // Again, trusted users can see all repos. For untrusted users, we first need to check | ||
795 | // whether the repo is public before we authorize. If the user is untrusted and the | ||
796 | // repo isn't public, we just act like it doesn't even exist. | ||
797 | if !trusted { | ||
798 | if !public { | ||
799 | return Err(REPO_NOT_FOUND) | ||
800 | } | ||
801 | return Ok(Trusted(false)) | ||
802 | } | ||
803 | return Ok(Trusted(true)); | ||
776 | } | 804 | } |
777 | return Err(REPO_NOT_FOUND); | ||
778 | } | ||
779 | if trusted { | ||
780 | return Ok(Trusted(true)); | ||
781 | } | ||
782 | |||
783 | if public { | ||
784 | Ok(Trusted(false)) | ||
785 | } else { | ||
786 | Err(REPO_NOT_FOUND) | ||
787 | } | 805 | } |
788 | } | 806 | } |
789 | 807 | ||
@@ -909,35 +927,32 @@ fn verify_claims( | |||
909 | const INVALID_AUTHZ_HEADER: GitLfsErrorResponse = | 927 | const INVALID_AUTHZ_HEADER: GitLfsErrorResponse = |
910 | make_error_resp(StatusCode::BAD_REQUEST, "Invalid authorization header"); | 928 | make_error_resp(StatusCode::BAD_REQUEST, "Invalid authorization header"); |
911 | 929 | ||
912 | if let Some(authz) = headers.get(header::AUTHORIZATION) { | 930 | let Some(authz) = headers.get(header::AUTHORIZATION) else { |
913 | if let Ok(authz) = authz.to_str() { | 931 | return Ok(false); |
914 | if let Some(val) = authz.strip_prefix("Gitolfs3-Hmac-Sha256 ") { | 932 | }; |
915 | let (tag, expires_at) = val.split_once(' ').ok_or(INVALID_AUTHZ_HEADER)?; | 933 | let authz = authz.to_str().map_err(|_| INVALID_AUTHZ_HEADER)?; |
916 | let tag: common::Digest<32> = tag.parse().map_err(|_| INVALID_AUTHZ_HEADER)?; | 934 | let val = authz.strip_prefix("Gitolfs3-Hmac-Sha256 ").ok_or(INVALID_AUTHZ_HEADER)?; |
917 | let expires_at: i64 = expires_at.parse().map_err(|_| INVALID_AUTHZ_HEADER)?; | 935 | let (tag, expires_at) = val.split_once(' ').ok_or(INVALID_AUTHZ_HEADER)?; |
918 | let expires_at = | 936 | let tag: common::Digest<32> = tag.parse().map_err(|_| INVALID_AUTHZ_HEADER)?; |
919 | DateTime::<Utc>::from_timestamp(expires_at, 0).ok_or(INVALID_AUTHZ_HEADER)?; | 937 | let expires_at: i64 = expires_at.parse().map_err(|_| INVALID_AUTHZ_HEADER)?; |
920 | let Some(expected_tag) = common::generate_tag( | 938 | let expires_at = |
921 | common::Claims { | 939 | DateTime::<Utc>::from_timestamp(expires_at, 0).ok_or(INVALID_AUTHZ_HEADER)?; |
922 | specific_claims: claims.specific_claims, | 940 | let expected_tag = common::generate_tag( |
923 | repo_path: claims.repo_path, | 941 | common::Claims { |
924 | expires_at, | 942 | specific_claims: claims.specific_claims, |
925 | }, | 943 | repo_path: claims.repo_path, |
926 | &conf.key, | 944 | expires_at, |
927 | ) else { | 945 | }, |
928 | return Err(make_error_resp( | 946 | &conf.key, |
929 | StatusCode::INTERNAL_SERVER_ERROR, | 947 | ).ok_or_else(|| make_error_resp( |
930 | "Internal server error", | 948 | StatusCode::INTERNAL_SERVER_ERROR, |
931 | )); | 949 | "Internal server error", |
932 | }; | 950 | ))?; |
933 | if tag == expected_tag { | 951 | if tag != expected_tag { |
934 | return Ok(true); | ||
935 | } | ||
936 | } | ||
937 | } | ||
938 | return Err(INVALID_AUTHZ_HEADER); | 952 | return Err(INVALID_AUTHZ_HEADER); |
939 | } | 953 | } |
940 | Ok(false) | 954 | |
955 | Ok(true) | ||
941 | } | 956 | } |
942 | 957 | ||
943 | fn authorize_get( | 958 | fn authorize_get( |