Boa Web Server 缺陷报告及其修正方法


转: http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=824840


综述 

Boa 作为一种轻巧实用的 WEB 服务器广泛应用于嵌入式设备上, 
但 Boa 对实现动态网页的 CGI  的支持上仍存在一些缺陷, 
本文描述了 Boa 对 CGI 的 Status/Location 的支持的缺陷及其修正方法.


版本: 所有版本 (0.94.13)
缺陷: BOA 解析 CGI 应答头时不能完整处理 Status 和 Location
缺陷描述:
CGI/1.1 标准规定, CGI 脚本可以通过 Status 设置 HTTP 应答状态(如, Status: 500 Internal Error) 和 
Location 进行地址重定向 (如, Location: www.xxx.com), 而不管它们在应答头中的位置.
Boa 支持 Stauts 和 Location 两种应答头, 但它的实现仅能正确处理 Stauts 和 Location 在应答第一行的
CGI 应答. 这将给 CGI 程序的移植带来很多不便, 进而影响 Boa 作为Web Server 的功能的发挥.

影响功能:
ASP/PHP/JSP/Perl/... 等的 header, redirect, ... 等都会应用到 Stauts/Location 进行设置应答状态和
地址重定向. Boa 的该实现将影响 CGI 脚本正常功能的使用.

缺陷功能对比(对Status/Location的支持程序):
Apache 1.3.x/2.x         IIS 4.x/5.x/6.X        Boa 0.9x                 thttpd                 mini-httpd
完全支持                        完全支持                * 部分支持                 完全支持               完全支持


缺陷分析

缺陷分析

CGI 应用程序进行应答时, 可以 HTTP 头进行有限的控制. 如,设置客户端不缓存页面可用下面的 C 脚本,
HTTP/1.0: printf("Pragma: no-cache\n"); 或
HTTP/1.1: printf("Cache-Control: no-cache; no-store\n"); 
如果, 同时还需要告诉浏览器进行设置 Cookie 和控制相应状态(200 OK) 或地址重定向, 
那么就必须输出多行 http 头控制语句, CGI 支持两个解析头 "Status: " 和 "Loction: ",
即协议规定, Web 服务器支持解析头时能使用 "Status: " 进行应答状态控制, 使用 "Location: " 进行地址重定向, 
并为应答添加状态头 "HTTP/1.0 302 Moved Temporarily\n" 或 "HTTP/1.1 302 Found\n".
而不管它们在 CGI 应答头的什么位置.

分析 Boa Source Code: 
cgi_header.c  Line 82-136 容易发现, Boa 只解析 CGI 应答的第一行, 是否为 "Status: ", "Location: ", 如下所示

