diff options
Diffstat (limited to 'server/src')
| -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( |