📄 mod_authz_svn.c
字号:
/*
* mod_authz_svn.c: an Apache mod_dav_svn sub-module to provide path
* based authorization for a Subversion repository.
*
* ====================================================================
* Copyright (c) 2003-2004 CollabNet. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://subversion.tigris.org/license-1.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
*
* This software consists of voluntary contributions made by many
* individuals. For exact contribution history, see the revision
* history and logs, available at http://subversion.tigris.org/.
* ====================================================================
*/
#include <httpd.h>
#include <http_config.h>
#include <http_core.h>
#include <http_request.h>
#include <http_protocol.h>
#include <http_log.h>
#include <ap_config.h>
#include <apr_uri.h>
#include <mod_dav.h>
#include "mod_dav_svn.h"
#include "svn_error.h"
#include "svn_path.h"
#include "svn_config.h"
#include "svn_string.h"
module AP_MODULE_DECLARE_DATA authz_svn_module;
enum {
AUTHZ_SVN_NONE = 0,
AUTHZ_SVN_READ = 1,
AUTHZ_SVN_WRITE = 2,
AUTHZ_SVN_RECURSIVE = 4
};
typedef struct {
int authoritative;
int anonymous;
const char *base_path;
const char *access_file;
} authz_svn_config_rec;
struct parse_authz_baton {
apr_pool_t *pool;
svn_config_t *config;
const char *user;
int allow;
int deny;
int required_access;
const char *repos_path;
const char *qualified_repos_path;
int access;
};
/*
* Configuration
*/
static void *create_authz_svn_dir_config(apr_pool_t *p, char *d)
{
authz_svn_config_rec *conf = apr_pcalloc(p, sizeof(*conf));
conf->base_path = d;
/* By default keep the fortress secure */
conf->authoritative = 1;
conf->anonymous = 1;
return conf;
}
static const command_rec authz_svn_cmds[] =
{
AP_INIT_FLAG("AuthzSVNAuthoritative", ap_set_flag_slot,
(void *)APR_OFFSETOF(authz_svn_config_rec, authoritative),
OR_AUTHCFG,
"Set to 'Off' to allow access control to be passed along to "
"lower modules. (default is On.)"),
AP_INIT_TAKE1("AuthzSVNAccessFile", ap_set_file_slot,
(void *)APR_OFFSETOF(authz_svn_config_rec, access_file),
OR_AUTHCFG,
"Text file containing permissions of repository paths."),
AP_INIT_FLAG("AuthzSVNAnonymous", ap_set_flag_slot,
(void *)APR_OFFSETOF(authz_svn_config_rec, anonymous),
OR_AUTHCFG,
"Set to 'Off' to skip access control when no authenticated "
"user is required. (default is On.)"),
{ NULL }
};
/*
* Access checking
*/
static int group_contains_user(svn_config_t *cfg,
const char *group, const char *user, apr_pool_t *pool)
{
const char *value;
apr_array_header_t *list;
int i;
svn_config_get(cfg, &value, "groups", group, "");
list = svn_cstring_split(value, ",", TRUE, pool);
for (i = 0; i < list->nelts; i++) {
const char *group_user = APR_ARRAY_IDX(list, i, char *);
if (!strcmp(user, group_user))
return 1;
}
return 0;
}
static svn_boolean_t parse_authz_line(const char *name, const char *value,
void *baton)
{
struct parse_authz_baton *b = baton;
if (strcmp(name, "*")) {
if (!b->user) {
return TRUE;
}
if (*name == '@') {
if (!group_contains_user(b->config, &name[1], b->user, b->pool))
return TRUE;
}
else if (strcmp(name, b->user)) {
return TRUE;
}
}
if (ap_strchr_c(value, 'r')) {
b->allow |= AUTHZ_SVN_READ;
}
else {
b->deny |= AUTHZ_SVN_READ;
}
if (ap_strchr_c(value, 'w')) {
b->allow |= AUTHZ_SVN_WRITE;
}
else {
b->deny |= AUTHZ_SVN_WRITE;
}
ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, b->pool,
"%s = %s => allow = %i, deny = %i",
name, value, b->allow, b->deny);
return TRUE;
}
/*
* Return TRUE when ACCESS has been determined.
*/
static int parse_authz_lines(svn_config_t *cfg,
const char *repos_name, const char *repos_path,
const char *user,
int required_access, int *access,
apr_pool_t *pool)
{
const char *qualified_repos_path;
struct parse_authz_baton baton = { 0 };
baton.pool = pool;
baton.config = cfg;
baton.user = user;
/* First try repos specific */
qualified_repos_path = apr_pstrcat(pool, repos_name, ":", repos_path,
NULL);
svn_config_enumerate(cfg, qualified_repos_path,
parse_authz_line, &baton);
*access = !(baton.deny & required_access)
|| (baton.allow & required_access);
if ((baton.deny & required_access)
|| (baton.allow & required_access))
return TRUE;
svn_config_enumerate(cfg, repos_path,
parse_authz_line, &baton);
*access = !(baton.deny & required_access)
|| (baton.allow & required_access);
return (baton.deny & required_access)
|| (baton.allow & required_access);
}
static svn_boolean_t parse_authz_section(const char *section_name,
void *baton)
{
struct parse_authz_baton *b = baton;
int conclusive;
if (strncmp(section_name, b->qualified_repos_path,
strlen(b->qualified_repos_path))
&& strncmp(section_name, b->repos_path,
strlen(b->repos_path))) {
/* No match, move on to the next section. */
return TRUE;
}
b->allow = b->deny = 0;
svn_config_enumerate(b->config, section_name,
parse_authz_line, b);
conclusive = (b->deny & b->required_access)
|| (b->allow & b->required_access);
b->access = !(b->deny & b->required_access)
|| (b->allow & b->required_access)
|| !conclusive;
/* If access isn't denied, move on to check the next section. */
return b->access;
}
static int parse_authz_sections(svn_config_t *cfg,
const char *repos_name, const char *repos_path,
const char *user,
int required_access,
apr_pool_t *pool)
{
struct parse_authz_baton baton = { 0 };
baton.pool = pool;
baton.config = cfg;
baton.user = user;
baton.required_access = required_access;
baton.repos_path = repos_path;
baton.qualified_repos_path = apr_pstrcat(pool, repos_name, ":",
repos_path, NULL);
baton.access = 1; /* Allow by default */
svn_config_enumerate_sections(cfg, parse_authz_section, &baton);
return baton.access;
}
static int check_access(svn_config_t *cfg, const char *repos_name,
const char *repos_path, const char *user,
int required_access, apr_pool_t *pool)
{
const char *base_name;
const char *original_repos_path = repos_path;
int access;
if (!repos_path) {
/* XXX: Check if the user has 'required_access' _anywhere_ in the
* XXX: repository. For now, make this always succeed, until
* XXX: we come up with a good way of figuring this out.
*/
return 1;
}
base_name = repos_path;
while (!parse_authz_lines(cfg, repos_name, repos_path,
user, required_access, &access,
pool)) {
if (base_name[0] == '/' && base_name[1] == '\0') {
/* By default, deny access */
return 0;
}
svn_path_split(repos_path, &repos_path, &base_name, pool);
}
if (access && (required_access & AUTHZ_SVN_RECURSIVE) != 0) {
/* Check access on entries below the current repos path */
access = parse_authz_sections(cfg,
repos_name, original_repos_path,
user, required_access,
pool);
}
return access;
}
/* Check if the current request R is allowed. Upon exit *REPOS_PATH_REF
* will contain the path and repository name that an operation was requested
* on in the form 'name:path'. *DEST_REPOS_PATH_REF will contain the
* destination path if the requested operation was a MOVE or a COPY.
* Returns OK when access is allowed, DECLINED when it isn't, or an HTTP_
* error code when an error occurred.
*/
static int req_check_access(request_rec *r,
authz_svn_config_rec *conf,
const char **repos_path_ref,
const char **dest_repos_path_ref)
{
const char *dest_uri;
apr_uri_t parsed_dest_uri;
const char *cleaned_uri;
int trailing_slash;
const char *repos_name;
const char *dest_repos_name;
const char *relative_path;
const char *repos_path;
const char *dest_repos_path = NULL;
dav_error *dav_err;
int authz_svn_type = 0;
svn_config_t *access_conf = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -