cyrus-sasl
sasldblistusers2 -f sasldb
saslpasswd2 -f sasldb -u tele125server fgy
15:44:18 104 ~:#saslpasswd2 -v
This product includes software developed by Computing Services
at Carnegie Mellon University (http://www.cmu.edu/computing/).
Built against SASL API version 2.1.23
LibSasl version 2.1.23 by "Cyrus SASL"
15:48:37 112 ~:#pluginviewer -a
Installed auxprop mechanisms are:
sasldb
List of auxprop plugins follows
Plugin "sasldb" , API version: 4
supports store: yes
15:49:58 113 ~:#pluginviewer -s
Installed SASL (server side) mechanisms are:
DIGEST-MD5 CRAM-MD5 ANONYMOUS EXTERNAL
List of server plugins follows
Plugin "digestmd5" [loaded], API version: 4
SASL mechanism: DIGEST-MD5, best SSF: 128, supports setpass: no
security flags: NO_ANONYMOUS|NO_PLAINTEXT|MUTUAL_AUTH
features: PROXY_AUTHENTICATION
Plugin "crammd5" [loaded], API version: 4
SASL mechanism: CRAM-MD5, best SSF: 0, supports setpass: no
security flags: NO_ANONYMOUS|NO_PLAINTEXT
features: SERVER_FIRST
Plugin "anonymous" [loaded], API version: 4
SASL mechanism: ANONYMOUS, best SSF: 0, supports setpass: no
security flags: NO_PLAINTEXT
features: WANT_CLIENT_FIRST
15:59:02 120 ~:#saslauthd -v
saslauthd 2.1.23
authentication mechanisms: getpwent kerberos5 pam rimap shadow ldap
cracklib-dicts
http://sourceforge.net/projects/cracklib/
https://github.com/cracklib/cracklib CrackLib Library and Dictionaries - migrated from SF.net 8/18/2015
Unix的安全性主要靠口令实现,因此,Unix口令加密算法几度改进,现在普遍采用DES算法对口令文件进行25次加密,而对每次DES加密产生的结果,都要用2的56次方次查找与匹配才能进行一次遍历,要破解这样的口令,其工作量是巨大的,所以从理论上说这种口令是相当安全的。然而不幸的是我们仍然不时听道口令被攻破的消息。这些口令是如何被攻破的,我们又怎样才能保证口令的安全呢。下面将对这一问题进行探讨。
Unix口令文件的格式及安全机制
Unix的口令文件passwd是一个加密后的文本文件,储存在/etc目录下。该文件用于用户登录时校验用户的口令,仅对root权限可写。口令文件中每行代表一个用户条目,格式为:LOGNAME:PASSWORD:UID:GID:USERINFO:HOME:SHELL。每行的头两项是登录名和加密后的口令,UID和GID是用户的ID号和用户所在组的ID号,USERINFO是系统管理员写入的有关该用户的信息,HOME是一个路径名,是分配给用户的主目录,SHELL是用户登录后将执行的shell(若为空格则缺省为/bin/sh)。目前多数Unix系统中,口令文件都做了Shadow变换,即把/etc/passwd文件中的口令域分离出来,单独存在/etc/shadow文件中,并加强对shadow的保护,以增强口令安全。
Unix系统使用一个单向函数crypt()来加密用户的口令。Crypt()是基于DES的加密算法,它将用户输入的口令作为密钥,加密一个64bit的0/1串,加密的结果又使用用户的口令再次加密;重复该过程,一共进行25次。最后的输出为一个13byte的字符串,存放在/etc/passwd的PASSWORD域。单向函数crypt()从数学原理上保证了从加密的密文得到加密前的明文是不可能的或是非常困难的。当用户登录时,系统并不是去解密已加密的口令,而是将输入的口令明文字符串传给加密函数,将加密函数的输出与/etc/passwd文件中该用户条目的PASSWORD域进行比较,若匹配成功,则允许用户登录系统。
口令破解通常有蛮力攻击和字典攻击两种方式。Unix中一共有[0x00~0xff]共128个字符,其中95个字符(10(数字)+33(标点符号)+26*2(大小写字母))可作为口令的字符。假设m为可能使用的字符集的大小,n为口令的长度,则可生成的口令数为m的n次幂,随着字符集的扩大与口令长度的增加,口令攻击尝试次数将迅速增加。如口令长度为6,取字母和数字组合,可能性是62的6次幂56,800,235,584。但如果5个字母是一个常用汉字的拼音或英文单词,估算一下常用词约为10000条,从10000个常用词中取一个词与任意一个数字字符组合成口令,则仅10000*10=100000(10万种可能)。在口令的设置过程中,还有许多个人因素在起作用,为使自己的口令容易记忆,许多人往往将个人的姓名、生日、电话号码、街道的号码等作为口令,这样便为口令的破解留下了方便之门。贝尔实验室的计算机安全专家R.Morris和K.Thompson提出了这样一种攻击的可能性:可以根据用户的信息建立一个他可能使用的口令的字典。比如:他父亲的名字、女朋友的生日或名字,街道的名字等等。然后对这个字典进行加密,每次拿出一个经过加密计算的条目与口令文件比较,若一致,口令就被猜到了。也许有人认为口令毫无规律可言,字典中不会有,计算机是破译不了的,那就大错特错了。有很多专门生成字典的程序,比如:Dictmake、txt2dict、xkey等等。以dictmake为例:启动程序后,计算机会要求输入最小口令长度、最大口令长度、口令包含的小写字符、大写字符、数字、有没有空格、含不含标点符号和特殊字符等一系列的问题。当回答完了计算机提出的问题后,计算机就会按照给定的条件自动将所有的组合方式列出来并存到文件中,而这个文件就是资料字典。目前,在因特网上,有一些数据字典可以下载,包含的条目从1万到几十万条。数据字典一般囊括了常用的单词。攻击者一旦通过某种途径获得了passwd文件,破译过程便只需一个简单的C程序即可完成。Unix中有一组子程序可对/etc/passwd文件进行方便的存取。getpwuid()函数可从/etc/passwd文件中获取指定的UID的入口项。getpwnam()函数可在/etc/passwd文件中获取指定的登录名入口项。这两个子程序返回一指向passwd结构的指针,该结构定义在/usr/include/pwd.h中,定义如下:
以下为引用的内容:
structpasswd{
char*pw_name;/*登录名*/
char*pw_passwd;/*加密后的口令*/
uid_tpw_uid;/*UID*/
gid_tpw_gid;/*GID*/
char*pw_age;/*代理信息*/
char*pw_comment;/*注释*/
char*pw_gecos;
char*pw_dir;/*主目录*/
char*pw_shell;/*使用的shell*/
char*pw_shell;/*使用的shell*/
}
getpwent(),setpwent(),endpwent()等函数可对口令文件作后续处理。首次调用getpwent()可打开/etc/passwd文件并返回指向文件中第一个用户条目的指针,再次调用getpwent()便可顺序地返回口令文件中的各用户条目,setpwent()可把口令文件的指针重新置为文件的开始处,endpwent()可关闭口令文件。
由此可见,攻击者只需建立一个字典文件,然后调用现成的cryp()加密例程来加密字典文件中的每一条目,再用上述函数打开口令文件,进行循环比较就很容易破解密码了。
实际上Internet网上有很多现成的密码破解软件工具,过于简单的口令很容易破解。那么,我们用什么方法来保证用户口令是一个安全的口令呢?运用CrackLib来构建安全的Unix口令是一个较好的办法。
CrackLib原理及应用
CrackLib是一个用于UNIX系统下的函数库,它可以用于编写和口令有关的程序。其基本思想就是通过限制用户使用使用过于简单、容易被猜测出来或容易被一些工具搜索到的密码,来提高系统的安全性。
CrackLib并不是一个可以直接运行使用的程序,它只是一个函数库,可以利用其中的函数写自己的程序或是加入其它程序中以提高安全性,如可以改写passwd使用户在选择密码时受到限制。CrackLib使用一个字典,它查找字典以判断用户所选密码是不是安全的密码。用户也可以加入其它信息,使用自己的字典。CrackLib通过建立索引和二元查找,效率非常高,其字典大小通常只有同等字典数的一半。下面介绍如何运用CrackLib。
1、构建cracklib字典
CrackLib可以很容易的在Internet上找到,现在使用的版本多是2.7,首先要确定字典安装的路径,即给DICTPATH赋值,形式为:目录+字典文件名(不包括后缀),如:DICTPATH=/usr/local/lib/pw_dict.。该变量值将在所有调用CrackLib函数的程序中用到,字典文件通常包括/usr/local/lib/pw_dict.pwd,/usr/local/lib/pw_dict.pwi,/usr/local/lib/pw_dict.hwm三个文件。
CrackLib字典可直接从网上下载,也可以用它提供的工具生成。如果想加入其它信息,使用自己的字典,可将含有新词的文件放到SOURCEDICT目录如"/usr/dict/words"下,CrackLib会将所有文件合并起来,删除多余的词,将其压缩成字典文件,通常只有原文件40%-60%的大小。
2、在程序中调用函数
CrackLib函数可以被应用于很多地方,只需加入简单的几行源码,就可以得到非常好的效果。char*FascistCheck(char*pw,char*dictpath)是CrackLib中最常用的函数。其中pw是用户选择的密码,将被验证是不是安全的,dictpath是字典所在路径。
FascistCheck()返回一个空指针,说明口令很安全,否则返回诊断出的字符串。下面是一个口令设置的简单示例,用以说明CrackLib函数用法.
以下为引用的内容:
#ifndefCRACKLIB_DICTPATH
#defineCRACKLIB_DICTPATH"/usr/local/lib/pw_dict"
#endif
...
...
...
char*msg;
while(1){
passbuf=getpass("请设定新密码:");
if(!*passbuf){
(void)printf("密码设定取消,继续使用旧密码\n");
break;
}
if(strlen(pussbuf)<=4││!strcmp(passbuf,newuser.userid)){
(void)printf("密码太短或与使用者代号相同,请重新输入\n");
continue;
}
if(msg=(char*)FascistCheck(passbuf,CRACKLIBPATH)){
printf("请另选密码!(%s)\n",msg);
continue;
}
strncpy(newuser.passwd,passbuf,PASSLEN);
passbuf=getpass("请再输入一次你的密码);
if(strncmp(passbuf,newuser.passwd,PASSLEN)!=0){
prints("密码输入错误,请重新输入密码.\n");
continue;
}
passbuf[8]='\0';
break;
}
认证机制
authentication mechanisms
plain
login
ldap的simple authentication支持plain与login,但这两个不够安全。要么在适当的位置有足够的安全保护来使用,要么结合tls来使用。因为这两种是明文传输。只要截获通信,就能得出用户名与密码。
cram-md5
digest-md5是ldapv3的强制实现,它不是一个强认证机制
gssapi 机制利用gss-api kerberos v
strong authentication mechanism
强认证包括:公钥,otp,srp等
明文
cleartext
plaintext
sasl使用工业标准的认证机制,如:gssapi for kerberos v,digest-md5,plain,external(利用更低层的网络服务如tls)
SMTP验证支持plain,login,cram-md5,GSSAPI、NTLM、Kerberos等
ldap验证支持plain,login,cram-md5,digest-md5,gssapi,kerberos_v4
[root@109-com1 log]# nc mail.sohu.com 25
220 zw_71_21 ESMTP ready
ehlo mail.sohu.com 表明自己需要身份验证
250-zw_71_21
250-AUTH PLAIN LOGIN
250 STARTTLS
auth login 表明身份验证开始
334 VXNlcm5hbWU6 经过base64编码过的Username:
Y3JlYXRleXVhbg== 经过base64编码过的用户名
334 UGFzc3dvcmQ6 经过base64编码过的Password:
MmQ0NTY3ODkt 经过base64编码过的密码
235 2.0.0 OK
mail from:createyuan@sohu.com 声明邮件发送人
250 2.1.0 Ok
rcpt to:createyuan1@163.com 声明邮件接收人
250 2.1.5 Ok
data 邮件开始
354 End data with <CR><LF>.<CR><LF> 下面是邮件头标识,与投递无关,服务器表明以点结束 在SMTP转发的邮件中包括信封和内容这两种东西。
subject:test mailfj 主题
from:createyuan@sohu.com 在邮件头中声明邮件来源,仅在outlook与foxmail中显示,与投递无关
to:createyuan1@163.com 这两个from与to无所谓,可以随便写
wo shi he da 空一行,标识邮件头与内容
sfj
please delete it.
. 以点结束
250 2.0.0 Ok: queued as 256062628456
quit
221 2.0.0 Bye
[root@109-com1 log]#
参照上例,分析如下
ESMTP的三个认证方式: CRAM-MD5 PLAIN和LOGIN. 下面对这三种认证方式的流程进行一个总结.
CRAM-MD5:
客户端首先向服务器端发送一个字符串: "AUTH[SPACE]CRAM-MD5[CRLF]".其中[SPACE]表示一个空格;[CRLF]表示回车换行符, 即"\r\n". 下同.
如果服务器拒绝认证方式, 则返回一个字符串: "[NUM][SPACE]str". 其中[NUM]为三位数字的服务器状态码( 下同 ).当状态码不等于334表示拒绝认证方式. str是一个服务器端定义的字符串, 用于描述错误.
如果服务器接受认证方式, 则返回一个字符串: "[NUM][SPACE]str_base64".其中str_base64是一个随机字符串经过base64编码后的字符串.
客户端收到服务器的信息后, 执行如下操作:
首先利用base64解码算法将str_base64解码. 解码后的字符串存入str
* call base64_decode
* input str_base64
* output str
之后利用hmac-md5算法计算出一个摘要digest
* call hmac_md5
* input password, str
* output digest
将摘要用小写字母的16进制表示, 并把字符串"username "与它合并, 成为字符串tmp
* string tmp = 'username digest'
将tmp进行base64编码
* call base64_encode
* input tmp
* output tmp_base64
最后, 客户端向服务器端发送字符串: "tmp_base64[CRLF]"
根据服务器的返回判断是否认证成功
PLAIN:
其发送用户名与口令的格式应该是“<NULL>username<NULL>password”。“username”是用户名,后边的字符串是口令,NULL是ASCII的0(所以无法使用telnet登录)。
客户端首先做如下操作:
* string tmp = '^username^password'
* for each character in tmp
* tmp[i] = '\0' where tmp[i] == '^'
* call base64_encode
* input tmp
* output tmp_base64
最后, 客户端向服务器端发送字符串: "tmp_base64[CRLF]"
根据服务器的返回判断是否认证成功
LOGIN:
客户端首先向服务器端发送一个字符串: "AUTH[SPACE]LOGIN[CRLF].
如果服务器拒绝认证方式, 则返回一个字符串: "[NUM][SPACE]str". 其中[NUM]不等于334, 表示拒绝认证方式. str是一个服务器端定义的字符串, 用于描述错误.
如果服务器接受认证方式, 则返回一个字符串: "[NUM][SPACE]user_base64". 其中user_base64是一个利用base64编码后的字符串
客户端收到服务器信息后做如下操作:
* call base64_decode
* input user_base64
* output tmp
此处仅仅对服务器返回的字符串做合法性检测. 之后的认证过程中不会用到它
* call base64_encode
* input username
* output username_base64
接着, 客户端向服务器发送一个字符串: "username_base64"
服务器返回一个字符串:"[NUM][SPACE]pass_base64".其中pass_base64是一个利用base64编码后的字符串
客户端收到服务器信息后做如下操作:
* call base64_decode
* input pass_base64
* output tmp
此处也仅对服务器返回的字符串做合法性检测. 之后的认证过程中不会用到它
* call base64_encode
* input password
* output password_base64
最后, 客户端向服务器发送一个字符串: "password_base64"
根据服务器的返回判断是否认证成功
本文粗略介绍了ESMTP的认证方式.
所提到的三种认证方式中, CRAM-MD5的安全性最强. 而其他两种认证方式个人认为与明文传输几乎没有区别. 因为base64本身就不是一个用于加密的算法.如果采用后两者的认证方式, 任何一个攻击者都可以轻易地通过简单的嗅探获得客户端与服务器端的通信, 最终获得用户的登录名和密码.