Radius authentication passphrase length



  • I've been running into an issue with the captive portal auth in that some users are unable to authenticate.  The common thread seems to be that they have a passphrase that is greater than 16 characters.  Has anyone else run into this?  I'm happy to try and rectify it myself but I have not found much documentation on the way the captive portal works, if anyone has any insight or pointers I'd be grateful.

    Thanks,
    nb



  • As a data point, I believe the captive portal is all based on this guy's code: http://www.mavetju.org/programming/php.php (according to the header in the file).  I'm investigating changes.



  • Hard to say, I do see a number of '16's in the PECL radius code, but I don't see anything that obviously points to a password length restriction (well..128 chars I think was the max).  OTOH, releng_1 uses the radius code in /usr/local/captiveportal - and that code is really difficult to read :)  There are some debug statements in there, you should be able to easily hand test the RADIUS_AUTHENTICATION() function.

    –Bill



  • I'm pretty sure it's just clipping the password before it sends it off.  I can't prove it yet.



  • Yup, it appears to be clipping at 16 characters (assuming I'm interpreting the debug correctly).

    "username is blahblah with len 8 encryptedpassword is ƒ “>˜nÝeA&$Í ‡G with len 16 …."

    when in reality my passphrase has been set to 25 characters.  Now I just need to figure out where that is happening....



  • @buraglio:

    Yup, it appears to be clipping at 16 characters (assuming I'm interpreting the debug correctly).

    "username is blahblah with len 8 encryptedpassword is ƒ “>˜nÝeA&$Í ‡G with len 16 …."

    when in reality my passphrase has been set to 25 characters.  Now I just need to figure out where that is happening....

    In Encrypt() (same file):
            for ($i=0;$i<=15;$i++) {

    heh…ouch - if you fix it, I'll commit it - this code is different I believe in HEAD (we're using PECL radius now), so you might want to test there also if you get a chance.

    --Bill



  • Where are you finding this?



  • radius_authentication.inc: unmodified: line 120 of 129.  That file exists in /usr/local/captiveportal

    –Bill



  • @billm:

    radius_authentication.inc: unmodified: line 120 of 129.  That file exists in /usr/local/captiveportal

    –Bill

    I've been looking at this too long.  Mine is a little different (I'm running BETA2) but it was right there.



  • @buraglio:

    @billm:

    radius_authentication.inc: unmodified: line 120 of 129.  That file exists in /usr/local/captiveportal

    –Bill

    I've been looking at this too long.  Mine is a little different (I'm running BETA2) but it was right there.

    This is just a wild ass guess, but try changing that line from:
    for ($i=0;$i<=15;$i++) {
    to:
    for ($i=0;$i<=strlen($md5checksum)/2; $i++) {

    **edit:**nm, this won't do anything
    –Bill



  • A quick test of md5() shows that it outputs 32 hex characters.

    bash-2.05b$ php t.php
    abeac07d3c28c1bef9e730002c753ed4
    2c9728a2138b2f25e9f89f99bdccf8db
    bash-2.05b$ cat t.php
    echo md5('1234567890123456') . "\n";
    echo md5('12345678901234567') . "\n";
    ?>

    And it doesn't seem to care whether you enter 16 or 17 characters, it really does calculate the hash based on ALL of what's input.

    That Encrypt() function is screwed though…
    if ($i>strlen($keyRA)) $k=0; else $k=ord(substr($keyRA,$i,1));
    does nothing...heh...barf

    --Bill



  • Cool, that jives with what I'm seeing.  Messing around with that number grabs more of the input but only the first 15 characters are encrypted.  With that change it appears to cut off at 17 characters for the password.



  • OK, here's my comments on the Encrypt() function

    
            // Loop 16 times (md5() output / 2)
            // This limits the effective password to 16 characters - is this really in the radius spec???
            for ($i=0;$i<=15;$i++) {
                // Convert md5 hex output to decimal
                if (2*$i>strlen($md5checksum)) $m=0; else $m=hexdec(substr($md5checksum,2*$i,2));
                // Do nothing????
                if ($i>strlen($keyRA)) $k=0; else $k=ord(substr($keyRA,$i,1));
                // get the decimal character value for this character in the password
                if ($i>strlen($password)) $p=0; else $p=ord(substr($password,$i,1));
                // xor the md5 character with the password character
                $c=$m^$p;
                // Convert back to 8-bit output
                $output.=chr($c);
            }
    
    

    In reading the PECL code, I think what it's doing is XORing only the first 16 chars and leaving the rest alone

    for (i = 0;  i < 16;  i++)
                            h->request[h->pass_pos + pos + i] =
                                md5 _^= h->pass[pos + i];

    Gut feel is that this also is wrong, but try adding:
    $outstr = substr_replace($password, $output, 0);
    right before the return in Encrypt() and return $outstr instead of $output

    The answer is in the PECL code I'm sure, I just don't have the time to do all the research on this one.

    –Bill_



  • In reading the RFC (http://www.faqs.org/rfcs/rfc2865.html Section 5.2), it sounds like it's skipping the second step to me:

    If the password is longer than 16 characters, a second one-way MD5
          hash is calculated over a stream of octets consisting of the
          shared secret followed by the result of the first xor.  That hash
          is XORed with the second 16 octet segment of the password and
          placed in the second 16 octets of the String field of the User-
          Password Attribute.

    If necessary, this operation is repeated, with each xor result
          being used along with the shared secret to generate the next hash
          to xor the next segment of the password, to no more than 128
          characters.

    This isn't really my core competency so I may be wrong.



  • @buraglio:

    In reading the RFC (http://www.faqs.org/rfcs/rfc2865.html Section 5.2), it sounds like it's skipping the second step to me:       
         
          If the password is longer than 16 characters, a second one-way MD5
          hash is calculated over a stream of octets consisting of the
          shared secret followed by the result of the first xor.  That hash
          is XORed with the second 16 octet segment of the password and
          placed in the second 16 octets of the String field of the User-
          Password Attribute.

    If necessary, this operation is repeated, with each xor result
          being used along with the shared secret to generate the next hash
          to xor the next segment of the password, to no more than 128
          characters.

    This isn't really my core competency so I may be wrong.

    Heh.  We'll definately want to get these fixes back to m0n0wall once this is settled.



  • I'm going to do more reading on it.  Do you believe that the asumption that it is skipping the second step (as referenced above) is correct?



  • @buraglio:

    I'm going to do more reading on it.  Do you believe that the asumption that it is skipping the second step (as referenced above) is correct?

    Yep.



  • OK, I have something for you to test - it's not complete, but it'll allow you (hopefully) to test passwords up to 32 chars.  If it works, I'll clean it up a bit and make it support the full 128 chars we should.

    http://www.pfsense.org/~billm/radius_auth.diff

    –Bill



  • Excellent, I'll patch and have some results for you by early tomorrow.

    nb



  • Just updated the patch - should work up to 128 chars now.  I'll run some quick tests through it myself.

    –Bill



  • What version are you patching this against?  I'm running BETA2 (BETA4 has issues booting on my dell 2850's) and had some errors with redirection after applying the patch.  I updated to the /usr/local/captiveportal in CVS (as well as added the authLDAP.inc that it requires) but still have some errors.  I'd like to mirror what you have been testing on if possible to rule out any version issues.



  • @buraglio:

    What version are you patching this against?  I'm running BETA2 (BETA4 has issues booting on my dell 2850's) and had some errors with redirection after applying the patch.  I updated to the /usr/local/captiveportal in CVS (as well as added the authLDAP.inc that it requires) but still have some errors.  I'd like to mirror what you have been testing on if possible to rule out any version issues.

    I don't have the box in front of my now that I'm at work, but it should apply cleanly against revision 1.12.2.1 of radius_authentication.inc:
    http://pfsense.com/cgi-bin/cvsweb.cgi/pfSense/usr/local/captiveportal/radius_authentication.inc?rev=1.12.2.1;content-type=text%2Fplain

    Here's the patched Encrypt() function (all I changed)

    
    /*
     * $password = users password
     * $key = shared secret
     * $RA = Request Authenticator (random value it seems like)
     */
    function Encrypt($password,$key,$RA) {
            global $debug;
    
            if ($debug)
                echo "
    key: $key
    password: $password
    
    * * *
    
    \n";
    
            $output="";
            $passlen = strlen($password);
            /* figure out the number of xor rounds we need to run through */
            for ($i=16; $i <= 128; $i += 16) {
                    if ($len <= $i) {
                            $rounds = $i/16;
                            break;
                    }
            }
    
            $z = 0; // How many chars have we xor'd
            for ($x=1; $x<=$rounds; $x++) {
                    $keyRA=$key.$RA;
                    $md5checksum=md5($keyRA);
    
                    // Loop 16 times (md5() output / 2)
                    // This limits the effective password to 16 characters - is this really in the radius spec???
                    for ($i=0;$i<=15;$i++) {
                            // Convert md5 hex output to decimal (md5 lengths are 32 chars)
                            if (2*$i>32) $m=0; else $m=hexdec(substr($md5checksum,2*$i,2));
                            // get the decimal character value for this character in the password
                            if ($z>$passlen-1) $p=0; else $p=ord(substr($password,$z,1));
                            // xor the md5 character with the password character
                            $c=$m^$p;
                            // Convert back to 8-bit output
                            $output.=chr($c);
                            $z++;
                    }
                    $RA=$output;
            }
    
            return $output;
    }
    
    

    –Bill



  • OK, I got it all cleaned up and patched it.  It is yielding the same error from the debug info.  From the debug output it looks liek it's grabbing 16 characters.

    "username is blahblah with len 8 encryptedpassword is …........with len 16 ........"



  • Any debug from the Encrypt() function?  I tested it with 15-17 character passwords and it seemed to do the right thing there.  I don't have a way to test against RADIUS, but the function looks good now :-/

    –Bill



  • No real debug info from the Encrypt() function.  I can dig a little deeper.  I can also give you access to the box if you'd like.



  • So it does allow for shorter paswords but generates some errors:

    
    radius-port: 1812
    radius-host: 10.10.102.2
    username: blahblah
    
    key: TestRadiusKey
    password: testpasswd
    username is blahblah with len 8 encryptedpassword is šJ»[à6%¤2ÍǃhÄ with len 10 nasHostname is portal-a.lab.local with len 18 
    writing 95 bytes
    
    Warning: Cannot modify header information - headers already sent by (output started at /usr/local/captiveportal/radius_authentication.inc:48) in /usr/local/captiveportal/index.php on line 335 
    radius-port: 1813
    radius-host: 10.10.2.25
    username: blahblah
    
    username is blahblah with len 8 nasHostname is portal-a.lab.local with len 18 
    writing 113 bytes
    [/code]
    
    The errors on the RADIUS server for a >16 char passphrase are as i'd expect for an incorrect passphrase.  
    
    


  • @buraglio:

    No real debug info from the Encrypt() function.  I can dig a little deeper.  I can also give you access to the box if you'd like.

    It'd be helpful to be able to point at a radius server with an account that has a 17 (or larger) character password.  I've got no way of testing that I'm following the RFC correctly - 16 and under still work with the new code I assume?

    –Bill



  • It authenticates with the new code with a RADIUS box with >=16 passwords but the redirection after fails with some php errors.  I assume that is a cosmetic fix and not critical.  I can work on getting a radius box up probably tomorrow if that'd be helpful.



  • @buraglio:

    It authenticates with the new code with a RADIUS box with >=16 passwords but the redirection after fails with some php errors.  I assume that is a cosmetic fix and not critical.  I can work on getting a radius box up probably tomorrow if that'd be helpful.

    So it now authenticates accounts with > 16 char passwords?  And authenticates accounts with < 16 char passwords?  Only a PHP error to cleanup?  Good news.  Maybe the PHP error is coming from the $debug define.

    –Bill



  • @billm:

    @buraglio:

    It authenticates with the new code with a RADIUS box with >=16 passwords but the redirection after fails with some php errors.  I assume that is a cosmetic fix and not critical.  I can work on getting a radius box up probably tomorrow if that'd be helpful.

    So it now authenticates accounts with > 16 char passwords?  And authenticates accounts with < 16 char passwords?  Only a PHP error to cleanup?  Good news.  Maybe the PHP error is coming from the $debug define.

    –Bill

    Actually it only authenticates 16 char or below passwords.  I mistyped.  Sorry.



  • Has anyone else noticed this behavior?  Would it be beneficial for me to set up a RADIUS box and give you access to test against?



  • @buraglio:

    Has anyone else noticed this behavior?  Would it be beneficial for me to set up a RADIUS box and give you access to test against?

    Yes, please do.  Bill does not have access to a tesitng environment for this.



  • @buraglio:

    Has anyone else noticed this behavior?  Would it be beneficial for me to set up a RADIUS box and give you access to test against?

    If you can provide me a radius target I can test this myself.

    –Bill



  • I'll work on this this afternoon and post when it's done.



  • @buraglio:

    I'll work on this this afternoon and post when it's done.

    I have this up, I'm still verifying correct functionality.  How would you like to go about testing?



  • ok, I have this working whenever you'd like to start working on it.



  • @buraglio:

    ok, I have this working whenever you'd like to start working on it.

    Can you PM me an IP to test against along with two usernames, one with a 15 char password, one with a 20 char password.  I can provide a static IP if needed that I'll be testing from.

    –Bill



  • Sent.



  • OK, fixed.  Thanks!

    Grab http://www.pfsense.org/~billm/radius_authentication.inc.txt until this is commited.

    –Bill



  • @billm:

    OK, fixed.  Thanks!

    Grab http://www.pfsense.org/~billm/radius_authentication.inc.txt until this is commited.

    –Bill

    I also verified that the code in HEAD works, it's only RELENG_1 that's affected by this.

    –Bill


Log in to reply