23
24         int process_cgi_header(request * req)
25         {
26             char *buf;
27             char *c;
28         
29             if (req->cgi_status != CGI_DONE)
30                 req->cgi_status = CGI_BUFFER;
31         
32             buf = req->header_line;
33         
34             c = strstr(buf, "\n\r\n");
35             if (c == NULL) {
36                 c = strstr(buf, "\n\n");
37                 if (c == NULL) {
38                     log_error_time();
39                     fputs("cgi_header: unable to find LFLF\n", stderr);
40         #ifdef FASCIST_LOGGING
41                     log_error_time();
42                     fprintf(stderr, "\"%s\"\n", buf);
43         #endif
44                     send_r_bad_gateway(req);
45                     return 0;
46                 }
47             }
48             if (req->simple) {
49                 if (*(c + 1) == '\r')
50                     req->header_line = c + 2;
51                 else
52                     req->header_line = c + 1;
53                 return 1;
54             }
55             if (!strncasecmp(buf, "Status: ", 8)) {
56                 req->header_line--;
57                 memcpy(req->header_line, "HTTP/1.0 ", 9);
58             } else if (!strncasecmp(buf, "Location: ", 10)) { /* got a location header */
59         #ifdef FASCIST_LOGGING
60         
61                 log_error_time();
62                 fprintf(stderr, "%s:%d - found Location header \"%s\"\n",
63                         __FILE__, __LINE__, buf + 10);
64         #endif
65         
66         
67                 if (buf[10] == '/') {   /* virtual path */
68                     log_error_time();
69                     fprintf(stderr,
70                             "server does not support internal redirection: " \
71                             "\"%s\"\n", buf + 10);
72                     send_r_bad_request(req);
73         
74                     /*
75                      * We (I, Jon) have declined to support absolute-path parsing
76                      * because I see it as a major security hole.
77                      * Location: /etc/passwd or Location: /etc/shadow is not funny.
78                      *
79                      * Also, the below code is borked.
80                      * request_uri could contain /cgi-bin/bob/extra_path
81                      */
82         
83                     /*
84                        strcpy(req->request_uri, buf + 10);
85                        return internal_redirect(req);
86                      */
87                 } else {                /* URL */
88                     char *c2;
89                     c2 = strchr(buf + 10, '\n');
90                     /* c2 cannot ever equal NULL here because we already have found one */
91         
92                     --c2;
93                     while (*c2 == '\r')
94                         --c2;
95                     ++c2;
96                     /* c2 now points to a '\r' or the '\n' */
97                     *c2++ = '\0';       /* end header */
98         
99                     /* first next header, or is at req->header_end */
100                    while ((*c2 == '\n' || *c2 == '\r') && c2 < req->header_end)
101                        ++c2;
102                    if (c2 == req->header_end)
103                        send_r_moved_temp(req, buf + 10, "");
104                    else
105                        send_r_moved_temp(req, buf + 10, c2);
106                }
107                req->status = DONE;
108                return 1;
109            } else {                    /* not location and not status */
110                char *dest;
111                int howmuch;
112                send_r_request_ok(req); /* does not terminate */
113                /* got to do special things because
114                   a) we have a single buffer divided into 2 pieces
115                   b) we need to merge those pieces
116                   Easiest way is to memmove the cgi data backward until
117                   it touches the buffered data, then reset the cgi data pointers
118                 */
119                dest = req->buffer + req->buffer_end;
120                if (req->method == M_HEAD) {
121                    if (*(c + 1) == '\r')
122                        req->header_end = c + 2;
123                    else
124                        req->header_end = c + 1;
125                    req->cgi_status = CGI_DONE;
126                }
127                howmuch = req->header_end - req->header_line;
128        
129                if (dest + howmuch > req->buffer + BUFFER_SIZE) {
130                    /* big problem */
131                    log_error_time();
130             fprintf(stderr, "Too much data to move! Aborting! %s %d\n",
131                    __FILE__, __LINE__);
132              /* reset buffer pointers because we already called
133                 send_r_request_ok... */
134              req->buffer_start = req->buffer_end = 0;
135              send_r_error(req);
136              return 0;
137          }
138          memmove(dest, req->header_line, howmuch);
139          req->buffer_end += howmuch;
140          req->header_line = req->buffer + req->buffer_end;
141          req->header_end = req->header_line;
142          req_flush(req);
143          if (req->method == M_HEAD)
144              return 0;
145      }
146      return 1;
147  }
148
149

修正方法

