Project

General

Profile

Patch #4905 » 0001-merged-LeeF-s-patch-with-Michael-Fox-s-security-fix.patch

Patch with LeeF's fix for allowing repositories to append .git after the project ID and Michael Fox's security fix - Antonio García-Domínguez, 2011-03-31 12:39

View differences:

extra/svn/Redmine.pm
93 93

  
94 94
And you need to upgrade at least reposman.rb (after r860).
95 95

  
96
=head1 GIT SMART HTTP SUPPORT
97

  
98
Git's smart HTTP protocol (available since Git 1.7.0) will not work with the
99
above settings. Redmine.pm normally does access control depending on the HTTP
100
method used: read-only methods are OK for everyone in public projects and
101
members with read rights in private projects. The rest require membership with
102
commit rights in the project.
103

  
104
However, this scheme doesn't work for Git's smart HTTP protocol, as it will use
105
POST even for a simple clone. Instead, read-only requests must be detected using
106
the full URL (including the query string): anything that doesn't belong to the
107
git-receive-pack service is read-only.
108

  
109
To activate this mode of operation, add this line inside your <Location /git>
110
block:
111

  
112
  RedmineGitSmartHttp yes
113

  
114
Here's a sample Apache configuration which integrates git-http-backend with
115
a MySQL database and this new option:
116

  
117
   SetEnv GIT_PROJECT_ROOT /var/www/git/
118
   SetEnv GIT_HTTP_EXPORT_ALL
119
   ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
120
   <Location /git>
121
       Order allow,deny
122
       Allow from all
123

  
124
       AuthType Basic
125
       AuthName Git
126
       Require valid-user
127

  
128
       PerlAccessHandler Apache::Authn::Redmine::access_handler
129
       PerlAuthenHandler Apache::Authn::Redmine::authen_handler
130
       # for mysql
131
       RedmineDSN "DBI:mysql:database=redmine;host=127.0.0.1"
132
       RedmineDbUser "redmine"
133
       RedmineDbPass "xxx"
134
       RedmineGitSmartHttp yes
135
    </Location>
136

  
137
Make sure that all the names of the repositories under /var/www/git/ have a
138
matching identifier for some project: /var/www/git/myproject and
139
/var/www/git/myproject.git will work. You can put both bare and non-bare
140
repositories in /var/www/git, though bare repositories are strongly
141
recommended. You should create them with the rights of the user running Redmine,
142
like this:
143

  
144
  cd /var/www/git
145
  sudo -u user-running-redmine mkdir myproject
146
  cd myproject
147
  sudo -u user-running-redmine git init --bare
148

  
149
Once you have activated this option, you have three options when cloning a
150
repository:
151

  
152
- Cloning using "http://user@host/git/repo(.git)" works, but will ask for the password
153
  all the time.
154

  
155
- Cloning with "http://user:pass@host/git/repo(.git)" does not have this problem, but
156
  this could reveal accidentally your password to the console in some versions
157
  of Git, and you would have to ensure that .git/config is not readable except
158
  by the owner for each of your projects.
159

  
160
- Use "http://host/git/repo(.git)", and store your credentials in the ~/.netrc
161
  file. This is the recommended solution, as you only have one file to protect
162
  and passwords will not be leaked accidentally to the console.
163

  
164
  IMPORTANT NOTE: It is *very important* that the file cannot be read by other
165
  users, as it will contain your password in cleartext. To create the file, you
166
  can use the following commands, replacing yourhost, youruser and yourpassword
167
  with the right values:
168

  
169
    touch ~/.netrc
170
    chmod 600 ~/.netrc
171
    echo -e "machine yourhost\nlogin youruser\npassword yourpassword" > ~/.netrc
172

  
96 173
=cut
97 174

  
98 175
use strict;
......
142 219
    args_how => TAKE1,
143 220
    errmsg => 'RedmineCacheCredsMax must be decimal number',
144 221
  },
222
  {
223
    name => 'RedmineGitSmartHttp',
224
    req_override => OR_AUTHCFG,
225
    args_how => TAKE1,
226
  },
145 227
);
146 228

  
147 229
sub RedmineDSN { 
......
178 260
  }
179 261
}
180 262

  
263
sub RedmineGitSmartHttp {
264
  my ($self, $parms, $arg) = @_;
265
  $arg = lc $arg;
266

  
267
  if ($arg eq "yes" || $arg eq "true") {
268
    $self->{RedmineGitSmartHttp} = 1;
269
  } else {
270
    $self->{RedmineGitSmartHttp} = 0;
271
  }
272
}
273

  
181 274
sub trim {
182 275
  my $string = shift;
183 276
  $string =~ s/\s{2,}/ /g;
......
194 287

  
195 288
my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
196 289

  
290
sub request_is_read_only {
291
  my ($r) = @_;
292
  my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
293

  
294
  # Do we use Git's smart HTTP protocol, or not?
295
  if (defined $cfg->{RedmineGitSmartHttp} and $cfg->{RedmineGitSmartHttp}) {
296
    my $uri = $r->unparsed_uri;
297
    my $location = $r->location;
298
    my $is_read_only = $uri !~ m{^$location/*[^/]+/+(info/refs\?service=)?git\-receive\-pack$}o;
299
    return $is_read_only;
300
  } else {
301
    # Old behaviour: check the HTTP method
302
    my $method = $r->method;
303
    return defined $read_only_methods{$method};
304
  }
305
}
306

  
197 307
sub access_handler {
198 308
  my $r = shift;
199 309

  
......
202 312
      return FORBIDDEN;
203 313
  }
204 314

  
205
  my $method = $r->method;
206
  return OK unless defined $read_only_methods{$method};
315
  return OK unless request_is_read_only($r);
207 316

  
208 317
  my $project_id = get_project_identifier($r);
209 318

  
......
320 429

  
321 430
      unless ($auth_source_id) {
322 431
	  my $method = $r->method;
323
          if ($hashed_password eq $pass_digest && ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) {
432
          if ($hashed_password eq $pass_digest && ((request_is_read_only($r) && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) {
324 433
              $ret = 1;
325 434
              last;
326 435
          }
......
339 448
                filter  =>      "(".$rowldap[6]."=%s)"
340 449
            );
341 450
            my $method = $r->method;
342
            $ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass) && ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/));
451
            $ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass)
452
			 && ((request_is_read_only($r) && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/));
343 453

  
344 454
          }
345 455
          $sthldap->finish();
......
373 483
    
374 484
    my $location = $r->location;
375 485
    my ($identifier) = $r->uri =~ m{$location/*([^/]+)};
486
    $identifier =~ s/\.git//;
376 487
    $identifier;
377 488
}
378 489

  
(11-11/24)