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

      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
    ");
     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
    ");
     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]
    ", 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]
    ", 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]
    ", 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]
    ", 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]
    ", 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]
    ", 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]
    ", curl_easy_strerror(code));
    152 
    153             goto finish;
    154 
    155         }
    156 
    157  
    158 
    159         CWMP_LOG_DEBUG(EVENT_MODULE, "acs usrname=%s, passwd=%s
    ", acs_usr, acs_passwd);
    160 
    161     }
    162 
    163     else
    164 
    165     {
    166 
    167         CWMP_LOG_ERROR(EVENT_MODULE, "get acs usrname or passwd fail
    ");
    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]
    ", 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]
    ", 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]
    ", 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
    ", 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
    ", 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
    ");
     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
    ");
     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
    ", 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
    ");
    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
    ");
    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
    ", 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
    ");
    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规范

  • 相关阅读:
    http协议的状态码——400,401,403,404,500,502,503,301,302等常见网页错误代码
    JS中的动态合集与静态合集
    对联
    诗词
    文言文
    Youth Is Not a Time of Life
    JS探秘——那些你理解存在偏差的问题
    JS中的加号+运算符详解
    支持HTTP2协议
    银行卡信息查询接口
  • 原文地址:https://www.cnblogs.com/myblesh/p/6256183.html
Copyright © 2020-2023  润新知