科创园

科创园地,分享技术知识,为科技助力发展,贡献一己之力。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

CWMP开源代码研究4——认证流程

Posted on 2017-01-06 14:26  科创园  阅读(3744)  评论(0编辑  收藏  举报

声明:原创作品,涉及的开源程序代码学习和研究,严禁用于商业目的。 如有任何问题,欢迎和我交流:408797506@qq.com(微信:408797506)

 相关视频学习链接:

B站视频链接TR069协议与商业应用

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 传输

  1 int http_init_curl(cwmp_context_t *cwmp_ctx, t_MemStruct *pmem, CURL **pcurl)
  2 
  3 {
  4 
  5     CURLcode    code;
  6 
  7     CURL          *curl = NULL;
  8 
  9     char           *acs_usr = NULL;
 10 
 11     char           *acs_passwd = NULL;
 12 
 13     char           error_buf[CURL_ERROR_SIZE];
 14 
 15     CURLcode   ret = FALSE;
 16 
 17  
 18 
 19  
 20 
 21     if(!cwmp_ctx || !pmem || !pcurl)
 22 
 23     {
 24 
 25         CWMP_LOG_ERROR(EVENT_MODULE, "some param is NULL\n");
 26 
 27         return FALSE;
 28 
 29     }
 30 
 31    
 32 
 33     curl = curl_easy_init();
 34 
 35     if (!curl)
 36 
 37     {
 38 
 39         CWMP_LOG_ERROR(EVENT_MODULE, "curl_easy_init fail\n");
 40 
 41         return FALSE;
 42 
 43     }
 44 
 45    
 46 
 47     memset(error_buf, 0, sizeof(error_buf));
 48 
 49     code = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buf);
 50 
 51     if (code != CURLE_OK)
 52 
 53     {
 54 
 55         CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set error buffer [%d]\n", code);
 56 
 57         return FALSE;
 58 
 59     }
 60 
 61    
 62 
 63     //设置回写函数
 64 
 65     code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cwmp_write_func_callback);
 66 
 67     if (code != CURLE_OK)
 68 
 69     {
 70 
 71         CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set writer [%s]\n", curl_easy_strerror(code));
 72 
 73         goto finish;
 74 
 75     }
 76 
 77  
 78 
 79     code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)pmem);
 80 
 81     if (code != CURLE_OK)
 82 
 83     {
 84 
 85         CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set write data [%s]\n", curl_easy_strerror(code));
 86 
 87         goto finish;
 88 
 89     }
 90 
 91  
 92 
 93     code = curl_easy_setopt(curl, CURLOPT_COOKIEFILE,  "");
 94 
 95     if (code != CURLE_OK)
 96 
 97     {
 98 
 99         CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set cookie file [%s]\n", curl_easy_strerror(code));
100 
101         goto finish;
102 
103     }
104 
105  
106 
107     //设置鉴权参数
108 
109     code = curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC|CURLAUTH_DIGEST);
110 
111     if (code != CURLE_OK)
112 
113     {
114 
115         CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set http auth [%s]\n", curl_easy_strerror(code));
116 
117         goto finish;
118 
119     }
120 
121     get_param_value_by_fullname(IGD_ManagementServer_Username, &acs_usr);
122 
123     get_param_value_by_fullname(IGD_ManagementServer_Password, &acs_passwd);
124 
125     if (acs_usr && acs_passwd)
126 
127     {
128 
129         //curl_easy_setopt(curl, CURLOPT_USERNAME, acs_usr);
130 
131         //curl_easy_setopt(curl, CURLOPT_PASSWORD, acs_passwd);
132 
133         code = curl_easy_setopt(curl, CURLOPT_USERNAME, acs_usr);
134 
135         if (code != CURLE_OK)
136 
137         {
138 
139             CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set username [%s]\n", curl_easy_strerror(code));
140 
141             goto finish;
142 
143         }
144 
145         code = curl_easy_setopt(curl, CURLOPT_PASSWORD, acs_passwd);
146 
147         if (code != CURLE_OK)
148 
149         {
150 
151             CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set password [%s]\n", curl_easy_strerror(code));
152 
153             goto finish;
154 
155         }
156 
157  
158 
159         CWMP_LOG_DEBUG(EVENT_MODULE, "acs usrname=%s, passwd=%s\n", acs_usr, acs_passwd);
160 
161     }
162 
163     else
164 
165     {
166 
167         CWMP_LOG_ERROR(EVENT_MODULE, "get acs usrname or passwd fail\n");
168 
169         goto finish;
170 
171     }
172 
173  
174 
175     code = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
176 
177     if (code != CURLE_OK)
178 
179     {
180 
181         CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set follow location [%s]\n", curl_easy_strerror(code));
182 
183         goto finish;
184 
185     }
186 
187  
188 
189     code = curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5);
190 
191     if (code != CURLE_OK)
192 
193     {
194 
195         CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set max redirs [%s]\n", curl_easy_strerror(code));
196 
197         goto finish;
198 
199     }
200 
201  
202 
203     code = curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
204 
205     if (code != CURLE_OK)
206 
207     {
208 
209         CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set POSTREDIR [%s]\n", curl_easy_strerror(code));
210 
211         goto finish;
212 
213     }
214 
215  
216 
217     // http timeout 30 seconds
218 
219  
220 
221     curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30);   
222 
223  
224 
225     // not support SSL
226 
227     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
228 
229     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
230 
231  
232 
233     (*pcurl) = curl;
234 
235     ret = TRUE;
236 
237    
238 
239 finish:
240 
241     if(ret == FALSE)
242 
243     {
244 
245         if(curl)
246 
247         {
248 
249             curl_easy_cleanup(curl);
250 
251         }
252 
253     }
254 
255    
256 
257     if (acs_usr)
258 
259     {
260 
261         free_check(acs_usr);
262 
263     }
264 
265    
266 
267     if (acs_passwd)
268 
269     {
270 
271         free_check(acs_passwd);
272 
273     }
274 
275  
276 
277     return ret;
278 
279 }
280 
281  
View Code

 

