欢迎光临
我们一直在努力

让Proftpd 的数据库模块支持MD5验证-网管专栏,FTP服务

建站超值云服务器,限时71元/月

    这个是笔者对论坛主机的ftp服务进行注册用户验证,论坛采用的是vbb,看了看vbb的密码加密方式,md5,faint。

    proftpd的mod_sql模块并不支持md5。vbb是直接调用mysql的md5()函数进行密码加密。怎么办?hack!笔者用的proftpd是最新的proftpd 1.2.8,mod_sql 版本是 4.10,查了一下 mod_sql.c 文件,发现增加一种验证方式还是很简单的,当然这个要归功mod_sql.c的程序架构设计得不错。

    下面是笔者修改后得mod_sql.c的部分代码,有中文的地方是笔者加的。

 


#include "conf.h"
#include "privs.h"
#include "mod_sql.h"

#define _mod_version "mod_sql/4.10"

#ifdef have_crypt_h
#include 
#endif

#ifdef have_limits_h
#include 
#endif
/**************/
/* 引入md5头文件 */
#include
/**************/
/* uncomment the following define to allow openssl hashed password checking; 
* youll also need to link with openssls crypto library ( -lcrypto ) 
*/
/* #define have_openssl */

#ifdef have_openssl
#include 
#endif

/* default information for tables and fields */
#define mod_sql_def_usertable "users"
#define mod_sql_def_usernamefield "userid"
#define mod_sql_def_useruidfield "uid"
#define mod_sql_def_usergidfield "gid"
#define mod_sql_def_userpasswordfield "password"
#define mod_sql_def_usershellfield "shell"
#define mod_sql_def_userhomedirfield "homedir"

#define mod_sql_def_grouptable "groups"
#define mod_sql_def_groupnamefield "groupname"
#define mod_sql_def_groupgidfield "gid"
#define mod_sql_def_groupmembersfield "members"

/* default minimum id / default uid / default gid info. 
* uids and gids less than mod_sql_min_user_uid and
* mod_sql_min_user_gid, respectively, get automatically
* mapped to the defaults, below. these can be
* overridden using directives
*/
#define mod_sql_min_user_uid 999
#define mod_sql_min_user_gid 999
#define mod_sql_def_uid 65533
#define mod_sql_def_gid 65533

#define mod_sql_bufsize 32

/* named query defines */
#define sql_select_c "select"
#define sql_insert_c "insert"
#define sql_update_c "update"
#define sql_freeform_c "freeform"

/* authmask defines */
#define sql_auth_users (1<<0)
#define sql_auth_groups (1<<1)
#define sql_auth_users_definitive (1<<2)
#define sql_auth_groups_definitive (1<<3)
#define sql_auth_userset (1<<4)
#define sql_auth_groupset (1<<5)
#define sql_fast_userset (1<<6)
#define sql_fast_groupset (1<<7)

#define sql_groups (cmap.authmask & sql_auth_groups)
#define sql_users (cmap.authmask & sql_auth_users)
#define sql_groupset (cmap.authmask & sql_auth_groupset)
#define sql_userset (cmap.authmask & sql_auth_userset)
#define sql_fastgroups (cmap.authmask & sql_fast_groupset)
#define sql_fastusers (cmap.authmask & sql_fast_userset)
#define sql_groupgod (cmap.authmask & sql_auth_groups_definitive)
#define sql_usergod (cmap.authmask & sql_auth_users_definitive)

/*
* externs, function signatures.. whatever necessary to make
* the compiler happy..
*/
extern pr_response_t *resp_list,*resp_err_list;
static char *_sql_where(pool *p, int cnt, ...);
modret cmd_getgrent(cmd_rec *);
modret cmd_setgrent(cmd_rec *);

pool *sql_pool;

/*
* cache typedefs
*/

#define cache_size 13

typedef struct cache_entry {
struct cache_entry *list_next;
struct cache_entry *bucket_next;
void *data;
} cache_entry_t;

/* this struct holds invariant information for the current session */

