关于sasl

sasl(Simple Authentication and Security Layer)是一个用于网络通信协议的安全验证框架,它可以使网络协议在互相验证的阶段可以有多种验证方式可以选择。

现在实现sasl的协议主要有imap,smtp,pop,xmpp,subersion,….。举个smtp的例子说明一下sasl的验证流程:

250-mail.example.com Hello pc.example.org [192.168.1.42], pleased to meet you
250-AUTH DIGEST-MD5 CRAM-MD5 LOGIN PLAIN
AUTH LOGIN 
334 VXNlcm5hbWU6
eHh4eAo=
334 UGFzc3dvcmQ6
b294eAo=
235 2.0.0 OK Authenticated

1 和smtp sever建立连接后,server发送欢迎信息
2 server发送自己支持的验证方式DIGEST-MD5 CRAM-MD5 LOGIN PLAIN
3 client择使用LOGIN
4 server发送base64后的challenge code,base64解码后为Username:要求client回应一个用户名
5 client回应base64后的用户名
6 server发送base64后的challenge code,base64解码后为Password:要求client回应一个密码
7 client回应base64后的密码
8 server返回验证成功的信息

很简单的流程,服务端给出选择,客户端选一个,然后根据验证方式不同进行验证流程,由于sasl仅仅是个框架,具体怎么实现是由协议决定的,比如xmpp协议的sasl验证流程

#client与server建立链接后发出一个strem头
C:
#server回应一个steam头
S:
#server发送自己支持的验证方式列表
S:
     
       DIGEST-MD5
       PLAIN
     

#client 说它要用DIGEST-MD5做验证
C:

#server 发送challenge code
S:
   cmVhbG09InNvbWVyZWFsbSIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixxb3A9ImF1dGgi
   LGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNzCg==

#client 回应challenge
C:

   dXNlcm5hbWU9InNvbWVub2RlIixyZWFsbT0ic29tZXJlYWxtIixub25jZT0i
   T0E2TUc5dEVRR20yaGgiLGNub25jZT0iT0E2TUhYaDZWcVRyUmsiLG5jPTAw
   MDAwMDAxLHFvcD1hdXRoLGRpZ2VzdC11cmk9InhtcHAvZXhhbXBsZS5jb20i
   LHJlc3BvbnNlPWQzODhkYWQ5MGQ0YmJkNzYwYTE1MjMyMWYyMTQzYWY3LGNo
   YXJzZXQ9dXRmLTgK
   
#server 回应验证结果
S:

当你实现一个协议的sasl部分时,如果你仅仅打算实现一两种验证方式,那么寥寥代码便可以搞定,但是如果希望提供尽可能多的验证方式,那么使用一些开源类库将是最好的选择。
对于C语言有两个成熟的lib:Cyrus SASL和libgsasl。以gsasl为例:

gsasl屏蔽了具体验证的细节,你要做的仅仅是为验证流程提供必要的信息,比如:用户名,密码,验证域等等
还是以上面的smtp验证为例,假设我们是client端,现在收到的server的mechlist,即验证方式列表,我们使用gsasl实现这次验证(虽然是我虚构的代码,但理论是可行的:)):

Gsasl *ctx = NULL;
char buffer[BUFSIZ] = "";
char *buf;
Gsasl_session *session;
int rc;
char *p;
//使用gsasl之前初始化
if ((rc = gsasl_init (&ctx)) != GSASL_OK)
{
    printf ("Cannot initialize libgsasl (%d): %s",
        rc, gsasl_strerror (rc));
    return;
}

//创建一个使用LOGIN验证方式的客户端session
if ((rc = gsasl_client_start (ctx, "LOGIN", &session)) != GSASL_OK)
{
    printf ("Cannot initialize client (%d): %s\n",
       rc, gsasl_strerror (rc));
    return;
}

//设置用户名和密码
gsasl_property_set (session, GSASL_AUTHID, "username");
gsasl_property_set (session, GSASL_PASSWORD, "password");

//假设socket_fd为我们与server已经建立连接的描述字

//告诉server我们选择使用LOGIN做验证
buf = buffer;
sprintf(buf, "AUTH LOGIN\r\n");
write(socket_fd, buf, strlen(buf));

do
 {
   buf = buffer;
   //从server读取一行,
   readline(buf, sizeof (buf) - 1, socket_fd);
   //334 是多余的 
   buf += 4;
   //将challenge code 交给gsasl处理
   rc = gsasl_step64 (session, buf, &p);

   if (rc == GSASL_NEEDS_MORE || rc == GSASL_OK)
     {
       //将gsasl的处理结果发送给server
       write(socket_fd, p, strlen(p));
       free (p);
     }
 }
while (rc == GSASL_NEEDS_MORE);

if (rc != GSASL_OK)
 {
   printf ("Authentication error (%d): %s\n",
           rc, gsasl_strerror (rc));
   return;
 }

printf("success!");

gsasl_finish (session);
gsasl_done (ctx);

使用gsasl可以让我们用类似上面代码处理所有的验证方式,唯一不同的就在于使用gsasl_property_set设置不同的字段。

除了像上面一样直接设置验证字段,还可以通过回调函数设置,当gsasl需要某一字段时会触发回调函数

int callback (Gsasl * ctx, Gsasl_session * sctx, Gsasl_property prop)
 {
   char buf[BUFSIZ] = "";
   int rc = GSASL_NO_CALLBACK;

   switch (prop)
     {
     case GSASL_AUTHID:
       gsasl_property_set (sctx, GSASL_AUTHID, "username");
       rc = GSASL_OK;
       break;
     // .............. 
     default:
       printf ("Unknown property!  Don't worry.\n");
       break;
     }
 
   return rc;
 }
//.................
gsasl_callback_set (ctx, callback);

好了,不能够再详细了,我的主要目的是分析jabberd2的验证逻辑,关于gsasl的更多请参考:http://www.gnu.org/software/gsasl/manual/gsasl.html

其他参考:http://wiki.jabbercn.org/index.php?title=RFC3920&variant=zh-cn

———–
post by gmail~

讨论

  1. Pingback: jabberd2源码分析:sasl认证部分 | 蓝色的华

加入讨论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据