]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MINOR] acl: add support for matching of RDP cookies
authorEmeric Brun <ebrun@exceliance.fr>
Tue, 30 Jun 2009 15:54:00 +0000 (17:54 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 14 Jul 2009 10:50:39 +0000 (12:50 +0200)
The RDP protocol is quite simple and documented, which permits
an easy detection and extraction of cookies. It can be useful
to match the MSTS cookie which can contain the username specified
by the client.

doc/configuration.txt
include/proto/proto_tcp.h
src/acl.c
src/proto_tcp.c

index e9a7fcb9c2cae0fbed611d8f8d6d1816ceaf4376..e8fd0903654991cb32d5a032c6815cf52f511183 100644 (file)
@@ -4822,7 +4822,7 @@ through TCP request content inspection. Please see the "tcp-request" keyword
 for more detailed information on the subject.
 
 req_len <integer>
-  Returns true when the lenght of the data in the request buffer matches the
+  Returns true when the length of the data in the request buffer matches the
   specified range. It is important to understand that this test does not
   return false as long as the buffer is changing. This means that a check with
   equality to zero will almost always immediately match at the beginning of the
@@ -4837,6 +4837,25 @@ req_proto_http
   to direct HTTP traffic to a given port and HTTPS traffic to another one
   using TCP request content inspection rules.
 
+req_rdp_cookie       <string>
+req_rdp_cookie(name) <string>
+  Returns true when data in the request buffer look like the RDP protocol, and
+  a cookie is present and equal to <string>. By default, any cookie name is
+  checked, but a specific cookie name can be specified in parenthesis. The
+  parser only checks for the first cookie, as illustrated in the RDP protocol
+  specification. The cookie name is case insensitive. This ACL can be useful
+  with the "MSTS" cookie, as it can contain the user name of the client
+  connecting to the server if properly configured on the client. This can be
+  used to restrict access to certain servers to certain users.
+
+req_rdp_cookie_cnt       <integer>
+req_rdp_cookie_cnt(name) <integer>
+  Returns true when the data in the request buffer look like the RDP protocol
+  and the number of RDP cookies matches the specified range (typically zero or
+  one). Optionally a specific cookie name can be checked. This is a simple way
+  of detecting the RDP protocol, as clients generally send the MSTS or MSTSHASH
+  cookies.
+
 req_ssl_ver <decimal>
   Returns true when data in the request buffer look like SSL, with a protocol
   version matching the specified range. Both SSLv2 hello messages and SSLv3
@@ -5055,6 +5074,7 @@ HTTP_URL_ABS     url_reg ^[^/:]*://            match absolute URL with scheme
 HTTP_URL_SLASH   url_beg /                     match URL begining with "/"
 HTTP_URL_STAR    url     *                     match URL equal to "*"
 HTTP_CONTENT     hdr_val(content-length) gt 0  match an existing content-length
+RDP_COOKIE       req_rdp_cookie_cnt gt 0       match presence of an RDP cookie
 REQ_CONTENT      req_len gt 0                  match data in the request buffer
 WAIT_END         wait_end                      wait for end of content analysis
 ---------------+-----------------------------+---------------------------------
index 61ec0bd0b3203a0b71aa285b8e00985a55fd10b6..c188c27badd4869f0b97c13e8bcf5c19057a3f17 100644 (file)
@@ -33,6 +33,8 @@ void tcpv4_add_listener(struct listener *listener);
 void tcpv6_add_listener(struct listener *listener);
 int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen);
 int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit);
+int acl_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, int dir,
+                         struct acl_expr *expr, struct acl_test *test);
 
 #endif /* _PROTO_PROTO_TCP_H */
 
