renran421 发表于 2017-1-7 10:03:28

[转载] Apache模块开发/用C语言扩展apache(4:一个生产环境使用的apache module– viewvc权限控制)

  下面公布一个目前在我们公司使用的apache module的源代码。
  我们公司开发人员很多,使用了SVN和viewvc来进行版本控制和查看,通过web界面,SVN能够根据每个用户的权限来控制能够浏览某个项目下的代码,但是viewvc只要你在SVN中有用户,你就可以看当前SVN中所有项目的代码。这个风险比较大,因此,我们开发了一个apache module,用来读取SVN的权限配置文件,把相应的权限集成到VIEWVC中。
  源代码:

#include “apr_strings.h”
#include “apr_hash.h”
#include “apr_tables.h”
#include “apr_md5.h”            
#include “apr_lib.h”            
#include “apr_base64.h”         
#define APR_WANT_STRFUNC      
#include “apr_want.h”
#include “ap_config.h”
#include “httpd.h”
#include “http_config.h”
#include “http_core.h”
#include “http_log.h”
#include “http_protocol.h”
#include “http_request.h”
#include “ap_provider.h”
#include
#define ENABLED      1
#define DISABLED       0

typedef struct
{
short    enabled;
short    debug;
char   *dir;
// the starting path
char   *prefixPath;
// the stop url pattern
char   *stopPattern;
// the svn access file
char   *accessFile;
} authSVN_rec;
struct access_rec
{
// 0: group
// 1: user
// 2: all
short            type;
// the group or user name
char            *name;
// 0: don’t have read access
// 1: have read access
short             access;
// the next access record
struct access_rec *next;
};
module AP_MODULE_DECLARE_DATA authSVN_module;
// src starts with start
static short start_with(const char *src, const char *start)
{
int i = 0;
if(strlen(src) < strlen(start))
return 0;
i = strlen(start) - 1;
while(i >= 0)
{
if(src != start)
return 0;
i–;
}
return 1;
}
// parse the SVN access file
static short parse_access_file(request_rec *r, const char* file,
const authSVN_rec *conf,
apr_hash_t* ugMap,
apr_hash_t* accessMap)
{
ap_configfile_t *f = NULL;
apr_status_t status;
char l, dir;
status = ap_pcfg_openfile(&f, r->pool, file);
short flag = 0;
if (status != APR_SUCCESS)
return 0;
while(!(ap_cfg_getline(l, MAX_STRING_LEN, f)))
{
const char *w = NULL;
char *last = NULL;
apr_table_t *apt= NULL;
struct access_rec*arec = NULL, *arecp = NULL;
if ((l == ‘#’) || (!l)) {
continue;
}
if(start_with(l, “”)) {
flag = 1;
continue;
}
if(l == ‘[’) {
flag = 2;
w = apr_strtok(l, “[]:\n”, &last);
if(w && w == ‘/’) {
// the root directory
snprintf(dir, sizeof(dir), “%s”, conf->prefixPath);
dir = ‘\0′;
}
else if(w && w != ‘/’)
{
const char *project = w;
w = apr_strtok(NULL, “[]:\n”, &last);
if(w)
{
snprintf(dir, sizeof(dir), “%s%s%s”, conf->prefixPath, project, w);
// make sure the dir is not end with /
int len = strlen(dir);
if(dir == ‘/’) dir = ‘\0′;
}
else
dir = ‘\0′;
}
else
{
dir = ‘\0′;
}
continue;
}
if(flag == 1) {
// this is the groups and users definition
w = apr_strtok(l, “=, \n”, &last);
if(w == NULL)
// group name not found
continue;
apt = (apr_table_t *)apr_hash_get(ugMap, (const void *)w, APR_HASH_KEY_STRING);
if(apt == NULL) {
apt = apr_table_make(r->pool, 10);
apr_hash_set(ugMap, (const void *)apr_pstrdup(r->pool, w),
APR_HASH_KEY_STRING, (const void *)apt);
}
while((w = apr_strtok(NULL, “=, \n”, &last)) != NULL) {
// this is group name or user name
if(w == ‘@’) {
w++;
if(w) {
apr_table_setn(apt, apr_pstrdup(r->pool, w), “0″);
}
}
else
{
// this is user name
apr_table_setn(apt, apr_pstrdup(r->pool, w), “1″);
}
}
}
if(flag == 2) {
if(dir == ‘\0′) continue;
w = apr_strtok(l, “= \n”, &last);
if(w)
{
arec = (struct access_rec *)apr_pcalloc(r->pool, sizeof(struct access_rec));
arec->access = 0;
if(w == ‘@’) {
w++;
if(w) {
arec->name = apr_pstrdup(r->pool, w);
arec->type = 0;
}
else continue;
}
else if(w == ‘*’)
{
arec->name = apr_pstrdup(r->pool, “*”);
// this is all
arec->type = 2;
}
else
{
arec->name = apr_pstrdup(r->pool, w);
// this is user name
arec->type = 1;
}
}
else continue;
w = apr_strtok(NULL, “= \n”, &last);
if(!w)
{
arec->access = 0;
}
else
{
arec->access = 1;
}
arecp = (struct access_rec *)apr_hash_get(accessMap, (const void *)dir, APR_HASH_KEY_STRING);
if(arecp == NULL) {
arec->next = NULL;
apr_hash_set(accessMap, (const void *)apr_pstrdup(r->pool, dir),
APR_HASH_KEY_STRING, (const void *)arec);
}
else
{
while(arecp->next != NULL) arecp = arecp->next;
arecp->next = arec;
}
}
}
ap_cfg_closefile(f);
return 1;
}
static void *create_authSVN_dir_config(apr_pool_t *p, char *d)
{
authSVN_rec *conf = (authSVN_rec *)apr_pcalloc(p, sizeof(*conf));
if(conf == NULL) return NULL;
conf->enabled   = DISABLED;
conf->debug       = DISABLED;
conf->dir         = d;
conf->prefixPath= NULL;
conf->stopPattern = NULL;
conf->accessFile= NULL;
return conf;
}

static char hex2int(char c)
{
if( c>=’0′ && c<=&apos;9&apos; ) return (c - &apos;0&apos;);
return (c - &apos;A&apos; + 10);
}

static void url_decode(char *url)
{
char *p= url;
char *p1 = NULL;
while(*p)
{
if(*p == &apos;+&apos;) *p = &apos; &apos;;

if(*p==&apos;%&apos; && *(p+1) && *(p+2))
{
*p = hex2int(toupper(*(p+1))) * 16 + hex2int(toupper(*(p+ 2)));
strcpy(p + 1, p + 3);
}
if(*p==&apos;\\&apos; && *(p+1) && *(p+2) && *(p+3))
{
p1 = p + 1;
if(*p1 && *p1==&apos;x&apos;)
{
*p = hex2int(toupper(*(p+2))) * 16 + hex2int(toupper(*(p+3)));
strcpy(p+1, p+4);
}
}
p++;
}
return;
}
  接上段
  


static void parent_path(char *url)
{
char *p = url + strlen(url) - 1;
while(p != url && *p != &apos;/&apos;) { *p = &apos;\0&apos;; p--; }
if(p != url && *p==&apos;/&apos;) *p = &apos;\0&apos;;
return;
}
// return
// 0: the user don&apos;t belong to this group
// 1: the user belong to this group
static short find_user_in_group(const char* user, const char *group, apr_hash_t* ugMap)
{
apr_table_t *apt = (apr_table_t *)apr_hash_get(ugMap,
(const void *)group,
APR_HASH_KEY_STRING);
if(apt == NULL) return 0;
apr_array_header_t *arr;
apr_table_entry_t*elts;
int i;
arr= (apr_array_header_t *)apr_table_elts(apt);
elts = (apr_table_entry_t *)arr->elts;
for(i=0; inelts; i++)
{
if(elts.key == NULL || elts.val == NULL) continue;
if(elts.val == ‘1′ && strcmp(elts.key, user) == 0)
{
return 1;
}
if(elts.val == ‘0′)
{
if(find_user_in_group(user, elts.key, ugMap))
return 1;
}
}
return 0;
}
// return
//0:don’t have access
//1:have read access
//2:access not found
static short find_access(const char* user, const char* url,
apr_hash_t* ugMap, apr_hash_t* accessMap)
{
struct access_rec *arec= (struct access_rec *)apr_hash_get(accessMap,
(const void *)url, APR_HASH_KEY_STRING);
short access = 2;
while(arec != NULL)
{
if(strcmp(arec->name, “*”) == 0)
{
// specified access to all users and groups on this url
access = arec->access;
}
if(arec->type == 1 && strcmp(arec->name, user) == 0)
{
// specified user access on this url
access = arec->access;
}
if(arec->type == 0)
{
// this is group access
if(find_user_in_group(user, arec->name, ugMap))
access = arec->access;
}
// if this user have access, we return
if(access == 1) return access;
arec = arec->next;
}
return access;
}
static short estimate_access( request_rec *r, const authSVN_rec* conf,
char* url, apr_hash_t* ugMap,
apr_hash_t* accessMap )
{
const char* user = r->user;
// unauthorized
if(!user || !user) return 0;
short access = find_access(user, url, ugMap, accessMap);
if(access < 2) return access;
if(url == &apos;/&apos; && url == &apos;\0&apos;) return 0;
parent_path(url);
return estimate_access(r, conf, url, ugMap, accessMap);
}
// do regexp matching
static short regexp_match(char *str, char *pattern)
{
regex_t      reg;
regmatch_t   pm;
const size_t nmatch = 1;
int res = 0;
short r = 0;
char ebuf;
res = regcomp(&reg, pattern, REG_EXTENDED);
if(res != 0)
{
regfree(&reg);
return 0;
}
res = regexec(&reg, str, nmatch, pm, 0);
if(res == REG_NOMATCH)
r = 0;
else
r = 1;
regfree(&reg);
return r;
}

static int authSVN_handler(request_rec *r)
{
authSVN_rec *conf = ap_get_module_config(r->per_dir_config,
&authSVN_module);
if(!conf || !conf->enabled)
return DECLINED;
if(conf->prefixPath == NULL || !start_with(r->uri, conf->prefixPath))
return DECLINED;
if(conf->stopPattern !=NULL && regexp_match(r->uri, conf->stopPattern))
return DECLINED;
apr_hash_t* ugMap   = apr_hash_make(r->pool);
apr_hash_t* accessMap = apr_hash_make(r->pool);
if(!parse_access_file(r, conf->accessFile, conf, ugMap, accessMap))
return 403;
if(conf->debug)
{
// run in debug mode
// print all users/groups and access information
apr_hash_index_t* hi;
char *key;
apr_table_t *val;
struct access_rec *arec;
apr_array_header_t *arr;
apr_table_entry_t*elts;
int i;
r->content_type=”text/plain”;
ap_rprintf(r, “Parsed Users and Groups:\n”);
hi = apr_hash_first(r->pool, ugMap);
while(hi != NULL)
{
apr_hash_this(hi, (void *)&key, NULL, (void *)&val);
ap_rprintf(r, “%s: “, key);
arr= (apr_array_header_t *)apr_table_elts(val);
elts = (apr_table_entry_t *)arr->elts;
for(i=0; inelts; i++)
{
if(elts.key == NULL || elts.val == NULL) continue;
if(elts.val == ‘0′)
{
ap_rprintf(r, “@”);
}
ap_rprintf(r, “%s “, elts.key);
}
ap_rprintf(r, “\n”);
hi = apr_hash_next(hi);
}
ap_rprintf(r, “Parsed Path Access:\n”);
hi = apr_hash_first(r->pool, accessMap);
while(hi != NULL)
{
apr_hash_this(hi, (void *)&key, NULL, (void *)&arec);
ap_rprintf(r, “%s:\n”, key);
while(arec != NULL)
{
if(arec->type == 0)
ap_rprintf(r, “group:%s “, arec->name);
else if(arec->type == 1)
ap_rprintf(r, “user:%s “, arec->name);
else
ap_rprintf(r, “all “);
ap_rprintf(r, “access:%d “, arec->access);
ap_rprintf(r, “\n”);
arec = arec->next;
}
ap_rprintf(r, “\n”);
hi = apr_hash_next(hi);
}
}
char *url = apr_pstrdup(r->pool, r->uri);
// decode the url for some chinese characters
url_decode(url);
// analyze the access
if(estimate_access(r, conf, url, ugMap, accessMap))
{
if(conf->debug)
{
ap_rprintf(r, “%s have access on:%s\n”, r->user, r->uri);
return OK;
}
return DECLINED;
}
else
{
if(conf->debug)
{
ap_rprintf(r, “%s don’t have access on:%s\n”, r->user, r->uri);
return OK;
}
}
return 403;
}

static const char *set_authSVN_enable(cmd_parms *cmd,
void *mconfig,
int arg)
{
authSVN_rec *conf = (authSVN_rec *) mconfig;
conf->enabled = arg;
return NULL;
}

static const char *set_authSVN_debug( cmd_parms *cmd,
void *mconfig,
int arg)
{
authSVN_rec *conf = (authSVN_rec *) mconfig;
conf->debug = arg;
return NULL;
}

static const char *set_prefix_path(cmd_parms *cmd,
void *mconfig,
const char *name)
{
authSVN_rec *conf = (authSVN_rec *) mconfig;
if(strlen(name) <= 0)
return "AuthSVNPrefixPath can not be null.";
if(name != &apos;/&apos; || name != &apos;/&apos;)
return "AuthSVNPrefixPath must start and end with &apos;/&apos;.";
conf->prefixPath = apr_pstrdup(cmd->pool, name);
return NULL;
}

static const char *set_stop_pattern(cmd_parms *cmd,
void *mconfig,
const char *name)
{
authSVN_rec *conf = (authSVN_rec *) mconfig;
if(strlen(name) <= 0)
return "AuthSVNStopPattern can not be null.";
conf->stopPattern = apr_pstrdup(cmd->pool, name);
return NULL;
}

static const char *set_authSVN_accessFile(cmd_parms *cmd,
void *mconfig,
const char *name)
{
authSVN_rec *conf = (authSVN_rec *) mconfig;
ap_configfile_t *f = NULL;
apr_status_t status;
if(strlen(name) <= 0)
return "SVNAccessFile can not be null.";
status = ap_pcfg_openfile(&f, cmd->pool, name);
if (status != APR_SUCCESS) {
return “Can not open given SVN access file.”;
}
ap_cfg_closefile(f);
conf->accessFile = apr_pstrdup(cmd->pool, name);
return NULL;
}
static const command_rec auth_cmds[] =
{
AP_INIT_FLAG(”EnableAuthSVN”, set_authSVN_enable, NULL, OR_FILEINFO,
“enable authSVN or not.”),
AP_INIT_FLAG(”DebugAuthSVN”,set_authSVN_debug, NULL, OR_FILEINFO,
“debug authSVN or not.”),
AP_INIT_TAKE1(”AuthSVNPrefixPath”,   set_prefix_path,   NULL, OR_FILEINFO,
“set prefix path.”),
AP_INIT_TAKE1(”AuthSVNStopPattern”,set_stop_pattern,   NULL, OR_FILEINFO,
“the url pattern we do not do the access checking.”),
AP_INIT_TAKE1(”SVNAccessFile”, set_authSVN_accessFile, NULL, OR_FILEINFO,
“set SVN access file.”),
{ NULL }
};
static void register_hooks(apr_pool_t *p)
{
ap_hook_handler(authSVN_handler, NULL, NULL, APR_HOOK_FIRST);
}
module AP_MODULE_DECLARE_DATA authSVN_module =
{
STANDARD20_MODULE_STUFF,
create_authSVN_dir_config,   
NULL,                        
NULL,                        
NULL,                        
auth_cmds,                     
register_hooks               
};
 
页: [1]
查看完整版本: [转载] Apache模块开发/用C语言扩展apache(4:一个生产环境使用的apache module– viewvc权限控制)