CWMP开源代码研究4——认证流程
TR069 Http Digest 认证流程
一 流程及流程图
1.1盒端主动发起Http Digest认证流程
盒端CPE ACS终端管理系统
1.------------------inform(http不带auth头)----------->
2.<------------------401(http不带auth头)--------------
3.------------------inform(http带auth头)------------->
4.<------------------200 OK---------------------------
5.------------------ Content-Length: 0--------------->
机顶盒(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="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
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
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
报文下载路径:
http://download.csdn.net/detail/eryunyong/9730525
三 代码片段:
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, , 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, ); 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, ); 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, ); // not support SSL curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, ); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, ); (*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 = ; const char *auth_str = NULL; int auth = ; //是否需要校验 int status = ; char *usrname = NULL; char *passwd = NULL; char resp[MAX_BUF_LEN+] = {}; char auth_opaque[] = {}; CWMP_LOG_ERROR(ACS_CONN_MODULE, "handle_sock begin, sock=%d\n", sock); len = http_parse_request(sock, &request); if(len == ) { CWMP_LOG_ERROR(ACS_CONN_MODULE, "socket:%d is closed\n", sock); return NULL; } if(len < ) { 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 <= ) //不需要校验 { status = ; goto response; } auth_str = http_header_get(request->header, "Authorization"); if(!auth_str) { status = ; 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 = ; } if (check_digest_auth(auth_str, usrname, passwd) == FALSE) { status = ; } else { status = ; CWMP_LOG_INFO(ACS_CONN_MODULE, "auth pass\n"); } response: if(status == ) { strcpy(resp, RESPONSE_200); } else if(status == ) { strcpy(resp, RESPONSE_400); } else if(status == ) { char buffer[] = {}; char nonce[]; g_auth_nonce++; snprintf(buffer, , "%d", g_auth_nonce); MD5(nonce, buffer, NULL); nonce[] = ; MD5(auth_opaque, g_auth_realm, NULL); snprintf(resp, MAX_BUF_LEN+, RESPONSE_401, g_auth_realm, "auth", nonce, auth_opaque); } //发送回应 if(status != ) { 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 == ) { //6 Connected Request加入事件队列 increase_event_set(EVENT_CONNECTIONREQUEST, ); 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协议比较适合对广域网内的设备进行管理。
五 参考
1)
2)TR069规范
CWMP开源代码研究4——认证流程的更多相关文章
- CWMP开源代码研究——git代码工程
原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 声明:本系列涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅 ...
- CWMP开源代码研究5——CWMP程序设计思想
声明:本文涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅号:408797506) 本文介绍自己用过的ACS,其中包括开源版(提供下载包)和商业版(仅提供安装包下载 ...
- CWMP开源代码研究1——开篇之作
原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 声明:本系列涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅 ...
- CWMP开源代码研究2——easycwmp安装和学习
声明:本文是对开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅号:408797506) 本文所有笔记和代码可以到csdn下载:http://download.csdn.n ...
- CWMP开源代码研究3——ACS介绍
声明:本文涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅号:408797506) 本文介绍自己用过的ACS,其中包括开源版(提供下载包)和商业版(仅提供安装包下载 ...
- CWMP开源代码研究——cwmp移植
原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 声明:本系列涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅 ...
- CWMP开源代码研究6——libcwmp动态库开发
原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 为了使程序具有通用性,便于扩展和维护.采用了"模块"插入的思想.将设备业务相 ...
- CWMP开源代码研究7——cwmp移植
原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 声明:本系列涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅 ...
- CWMP开源代码研究——stun的NAT穿透
原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 参考: http://www.cnblogs.com/myblesh/p/6259765.htm ...
随机推荐
- 解决jdk1.8上编译dubbo失败
首先从https://github.com/alibaba/dubbo/archive/master.zip下载最新源码,目前最新版本为2.5.4-SNAPSHOT. 我使用的是JDK 1.8.0_0 ...
- C#语言和数据库基础
第一章 第一个C#程序 Vs2012密钥 RBCXF-CVBGR-382MK-DFHJ4-C69G8 01..net和C#的区别 大概在2000年,微软推出了一种革命性的产品--.NET(战略) 目标 ...
- RequireJS+JQueryMobile
RequireJS提供了JS下模块化开发的充分条件.之前我自己也在多个项目中尝试模块化开发,但是由于没有类似RequireJS这样的框架,最后的效果都不是很理想. 在RequireJS中,所有的JS都 ...
- jgGrid中的editrules使用函数来进行验证
jgGrid中的editrules 用于设置一些用于可编辑列的colModel的额外属性,大多数的时候是用来在提交到服务器之前验证用户的输入合法性的.比如editrules:{edithidden:t ...
- 今天大雪 看雪花飘落HTML5特效
今天大雪,弄一个下雪的特效.html5飘落的雪花堆积动画特效 查看效果:http://hovertree.com/texiao/js/snow.htm 以下是完整源代码,保存到HTML文件也可以看效果 ...
- jquery自定义插件结合baiduTemplate.js实现异步刷新(附源码)
上一篇记录了BaiduTemplate模板引擎使用示例附源码,在此基础上对使用方法进行了封装 自定义插件jajaxrefresh.js 代码如下: //闭包限定命名空间 (function ($) { ...
- autocomplete="off" 不起作用
首先来了解一下 表单自动填充的原理,当我们登录的时候,如果选择的记住登录密码,那么后续界面中如果有 <input type="text" name="field1& ...
- SharePoint 2013 图文开发系列之定义站点模板
SharePoint站点模板是一个非常好的功能,方便我们开发一类网站,然后在此基础上做二次开发,对于SharePoint的使用,有着举足轻重的作用. 因为篇幅比较长,所以加上目录,方便大家查看: 一. ...
- Nginx中文详解、配置部署及高并发优化
一.Nginx常用命令: 1. 启动 Nginx /usr/local/nginx/sbin/nginxpoechant@ubuntu:sudo ./sbin/nginx2. 停 ...
- 比Ansible更吊的自动化运维工具,自动化统一安装部署自动化部署udeploy 1.0 版本发布
新增功能: 逻辑与业务分离,完美实现逻辑与业务分离,业务实现统一shell脚本开发,由框架统一调用. 并发多线程部署,不管多少台服务器,多少个服务,同时发起线程进行更新.部署.启动. 提高list规则 ...