Sorry,
I did a mistake I send an intermediate patch. This one is the correct. The comments in my previous mail are correct (I hope).
I am marking the new patch as v4.



On 06/10/2011 07:58 PM, Tsantilas Christos wrote:
Hi all,

The problem has been discussed here:
http://www.squid-cache.org/mail-archive/squid-dev/201105/0116.html>
This patch add the method ACLChecklist::reusableFastCheck which is
similar to the ACLChecklist::fastCheck method but it can be called many
times, and each time called a new independed access list check done.


Also I found some problems in ACLChecklist::fastCheck which the current
patch does not try to solve them, but we may take care of.


1) Documentation of ::fastCheck does not match implementation
--------------------------------------------------------------
Currently the documentation of the ACLChecklist::fastCheck does not
match the implementation. From the documentation:

* If there is no access list to check the default is to return DENIED.
...
* \retval 1/true Access Allowed
* \retval 0/false Access Denied


But in the case no access list exist (accessList = NULL) we have inside
::fastCheck():

currentAnswer(ACCESS_DENIED);
while (accessList) {
....
}
return currentAnswer() == ACCESS_DENIED;

In the case no access list defined, it return true ("access allowed").
We should fix the ::fastCheck or its documentation.


2) Bug in ::fastCheck() method
-------------------------------
Now, when the ::fastCheck() called for a second time or third time, the
accessList is NULL always:
- if not match because list exhausted in first call
- if match because of the "cbdataReferenceDone(accessList);" line
(src/acl/Checklist.cc line 350).

So a second call of the ::fastCheck() will return access allowed, even
if the initial decision was access denied...

This is not normal (I think it is a bug).
I propose to replace the old ::fastCheck() method with the new
::reusableFastCheck() code so the fastCheck() calls have simpler
semantics and are always reusable.





New ACLChecklist method to make ACLChecklist objects reusable

This patch investigates the ACLChecklist::reusableFastCheck method which
is similar to the ACLChecklist::fastCheck method. 
The new methd does not change the internal state of the ACLChecklist
object and each execution run an indepeneded access list check.

=== modified file 'src/acl/Checklist.cc'
--- src/acl/Checklist.cc	2009-03-08 19:41:27 +0000
+++ src/acl/Checklist.cc	2011-06-10 17:04:54 +0000
@@ -352,40 +352,63 @@
         }
 
         /*
          * Reference the next access entry
          */
         const acl_access *A = accessList;
 
         assert (A);
 
         accessList = cbdataReference(A->next);
 
         cbdataReferenceDone(A);
     }
 
     debugs(28, 5, "aclCheckFast: no matches, returning: " << (currentAnswer() == ACCESS_DENIED));
 
     PROF_stop(aclCheckFast);
     return currentAnswer() == ACCESS_DENIED;
 }
 
+bool
+ACLChecklist::reusableFastCheck()
+{
+    currentAnswer(ACCESS_DENIED);
+    debugs(28, 5, "reusableCheckFast: list: " << accessList);
+    const acl_access *acl = cbdataReference(accessList);
+    while (acl != NULL && cbdataReferenceValid(acl)) {
+        currentAnswer(acl->allow);
+        if (matchAclListFast(acl->aclList)) {
+            cbdataReferenceDone(acl);
+            return currentAnswer() == ACCESS_ALLOWED;
+        }
+
+        /*
+         * Reference the next access entry
+         */
+        const acl_access *A = acl;
+        acl = cbdataReference(acl->next);
+        cbdataReferenceDone(A);
+    }
+    debugs(28, 5, "reusableCheckFast: no matches, returning: " << (currentAnswer() == ACCESS_DENIED));
+    return currentAnswer() == ACCESS_DENIED;
+}
 
 bool
 ACLChecklist::checking() const
 {
     return checking_;
 }
 
 void
 ACLChecklist::checking (bool const newValue)
 {
     checking_ = newValue;
 }
 
 bool
 ACLChecklist::callerGone()
 {
     return !cbdataReferenceValid(callback_data);
 }
 
 bool

