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

2023-10-16 06:18

本文主要是介绍让Proftpd的数据库模块支持MD5验证,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原文:http://www.verydemo.com/demo_c92_i396592.html

  这个是笔者对论坛主机的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 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 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;
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 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
#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验证啦。至于相关的数据库设置方法我就不多嘴了,到处都是。

  ----------------------------------------------------------------

  欢迎访问 UNIX 空间论坛。地址为http://www.unixsp.com/forum/

(责任编辑:西门吹雪)

这篇关于让Proftpd的数据库模块支持MD5验证的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/219613

相关文章

华为鸿蒙HarmonyOS 5.1官宣7月开启升级! 首批支持名单公布

《华为鸿蒙HarmonyOS5.1官宣7月开启升级!首批支持名单公布》在刚刚结束的华为Pura80系列及全场景新品发布会上,除了众多新品的发布,还有一个消息也点燃了所有鸿蒙用户的期待,那就是Ha... 在今日的华为 Pura 80 系列及全场景新品发布会上,华为宣布鸿蒙 HarmonyOS 5.1 将于 7

Druid连接池实现自定义数据库密码加解密功能

《Druid连接池实现自定义数据库密码加解密功能》在现代应用开发中,数据安全是至关重要的,本文将介绍如何在​​Druid​​连接池中实现自定义的数据库密码加解密功能,有需要的小伙伴可以参考一下... 目录1. 环境准备2. 密码加密算法的选择3. 自定义 ​​DruidDataSource​​ 的密码解密3

Maven项目中集成数据库文档生成工具的操作步骤

《Maven项目中集成数据库文档生成工具的操作步骤》在Maven项目中,可以通过集成数据库文档生成工具来自动生成数据库文档,本文为大家整理了使用screw-maven-plugin(推荐)的完... 目录1. 添加插件配置到 pom.XML2. 配置数据库信息3. 执行生成命令4. 高级配置选项5. 注意事

在Java中基于Geotools对PostGIS数据库的空间查询实践教程

《在Java中基于Geotools对PostGIS数据库的空间查询实践教程》本文将深入探讨这一实践,从连接配置到复杂空间查询操作,包括点查询、区域范围查询以及空间关系判断等,全方位展示如何在Java环... 目录前言一、相关技术背景介绍1、评价对象AOI2、数据处理流程二、对AOI空间范围查询实践1、空间查

Python+PyQt5实现MySQL数据库备份神器

《Python+PyQt5实现MySQL数据库备份神器》在数据库管理工作中,定期备份是确保数据安全的重要措施,本文将介绍如何使用Python+PyQt5开发一个高颜值,多功能的MySQL数据库备份工具... 目录概述功能特性核心功能矩阵特色功能界面展示主界面设计动态效果演示使用教程环境准备操作流程代码深度解

MySQL数据库实现批量表分区完整示例

《MySQL数据库实现批量表分区完整示例》通俗地讲表分区是将一大表,根据条件分割成若干个小表,:本文主要介绍MySQL数据库实现批量表分区的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考... 目录一、表分区条件二、常规表和分区表的区别三、表分区的创建四、将既有表转换分区表脚本五、批量转换表为分区

Java如何将文件内容转换为MD5哈希值

《Java如何将文件内容转换为MD5哈希值》:本文主要介绍Java如何将文件内容转换为MD5哈希值的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java文件内容转换为MD5哈希值一个完整的Java示例代码代码解释注意事项总结Java文件内容转换为MD5

MySQL Workbench工具导出导入数据库方式

《MySQLWorkbench工具导出导入数据库方式》:本文主要介绍MySQLWorkbench工具导出导入数据库方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录mysql Workbench工具导出导入数据库第一步 www.chinasem.cn数据库导出第二步

Mysql数据库中数据的操作CRUD详解

《Mysql数据库中数据的操作CRUD详解》:本文主要介绍Mysql数据库中数据的操作(CRUD),详细描述对Mysql数据库中数据的操作(CRUD),包括插入、修改、删除数据,还有查询数据,包括... 目录一、插入数据(insert)1.插入数据的语法2.注意事项二、修改数据(update)1.语法2.有

Python logging模块使用示例详解

《Pythonlogging模块使用示例详解》Python的logging模块是一个灵活且强大的日志记录工具,广泛应用于应用程序的调试、运行监控和问题排查,下面给大家介绍Pythonlogging模... 目录一、为什么使用 logging 模块?二、核心组件三、日志级别四、基本使用步骤五、快速配置(bas