Config: allow custom Git user agent string settings
[girocco.git] / bin / git-http-backend-verify
blobc3c5b1bc6fbcc99a19d4422954fb403c4320ce1d
1 #!/bin/sh
3 # Abort any push early if the pushing user doesn't have any push permissions
4 # at all. This avoids unnecessary traffic and unpacked object pollution.
6 # Set GIT_HTTP_BACKEND_BIN to change the default http-backend binary from
7 # the default of Config.pm $git_http_backend_bin (which itself has a default
8 # of "/usr/lib/git-core/git-http-backend")
10 # Note that GIT_PROJECT_ROOT must be set to use this script.
12 # Also prevents standard error output from git-http-backend cluttering up the
13 # server's log unless GIT_HTTP_BACKEND_SHOW_ERRORS is set to a non-empty value.
15 set -e
17 . @basedir@/shlib.sh
19 unset GIT_USER_AGENT
20 if [ -n "$defined_cfg_git_server_ua" ]; then
21 GIT_USER_AGENT="$cfg_git_server_ua"
22 export GIT_USER_AGENT
25 [ -z "$GIT_HTTP_BACKEND_BIN" ] || cfg_git_http_backend_bin="$GIT_HTTP_BACKEND_BIN"
26 [ -n "$cfg_git_http_backend_bin" ] ||
27 cfg_git_http_backend_bin="$($cfg_git_bin --exec-path)/git-http-backend"
29 # This script is called for both fetch and push.
30 # Only the following conditions trigger a push permissions check:
32 # 1. REQUEST_METHOD=GET
33 # and PATH_INFO ends with "/info/refs"
34 # and QUERY_STRING has "service=git-receive-pack"
36 # 2. REQUEST_METHOD=POST
37 # and PATH_INFO ends with "/git-receive-pack"
39 # Note that there is no check for PATH_INFO being under a certain root as
40 # it's presumed that GIT_PROJECT_ROOT has been set and so all PATH_INFO
41 # values are effectively forced under the desired root.
43 # The REQUEST_METHOD is validated. For smart HTTP requests only the project
44 # name is extracted and validated and the corresponding project directory must
45 # exist under $cfg_reporoot. Non-smart HTTP fetch requests (GET or HEAD) are
46 # passed on unchanged and unchecked.
48 errorhdrs()
50 printf '%s\r\n' "Status: $1 $2"
51 printf '%s\r\n' "Expires: Fri, 01 Jan 1980 00:00:00 GMT"
52 printf '%s\r\n' "Pragma: no-cache"
53 printf '%s\r\n' "Cache-Control: no-cache, max-age=0, must-revalidate"
54 [ -z "$3" ] || printf '%s\r\n' "$3"
55 printf '%s\r\n' "Content-Type: text/plain"
56 printf '\r\n'
59 msglines()
61 while [ $# -gt 0 ]; do
62 printf '%s\n' "$1"
63 shift
64 done
67 internalerr()
69 errorhdrs 500 "Internal Server Error"
70 if [ $# -eq 0 ]; then
71 msglines "Internal Server Error"
72 echo "Internal Server Error" >&2
73 else
74 msglines "$@"
75 while [ $# -gt 0 ]; do
76 printf '%s\n' "$1" >&2
77 shift
78 done
80 exit 0
83 methodnotallowed()
85 errorhdrs 405 "Method Not Allowed" "Allow: GET, HEAD, POST"
86 if [ $# -eq 0 ]; then
87 msglines "Method Not Allowed"
88 else
89 msglines "$@"
91 exit 0
94 forbidden()
96 errorhdrs 403 Forbidden
97 if [ $# -eq 0 ]; then
98 msglines "Forbidden"
99 else
100 msglines "$@"
102 exit 0
105 needsauth()
107 errorhdrs 401 "Authorization Required"
108 if [ $# -eq 0 ]; then
109 msglines "Authorization Required"
110 else
111 msglines "$@"
113 exit 0
116 [ -n "$GIT_PROJECT_ROOT" ] || { internalerr 'GIT_PROJECT_ROOT must be set'; exit 1; }
118 proj=
119 smart=
120 suffix=
121 needsauthcheck=
122 pathcheck="${PATH_INFO#/}"
123 if [ "$REQUEST_METHOD" = "GET" -o "$REQUEST_METHOD" = "HEAD" ]; then
124 # We do not currently validate non-smart GET/HEAD requests.
125 # There are only 8 possible suffix values that need to be allowed for
126 # non-smart HTTP GET/HEAD fetches (see http-backend.c):
127 # /HEAD
128 # /info/refs
129 # /objects/info/alternates
130 # /objects/info/http-alternates
131 # /objects/info/packs
132 # /objects/[0-9a-f]{2}/[0-9a-f]{38}
133 # /objects/pack/pack-[0-9a-f]{40}.idx
134 # /objects/pack/pack-[0-9a-f]{40}.pack
135 case "$pathcheck" in *"/info/refs")
136 case "&$QUERY_STRING&" in
137 *"&service=git-receive-pack&"*)
138 smart=1
139 needsauthcheck=1
140 proj="${pathcheck%/info/refs}"
141 suffix=info/refs
143 *"&service=git-upload-pack&"*)
144 smart=1
145 proj="${pathcheck%/info/refs}"
146 suffix=info/refs
148 esac
149 esac
150 elif [ "$REQUEST_METHOD" = "POST" ]; then
151 case "$pathcheck" in
152 *"/git-receive-pack")
153 smart=1
154 needsauthcheck=1
155 proj="${pathcheck%/git-receive-pack}"
156 suffix=git-receive-pack
158 *"/git-upload-pack")
159 smart=1
160 proj="${pathcheck%/git-upload-pack}"
161 suffix=git-upload-pack
164 forbidden
165 exit 1
167 esac
168 else
169 methodnotallowed
170 exit 1
173 if [ -n "$smart" ]; then
174 # add a missing trailing .git
175 case "$proj" in
176 *.git) :;;
178 proj="$proj.git"
179 esac
181 reporoot="$cfg_reporoot"
182 dir="$reporoot/$proj"
184 # Valid project names never end in .git (we add that automagically), so a valid
185 # fork can never have .git at the end of any path component except the last.
186 # We check this to avoid a situation where a certain collection of pushed refs
187 # could be mistaken for a GIT_DIR. Git would ultimately complain, but some
188 # undesirable things could happen along the way.
190 # Remove the leading $reporoot and trailing .git to get a test string
191 testpath="${dir#$reporoot/}"
192 testpath="${testpath%.git}"
193 case "$testpath/" in *.[Gg][Ii][Tt]/*)
194 forbidden
195 exit 1
196 esac
198 if ! [ -d "$dir" ] || ! [ -f "$dir/HEAD" ] || ! [ -d "$dir/objects" ]; then
199 forbidden
200 exit 1
204 if [ -z "$needsauthcheck" ] || [ -z "$smart" ]; then
205 if [ -n "$GIT_HTTP_BACKEND_SHOW_ERRORS" ]; then
206 exec "$cfg_git_http_backend_bin" "$@"
207 else
208 exec "$cfg_git_http_backend_bin" "$@" 2>/dev/null
210 internalerr "exec failed: $cfg_git_http_backend_bin"
211 exit 1
214 projbare="${proj%.git}"
216 if ! [ -f "$dir/.nofetch" ]; then
217 forbidden "The $proj project is a mirror and may not be pushed to, sorry"
218 exit 1
221 authuser="${REMOTE_USER#/UID=}"
222 authuuid="${authuser}"
223 authuser="${authuser%/dnQualifier=*}"
224 authuuid="${authuuid#$authuser}"
225 authuuid="${authuuid#/dnQualifier=}"
226 if [ -z "$authuser" ]; then
227 needsauth "Only authenticated users may push, sorry"
228 exit 1
230 if [ "$authuser" != "mob" -o "$cfg_mob" != "mob" ]; then
231 if ! useruuid="$("$cfg_basedir/bin/get_user_uuid" "$authuser")" || [ "$useruuid" != "$authuuid" ]; then
232 forbidden "The user '$authuser' certificate being used is no longer valid." \
233 "You may download a new user certificate at $cfg_webadmurl/edituser.cgi"
234 exit 1
238 if ! "$cfg_basedir/bin/can_user_push_http" "$projbare" "$authuser"; then
239 # If mob is enabled and mob has push permissions and
240 # the current user is not the mob then it's a personal mob push
241 # presuming the special mob directory has been set up
242 if [ "$cfg_mob" = "mob" -a "$authuser" != "mob" -a -d "$cfg_reporoot/$proj/mob" ] &&
243 "$cfg_basedir/bin/can_user_push_http" "$projbare" "mob"; then
245 umask 113
246 > "$cfg_chroot/etc/sshactive/${authuser},"
247 mv -f "$cfg_chroot/etc/sshactive/${authuser}," "$cfg_chroot/etc/sshactive/${authuser}"
249 export PATH_INFO="/$proj/mob/$suffix"
250 if [ -n "$GIT_HTTP_BACKEND_SHOW_ERRORS" ]; then
251 exec "$cfg_git_http_backend_bin" "$@"
252 else
253 exec "$cfg_git_http_backend_bin" "$@" 2>/dev/null
255 internalerr "exec failed: $cfg_git_http_backend_bin"
256 exit 1
258 forbidden "The user '$authuser' does not have push permissions for project '$proj'." \
259 "You may adjust push permissions at $cfg_webadmurl/editproj.cgi?name=$proj"
260 exit 1
264 umask 113
265 > "$cfg_chroot/etc/sshactive/${authuser},"
266 mv -f "$cfg_chroot/etc/sshactive/${authuser}," "$cfg_chroot/etc/sshactive/${authuser}"
268 if [ -n "$GIT_HTTP_BACKEND_SHOW_ERRORS" ]; then
269 exec "$cfg_git_http_backend_bin" "$@"
270 else
271 exec "$cfg_git_http_backend_bin" "$@" 2>/dev/null
273 internalerr "exec failed: $cfg_git_http_backend_bin"
274 exit 1