当前位置:操作系统 > 玩转网络 >>

让Proftpd 的数据库模块支持MD5验证

答案:

    这个是笔者对论坛主机的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; * you'll also need to link with OpenSSL's 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 13typedef 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 doesn't 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;elseentry = 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_OPENSSLstatic 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 we're 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#endiftypedef 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;}

上一个:文件传主输协议FTP命令不完全手册
下一个:proftp+mysql认证实现

CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,