git-daemon-verify/git-http-backend-verify: validate bin and set PATH
[girocco.git] / bin / git-http-backend-verify
blob32e0f9b9dbc0fb0c7236c372a487655c75447396
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 # Also prevents standard error output from git-http-backend cluttering up the
14 # server's log unless GIT_HTTP_BACKEND_SHOW_ERRORS is set to a non-empty value.
16 set -e
18 . @basedir@/shlib.sh
20 unset GIT_USER_AGENT
21 if [ -n "$defined_cfg_git_server_ua" ]; then
22 GIT_USER_AGENT="$cfg_git_server_ua"
23 export GIT_USER_AGENT
26 [ -z "$GIT_HTTP_BACKEND_BIN" ] || cfg_git_http_backend_bin="$GIT_HTTP_BACKEND_BIN"
27 [ -n "$cfg_git_http_backend_bin" ] ||
28 cfg_git_http_backend_bin="$($cfg_git_bin --exec-path)/git-http-backend"
30 GIT_PROJECT_ROOT="$cfg_reporoot"
31 export GIT_PROJECT_ROOT
33 # This script is called for both fetch and push.
34 # Only the following conditions trigger a push permissions check:
36 # 1. REQUEST_METHOD=GET
37 # and PATH_INFO ends with "/info/refs"
38 # and QUERY_STRING has "service=git-receive-pack"
40 # 2. REQUEST_METHOD=POST
41 # and PATH_INFO ends with "/git-receive-pack"
43 # Note that there is no check for PATH_INFO being under a certain root as
44 # GIT_PROJECT_ROOT will be exported and set so all PATH_INFO values are
45 # effectively forced under the desired root.
47 # The REQUEST_METHOD is validated. For smart HTTP requests only the project
48 # name is extracted and validated and the corresponding project directory must
49 # exist under $cfg_reporoot. Non-smart HTTP fetch requests (GET or HEAD) are
50 # passed on unchanged and unchecked.
52 errorhdrs()
54 printf '%s\r\n' "Status: $1 $2"
55 printf '%s\r\n' "Expires: Fri, 01 Jan 1980 00:00:00 GMT"
56 printf '%s\r\n' "Pragma: no-cache"
57 printf '%s\r\n' "Cache-Control: no-cache, max-age=0, must-revalidate"
58 [ -z "$3" ] || printf '%s\r\n' "$3"
59 printf '%s\r\n' "Content-Type: text/plain"
60 printf '\r\n'
63 msglines()
65 while [ $# -gt 0 ]; do
66 printf '%s\n' "$1"
67 shift
68 done
71 internalerr()
73 errorhdrs 500 "Internal Server Error"
74 if [ $# -eq 0 ]; then
75 msglines "Internal Server Error"
76 echo "Internal Server Error" >&2
77 else
78 msglines "$@"
79 while [ $# -gt 0 ]; do
80 printf '%s\n' "$1" >&2
81 shift
82 done
84 exit 0
87 methodnotallowed()
89 errorhdrs 405 "Method Not Allowed" "Allow: GET, HEAD, POST"
90 if [ $# -eq 0 ]; then
91 msglines "Method Not Allowed"
92 else
93 msglines "$@"
95 exit 0
98 forbidden()
100 errorhdrs 403 Forbidden
101 if [ $# -eq 0 ]; then
102 msglines "Forbidden"
103 else
104 msglines "$@"
106 exit 0
109 needsauth()
111 errorhdrs 401 "Authorization Required"
112 if [ $# -eq 0 ]; then
113 msglines "Authorization Required"
114 else
115 msglines "$@"
117 exit 0
120 # A quick sanity check
121 if [ -z "$cfg_git_http_backend_bin" ] || ! [ -x "$cfg_git_http_backend_bin" ]; then
122 internalerr "bad cfg_git_http_backend_bin: $cfg_git_http_backend_bin"
123 exit 1
125 case "$cfg_reporoot" in /?*) :;; *)
126 internalerr "bad reporoot: $cfg_reporoot"
127 exit 1
128 esac
129 [ -n "$GIT_PROJECT_ROOT" ] || { internalerr 'GIT_PROJECT_ROOT must be set'; exit 1; }
131 PATH="$(dirname "$cfg_git_http_backend_bin"):$PATH"
132 export PATH
134 proj=
135 smart=
136 suffix=
137 needsauthcheck=
138 pathcheck="${PATH_INFO#/}"
139 if [ "$REQUEST_METHOD" = "GET" -o "$REQUEST_METHOD" = "HEAD" ]; then
140 # We do not currently validate non-smart GET/HEAD requests.
141 # There are only 8 possible suffix values that need to be allowed for
142 # non-smart HTTP GET/HEAD fetches (see http-backend.c):
143 # /HEAD
144 # /info/refs
145 # /objects/info/alternates
146 # /objects/info/http-alternates
147 # /objects/info/packs
148 # /objects/[0-9a-f]{2}/[0-9a-f]{38}
149 # /objects/pack/pack-[0-9a-f]{40}.idx
150 # /objects/pack/pack-[0-9a-f]{40}.pack
151 case "$pathcheck" in *"/info/refs")
152 proj="${pathcheck%/info/refs}"
153 case "&$QUERY_STRING&" in
154 *"&service=git-receive-pack&"*)
155 smart=1
156 needsauthcheck=1
157 suffix=info/refs
159 *"&service=git-upload-pack&"*)
160 smart=1
161 suffix=info/refs
163 esac
164 esac
165 elif [ "$REQUEST_METHOD" = "POST" ]; then
166 case "$pathcheck" in
167 *"/git-receive-pack")
168 smart=1
169 needsauthcheck=1
170 proj="${pathcheck%/git-receive-pack}"
171 suffix=git-receive-pack
173 *"/git-upload-pack")
174 smart=1
175 proj="${pathcheck%/git-upload-pack}"
176 suffix=git-upload-pack
179 forbidden
180 exit 1
182 esac
183 else
184 methodnotallowed
185 exit 1
188 # Reject any project names that start with _
189 case "$pathcheck" in _*)
190 forbidden
191 esac
193 if [ -n "$smart" ]; then
194 # add a missing trailing .git
195 case "$proj" in
196 *.git) :;;
198 proj="$proj.git"
199 esac
201 reporoot="$cfg_reporoot"
202 dir="$reporoot/$proj"
204 # Valid project names never end in .git (we add that automagically), so a valid
205 # fork can never have .git at the end of any path component except the last.
206 # We check this to avoid a situation where a certain collection of pushed refs
207 # could be mistaken for a GIT_DIR. Git would ultimately complain, but some
208 # undesirable things could happen along the way.
210 # Remove the leading $reporoot and trailing .git to get a test string
211 testpath="${dir#$reporoot/}"
212 testpath="${testpath%.git}"
213 case "$testpath/" in *.[Gg][Ii][Tt]/*|_*)
214 forbidden
215 exit 1
216 esac
218 if ! [ -d "$dir" ] || ! [ -f "$dir/HEAD" ] || ! [ -d "$dir/objects" ]; then
219 forbidden
220 exit 1
224 if [ -z "$needsauthcheck" ] || [ -z "$smart" ]; then
225 if [ -n "$GIT_HTTP_BACKEND_SHOW_ERRORS" ]; then
226 exec "$cfg_git_http_backend_bin" "$@"
227 else
228 exec "$cfg_git_http_backend_bin" "$@" 2>/dev/null
230 internalerr "exec failed: $cfg_git_http_backend_bin"
231 exit 1
234 projbare="${proj%.git}"
236 if ! [ -f "$dir/.nofetch" ]; then
237 forbidden "The $proj project is a mirror and may not be pushed to, sorry"
238 exit 1
241 authuser="${REMOTE_USER#/UID=}"
242 authuuid="${authuser}"
243 authuser="${authuser%/dnQualifier=*}"
244 authuuid="${authuuid#$authuser}"
245 authuuid="${authuuid#/dnQualifier=}"
246 if [ -z "$authuser" ]; then
247 needsauth "Only authenticated users may push, sorry"
248 exit 1
250 if [ "$authuser" != "mob" -o "$cfg_mob" != "mob" ]; then
251 if ! useruuid="$("$cfg_basedir/bin/get_user_uuid" "$authuser")" || [ "$useruuid" != "$authuuid" ]; then
252 forbidden "The user '$authuser' certificate being used is no longer valid." \
253 "You may download a new user certificate at $cfg_webadmurl/edituser.cgi"
254 exit 1
258 if ! "$cfg_basedir/bin/can_user_push_http" "$projbare" "$authuser"; then
259 # If mob is enabled and mob has push permissions and
260 # the current user is not the mob then it's a personal mob push
261 # presuming the special mob directory has been set up
262 if [ "$cfg_mob" = "mob" -a "$authuser" != "mob" -a -d "$cfg_reporoot/$proj/mob" ] &&
263 "$cfg_basedir/bin/can_user_push_http" "$projbare" "mob"; then
265 umask 113
266 > "$cfg_chroot/etc/sshactive/${authuser},"
267 mv -f "$cfg_chroot/etc/sshactive/${authuser}," "$cfg_chroot/etc/sshactive/${authuser}"
269 export PATH_INFO="/$proj/mob/$suffix"
270 if [ -n "$GIT_HTTP_BACKEND_SHOW_ERRORS" ]; then
271 exec "$cfg_git_http_backend_bin" "$@"
272 else
273 exec "$cfg_git_http_backend_bin" "$@" 2>/dev/null
275 internalerr "exec failed: $cfg_git_http_backend_bin"
276 exit 1
278 forbidden "The user '$authuser' does not have push permissions for project '$proj'." \
279 "You may adjust push permissions at $cfg_webadmurl/editproj.cgi?name=$proj"
280 exit 1
284 umask 113
285 > "$cfg_chroot/etc/sshactive/${authuser},"
286 mv -f "$cfg_chroot/etc/sshactive/${authuser}," "$cfg_chroot/etc/sshactive/${authuser}"
288 if [ -n "$GIT_HTTP_BACKEND_SHOW_ERRORS" ]; then
289 exec "$cfg_git_http_backend_bin" "$@"
290 else
291 exec "$cfg_git_http_backend_bin" "$@" 2>/dev/null
293 internalerr "exec failed: $cfg_git_http_backend_bin"
294 exit 1