index b26a527f9f8979ce10293a0e99ac92e2f5a1a366..f86b1b05f0bd8e7e7a82228055692d3a3d766fc0 100644 (file)
--- a/src/acl.c
+++ b/src/acl.c
@@ -810,6 +810,7 @@ const struct {
        { .name = "HTTP_URL_SLASH", .expr = {"url_beg","/",""}},
        { .name = "HTTP_URL_STAR",  .expr = {"url","*",""}},
        { .name = "HTTP_CONTENT",   .expr = {"hdr_val(content-length)","gt","0",""}},
+       { .name = "RDP_COOKIE",     .expr = {"req_rdp_cookie_cnt","gt","0",""}},
        { .name = "REQ_CONTENT",    .expr = {"req_len","gt","0",""}},
        { .name = "WAIT_END",       .expr = {"wait_end",""}},
        { .name = NULL, .expr = {""}}
index 4488a49baee10e0019b112fef2de7cc15cc0273d..93fc126e113f1f89b6bbbaa68445903831fc21fe 100644 (file)
@@ -690,6 +690,112 @@ acl_fetch_req_ssl_ver(struct proxy *px, struct session *l4, void *l7, int dir,
        return 0;
 }
 
+int
+acl_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, int dir,
+                     struct acl_expr *expr, struct acl_test *test)
+{
+       int bleft;
+       const unsigned char *data;
+
+       if (!l4 || !l4->req)
+               return 0;
+
+       test->flags = 0;
+
+       bleft = l4->req->l;
+       if (bleft <= 11)
+               goto too_short;
+
+       data = (const unsigned char *)l4->req->w + 11;
+       bleft -= 11;
+
+       if (bleft <= 7)
+               goto too_short;
+
+       if (strncasecmp((const char *)data, "Cookie:", 7) != 0)
+               goto not_cookie;
+
+       data += 7;
+       bleft -= 7;
+
+       while (bleft > 0 && *data == ' ') {
+               data++;
+               bleft--;
+       }
+
+       if (expr->arg_len) {
+
+               if (bleft <= expr->arg_len)
+                       goto too_short;
+
+               if ((data[expr->arg_len] != '=') ||
+                   strncasecmp(expr->arg.str, (const char *)data, expr->arg_len) != 0)
+                       goto not_cookie;
+
+               data += expr->arg_len + 1;
+               bleft -= expr->arg_len + 1;
+       } else {
+               while (bleft > 0 && *data != '=') {
+                       if (*data == '\r' || *data == '\n')
+                               goto not_cookie;
+                       data++;
+                       bleft--;
+               }
+
+               if (bleft < 1)
+                       goto too_short;
+
+               if (*data != '=')
+                       goto not_cookie;
+
+               data++;
+               bleft--;
+       }
+
+       /* data points to cookie value */
+       test->ptr = (char *)data;
+       test->len = 0;
+
+       while (bleft > 0 && *data != '\r') {
+               data++;
+               bleft--;
+       }
+
+       if (bleft < 2)
+               goto too_short;
+
+       if (data[0] != '\r' || data[1] != '\n')
+               goto not_cookie;
+
+       test->len = (char *)data - test->ptr;
+       test->flags = ACL_TEST_F_VOLATILE;
+       return 1;
+
+ too_short:
+       test->flags = ACL_TEST_F_MAY_CHANGE;
+ not_cookie:
+       return 0;
+}
+
+static int
+acl_fetch_rdp_cookie_cnt(struct proxy *px, struct session *l4, void *l7, int dir,
+                       struct acl_expr *expr, struct acl_test *test)
+{
+       int ret;
+
+       ret = acl_fetch_rdp_cookie(px, l4, l7, dir, expr, test);
+
+       test->ptr = NULL;
+       test->len = 0;
+
+       if (test->flags & ACL_TEST_F_MAY_CHANGE)
+               return 0;
+
+       test->flags = ACL_TEST_F_VOLATILE;
+       test->i = ret;
+
+       return 1;
+}
 
 static struct cfg_kw_list cfg_kws = {{ },{
        { CFG_LISTEN, "tcp-request", tcp_parse_tcp_req },
@@ -699,6 +805,8 @@ static struct cfg_kw_list cfg_kws = {{ },{
 static struct acl_kw_list acl_kws = {{ },{
        { "req_len",      acl_parse_int,        acl_fetch_req_len,     acl_match_int, ACL_USE_L4REQ_VOLATILE },
        { "req_ssl_ver",  acl_parse_dotted_ver, acl_fetch_req_ssl_ver, acl_match_int, ACL_USE_L4REQ_VOLATILE },
+       { "req_rdp_cookie",     acl_parse_str,  acl_fetch_rdp_cookie,     acl_match_str, ACL_USE_L4REQ_VOLATILE },
+       { "req_rdp_cookie_cnt", acl_parse_int,  acl_fetch_rdp_cookie_cnt, acl_match_int, ACL_USE_L4REQ_VOLATILE },
        { NULL, NULL, NULL, NULL },
 }};