static struct
{
/*
* info valid after getpwnam
*/

char *authuser; /* current authorized user */
struct passwd *authpasswd; /* and their passwd struct */

/*
* generic status information
*/

int status; /* is mod_sql on? */
int authmask; /* authentication mask.
* see set_sqlauthenticate for info */

/*
* user table and field information
*/

char *usrtable; /* user info table name */
char *usrfield; /* user name field */
char *pwdfield; /* user password field */
char *uidfield; /* user uid field */
char *gidfield; /* user gid field */
char *homedirfield; /* user homedir field */
char *shellfield; /* user login shell field */
char *userwhere; /* users where clause */

/*
* group table and field information
*/

char *grptable; /* group info table name */
char *grpfield; /* group name field */
char *grpgidfield; /* group gid field */
char *grpmembersfield; /* group members field */
char *groupwhere; /* groups where clause */

/*
* other information
*/

array_header *authlist; /* auth handler list */
char *defaulthomedir; /* default homedir if no field specified */
int buildhomedir; /* create homedir if it doesnt exist? */

uid_t minid; /* users uid must be this or greater */
uid_t minuseruid; /* users uid must be this or greater */
gid_t minusergid; /* users uid must be this or greater */
uid_t defaultuid; /* default uid if none in database */
gid_t defaultgid; /* default gid if none in database */

cache_entry_t *curr_group; /* next group in group array for getgrent */
cache_entry_t *curr_passwd; /* next passwd in passwd array for getpwent */
int group_cache_filled;
int passwd_cache_filled;
unsigned char negative_cache; /* cache negative as well as positive lookups */

/*
* mod_ratio data -- someday this needs to be removed from mod_sql
*/

char *sql_fstor; /* fstor int(11) not null default 0, */
char *sql_fretr; /* fretr int(11) not null default 0, */
char *sql_bstor; /* bstor int(11) not null default 0, */
char *sql_bretr; /* bretr int(11) not null default 0, */

char *sql_frate; /* frate int(11) not null default 5, */
char *sql_fcred; /* fcred int(2) not null default 15, */
char *sql_brate; /* brate int(11) not null default 5, */
char *sql_bcred; /* bcred int(2) not null default 150000, */

/*
* precomputed strings
*/
char *usrfields;
char *grpfields;
}
cmap;

/*
* cache functions
*/

typedef unsigned int ( * val_func ) ( const void * ); 
typedef int ( * cmp_func ) ( const void *, const void * );

typedef struct {
/* memory pool for this object */
pool *pool;

/* cache buckets */
cache_entry_t *buckets[ cache_size ];

/* cache functions */
val_func hash_val;
cmp_func cmp;

/* list pointers */
cache_entry_t *head;

/* list size */
unsigned int nelts;
} cache_t;

cache_t *group_name_cache;
cache_t *group_gid_cache;
cache_t *passwd_name_cache;
cache_t *passwd_uid_cache;

static cache_t *make_cache( pool *p, val_func hash_val, cmp_func cmp )
{
cache_t *res;

if ( ( p == null ) || ( hash_val == null ) || 
( cmp == null ) )
return null;

res = ( cache_t * ) pcalloc( p, sizeof( cache_t ) );

res->pool = p;
res->hash_val = hash_val;
res->cmp = cmp;

res->head = null;

res->nelts = 0;

return res;
}

static cache_entry_t *cache_addentry( cache_t *cache, void *data )
{
cache_entry_t *entry;
int hashval;

if ( ( cache == null ) || ( data == null ) )
return null;

/* create the entry */
entry = ( cache_entry_t * ) pcalloc( cache->pool, 
sizeof( cache_entry_t ) );
entry->data = data;

/* deal with the list */

if ( cache->head == null ) {
cache->head = entry;
} else {
entry->list_next = cache->head;
cache->head = entry;
}

/* deal with the buckets */
hashval = cache->hash_val( data ) % cache_size;
if ( cache->buckets[ hashval ] == null ) {
cache->buckets[ hashval ] = entry;
} else {
entry->bucket_next = cache->buckets[ hashval ];
cache->buckets[ hashval ] = entry;
}

cache->nelts++;

return entry;
}

static void *cache_findvalue( cache_t *cache, void *data )
{
cache_entry_t *entry;
int hashval;

if ( ( cache == null ) || ( data == null ) ) return null;

hashval = cache->hash_val( data ) % cache_size;

entry = cache->buckets[ hashval ];
while ( entry != null ) {
if ( cache->cmp( data, entry->data ) )
break;
else
entry = entry->bucket_next;
}

return ( ( entry == null ) ? null : entry->data );
}

