Apache模組開發/用C語言擴充套件apache(4:一個生產環境使用的apache module)

Just4life發表於2013-07-19
Apache模組開發/用C語言擴充套件apache(4:一個生產環境使用的apache module– viewvc許可權控制)
by linux_prog
     下面公佈一個目前在我們公司使用的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” /* for apr_password_validate */
#include ”apr_lib.h” /* for apr_isspace */
#include ”apr_base64.h” /* for apr_base64_decode et al */
#define APR_WANT_STRFUNC /* for strcasecmp */
#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

/* data need by our module */
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[i] != start[i])
      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[MAX_STRING_LEN], dir[256];
    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[0] ==#') || (!l[0])) {
            continue;
        }
       
        if(start_with(l, ”[groups]”)) {
            flag = 1;
            continue;
        }
       
        if(l[0] == ’['
) {
            flag = 2;
            w = apr_strtok(l,[]:\n”, &last);
           
            if(w && w[0] ==/') {
                // the root directory
                snprintf(dir, sizeof(dir), ”%s”, conf->prefixPath);
                dir[strlen(dir) - 1] = ’\0′;
            }
            else if(w && w[0] != ’/'
)
            {
                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[len - 1] ==/') dir[len - 1] = ’\0′;
                }
                else
                    dir[0] = ’\0′;
            }
            else
            {
                dir[0] = ’\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[0] == ‘@’) {
                    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] == ’\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[0] == ‘@’) {
                    w++;
                    if(w) {
                        arec->name = apr_pstrdup(r->pool, w);
                        arec->type = 0;
                    }
                    else continue;
                }
                else if(w[0] == ’*'
)
                {
                    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;
}
/* init per dir */
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;
}

/* hex to int */
static char hex2int(char c)
{
 if( c>=’0′ && c<='9' ) return (c - '0');
 return (c - 'A' + 10);
}

/* url decode */
static void url_decode(char *url)
{
 char *p = url;
 char *p1 = NULL;
 
 while(*p)
 {
  if(*p == '+') *p = ' ';
  
  /* %AB */
  if(*p=='%' && *(p+1) && *(p+2))
  {
   *p = hex2int(toupper(*(p+1))) * 16 + hex2int(toupper(*(p+ 2)));
   strcpy(p + 1, p + 3);
  }
  /* \xAB */
  if(*p=='\\' && *(p+1) && *(p+2) && *(p+3))
       {
              p1 = p + 1;
              if(*p1 && *p1=='x')
              {
                   *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 != '/') { *p = '\0'; p--; }
    if(p != url && *p=='/') *p = '\0';
   
    return;
}

// return

// 0: the user don'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[i].key == NULL || elts[i].val == NULL) continue;
                   
        if(elts[i].val[0] == ’1′ && strcmp(elts[i].key, user) == 0)
        {
            return 1;
        }
       
        if(elts[i].val[0] == ’0′)
        {
            if(find_user_in_group(user, elts[i].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[0]) return 0;
   
    short access = find_access(user, url, ugMap, accessMap);
    if(access < 2) return access;
   
    if(url[0] == '/' && url[1] == '\0') 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[1];
   const size_t nmatch = 1;
   int res = 0;
 short r = 0;
   char ebuf[MAX_STRING_LEN];
   
 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;
}

/* all pages need to pass from this handler */
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[i].key == NULL || elts[i].val == NULL) continue;
                    if(elts[i].val[0] == ’0′)
                    {
                        ap_rprintf(r, ”@”);
                    }
                    ap_rprintf(r,%s ”, elts[i].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;
}

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

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

/* setting prefix path */
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[0] != '/' || name[strlen(name) - 1] != '/')
        return "AuthSVNPrefixPath must start and end with '/'.";
   
    conf->prefixPath = apr_pstrdup(cmd->pool, name);
   
    return NULL;
}

/* setting stop url pattern */
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;
}

/* setting SVN access file */
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, /* dir config creater */
    NULL, /* dir merger — default is to override */
    NULL, /* server config */
    NULL, /* merge server config */
    auth_cmds, /* command apr_table_t */
    register_hooks /* register hooks */
};


安裝方法:
install the module:
./bin/apachectl stop
./bin/apxs -c mod_authSVN.c
./bin/apxs -a -i -n authSVN mod_authSVN.la
./bin/apachectl start

add the following to httpd.conf(insite the viewvc location directory):
EnableAuthSVN            on
DebugAuthSVN             off
SVNAccessFile             /usr/local/apache2.2.4/access
AuthSVNPrefixPath     /viewvc/
AuthSVNStopPattern   ^\/viewvc\/\*docroot\*\/


相關文章