CGI 应答头包括多行, 我们必须对其进行逐行分析, 并作出正确的应答.
下面是修改好的源程序, 即将原来的 82-136 (即相当下文#else, #endif内部分) 替换成如下代码:

#if 1
    while(1) {
                int         len;
                char *        pnext = NULL;
                char *         ptmp = NULL;
        
                /* not find HTTP header tailer */
                if (NULL == (pnext=strchr(buf, '\n')))        /* has no '\n' */
                        break;
                
                /* the length of this line,  
                 * include '\n'
                 */
                len = pnext - buf + 1;        

                if (!strncasecmp(buf, "Location: ", 10)) {        /* got a location header */
                    /* not the first one
                     * exchange this line to the first line
                     */
                    if (buf != req->header_line)
                        {                    
                                if (NULL == (ptmp=(char *)malloc(len)))
                        {
                                log_error_time();
                                perror("malloc");
                            send_r_error(req);
                            return 0;
                        }
                
                        /* move Status: to line header */
                        memcpy(ptmp, buf, len);
                        memmove(req->header_line+len, req->header_line, buf-req->header_line);       
                        memcpy(req->header_line, ptmp, len);
                        free(ptmp);
                    }
                    
                    /* force pointer header */
                    buf = req->header_line;
                    
#ifdef FASCIST_LOGGING
        
                log_error_time();
                fprintf(stderr, "%s:%d - found Location header \"%s\"\n",
                        __FILE__, __LINE__, buf + 10);
#endif
        
        
                if (buf[10] == '/') {   /* virtual path */
                    log_error_time();
                    fprintf(stderr,
                            "server does not support internal redirection: " \
                            "\"%s\"\n", buf + 10);
                    send_r_bad_request(req);
        
                    /*
                     * We (I, Jon) have declined to support absolute-path parsing
                     * because I see it as a major security hole.
                     * Location: /etc/passwd or Location: /etc/shadow is not funny.
                     *
                     * Also, the below code is borked.
                     * request_uri could contain /cgi-bin/bob/extra_path
                     */
        
                    /*
                       strcpy(req->request_uri, buf + 10);
                       return internal_redirect(req);
                     */
                } else {                /* URL */
                    char *c2;
                    c2 = strchr(buf + 10, '\n');
                    /* c2 cannot ever equal NULL here because we already have found one */
        
                    --c2;
                    while (*c2 == '\r')
                        --c2;
                    ++c2;
                    /* c2 now points to a '\r' or the '\n' */
                    *c2++ = '\0';       /* end header */
        
                    /* first next header, or is at req->header_end */
                    while ((*c2 == '\n' || *c2 == '\r') && c2 < req->header_end)
                        ++c2;
                    if (c2 == req->header_end)
                        send_r_moved_temp(req, buf + 10, "");
                    else
                        send_r_moved_temp(req, buf + 10, c2);
                }
                req->status = DONE;
                return 1;
            }  else if (!strncasecmp(buf, "Status: ", 8)) {
                    /* not the first one
                     * exchange this line to the first line
                     */
                    if (buf != req->header_line)
                        {                    
                                if (NULL == (ptmp=(char *)malloc(len)))
                        {
                                log_error_time();
                                perror("malloc");
                            send_r_error(req);
                            return 0;
                        }
                
                        /* move Status: to line header */
                        memcpy(ptmp, buf, len);
                        memmove(req->header_line+len, req->header_line, buf-req->header_line);       
                        memcpy(req->header_line, ptmp, len);
                        free(ptmp);
                    }
                    
                req->header_line--;
                memcpy(req->header_line, "HTTP/1.0 ", 9);
                return 1;
            }
            
            /* pointer to next line */
            buf = pnext + 1;
            
            /* reach the end of HTTP header */
            if ('\0' == buf[0] || '\n' == buf[0] || '\r' == buf[0])
                    break;
        } 
        
    if (1) {        /* always done */
#else
    if (!strncasecmp(buf, "Status: ", 8)) {
        req->header_line--;
        memcpy(req->header_line, "HTTP/1.0 ", 9);
    } else if (!strncasecmp(buf, "Location: ", 10)) { /* got a location header */
#ifdef FASCIST_LOGGING

        log_error_time();
        fprintf(stderr, "%s:%d - found Location header \"%s\"\n",
                __FILE__, __LINE__, buf + 10);
#endif


        if (buf[10] == '/') {   /* virtual path */
            log_error_time();
            fprintf(stderr,
                    "server does not support internal redirection: " \
                    "\"%s\"\n", buf + 10);
            send_r_bad_request(req);

            /*
             * We (I, Jon) have declined to support absolute-path parsing
             * because I see it as a major security hole.
             * Location: /etc/passwd or Location: /etc/shadow is not funny.
             *
             * Also, the below code is borked.
             * request_uri could contain /cgi-bin/bob/extra_path
             */

            /*
               strcpy(req->request_uri, buf + 10);
               return internal_redirect(req);
             */
        } else {                /* URL */
            char *c2;
            c2 = strchr(buf + 10, '\n');
            /* c2 cannot ever equal NULL here because we already have found one */

            --c2;
            while (*c2 == '\r')
                --c2;
            ++c2;
            /* c2 now points to a '\r' or the '\n' */
            *c2++ = '\0';       /* end header */

            /* first next header, or is at req->header_end */
            while ((*c2 == '\n' || *c2 == '\r') && c2 < req->header_end)
                ++c2;
            if (c2 == req->header_end)
                send_r_moved_temp(req, buf + 10, "");
            else
                send_r_moved_temp(req, buf + 10, c2);
        }
        req->status = DONE;
        return 1;
    } else {                    /* not location and not status */
#endif 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在嵌入式web服务器boa框架的基础上, 使用C语言cgi, 或者Python脚本, 结合HTML + javascript + ajax 的嵌入式web系统的开发实例 html 中使用javascritp + ajax 从C语言生成的cgi文件的get, set 一些值. boa服务器的相关配置参数说明: http://www.cnblogs.com/liuweiqiang/p/3859130.html boa安装包文件名: boa-for-hi3516a.tar.gz boa.conf 文件的保存路径: cat /etc/boa/boa.conf boa可 执行文件的路径: /usr/local/bin/boa, 可以设置为: 系统启动的时候, 这个进程自动启动 boa.conf 文件的重要参数 保存html文件的目录 DocumentRoot /www 可以将这个目录, 设置为samb共享文件夹的目录, 方便修改调试 修改完成以后, 肯定要重启boa进程的 保存python脚本, 或者C语言cgi文件的目录 ScriptAlias /cgi-bin/ /var/www/cgi-bin/ 说明: cgi-bin/ 后面的斜杠, 一定要加上 可以将这个目录, 设置为samb共享文件夹的目录, 方便修改调试 修改完成以后, 肯定要重启boa进程的 html文件文件中, 调用python脚本的时候, 指定的路径, 需要有: /cgi-bin, 比如: var url = "/cgi-bin/getuser.py"; 这个是python 或者 var url = "/cgi-bin/output.cgi"; 这个是C语言 说明: 如果发现, html文件, 修改了, 可是在浏览器中, 查看html源代码的时候, 这个代码, 还是旧的, 那么可以通过清空"IE浏览器", "360浏览器"的浏览记录 以上, javascript 可以调用python 同样, 也可以调用C语言生成的cgi文件(其实, 就是可执行文件) C语言 + Html 例子 C语言 CGI实例 http://blog.csdn.net/ajrm0925/article/details/8810342 http://blog.csdn.net/liang890319/article/details/6277900 http://blog.csdn.net/gnefniu/article/details/42432657 上传文件: http://blog.csdn.net/yu_xiang/article/details/7996670 查找文件 find . -type f -name "boa.conf" -print -mount find . -type f -name "boa" -print -mount 四、嵌入式web服务器boa的配置和使用 嵌入式web服务器boa的配置文件为boa.conf, 在boa-0.94.13目录下面,复制该文件到文件 系统的/etc/boa目录下面,打开boa.conf,修改为如下内容: Port 80 User root Group root ErrorLog /dev/console AccessLog /dev/null ServerName SoftEmbed.com DocumentRoot /www DirectoryIndex index.html KeepAliveMax 1000 KeepAliveTimeout 10 MimeTypes /etc/mime.types DefaultType text/plain CGIPath /bin:/usr/bin:/usr/local/bin ScriptAlias /cgi-bin/ /www/cgi-bin/ 几个重要配置参数如下: DocumentRoot: 存放html文档的主目录; DirectoryIndex: 默认返回的html文档; ScriptAlias:cgi脚本虚拟路径对应的实际路径,/www/cgi-bin/为cgi脚本存放的实际路径; 其他配置选项的意义请参考相关资料。 复制boa可执行文件到/usr/sbin目录中, 启动boa进程 重新制作文件系统,系统启动后,在客户端浏览器上输入开发板的ip 地址,例如: http://192.168.0.218, 就可以看到显示的测试网页了,如下图所示 CGI getenv函数的参数详解: http://www.cnblogs.com/ser0632/p/5498228.html s = getenv("环境变量名"); 取得环境变量内容 putenv改变或增加环境变量 int putenv(const char * string); setenv(改变或增加环境变量) http://www.jb51.net/article/71940.htm
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值