cmd_rec *_sql_make_cmd(pool * cp, int argc, ...)
{
pool *newpool = null;
cmd_rec *c = null;
va_list args;
int i = 0;

newpool = make_sub_pool( cp );
c = pcalloc(newpool, sizeof(cmd_rec));
c->argc = argc;
c->stash_index = -1;
c->pool = newpool;

c->argv = pcalloc(newpool, sizeof(void *) * (argc));
c->tmp_pool = newpool;

va_start(args, argc);

for (i = 0; i < argc; i++)
c->argv[i] = (void *) va_arg(args, char *);

va_end(args);

return c;
}

void _sql_free_cmd( cmd_rec *cmd )
{
destroy_pool( cmd->pool );

return;
}

static void _sql_check_cmd(cmd_rec *cmd, char *msg)
{
if ((!cmd) || (!cmd->tmp_pool)) {
log_pri(pr_log_err, _mod_version ": %s was passed an invalid cmd_rec. "
"shutting down.", msg);
sql_log(debug_warn, "%s was passed an invalid cmd_rec. shutting down.",
msg);
end_login(1);
} 
return;
}

static modret_t *_sql_check_response(modret_t * mr)
{
if (!modret_iserror(mr))
return mr;

sql_log(debug_warn, "%s", "unrecoverable backend error");
sql_log(debug_warn, "error: %s", mr->mr_numeric);
sql_log(debug_warn, "message: %s", mr->mr_message);

end_login(1);

/* make the compiler happy */
return null;
}

static modret_t * _sql_dispatch(cmd_rec *cmd, char *cmdname)
{
modret_t *mr = null;
int i = 0;

for(i = 0; sql_cmdtable[i].command; i++)
if(!strcmp(cmdname,sql_cmdtable[i].command)) {
pr_signals_block();
mr = sql_cmdtable[i].handler(cmd);
pr_signals_unblock();
return mr;
}

sql_log(debug_warn, "unknown backend handler %s", cmdname );
return error(cmd);
}

static char *_sql_strip_spaces( pool *p, char *str )
{
char *start = null, *finish = null;

if (!str) return null;

/* first, find the non-whitespace start of the given string */
for (start = str; isspace((int) *start); start++);

/* now, find the non-whitespace end of the given string */
for (finish = &str[strlen(str)-1]; isspace((int) *finish); finish--);
*++finish = ;

/* the space-stripped string is, then, everything from start to finish */
return pstrdup( p, start );
}

/*****************************************************************
*
* authentication functions
*
*****************************************************************/

static modret_t *check_auth_crypt(cmd_rec * cmd, const char *c_clear,
const char *c_hash)
{
int success = 0;

if (*c_hash == ) return error_int(cmd, pr_auth_badpwd);

success = !strcmp((char *) crypt(c_clear, c_hash), c_hash);

return success ? handled(cmd) : error_int(cmd, pr_auth_badpwd);
}

static modret_t *check_auth_plaintext(cmd_rec * cmd, const char *c_clear,
const char *c_hash)
{
int success = 0;

if (*c_hash ==  ) return error_int(cmd, pr_auth_badpwd);

success = !strcmp(c_clear, c_hash);

return success ? handled(cmd) : error_int(cmd, pr_auth_badpwd);
}

static modret_t *check_auth_empty(cmd_rec * cmd, const char *c_clear,
const char *c_hash)
{
int success = 0;

success = !strcmp(c_hash, "");

return success ? handled(cmd) : error_int(cmd, pr_auth_badpwd);
}

static modret_t *check_auth_backend(cmd_rec * cmd, const char *c_clear,
const char *c_hash)
{
modret_t * mr = null;

if (*c_hash ==  ) return error_int(cmd, pr_auth_badpwd);

mr = _sql_dispatch( _sql_make_cmd(cmd->tmp_pool, 3, "default", 
c_clear, c_hash),
"sql_checkauth" );

return mr;
}
/******************************************************/
/* 增加 md5 验证函数*, 简单之至, 照猫画虎*/
static modret_t *check_auth_md5(cmd_rec * cmd, const char *c_clear, const char *c_hash)
{
int success = 0;
size_t len = strlen(c_clear);
char buf[33]; 
/* specifically disallow empty passwords */
if (*c_hash ==  ) return error_int(cmd, pr_auth_badpwd);
md5data(c_clear, len, buf); /* 调用md5函数 */

success = !strcmp(buf, c_hash);

return success ? handled(cmd) : error_int(cmd, pr_auth_badpwd);
}
/****************************************************/ 