=== modified file 'src/acl/Checklist.h'
--- src/acl/Checklist.h	2009-04-07 13:51:57 +0000
+++ src/acl/Checklist.h	2011-06-10 12:42:35 +0000
@@ -96,40 +96,48 @@
     void nonBlockingCheck(PF * callback, void *callback_data);
 
     /**
      * Trigger a blocking access check for a set of *_access options.
      *
      * ACLs which cannot be satisfied directly from available data are ignored.
      * This means any proxy_auth, external_acl, DNS lookups, Ident lookups etc
      * which have not already been performed and cached will not be checked.
      *
      * If there is no access list to check the default is to return DENIED.
      * However callers should perform their own check and default based on local
      * knowledge of the ACL usage rather than depend on this default.
      * That will also save on work setting up ACLChecklist fields for a no-op.
      *
      * \retval  1/true    Access Allowed
      * \retval 0/false    Access Denied
      */
     int fastCheck();
 
     /**
+     * Trigger a blocking access check for a given access list 
+     * The behavior and the result is similar to the ::fastCheck method, 
+     * but this method does not change the internal state of the ACLChecklist
+     * object and each execution run an indepeneded access list check.
+     */
+    bool reusableFastCheck();
+
+    /**
      * Trigger a blocking access check for a single ACL line (a AND b AND c).
      *
      * ACLs which cannot be satisfied directly from available data are ignored.
      * This means any proxy_auth, external_acl, DNS lookups, Ident lookups etc
      * which have not already been performed and cached will not be checked.
      *
      * \retval  1/true    Access Allowed
      * \retval 0/false    Access Denied
      */
     bool matchAclListFast(const ACLList * list);
 
     /**
      * Attempt to check the current checklist against current data.
      * This is the core routine behind all ACL test routines.
      * As much as possible of current tests are performed immediately
      * and the result is maybe delayed to wait for async lookups.
      *
      * When all tests are done callback is presented with one of:
      *  - ACCESS_ALLOWED     Access explicitly Allowed
      *  - ACCESS_DENIED      Access explicitly Denied

=== modified file 'src/ssl/support.cc'
--- src/ssl/support.cc	2011-05-13 07:59:19 +0000
+++ src/ssl/support.cc	2011-06-10 16:09:21 +0000
@@ -226,41 +226,41 @@
                 debugs(83, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " << buffer << " does not match domainname " << server);
                 ok = 0;
                 error_no = SQUID_X509_V_ERR_DOMAIN_MISMATCH;
 
                 if (check)
                     Filled(check)->ssl_error = SQUID_X509_V_ERR_DOMAIN_MISMATCH;
             }
         }
     } else {
         error_no = ctx->error;
         if (const char *err_descr = Ssl::GetErrorDescr(ctx->error))
             debugs(83, 5, err_descr << ": " << buffer);
         else
             debugs(83, 1, "SSL unknown certificate error " << ctx->error << " in " << buffer);
 
         if (check)
             Filled(check)->ssl_error = ctx->error;
     }
 
     if (!ok && check) {
-        if (check->fastCheck()) {
+        if (check->reusableFastCheck()) {
             debugs(83, 3, "bypassing SSL error " << ctx->error << " in " << buffer);
             ok = 1;
         } else {
             debugs(83, 5, "confirming SSL error " << ctx->error);
         }
     }
 
     if (!dont_verify_domain && server) {}
 
     if (error_no != SSL_ERROR_NONE && !SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail) ) {
         Ssl::ErrorDetail *errDetail = new Ssl::ErrorDetail(error_no, peer_cert);
         if (!SSL_set_ex_data(ssl, ssl_ex_index_ssl_error_detail,  errDetail)) {
             debugs(83, 2, "Failed to set Ssl::ErrorDetail in ssl_verify_cb: Certificate " << buffer);
             delete errDetail;
         }
     }
 
     return ok;
 }
 

Reply via email to