声明:原创作品,涉及的开源程序代码学习和研究,严禁用于商业目的。 如有任何问题,欢迎和我交流:408797506@qq.com(微信:408797506)
?
TR069 Http Digest?认证流程
一?流程及流程图
1.1盒端主动发起Http Digest认证流程?
? ? ? ? ? ?盒端CPE???????????? ?????????????????????????????ACS终端管理系统
机顶盒(CPE)通过HTTP Digest Authentication发起与终端管理系统(ACS)的认证连接,连接方式遵循RFC 2617的规定。
机顶盒连接终端管理系统的地址由Device.ManagementServer.URL参数提供。
机顶盒主动想终端管理系统发起一个HTTP 连接请求,终端管理系统会要求进行HTTP Digest Authentication认证。并按照RFC2671规范,盒端和盒端管理系统在之后的请求和应答时信息均带有认证头信息。
认证中的用户名、密码为Device.ManagementServer.Username 及Device.ManagementServer.Password
1.2???ACS主动发起Http Digest认证流程
盒端CPE??????????? ????????????????????????????ACS终端管理系统
1.<------------------http(不带auth头信息)-------------
2.------------------ 401 Unauthorized---------------->
3.<------------------http get(带auth头信息)-----------
4.------------------200 OK--------------------------->
5.<-----------------100 continue----------------------
6.------------------6 connect request---------------->
7.<------------------200 OK---------------------------
ACS终端管理系统主动发起一个HTTP 请求,CPE终端会要求进行HTTP Digest Authentication认证。并按照RFC2671规范,盒端和盒端管理系统在之后的请求和应答时信息均带有认证头信息。
认证中的用户名、密码为Device.ManagementServer.Connection.RequestUsername及Device.ManagementServer.ConnectionRequestPassword
二 详细交互流程:
2.1??盒端主动发起Http Digest认证报文?
CPE IP地址: 192.168.20.11
ACS IP地址: 192.168.20.36
1)盒端(CPE)首先发起不带认证头的Inform请求报文,内容如下:
POST /acs HTTP/1.1
Host: 192.168.20.36
Accept: */*
Connection: TE, Keep-Alive
Content-Type: text/xml; charset=utf-8
SOAPAction: ""
Content-Length: 3814
Expect: 100-continue
HTTP/1.1 100 Continue
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0">
??? <SOAP-ENV:Header>
??????? <cwmp:ID SOAP-ENV:mustUnderstand="1">1</cwmp:ID>
??? </SOAP-ENV:Header>
??? <SOAP-ENV:Body>
??????? <cwmp:Inform>
??????????? <DeviceId xsi:type="cwmp:DeviceIdStruct">
??????????????? <Manufacturer>Test</Manufacturer>
??????????????? <OUI>A1B2C4</OUI>
??????????????? <ProductClass>Test_PC</ProductClass>
??????????????? <SerialNumber>821281000054321</SerialNumber>
??????????? </DeviceId>
??????????? <Event SOAP-ENC:arrayType="cwmp:EventStruct[2]">
??? ????????????<EventStruct>
??????????????????? <EventCode>1 BOOT</EventCode>
??????????????????? <CommandKey></CommandKey>
??????????????? </EventStruct>
??????????????? <EventStruct>
……..
2) 盒端管理系统(ACS)收到上述报文后,发现没有认证消息(带有Authorization:标识的报文),然后发送401错误报文:
HTTP/1.1 401 Unauthorized
Date: Fri, 06 Jan 2017 02:47:12 GMT
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Set-Cookie: JSESSIONID=12rxzt10p2rtb;Path=/
Content-Type: text/xml; charset=utf-8
WWW-Authenticate: Digest realm="XACS",qop="auth",nonce="fd171d5efcc65e79bfd8150af7f9cb21"
Content-Length: 0
Server: Jetty(6.1.20)
3 ) 盒端(cpe)收到报文后,经过分析得到报文错误为401,代码中通过分析报文中是否有WWW-Authenticate:? Digest 字段,如果具有那么通过设置函数
? //设置鉴权参数
code=curl_easy_setopt(curl,CURLOPT_HTTPAUTH,CURLAUTH_BASIC|CURLAUTH_DIGEST);
(本地配置的realm必须与收到的realm一致否则验证不能通过)将本地文件配置的realm和从盒端管理系统(ACS)收到的nonce,opaque,qop等值通过函数http_da_calc_HA1,生成一个唯一的字符串并存入response字段,并将这些信息组合到报文的头部,最后发送给ACS的报文为:
POST /acs HTTP/1.1
Authorization: Digest username="cpe",?realm=.......
POST /acs HTTP/1.1
Authorization: Digest username="cpe",?realm="XACS",?nonce="fd171d5efcc65e79bfd8150af7f9cb21", uri="/acs", cnonce="MDQ1NzA0", nc=00000001,?qop="auth", response="5f6059675ea5da97e45be615c2466ff7"
Host: 192.168.20.36
Accept: */*
Cookie: JSESSIONID=12rxzt10p2rtb
Connection: TE, Keep-Alive
Content-Type: text/xml; charset=utf-8
SOAPAction: ""
Content-Length: 3814
Expect: 100-continue
HTTP/1.1 100 Continue
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0">
??? <SOAP-ENV:Header>
??????? <cwmp:ID SOAP-ENV:mustUnderstand="1">1</cwmp:ID>
??? </SOAP-ENV:Header>
??? <SOAP-ENV:Body>
??????? <cwmp:Inform>
??????????? <DeviceId xsi:type="cwmp:DeviceIdStruct">
?????????????? ?<Manufacturer>Test</Manufacturer>
??????????????? <OUI>A1B2C4</OUI>
??????????????? <ProductClass>Test_PC</ProductClass>
??????????????? <SerialNumber>821281000054321</SerialNumber>
??????????? </DeviceId>
??????????? <Event SOAP-ENC:arrayType="cwmp:EventStruct[2]">
??????????????? <EventStruct>
??????????????????? <EventCode>1 BOOT</EventCode>
??????????????????? <CommandKey></CommandKey>
??????????????? </EventStruct>……….
????? …….
4)盒端管理系统(ACS)收到上述报文后,确认其含Authorization:字段,并且Authorization:字段中的response的值正确,那么认证通过,并发送回复报文:
HTTP/1.1 200 OK
HTTP/1.1 200 OK
Date: Fri, 06 Jan 2017 02:47:12 GMT
Content-Type: text/xml; charset=utf-8
Content-Length: 526
Server: Jetty(6.1.20)
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:cwmp="urn:dslforum-org:cwmp-1-0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><SOAP-ENV:Header><cwmp:ID SOAP-ENV:mustUnderstand="1">1</cwmp:ID><cwmp:NoMoreRequests>0</cwmp:NoMoreRequests></SOAP-ENV:Header><SOAP-ENV:Body><cwmp:InformResponse><MaxEnvelopes>1</MaxEnvelopes></cwmp:InformResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>
5)? 盒端(CPE)收到回复报文后,分析为认证通过报文后,发送一个inform内容为空的确认报文:——其实就是个空报文
POST /acs HTTP/1.1
POST /acs HTTP/1.1
Authorization: Digest username="cpe", realm="XACS", nonce="fd171d5efcc65e79bfd8150af7f9cb21", uri="/acs", cnonce="MDQ1NzA0", nc=00000002, qop="auth", response="1b58ed5321c916998e4af9de375177fc"
Host: 192.168.20.36
Accept: */*
Cookie: JSESSIONID=12rxzt10p2rtb
Connection: TE, Keep-Alive
Content-Length: 0
Content-Type: application/x-www-form-urlencoded
2.2???ACS主动发起Http Digest认证报文
略,和2.1流程类似,具体可以下载报文,自行分析。
2.3???同时开启双向认证?
即CPE终端认证ACS管理系统,和ACS管理系统认证CPE终端同时认证。成功后盒端和盒端管理系统在之后的请求和应答时信息均带有认证头信息,认证中的用户名、密码为Device.ManagementServer.Username 及Device.ManagementServer.Password
报文下载路径:
TR069HttpDigest认证流程-C文档类资源-CSDN下载
三?代码片段:
1) CPE 认证ACS管理系统的代码
//初始化curl库,设置参数用于http 传输
int http_init_curl(cwmp_context_t *cwmp_ctx, t_MemStruct *pmem, CURL **pcurl)
{
CURLcode code;
CURL *curl = NULL;
char *acs_usr = NULL;
char *acs_passwd = NULL;
char error_buf[CURL_ERROR_SIZE];
CURLcode ret = FALSE;
if(!cwmp_ctx || !pmem || !pcurl)
{
CWMP_LOG_ERROR(EVENT_MODULE, "some param is NULL\n");
return FALSE;
}
curl = curl_easy_init();
if (!curl)
{
CWMP_LOG_ERROR(EVENT_MODULE, "curl_easy_init fail\n");
return FALSE;
}
memset(error_buf, 0, sizeof(error_buf));
code = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buf);
if (code != CURLE_OK)
{
CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set error buffer [%d]\n", code);
return FALSE;
}
//设置回写函数
code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cwmp_write_func_callback);
if (code != CURLE_OK)
{
CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set writer [%s]\n", curl_easy_strerror(code));
goto finish;
}
code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)pmem);
if (code != CURLE_OK)
{
CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set write data [%s]\n", curl_easy_strerror(code));
goto finish;
}
code = curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "");
if (code != CURLE_OK)
{
CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set cookie file [%s]\n", curl_easy_strerror(code));
goto finish;
}
//设置鉴权参数
code = curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC|CURLAUTH_DIGEST);
if (code != CURLE_OK)
{
CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set http auth [%s]\n", curl_easy_strerror(code));
goto finish;
}
get_param_value_by_fullname(IGD_ManagementServer_Username, &acs_usr);
get_param_value_by_fullname(IGD_ManagementServer_Password, &acs_passwd);
if (acs_usr && acs_passwd)
{
//curl_easy_setopt(curl, CURLOPT_USERNAME, acs_usr);
//curl_easy_setopt(curl, CURLOPT_PASSWORD, acs_passwd);
code = curl_easy_setopt(curl, CURLOPT_USERNAME, acs_usr);
if (code != CURLE_OK)
{
CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set username [%s]\n", curl_easy_strerror(code));
goto finish;
}
code = curl_easy_setopt(curl, CURLOPT_PASSWORD, acs_passwd);
if (code != CURLE_OK)
{
CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set password [%s]\n", curl_easy_strerror(code));
goto finish;
}
CWMP_LOG_DEBUG(EVENT_MODULE, "acs usrname=%s, passwd=%s\n", acs_usr, acs_passwd);
}
else
{
CWMP_LOG_ERROR(EVENT_MODULE, "get acs usrname or passwd fail\n");
goto finish;
}
code = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
if (code != CURLE_OK)
{
CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set follow location [%s]\n", curl_easy_strerror(code));
goto finish;
}
code = curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5);
if (code != CURLE_OK)
{
CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set max redirs [%s]\n", curl_easy_strerror(code));
goto finish;
}
code = curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
if (code != CURLE_OK)
{
CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set POSTREDIR [%s]\n", curl_easy_strerror(code));
goto finish;
}
// http timeout 30 seconds
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30);
// not support SSL
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
(*pcurl) = curl;
ret = TRUE;
finish:
if(ret == FALSE)
{
if(curl)
{
curl_easy_cleanup(curl);
}
}
if (acs_usr)
{
free_check(acs_usr);
}
if (acs_passwd)
{
free_check(acs_passwd);
}
return ret;
}
2) CPE处理来自ACS的socket连接报文
// 处理socket连接
static void *handle_sock(void *data)
{
Http_request *request = NULL;
int sock = (int)(long)data;
int len = 0;
const char *auth_str = NULL;
int auth = 0; //是否需要校验
int status = 0;
char *usrname = NULL;
char *passwd = NULL;
char resp[MAX_BUF_LEN+1] = {0};
char auth_opaque[33] = {0};
CWMP_LOG_ERROR(ACS_CONN_MODULE, "handle_sock begin, sock=%d\n", sock);
len = http_parse_request(sock, &request);
if(len == 0)
{
CWMP_LOG_ERROR(ACS_CONN_MODULE, "socket:%d is closed\n", sock);
return NULL;
}
if(len < 0)
{
CWMP_LOG_INFO(ACS_CONN_MODULE, "read data finish\n");
goto finish;
}
if(!request)
{
CWMP_LOG_ERROR(ACS_CONN_MODULE, "http_parse_request fail\n");
goto finish;
}
//判断是否需要验证
if(g_pcwmp_ctx->dev_info.func_get_auth)
{
auth = g_pcwmp_ctx->dev_info.func_get_auth();
}
CWMP_LOG_ERROR(ACS_CONN_MODULE, "auth=%d\n", auth);
if(auth <= 0) //不需要校验
{
status = 200;
goto response;
}
auth_str = http_header_get(request->header, "Authorization");
if(!auth_str)
{
status = 401;
CWMP_LOG_ERROR(ACS_CONN_MODULE, "have not Authorization\n");
goto response;
}
//校验
get_param_value_by_fullname(IGD_ManagementServer_ConnectionRequestUsername, &usrname);
get_param_value_by_fullname(IGD_ManagementServer_ConnectionRequestPassword, &passwd);
if(!usrname || !passwd)
{
status = 200;
}
if (check_digest_auth(auth_str, usrname, passwd) == FALSE)
{
status = 401;
}
else
{
status = 200;
CWMP_LOG_INFO(ACS_CONN_MODULE, "auth pass\n");
}
response:
if(status == 200)
{
strcpy(resp, RESPONSE_200);
}
else if(status == 400)
{
strcpy(resp, RESPONSE_400);
}
else if(status == 401)
{
char buffer[256] = {0};
char nonce[33];
g_auth_nonce++;
snprintf(buffer, 256, "%d", g_auth_nonce);
MD5(nonce, buffer, NULL);
nonce[32] = 0;
MD5(auth_opaque, g_auth_realm, NULL);
snprintf(resp, MAX_BUF_LEN+1, RESPONSE_401, g_auth_realm, "auth", nonce, auth_opaque);
}
//发送回应
if(status != 0)
{
write_all(sock, resp, strlen(resp));
CWMP_LOG_DEBUG(ACS_CONN_MODULE, "response to acs ok, status=%d\n", status);
}
finish:
close(sock);
if(request)
{
http_destroy_request(request);
}
if(usrname)
{
free_check(usrname);
}
if(passwd)
{
free_check(passwd);
}
if(status == 200)
{
//6 Connected Request加入事件队列
increase_event_set(EVENT_CONNECTIONREQUEST, 1);
sem_post(&g_pcwmp_ctx->sem_send_acs);
}
CWMP_LOG_DEBUG(ACS_CONN_MODULE, "handle_sock end\n");
return NULL;
}
四?总结
TR069 协议采用SSL/TLS、HTTP basic或者HTTP digest等加密认证方式可以保证数据的安全性;采用较多Web中成熟的技术,实现简单,降低了开发难度;采用HTTP协议,可以有效地穿越复杂的网络环境。因此,TR069协议比较适合对广域网内的设备进行管理。
|