Possible bug in configurable failover

Brian Candler B.Candler at pobox.com
Tue Mar 15 13:10:34 CET 2011


On Tue, Mar 15, 2011 at 08:44:22AM +0000, Brian Candler wrote:
> Alan DeKok wrote:
> > The "update" section should behave identically to the "if".
> 
> It doesn't at the moment.

I understand it a little bit more now. Here's a version which combines both
behaviours in one.

        testing_module {
                update reply {
                        Reply-Message += "Foo"
                }
                if (1) {
                        update reply {
                                Reply-Message += "Bar"
                        }
                } 
                ok
        }

And here's the debug (it doesn't reach the 'ok', but it does add both
Reply-Message attributes):

Tue Mar 15 10:56:48 2011 : Info: ++- entering policy testing_module {...}
Tue Mar 15 10:56:48 2011 : Info: +++[reply] returns reject
Tue Mar 15 10:56:48 2011 : Info: +++? if (1)
Tue Mar 15 10:56:48 2011 : Info: ? Evaluating (1) -> TRUE
Tue Mar 15 10:56:48 2011 : Info: +++? if (1) -> TRUE
Tue Mar 15 10:56:48 2011 : Info: +++- entering if (1) {...}
Tue Mar 15 10:56:48 2011 : Info: ++++[reply] returns reject
Tue Mar 15 10:56:48 2011 : Info: +++- if (1) returns reject
Tue Mar 15 10:56:48 2011 : Info: ++- policy testing_module returns reject
Tue Mar 15 10:56:48 2011 : Info: Failed to authenticate the user.

It seems that two things are going on here:

1. The 'update reply' statement is returning 'reject'

2. At the top level of the policy group, the 'reject' is not causing an
immediate return - but it does at the end of an 'if' block.

Taking point (1) first, one thing I notice is here:

                if (child->type == MOD_UPDATE) {
                        ...
                        if (rcode != RLM_MODULE_UPDATED) {
                                myresult = rcode;
                        } else {
                                /*
                                 *      FIXME: Set priority based on
                                 *      previous priority, so that we
                                 *      don't stop on reject when the
                                 *      default priority was to
                                 *      continue...
                                 *      
                                 */
                        }
                        goto handle_result;

So the 'updated' status from MOD_UPDATE is being explicitly ignored. In that
case, myresult defaults to...  well, at the top of modcall it's initialised
to:

        myresult = stack.result[0] = default_component_results[component];

Since we're being called from module_authenticate (from rad_check_password)
we're passed component == 0, and the default is RLM_MODULE_REJECT.

However I don't see the reason why myresult = RLM_MODULE_UPDATED can't be
set after an update statement.  It's certainly fine for authentication; in
auth.c the only return value from module_authenticate which causes an
Access-Accept is RLM_MODULE_OK.  So there must be some other case this was
intended to deal with.

As for point (2), this appears to be fortuitously because I had mapped the
'reject' result to a different code:

        Auth-Type PAP {
                pap {
                        ok = return
                        reject = 1
                }
                testing_module
        }

Which in turn begs the question, why does the 'if' statement still act on
the reject?  I'm wondering if the child->actions table is not inherited
inside the 'if' body, because I can make it work like this:

        testing_module {
                update reply {
                        Reply-Message += "Foo"
		}
                if (1) {
                        reject = 1          <<<<< EXTRA LINE
                        update reply {
                                Reply-Message += "Bar"
                        }
                }
                ok
        }

But then, the expected actions seemed to get from the pap { ... } block to
testing_module...

Here's what I actually see on entry to modcall (without the extra line shown
above):

(gdb) print ((modgroup*)c)->mc
$1 = {parent = 0x0, next = 0x0, name = 0x66f482 "PAP", type = MOD_GROUP, 
  method = 0, actions = {-1, 1, -1, -1, 1, -1, -1, 1, 1}}
(gdb) print ((modgroup*)c)->children
$2 = (modcallable *) 0x7a3030
(gdb) print *(((modgroup*)c)->children)
$3 = {parent = 0x7a2640, next = 0x7a25d0, name = 0x66f638 "pap", 
  type = MOD_SINGLE, method = 0, actions = {1, -1, -1, -1, -1, -1, 1, 2, 4}}
(gdb) print *(((modgroup*)c)->children->next)
$4 = {parent = 0x7a2640, next = 0x0, name = 0x6543f8 "testing_module", 
  type = MOD_POLICY, method = 0, actions = {-1, -1, 3, -1, -1, -1, 1, 2, 4}}
(gdb) print ((modgroup*)(((modgroup*)c)->children->next))->mc
$5 = {parent = 0x7a2640, next = 0x0, name = 0x6543f8 "testing_module", 
  type = MOD_POLICY, method = 0, actions = {-1, -1, 3, -1, -1, -1, 1, 2, 4}}
(gdb) print ((modgroup*)(((modgroup*)c)->children->next))->children
$6 = (modcallable *) 0x7a3740
(gdb) print *(((modgroup*)(((modgroup*)c)->children->next))->children)
$7 = {parent = 0x7a25d0, next = 0x7a37b0, name = 0x65457f "reply", 
  type = MOD_UPDATE, method = 0, actions = {0, 0, 0, 0, 0, 0, 0, 0, 0}}
(gdb) print *(((modgroup*)(((modgroup*)c)->children->next))->children->next)
$8 = {parent = 0x7a25d0, next = 0x7a4460, name = 0x6547cb "(1)", 
  type = MOD_IF, method = 0, actions = {-1, -1, 3, -1, -1, -1, 1, 2, 4}}
(gdb) print *(((modgroup*)(((modgroup*)c)->children->next))->children->next->next)
$9 = {parent = 0x7a25d0, next = 0x0, name = 0x654b50 "ok", type = MOD_SINGLE, 
  method = 0, actions = {-1, -1, 3, -1, -1, -1, 1, 2, 4}}
(gdb) print *((modgroup*)(((modgroup*)(((modgroup*)c)->children->next))->children->next))->children
$10 = {parent = 0x7a37b0, next = 0x0, name = 0x65494f "reply", 
  type = MOD_UPDATE, method = 0, actions = {0, 0, 0, 0, 0, 0, 0, 0, 0}}

I see the actions table isn't set for MOD_UPDATE.

As far as I can tell, the reject action is actions[0]. This is 1 in the
pap{} section, but is -1 in testing_module, both at the top level and inside
the MOD_IF node.  So this rather spoils my theory :-(

Regards,

Brian.



More information about the Freeradius-Devel mailing list