2) CPE处理来自ACS的socket连接报文

  1 //处理socket连接
  2 
  3 static void *handle_sock(void *data)
  4 
  5 {
  6 
  7     Http_request    *request = NULL;
  8 
  9     int             sock = (int)(long)data;
 10 
 11     int             len = 0;
 12 
 13     const char      *auth_str = NULL;
 14 
 15     int             auth = 0;   //是否需要校验
 16 
 17     int             status = 0;
 18 
 19     char            *usrname = NULL;
 20 
 21     char            *passwd = NULL;
 22 
 23     char            resp[MAX_BUF_LEN+1] = {0};
 24 
 25     char            auth_opaque[33] = {0};
 26 
 27    
 28 
 29     CWMP_LOG_ERROR(ACS_CONN_MODULE, "handle_sock begin, sock=%d\n", sock);
 30 
 31     len = http_parse_request(sock, &request);
 32 
 33     if(len == 0)
 34 
 35     {
 36 
 37         CWMP_LOG_ERROR(ACS_CONN_MODULE, "socket:%d is closed\n", sock);
 38 
 39         return NULL;
 40 
 41     }
 42 
 43  
 44 
 45     if(len < 0)
 46 
 47     {
 48 
 49         CWMP_LOG_INFO(ACS_CONN_MODULE, "read data finish\n");
 50 
 51         goto finish;
 52 
 53     }
 54 
 55  
 56 
 57     if(!request)
 58 
 59     {
 60 
 61         CWMP_LOG_ERROR(ACS_CONN_MODULE, "http_parse_request fail\n");
 62 
 63         goto finish;
 64 
 65     }
 66 
 67    
 68 
 69     //判断是否需要验证
 70 
 71     if(g_pcwmp_ctx->dev_info.func_get_auth)
 72 
 73     {
 74 
 75         auth = g_pcwmp_ctx->dev_info.func_get_auth();
 76 
 77     }
 78 
 79     CWMP_LOG_ERROR(ACS_CONN_MODULE, "auth=%d\n", auth);
 80 
 81     if(auth <= 0)   //不需要校验
 82 
 83     {
 84 
 85         status = 200;
 86 
 87         goto response;
 88 
 89     }
 90 
 91  
 92 
 93     auth_str = http_header_get(request->header, "Authorization");
 94 
 95     if(!auth_str)
 96 
 97     {
 98 
 99         status = 401;
100 
101         CWMP_LOG_ERROR(ACS_CONN_MODULE, "have not Authorization\n");
102 
103         goto response;
104 
105     }
106 
107    
108 
109     //校验
110 
111     get_param_value_by_fullname(IGD_ManagementServer_ConnectionRequestUsername, &usrname);
112 
113     get_param_value_by_fullname(IGD_ManagementServer_ConnectionRequestPassword, &passwd);
114 
115     if(!usrname || !passwd)
116 
117     {
118 
119         status = 200;
120 
121     }
122 
123     if (check_digest_auth(auth_str, usrname, passwd) == FALSE)
124 
125     {
126 
127         status = 401;       
128 
129     }
130 
131     else
132 
133     {
134 
135         status = 200;
136 
137         CWMP_LOG_INFO(ACS_CONN_MODULE, "auth pass\n");
138 
139     }
140 
141 response:
142 
143     if(status == 200)
144 
145     {
146 
147         strcpy(resp, RESPONSE_200);
148 
149     }
150 
151     else if(status == 400)
152 
153     {
154 
155         strcpy(resp, RESPONSE_400);
156 
157     }
158 
159     else if(status == 401)
160 
161     {
162 
163         char buffer[256] = {0};
164 
165         char nonce[33];
166 
167        
168 
169         g_auth_nonce++;
170 
171         snprintf(buffer, 256,  "%d", g_auth_nonce);
172 
173         MD5(nonce, buffer, NULL);
174 
175        
176 
177         nonce[32] = 0;
178 
179         MD5(auth_opaque, g_auth_realm, NULL);
180 
181         snprintf(resp, MAX_BUF_LEN+1, RESPONSE_401, g_auth_realm, "auth", nonce, auth_opaque);
182 
183     }
184 
185  
186 
187     //发送回应
188 
189     if(status != 0)
190 
191     {
192 
193         write_all(sock, resp, strlen(resp));
194 
195         CWMP_LOG_DEBUG(ACS_CONN_MODULE, "response to acs ok, status=%d\n", status);
196 
197     }
198 
199    
200 
201 finish:
202 
203     close(sock);
204 
205     if(request)
206 
207     {
208 
209         http_destroy_request(request);
210 
211     }
212 
213  
214 
215     if(usrname)
216 
217     {
218 
219         free_check(usrname);
220 
221     }
222 
223     if(passwd)
224 
225     {
226 
227         free_check(passwd);
228 
229     }
230 
231     if(status == 200)
232 
233     {
234 
235         //6 Connected Request加入事件队列      
236 
237         increase_event_set(EVENT_CONNECTIONREQUEST, 1);
238 
239         sem_post(&g_pcwmp_ctx->sem_send_acs);
240 
241     }
242 
243     CWMP_LOG_DEBUG(ACS_CONN_MODULE, "handle_sock end\n");
244 
245     return NULL;
246 
247 }
View Code

 

总结

TR069 协议采用SSL/TLS、HTTP basic或者HTTP digest等加密认证方式可以保证数据的安全性;采用较多Web中成熟的技术,实现简单,降低了开发难度;采用HTTP协议,可以有效地穿越复杂的网络环境。因此,TR069协议比较适合对广域网内的设备进行管理。

参考

1)

http://wenku.baidu.com/link?url=w0eT-wBfONNGajF3mVOUL_KUOvZAwXnIBw7B6mBj48ySO7vXE6M7xtOo48-NEn60Dpy1pST1ATqWGleyMdLIRogQryLB72n9PJZAd5znfS3

2)TR069规范