#ifdef have_openssl
static modret_t *check_auth_openssl(cmd_rec * cmd, const char *c_clear,
const char *c_hash)
{
/*
* c_clear : plaintext password provided by user 
* c_hash : combination digest name and hashed 
* value, of the form {digest}hash 
*/

evp_md_ctx mdctx;
evp_encode_ctx evp_encode;
const evp_md *md;
unsigned char md_value[evp_max_md_size];
int md_len, returnvalue;

char buff[evp_max_key_length];

char *digestname; /* ptr to name of the digest function */
char *hashvalue; /* ptr to hashed value were comparing to */
char *copyhash; /* temporary copy of the c_hash string */

if (c_hash[0] != {) {
return error_int(cmd, pr_auth_badpwd);
}

/*
* we need a copy of c_hash 
*/
copyhash = pstrdup(cmd->tmp_pool, c_hash);

digestname = copyhash + 1;

hashvalue = (char *) strchr(copyhash, });

if (hashvalue == null) {
return error_int(cmd, pr_auth_badpwd);
}

*hashvalue = ;
hashvalue++;

openssl_add_all_digests();

md = evp_get_digestbyname(digestname);

if (!md) {
return error_int(cmd, pr_auth_badpwd);
}

evp_digestinit(&mdctx, md);
evp_digestupdate(&mdctx, c_clear, strlen(c_clear));
evp_digestfinal(&mdctx, md_value, &md_len);

evp_encodeinit(&evp_encode);
evp_encodeblock(buff, md_value, md_len);

returnvalue = strcmp(buff, hashvalue);

return returnvalue ? error_int(cmd, pr_auth_badpwd) : handled(cmd);
}
#endif

/*
* support for general-purpose authentication schemes 
*/

#define plaintext_auth_flag 1<<0
#define crypt_auth_flag 1<<1
#define backend_auth_flag 1<<2
#define empty_auth_flag 1<<3

/* 插入md5验证模式,把openssl往后推,openssl本来是 1<<4 */
#define md5_auth_flag 1<<4
/***************/
#ifdef have_openssl
#define openssl_auth_flag 1<<5
#endif

typedef modret_t *(*auth_func_ptr) (cmd_rec *, const char *, const char *);

typedef struct
{
char *name;
auth_func_ptr check_function;
int flag;
}
auth_type_entry;

static auth_type_entry supported_auth_types[] = {
{"plaintext", check_auth_plaintext, plaintext_auth_flag},
{"crypt", check_auth_crypt, crypt_auth_flag},
{"backend", check_auth_backend, backend_auth_flag}, 
{"empty", check_auth_empty, empty_auth_flag},
{"md5", check_auth_md5, md5_auth_flag}, /* 这里往验证类型条目结构里增加md5验证 */
#ifdef have_openssl
{"openssl", check_auth_openssl, openssl_auth_flag},
#endif
/*
* add additional encryption types below 
*/
{null, null, 0}
};

static auth_type_entry *get_auth_entry(char *name)
{
auth_type_entry *ate = supported_auth_types;

while (ate->name) {
if (!strcasecmp(ate->name, name)) {
return ate;
}
ate++;
}
return null;
}

以下略

经以上修改后, mod_sql.c 就可以支持md5方式的密码验证,当然,别忘了在makefile的libs里增加 -lmd 指明要链接md5函数库,就一切ok

编译成功后,在 proftpd.conf 的sqlauthtypes 指令 后面加一个 md5,重新启动 proftpd,这家伙就认md5验证啦。至于相关的数据库设置方法我就不多嘴了,到处都是。

赞(0)
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com 特别注意:本站所有转载文章言论不代表本站观点! 本站所提供的图片等素材,版权归原作者所有,如需使用,请与原作者联系。未经允许不得转载:IDC资讯中心 » 让Proftpd 的数据库模块支持MD5验证-网管专栏,FTP服务
分享到: 更多 (0)