基本的smtp协议没有验证用户身份的能力。虽然信封上的寄件人地址已经隐含了发信者的身份,然而,由于信封地址实在太容易假造,所以不能当成身份凭据。 为了判断客户端是否有权使用转发服务(relay),服务器端必须确认客户端(寄件人)是否当真是对方所自称的那个人。在不能以寄件人地址为×××书的前 提下,smtp势必需要其他补充机制,才能验证客户端的身份。

从postfix的角度看,它需要扮演两种角色:当它身为smtp server时,需要能够验证用户个人的身份(让他们能使用smtp server寄出邮件);当它身为smtp client时,它需要能够提供出自己的×××书给其他mta检验(以便通过远程mta将邮件递送到最终目的地)。因此,我们也会解释如何设定 postfix,使其能通过其他mta的身份验证。
大多数邮件系统只容许内部网络上的客户端使用转发服务,换言之,ip地址成了客户端身份的识别 凭据。然而,并非所有合法用户都具有固定ip地址。比方说,带着笔记本电脑出差远方的同事,他们可能使用领近isp或饭店旅馆提供的临时性ip地址;或 者,有些在家工作的用户,他们位于办公室之外的网络,但是需要透过办公室里的邮件系统来寄信。不管你是否能够事先知道用户的ip地址,sasl都能提供可 靠的身份验证。
RFC 2554 “smtp service extension for authentication”制定了如何在基本smtp协议上增加验证功能的机制,此机制使得smtp能使用sasl协议来验证客户端身份。我们将示范 如何使用carnegie mellon大学开发的cyrus sasl函数库来扩充postfix,使其具备sasl验证能力。
由于cyrus sasl以“函数库”的形式存在,要让你的postfix支持sasl,你必须在编译postfix时就将cyrus sasl函数库链接进去,此外,远程用户也必须设定他们的mua,使其在通过你的邮件系统转发邮件时,能送出正确的标识信息。
sasl概论
有 许多客户机/服务器协议没有验证能力,sasl就是用于加强或增加这类协议的一种通用方法。当你设定sasl时,你必须决定两件事;一是用于交换“标识信 息”(或称×××书)的验证机制;一是决定标识信息存储方法的验证架构。sasl验证机制规范client与server之间的应答过程以及传输内容的编码方法,sasl验证架构决定服务器本身如何存储客户端的×××书以及如何核验客户端提供的密码。如果客户端能成功通过验证,服务器端就能确定用户的身份, 并借此决定用户具有怎样的权限。对postfix而言,所谓的“权限”指的就是转发服务的访问权。你也可以决定通过验证的用户在转发邮件时,是否要使用特 定的寄件人地址。
选择适当的验证机制
cyrus sasl支持多种验证机制,至于要使用哪一种验证机制,客户端与服务器双方必须事先取得共识。以下是一些比较常见的机制;
plain
         plain是最简单的机制,但同时也是最危险的机制,因为×××书(登录名称与密码)是以base64字符串格式通过网络,没有任何加密保护措施。因此,使用plain机制时,你可能会想要结合tls。
login
       login不是其正式支持的机制,但某些旧版的mua使用这种机制,所以cyrus sasl让你可选择其是否支持login机制。如果你的用户仍在使用这类老掉牙的mua,你必须在编译sasl函数库时,指定要包含login的支持。 login的证书交换过程类似plain。
otp
       otp是一种使用“单次密码”的验证机制。此机制不提供任何加密保护,因为没必要--每个密码都只能使用一次,每次联机都要改用新密码。smto client必须能够产生otp证书。
digest-md5
       使用这种机制时,client与server共享同一个隐性密码,而且此密码不通过网络传输。验证过程是从服务器先提出challenge(质询)开始, 客户端使用此challenge与隐性密码计算出一个response(应答)。不同的challenge,不可能计算出相同的response;任何拥 有secret password的一方,都可以用相同的challenge算出相同的response。因此,服务器只要比较客户端返回的response是否与自己算 出的response相同,就可以知道客户端所拥有的密码是否正确。由于真正的密码并没有通过网络,所以不怕网络监测。
kerberos
          kerberos是一种网络型验证协议。除非你的网络已经使用kerberos,否则你应该用不到kerberos机制;相对的,如果你的网络已经架设了kerberos验证中心,sasl就能完美的将smtp验证整合进现有的体系。
