webconfig: move GIT_HTTP_EXPORT_ALL=1 into git-http-backend-verify
[girocco.git] / bin / git-http-backend-verify
blob0f5676c0a23bdafaf23ce5db866a06ee128b00e5
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 is automatically set to $cfg_reporoot and exported
11 # any incoming value for it will be ignored.
13 # Note that GIT_HTTP_EXPORT_ALL is automatically set to 1 and exported.
15 # Also prevents standard error output from git-http-backend cluttering up the
16 # server's log unless GIT_HTTP_BACKEND_SHOW_ERRORS is set to a non-empty value.
18 set -e
20 . @basedir@/shlib.sh
22 unset GIT_USER_AGENT
23 unset GIT_HTTP_USER_AGENT
24 if [ -n "$defined_cfg_git_server_ua" ]; then
25 GIT_USER_AGENT="$cfg_git_server_ua"
26 export GIT_USER_AGENT
27 GIT_HTTP_USER_AGENT="$cfg_git_server_ua"
28 export GIT_HTTP_USER_AGENT
31 [ -z "$GIT_HTTP_BACKEND_BIN" ] || cfg_git_http_backend_bin="$GIT_HTTP_BACKEND_BIN"
32 [ -n "$cfg_git_http_backend_bin" ] ||
33 cfg_git_http_backend_bin="$($cfg_git_bin --exec-path)/git-http-backend"
35 GIT_PROJECT_ROOT="$cfg_reporoot"
36 GIT_HTTP_EXPORT_ALL=1
37 export GIT_PROJECT_ROOT
38 export GIT_HTTP_EXPORT_ALL
40 # This script is called for both fetch and push.
41 # Only the following conditions trigger a push permissions check:
43 # 1. REQUEST_METHOD=GET
44 # and PATH_INFO ends with "/info/refs"
45 # and QUERY_STRING has "service=git-receive-pack"
47 # 2. REQUEST_METHOD=POST
48 # and PATH_INFO ends with "/git-receive-pack"
50 # Note that there is no check for PATH_INFO being under a certain root as
51 # GIT_PROJECT_ROOT will be exported and set so all PATH_INFO values are
52 # effectively forced under the desired root.
54 # The REQUEST_METHOD is validated. For smart HTTP requests only the project
55 # name is extracted and validated and the corresponding project directory must
56 # exist under $cfg_reporoot. Non-smart HTTP fetch requests (GET or HEAD) are
57 # passed on unchanged and unchecked.
59 errorhdrs()
61 printf '%s\r\n' "Status: $1 $2"
62 printf '%s\r\n' "Expires: Fri, 01 Jan 1980 00:00:00 GMT"
63 printf '%s\r\n' "Pragma: no-cache"
64 printf '%s\r\n' "Cache-Control: no-cache, max-age=0, must-revalidate"
65 [ -z "$3" ] || printf '%s\r\n' "$3"
66 printf '%s\r\n' "Content-Type: text/plain"
67 printf '\r\n'
70 msglines()
72 while [ $# -gt 0 ]; do
73 printf '%s\n' "$1"
74 shift
75 done
78 internalerr()
80 errorhdrs 500 "Internal Server Error"
81 if [ $# -eq 0 ]; then
82 msglines "Internal Server Error"
83 echo "Internal Server Error" >&2
84 else
85 msglines "$@"
86 while [ $# -gt 0 ]; do
87 printf '%s\n' "$1" >&2
88 shift
89 done
91 exit 0
94 methodnotallowed()
96 errorhdrs 405 "Method Not Allowed" "Allow: GET, HEAD, POST"
97 if [ $# -eq 0 ]; then
98 msglines "Method Not Allowed"
99 else
100 msglines "$@"
102 exit 0
105 forbidden()
107 errorhdrs 403 Forbidden
108 if [ $# -eq 0 ]; then
109 msglines "Forbidden"
110 else
111 msglines "$@"
113 exit 0
116 needsauth()
118 errorhdrs 401 "Authorization Required"
119 if [ $# -eq 0 ]; then
120 msglines "Authorization Required"
121 else
122 msglines "$@"
124 exit 0
127 # A quick sanity check
128 if [ -z "$cfg_git_http_backend_bin" ] || ! [ -x "$cfg_git_http_backend_bin" ]; then
129 internalerr "bad cfg_git_http_backend_bin: $cfg_git_http_backend_bin"
130 exit 1
132 case "$cfg_reporoot" in /?*) :;; *)
133 internalerr "bad reporoot: $cfg_reporoot"
134 exit 1
135 esac
136 [ -n "$GIT_PROJECT_ROOT" ] || { internalerr 'GIT_PROJECT_ROOT must be set'; exit 1; }
138 PATH="$(dirname "$cfg_git_http_backend_bin"):$PATH"
139 export PATH
141 proj=
142 smart=
143 suffix=
144 needsauthcheck=
145 pathcheck="${PATH_INFO#/}"
146 if [ "$REQUEST_METHOD" = "GET" -o "$REQUEST_METHOD" = "HEAD" ]; then
147 # We do not currently validate non-smart GET/HEAD requests.
148 # There are only 8 possible suffix values that need to be allowed for
149 # non-smart HTTP GET/HEAD fetches (see http-backend.c):
150 # /HEAD
151 # /info/refs
152 # /objects/info/alternates
153 # /objects/info/http-alternates
154 # /objects/info/packs
155 # /objects/[0-9a-f]{2}/[0-9a-f]{38}
156 # /objects/pack/pack-[0-9a-f]{40}.idx
157 # /objects/pack/pack-[0-9a-f]{40}.pack
158 case "$pathcheck" in *"/info/refs")
159 proj="${pathcheck%/info/refs}"
160 case "&$QUERY_STRING&" in
161 *"&service=git-receive-pack&"*)
162 smart=1
163 needsauthcheck=1
164 suffix=info/refs
166 *"&service=git-upload-pack&"*)
167 smart=1
168 suffix=info/refs
170 esac
171 esac
172 elif [ "$REQUEST_METHOD" = "POST" ]; then
173 case "$pathcheck" in
174 *"/git-receive-pack")
175 smart=1
176 needsauthcheck=1
177 proj="${pathcheck%/git-receive-pack}"
178 suffix=git-receive-pack
180 *"/git-upload-pack")
181 smart=1
182 proj="${pathcheck%/git-upload-pack}"
183 suffix=git-upload-pack
186 forbidden
187 exit 1
189 esac
190 else
191 methodnotallowed
192 exit 1
195 # Reject any project names that start with _
196 case "$pathcheck" in _*)
197 forbidden
198 esac
200 if [ -n "$smart" ]; then
201 # add a missing trailing .git
202 case "$proj" in
203 *.git) :;;
205 proj="$proj.git"
206 esac
208 reporoot="$cfg_reporoot"
209 dir="$reporoot/$proj"
211 # Valid project names never end in .git (we add that automagically), so a valid
212 # fork can never have .git at the end of any path component except the last.
213 # We check this to avoid a situation where a certain collection of pushed refs
214 # could be mistaken for a GIT_DIR. Git would ultimately complain, but some
215 # undesirable things could happen along the way.
217 # Remove the leading $reporoot and trailing .git to get a test string
218 testpath="${dir#$reporoot/}"
219 testpath="${testpath%.git}"
220 case "$testpath/" in *.[Gg][Ii][Tt]/*|_*)
221 forbidden
222 exit 1
223 esac
225 if ! [ -d "$dir" ] || ! [ -f "$dir/HEAD" ] || ! [ -d "$dir/objects" ]; then
226 forbidden
227 exit 1
231 if [ -z "$needsauthcheck" ] || [ -z "$smart" ]; then
232 if [ -n "$GIT_HTTP_BACKEND_SHOW_ERRORS" ]; then
233 exec "$cfg_git_http_backend_bin" "$@"
234 else
235 exec "$cfg_git_http_backend_bin" "$@" 2>/dev/null
237 internalerr "exec failed: $cfg_git_http_backend_bin"
238 exit 1
241 projbare="${proj%.git}"
243 if ! [ -f "$dir/.nofetch" ]; then
244 forbidden "The $proj project is a mirror and may not be pushed to, sorry"
245 exit 1
248 authuser="${REMOTE_USER#/UID=}"
249 authuuid="${authuser}"
250 authuser="${authuser%/dnQualifier=*}"
251 authuuid="${authuuid#$authuser}"
252 authuuid="${authuuid#/dnQualifier=}"
253 if [ -z "$authuser" ]; then
254 needsauth "Only authenticated users may push, sorry"
255 exit 1
257 if [ "$authuser" != "mob" -o "$cfg_mob" != "mob" ]; then
258 if ! useruuid="$("$cfg_basedir/bin/get_user_uuid" "$authuser")" || [ "$useruuid" != "$authuuid" ]; then
259 forbidden "The user '$authuser' certificate being used is no longer valid." \
260 "You may download a new user certificate at $cfg_webadmurl/edituser.cgi"
261 exit 1
265 if ! "$cfg_basedir/bin/can_user_push_http" "$projbare" "$authuser"; then
266 # If mob is enabled and mob has push permissions and
267 # the current user is not the mob then it's a personal mob push
268 # presuming the special mob directory has been set up
269 if [ "$cfg_mob" = "mob" -a "$authuser" != "mob" -a -d "$cfg_reporoot/$proj/mob" ] &&
270 "$cfg_basedir/bin/can_user_push_http" "$projbare" "mob"; then
272 umask 113
273 > "$cfg_chroot/etc/sshactive/${authuser},"
274 mv -f "$cfg_chroot/etc/sshactive/${authuser}," "$cfg_chroot/etc/sshactive/${authuser}"
276 export PATH_INFO="/$proj/mob/$suffix"
277 if [ -n "$GIT_HTTP_BACKEND_SHOW_ERRORS" ]; then
278 exec "$cfg_git_http_backend_bin" "$@"
279 else
280 exec "$cfg_git_http_backend_bin" "$@" 2>/dev/null
282 internalerr "exec failed: $cfg_git_http_backend_bin"
283 exit 1
285 forbidden "The user '$authuser' does not have push permissions for project '$proj'." \
286 "You may adjust push permissions at $cfg_webadmurl/editproj.cgi?name=$proj"
287 exit 1
291 umask 113
292 > "$cfg_chroot/etc/sshactive/${authuser},"
293 mv -f "$cfg_chroot/etc/sshactive/${authuser}," "$cfg_chroot/etc/sshactive/${authuser}"
295 if [ -n "$GIT_HTTP_BACKEND_SHOW_ERRORS" ]; then
296 exec "$cfg_git_http_backend_bin" "$@"
297 else
298 exec "$cfg_git_http_backend_bin" "$@" 2>/dev/null
300 internalerr "exec failed: $cfg_git_http_backend_bin"
301 exit 1