anonymous
       anonymous机制对smtp没有意义,因为smtp验证的用意在于限制转发服务的使用对象,而不是为了形成open relay,sasl之所以提供这种机制,主要是为了支持其他协议。
当 客户端链接到一个支持sasl的邮件服务器时,服务器会以优先级列出可用的机制供客户端选择。如果客户端也支持多钟机制,则当第一种机制验证失败时,客户 端可能会继续尝试第二种机制,直到通过验证或是所有机制都失败为止。如果双方在一开始就无法协调出共同的机制,验证过程就算失败。
一旦双方在使用哪种机制上达成共识,就开始进行验证过程。实际的交互过程随机制而定,但通常包含一次或多次应答过程。验证协议本身也规定了应答内容的编码格式。
选择适当的验证架构
sasl验证架构可以使用现有的unix系统密码,也可用smtp用户专用密码文件。如果你的网路上kerberos之类的中间控制式验证构架,也可以使用。
哪 一种验证架构最适合你,取决于你的服务器从何、如何取得证书信息。举例来说,如果smtp与pop/imap的所有用户的证书数据都是储存在系统密码文 件,并通过pam来验证,那么sasl就应该透过pam来取得证书数据。另一方面,如果所有smtp user都只有虚账户,你或许应该将证书数据存放在专用的数据库,并设定sasl与POP/IMAP server从该数据库取得证书数据。
postfix与sasl
在开始使用sasl之前,你应该决定好,要采用哪一种机制与架构的组合,因为你的决定将影响编译、安装、设定的过程。首先,你必须先将sasl函数库安装 到你的系统上,或是确定sasl函数库的安装目录与版本。接着,使用你收集到的sasl安装信息来设定postfix的编译选项,然后重新编译 postfix,使其具备sasl验证能力。某些系统平台可能已经预先安装了sasl函数库,甚至提供支持sasl的postfix包,不过,大部分预先 编译好的postfix包都不支持sasl。因此,如果你使用现成的postfix包,最好先研读相关说明文件,或是按照本章“测试验证配置”一节所说的 测试方法,确认你的postfix确实支持sasl。此外,你也要确认你的sasl函数库支持客户端可能使用的每一种验证机制。比方说,若你的用户中还有 人使用老版的outlook express,你的sasl函数库就必须支持login验证机制。
cyrus sasl函数库的研发进度,目前分为两条线路,即sasl与saslv2。其中,sasl已经逐渐被saslv2所取代。在未来,postfix可能只支 持saslv2,所以本章只讨论saslv2。此外,postfix与sasl函数库两者的版本都必须正确,才能组合在一起。
postfix从1.17-20020331实验版开始支持saslv2函数库,在这之前的版本,只能使用saslv1。理论上,最新版的postfix与最新版的cyrus saslv2应该可以顺利结合在一起。
postfix的sasl配置
假设你已经安装好sasl函数库,而且postfix也支持sasl,并下定决心要使用哪一种验证机制与架构的组合。现在,让我们逐步设定postfix,使其能够使用sasl来验证用户的身份。
设定验证架构
对于每个使用cyrus sasl函数库的应用系统,cyrus sasl各提供一个独立的配置文件。影响postfix的sasl配置文件是smtpd.conf。此文件通常位于 /usr/local/lib/sasl2/smtpd.conf。基本上,smtpd.conf至少要指出所要使用的验证架构。我们打算讨论两种最常用 的架构:unix系统密码以及独立的sasl专用密码。
unix系统密码
通常,让sasl直接使用现有的系统密码来验证用户身份是最方便的。传统的系统密码文件应该是/etc/passwd,但是讲究安全性的现代系统则比较可 能使用/etc/shadow、pam或诸如此类的证书数据库。由于这类密码文件只有特权进程才能访问,而postfix却被刻意设计成避开特权身份,所以postfix不能直接访问系统密码文件。
cyrus函数库对于这个问题的解决办法,是提供一个特殊的验证服务器程序,称为saslauthd,它能够代替postfix来取得密码数据。 saslauthd daemon本身需要特权身份,不过,由于它是一个独立于postfix之外的进程,而且通常不必于外界进行网络通信,所以安全性的危害已经被降到最低。 如果你打算让sasl使用unix系统密码,你必须启动cyrus sasl包随附的saslauthd daemon。请注意,使用saslauthd来访问unix系统密码,表示你只能使用明文密码,因为asalauthd需要实际密码才能进行核验。
要让sasl知道postfix将通过saslauthd daemon来访问证书数据库,你必须将下列内容加入smtpd.conf配置文件:
pwcheck_method:  saslauthd
cyrus sasl包随附的saslauthd应该会被安装在$PATH环境变量所列的某个目录下。你必须先在后台启动saslauthd daemon,postfix才能使用它来验证客户端。当你启动saslauthd时,你必须使用-a选项指定密码系统的类型最常见的类型包括pam、 shadow或getpwent。举例来说,在一个使用pam的系统上,你应该使用下列命令来启动saslauthd daemon:
       saslauthd -a pam
sasl专用密码
如果你不想使用系统密码来验证smtp client,你刻意另外建一个无关系统密码的独立账户数据库。当你的postfix系统纯粹用于提供寄信服务,而不用于接收外来邮件,用户也不用登录服 务器系统本身时,使用sasl密码可能是个好主意。请将下列内容加入你的smtpd.conf配置文件:
       pwcheck_method: auxprop
在cyrus的术语中,auxprop的意义是auxiliary property plug-ins(辅助性的专属外挂模块),其作用是使用外部程序来进行验证。默认的辅助外挂模块是sasldb(这也是cyrus sasl包随附的程序之一),它应该能满足postfix的所有需求。关键字auxprop只是要求使用外部的sasl密码文件。
使用sasl密码时,不需要用到saslauthd daemon,但是你必须将所有的smtp client账户与密码存放在一个专用的外部密码文件中。sasl默认使用的密码文件是/etc/sasldb2。postfix smtp server至少要具备能读取此文件的权限;如果你使用cyrus sasl的auto_transition功能,则postfix将需要能够写此文件的权限;如果你用不到auto_transition功能,最好不要 将写权限开放给postfix。
如果还有其他进程也需要能够访问sasl密码文件(比方说,pop/imap server),你必须适当调整该文件的拥有权与访问权限,让相关进程都能访问它。举例来说,你可以建立一个sasl组,并确定postfix与其他需要 访问该文件的账户都隶属于此组。如果有任何去他进程需要更新该文件,则只读模式可能太严格,而你必须提供写权限给必要的进程。下列命令将 /etc/sasldb2的访问模式设定为440,这使得sasl组的所有成员都能够读取该文件,除此之外的其他进程则没有访问权:
chown postfix:sasl /etc/sasldb2
chmod 440 /etc/sasldb2
cyrus sasl包所提供的saslpasswd2工具,可用来产生、维护/etc/sasldb2密码文件。对于每一个账户,你必须提供三项信息:登录名称、 sasl网域名称、密码。就postfix而言,网域名称必须与myhostname参数的值吻合。所以,最保险的设定方法,就是利用postconf -h myhostname来决定网域名称,如下:
       saslpasswd2 -c -u `postconf - h myhostname` kdent
-c选项要求saslpasswd2创建(create)一个账户。“-u”选项指出该账户所属的网域,其值直接取自postfix的配置文件。
设定postfix
所有与sasl密码验证相关的postfix参数,全部都是以smtpd_sasl*(关于smtp server的参数)或smtp_sasl*(关于smtp client的参数)为前缀。对于服务器端的配置,你至少需要设定smtpd_sasl_auth_enable参数,并且将 permit_sasl_authenticated限制条件列在某一个smtpd_*_restriction的过滤规则里。
启用sasl验证
smtpd_sasl_auth_enable参数决定postfix smtp server是否支持sasl验证:
          smtpd_sasl_auth_enable = yes
有些老的mua没有完全遵守smtp验证协议。依照规范说明书的标准规定,当smtp client送出ehlo命令之后,smtp server应该要列出其验证机制支持列表,而且此列表是出现在关键字auth与一个空格之后。
不过,有些mua却期待收到auth与一个等号;
postfix容许你接受这种不遵守规定的行为:
       broken_sasl_auth_clients = yes
设定此参数之后,postfix会分别以标准与非标准两种格式来列出它所支持的smtp验证机制。
由于两种格式都出现在smtp server的响应中,所以既不会影响标准的mua,同时又能让那些不标准的mua使用smtp sasl验证。
避免寄件人冒名
当客户端通过postfix寄信时,要如何确定客户端使用的真实的寄件人地址?比方说,某人以A身份通过smtp验证,但是却以B为发行人地址,要如何避 免这种冒名情况?postfix容许你设定寄件地址与sasl登录身份的对应关系。举例来说,假设某人的邮件地址是, 其sasl登录身份为kdent,如果你希望kdent只能以该地址的名义来发邮件,而不能使用其他寄件地址,你应该将下列对应关系定义在一个查询表中:
       
                  kdent
这是一个普通的postfix查询表,你可以逐一列出每一个地址与每一位sasl用户的对应关系,也可以使用正则表达式来表示邮件地址的人名部分或网域部分。制作好查询表之后,请将main.cf的smtpd_sender_login_maps参数指向此表:
       smtpd_sender_login_maps = hash:/etc/postfxi/sasl_senders
下一步是将reject_sender_login_mismatch限制条件纳入某个smtpd_*_restrictions过滤规则组合里,这样一 来,如果kdent通过sasl验证,但是他却试图以
的名义寄出邮件,那么postfix将拒绝帮他寄信。
核准授权用户
如果你的ube过滤规则里包含了smtpd_recipient_restrictions,你得使postfix准许通过验证得用户使用转发服务,也就是将permit_sasl_authenticated安插在限制条件里得适当位置。
设定验证机制
当客户端联机到smtp server时,有哪些密码验证机制可使用,由smtpd_sasl_security_options参数决定。完整得机制选项,取决于你得系统上有哪 些机制可用以及你得sasl函数库支持哪些机制。如果不指定任何选项,默认值是接受包括明文密码在内得所有可用机制,但匿名登录除外。如果使用了 saslauthd daemon,就必须接受明文密码,所以,默认值的设想是合情合理的。如果你指定了任何选项,则默认值无效,所以你的选项里必须包含 noanonymous。例如:
       smtpd_asal_security_options = noanonymous, noplaintext
以下是通用的机制选项:
noplaintext
          此选项将plaintext密码验证排除在外。这使得sasl选项challenge/response技术来使用。如果你的安全政策不容许密码以明文形 式流经网络,那就指定noplaintext选项吧,但是这也表示你不能使用saslauthd。
noactive
       此选项将可能遭受“主动***”的密码机制排除在外。在“主动***”中,***者想办法将他们安插到client与server之间,所以,某些类型的主动攻 击又被称为"中间人***“。***者可以读取或改变数据,让client或server误以为数据真的是对方送过来的。
nodictionary
       此选项将可能遭受”字典***“的密码机制排除在外。在”字典***“中,***者使用预编的密码数据库,逐一测试哪个密码”恰好“能闯入你的系统。这类密码数 据库通常由城市、团队、宠物、常见人名、字典词汇,加上各种明显的词汇变化等组成,所以称为”字典***“。
noanonymous
       排除匿名登录。smtp验证的最主要目的,就是要确认用户的身份,所以”匿名登录“对smtp server是没有意义的。postfix的默认行为是禁止匿名登录。如果你指定了其他选项,你必须明确设定noanonymous。
mutual auth
       要求使用互证机制--client与server双方都要提供证明自己身份的证据。
设定步骤汇整
在postfix系统中添加sasl功能所涉及的步骤有点繁复。总结本章前述的知识,以下是我们整理出来的设定步骤:
1、决定你打算支持的验证机制与架构。
2、安装sasl函数库,并重新编译postfix,使其包含sasl。或者取得已经内含sasl的postfix包,以及相关的sasl验证机制。
3、重新安装postfix
4、创建/usr/local/lib/sasl2/smtpd.conf配置文件,将pwcheck_method参数设定为saslauthd(如果使用sasl saslauthd与系统密码的话)或auxprop(如果使用sasl的专属密码文件的话)。
5、如果你选择的验证架构是unix系统密码,请启动saslauthd daemon,并且指出你的系统所用的验证方式;否则,使用saslpasswd2命令在你的系统上创建smtp client的账户与密码。
6、编辑main.cf,启动sasl验证功能,并指出验证方式。基本的设定至少需要下列两项参数:
       smtpd_sasl_auth_enable = yes
       smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authicated, reject_unauth_destination
7、重新加载postfix,使我们在main.cf配置文件所做的改变生效:
          postfix reload
测试sasl验证配置
经过重重难关之后完成安装,要如何要确定所有步骤都没有出错,证明postfix确实真的已依照我们的要求来进行验证?等到用户旅行到外地了以后,看看他 们会不会打长途电话回来抱怨,肯定不是好办法。最好的办法,是直接观察smtp server的交互情况,实际体验验证过程,并立刻查阅日志文件留下的线索。
要想联机到smtp server,最容易的方法是使用telnet工具程序,然后与服务器进程smtp对话。最容易测试的plain机制,如果你禁用这项机制,建议你暂时先启用它,等完成测试实验之后再关掉它。
先让我们了解plain机制的细节,然后再开始动手做实验。plain机制要求你在auth命令之后提供一个身份标识串,此字符串编码成base64格 式。构成此字符串的各项数据的次序是“登录身份”,其后跟着一个null字符,然后是“密码拥有者的身份”,再跟一个null字符,最后是“密码”本身。 通常,“登录身份”与“密码拥有者的身份”是相同的。举例来说,假设用户kdnet的密码为rumpelstiltskin,而且其登录身份与密码拥有者 身份相同,那么,他的身份标识符串是“kdent\okdent\orumpelistiltskin”(编码前的格式)。
麻烦之处,在于如何将标识符串编码成base64格式且不包含字符串末端的CR字符。如果你的系统上有mmcncode与printf命令,这步骤应该不 难。printf命令能显出指定格式的字符串,但是不会像echo之类的命令那样,自动在字符串末端补一个换行字符。mmencode命令能将输入字符串 编码成各种mime格式,且其默认格式正好是我们所需要的base64。
所以,我们可用下列命令来产生base64格式的标识符串:
       printf 'kdent\0kdent\0rumpelstiltskin' | mmencode
某些系统平台的printf可能不能正确地处理字符串中间地null字符(\0),要想知道自己系统上地printf有没有这个毛病,只要看base64编码结果是否比原字符串短。如果你地printf有问题,不妨使用echo -n来代替printf。
smtp客户端验证
本章前半段地讨论,着重于postfix如何扮演smtp验证地服务器端角色--验证远程mua或mta地身份,借此判断对方是否有权使用转发服务。现 在,我们要换个角度,讨论postfix如何扮演客户端角色--提出自己地×××明,借此获得远程mta地转发服务地使用权。
首先,你得提供一个密码文件给postfix,其中包含能通过远程服务器得证书数据。此密码文件得每一笔记录,各包含一个代表远程服务器的网域以及一组能通过该服务器验证的账户与密码,格式如下:
       destination      username:password
当postfix要寄出一封邮件时,它先检查收件地址的网域部分,如果不能在密码文件中找到完全相符的destination,再寻找打算联机的主机名 称。这种检查过程使得postfix可以轻易连接共享相同账户数据库的多个mx主机。决定密码文件位置的参数是 smtp_sasl_password_maps。
决定客户端行为的是smtp_sasl_security_options参数,其设定方法与服务器端的 smtpd_sasl_security_options参数相同,所以不再赘述。如果你没指定任何选项,则默认行为是容许系统上所有能找到的机制,唯独 匿名登录除外。
smtp客户端验证的设定过程
让我们举个例子来示范要如何设定postfix,才能使其在通过远程系统转发邮件时,提出自己的×××明。在这个例子中,假设我们有一组可以通过ora.com网域的所有服务器主机的账户以及一组可以通过mail.postfix.org主机的账户。
1、创建/etc/postfix/sasl_passwd密码文件,并填入我们拥有的账户与密码,例如:
       ora.com               kdent:rumpelstiltskin
       mail.postfix.org        kyle:quixote
2、使用postmap产生数据库文件:
        postmap /etc/postfix/sasl_passwd
3、编辑main.cf配置文件,启动客户端验证功能。请注意,这次要设定的参数是smtp_sasl_auth_enable,而不是 smtpd_sasl_auth_enable(后者用于启动服务器端验证)。接着,在smtp_sasl_password_maps参数中指出密码文 件的位置:
        smtp_asal_auth_enable = yes
        smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
4、重新加载postfix,使我们所做的改变生效:
        postfix reload
现在,每当postfix smtp client试图通过/etc/postfix/sasl_passwd所列的任何网域或主机来转发邮件时,它就会提供对应的验证书籍。比方说,如果你的 postfix smtp client联机到mail.ora.com服务器,则它会使用kden与rumpelstiltskin这组账户与密码。