[Swan] UPDATE Re: Authentication with pam_url and nonces

Mirsad Goran Todorovac mirsad.todorovac at alu.unizg.hr
Mon Feb 7 18:00:51 EET 2022


Hi again,

I have updated the nonce generation subroutine according to the 
recommendations in RFC 5116,
chapter 3.2, "Recommended Nonce Formation". I have added a nonce counter 
to the implementation,
while keeping the old stuff. This SHOULD be sufficient to keep the 
nonces unique:

char *get_random_string (void)
{
         static bool _first_run = true;
         struct timespec tv, tv2;
         static struct drand48_data buffer;
         double result, result2;
         char *randomstr = NULL;
         char *nonce_ctr = get_nonce_ctr();
         char * retval = NULL;

         if (nonce_ctr == NULL)
                 goto get_random_exit2;

         if (clock_gettime (CLOCK_MONOTONIC, &tv) == -1)
                 goto get_random_exit;

         if (clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &tv2) == -1)
                 goto get_random_exit;

         if (_first_run) {
                 _first_run = false;
                 srand48_r (tv2.tv_nsec, &buffer);
         }
         drand48_r (&buffer, &result);
         drand48_r (&buffer, &result2);

         int ret = asprintf (&randomstr, "%lf:%ld:%ld:%ld:%ld:%d:%lf:%s",
                                 result, tv.tv_sec, tv.tv_nsec,
                                 tv2.tv_sec, tv2.tv_nsec, getpid(), 
result2, nonce_ctr);

         if (ret != -1 && randomstr != NULL)
                 retval = sha256_string (randomstr);

         if (get_serial_debug)
                 fprintf (stderr, "randomstr = %s\n", randomstr);

         if (randomstr != NULL)
                 free (randomstr);
get_random_exit:
         if (nonce_ctr != NULL)
                 free (nonce_ctr);
get_random_exit2:
         return retval;
}

I have also added authorization request sequence serial numbers 
(ARSSNs). These would like DNS zone
serials. The request is protected from tampering with an RFC 2104-style 
HMAC using:

H(str) ::= sha256sum(str)
N = H(rnd)
HMAC = H ( N || username || password || clientIP || serial || secret || N )
HMACret = H ( N || serial || secret || N )

Nonce is re-added at the end of "data" to prevent length extension 
attacks in theory.

Authorization in the script goes like this:

<?php

$serial_file = "/usr/local/etc/myauth/serial";

$ip_address = $_SERVER['REMOTE_ADDR'];
$ip_srv_address = $_SERVER['SERVER_ADDR'];

if ( $ip_address !== $ip_srv_address )
{
         header("HTTP/1.1 403 Forbidden");
         echo "HOST NOT PERMITTED";
         exit(0);
}
else if( isset($_POST["user"]) && isset($_POST["pass"]) && 
isset($_POST["mode"]) )
{
         $ret=-1;

         $nonce = $_POST["nonce"];
         $serial = $_POST["serial"];
         $sha256 = $_POST["hash"];
         if (($rawsecret = 
file_get_contents("/usr/local/etc/myauth/secret")) !== false) {
                 $secret = trim($rawsecret);
                 $mysha256 = hash("sha256", $nonce . $_POST["user"] . 
$_POST["pass"] . $_POST["mode"] . $_POST["clientIP"] . $_POST["serial"] 
. $secret . $nonce);
                 if ($sha256 !== $mysha256) {
                         $secret = "";
                         $ret = 401;
                 } else {
                         $rethash = hash("sha256", $nonce . $serial . 
$secret . $nonce);
                         $secret = "";
                         $ret = 0;
                 }
         } else {
                 $ret = 402;
                 $secret = "";
         }

         if ( $ret !== 0 ) {
                 header("HTTP/1.1 $ret Forbidden");
                 echo "HOST NOT PERMITTED";
                 exit(0);
         }

         if ( $ret == 0 && ($rawserial = 
file_get_contents($serial_file)) !== false) {
                 $myserial = trim($rawserial);
                 if ($myserial >= $serial)
                         $ret = 405;
                 else {
                         // remote serial is greater, we are going to 
the next stage
                         if (file_put_contents($serial_file, $serial) == 
false)
                                 $ret = 406;
                         else
                                 $ret = 0;
                 }
         } else
                 $ret = 404;

         if ( $ret !== 0 ) {
                 header("HTTP/1.1 $ret Forbidden");
                 echo "HOST NOT PERMITTED";
                 exit(0);
         }

         switch($_POST["mode"])
         {

         // additional username checking ...

So, if authorization request serial counter unlikely flips over 
INT64_MAX, it is rolls over to 1, and future
authorizations are impossible until the administrator intervention.

This works in theory, however the implementation may still be buggy. I 
am hoping to get this working
so I could perform additional PAM authorizations like temporary locking 
out of certs without revoking
them completely, working days and working hours access etc. ...

I am planning to disseminate the code on GitHub, and make the 
installation more user-friendly,
however the code may need to be more stable before publishing if you 
know what I mean.
I started HMAC authentication on Saturday afternoon, and several times I 
thought I had it, but
the circumstances proved me otherwise.

I attribute my progress to the highly motivating atmosphere on the 
project:-)  and the help from Above.

Any idea? Am I on the right track?

(Of course, this is still just as secure as the shell account's password 
and the www-data user
running PHP scripts.)

RATIONALE is:

Since the original pam_url did not authenticate either pam_url module 
host and neither the
authentication PHP script running module, it seemed necessary to 
authenticate them and using
HMAC-SHA-256 seemed as a logical solution, when compared to transmission 
of secret in cleartext
over TLS channel, which later also implied the use of nonces and serials
that prevent replay attack in case that the TLS session had 
theoretically been recorded for offline
brute force or other form of man-in-the-middle attack.

Kind regards,
Mirsad

On 7.2.2022. 8:58, Mirsad Goran Todorovac wrote:
> Hi Paul,
>
> On 7.2.2022. 1:56, Paul Wouters wrote:
>> On Sun, 6 Feb 2022, Mirsad Goran Todorovac wrote:
>>
>>> The passwordless authentication over pam_url used with IKEv2 with 
>>> the certificates was considered
>>> a source of brute force attacks and a dangerous module to implement 
>>> for it could allow everyone to
>>> access the system if accidentally left as the only and sufficient 
>>> module in PAM stack.
>>
>> You can't really brute force the certificate validation part.
>>
>> The pam module is just an _additional_ restriction that can restrict an
>> otherwise validated certificate. It is never even called for invalid,
>> bad or revoked certificates as the connection is rejected before the pam
>> phase due to the failed verification.
> Yes. Agreed. But: if TLS is somehow compromised (i.e. by a quantum 
> computer attack), I would still want
> to be able to authenticate safely. Or as safe as my hash function 
> (currently being SHA-256, but nothing
> prevents using a stronger one ...).
>
> I try this by sending message digest:
>
> H(str) = sha256sum(str);
> N = H(rnd);
> HMAC = H( N || username || password || clientIP || secret || N);
> retHMAC = H( N || secret || N);
>
> So, the authorization script MUST prove knowledge of the secret and my 
> nonce N as a challenge
> by sending retHMAC which I check.
>
> It can only do so if knowing the nonce of the session N and the 
> pre-shared secret.
>
> The key is to generate unique nonce. I do so like this:
>
> char *get_random_string (void)
> {
>         static bool _first_run = true;
>         struct timespec tv, tv2;
>         static struct drand48_data buffer;
>         double result, result2;
>         char *randomstr = NULL;
>
>         if (clock_gettime (CLOCK_MONOTONIC, &tv) == -1)
>                 return NULL;
>
>         if (clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &tv2) == -1)
>                 return NULL;
>
>         if (_first_run) {
>                 _first_run = false;
>                 srand48_r (tv2.tv_nsec, &buffer);
>         }
>         drand48_r (&buffer, &result);
>         drand48_r (&buffer, &result2);
>         int ret = asprintf (&randomstr, "%lf%ld%ld%ld%ld%d%lf",
>                                 result, tv.tv_sec, tv.tv_nsec,
>                                 tv2.tv_sec, tv2.tv_nsec, getpid(), 
> result2);
>         if (ret == -1)
>                 return NULL;
>         char * hashstr = sha256_string (randomstr);
>         free (randomstr);
>         return hashstr;
> }
>
> So, I am not sure that I get enough randomness i.e. if the PAM module 
> is (theoretically) called for the
> second time within the same nanosecond (or time resolution interval of 
> the clock, which can be of lesser
> quality!).
>
>>> So, the main question appears to be if there is a smarter way of 
>>> preventing brute force replay attacks
>>
>> IKE has build-in protection against replay attacks. Both sides you a
>> nonce for different connection attempts. So it is always different and
>> there is no replaying possible.
> Indeed, but I do not authenticate the second stage pam_url over IKE, 
> but over TLS connection instead
> to a CGI-bin module. In particular, this module is vulnerable to brute 
> force attacks. I defend my system
> by limiting to IP address, but that is easily defeated. So I thought 
> of knowing a common secret like RADIUS
> protocol.
>
> In particular, I did not want to send the secret in cleartext even 
> over TLS connection, as i.e. private key
> might have been compromised or brute force cracked by a quantum 
> computer by yet undocumented
> man-in-the-middle attack.
>
> I defend against these like this:
>
> $ip_address = $_SERVER['REMOTE_ADDR'];
> $ip_srv_address = $_SERVER['SERVER_ADDR'];
>
> if ( $ip_address !== $ip_srv_address )
> {
>         header("HTTP/1.1 403 Forbidden");
>         echo "HOST NOT PERMITTED";
>         exit(0);
> }
> else if( isset($_POST["user"]) && isset($_POST["pass"]) && 
> isset($_POST["mode"]) )
> {
>         $ret=-1;
>
>         $nonce = $_POST["nonce"];
>         $sha256 = $_POST["hash"];
>         if (($rawsecret = 
> file_get_contents("/usr/local/etc/myauth/secret")) !== false) {
>                 $secret = trim($rawsecret);
>                 $mysha256 = hash("sha256", $nonce . $_POST["user"] . 
> $_POST["pass"] . \
>                                            $_POST["mode"] . 
> $_POST["clientIP"] . $secret . $nonce);
>                 if ($sha256 !== $mysha256) {
>                         $secret = "";
>                         $ret = 401;
>                 } else {
>                         $rethash = hash("sha256", $nonce . $secret . 
> $nonce);
>                         $secret = "";
>                         $ret = 0;
>                 }
>         } else {
>                 $ret = 402;
>         }
>
>         if ( $ret == 0 )
>         switch($_POST["mode"])
>         {
>                 case "PAM_SM_AUTH";
>                         // Perform authing here
>                 case "PAM_SM_ACCOUNT";
>                         // Perform account aging here
>
> // .... the authorization code for the certificate ....
>
> So, I wonder how to generate unique-enough nonce, so my authorization 
> process is safe.
> (Safe as SHA256(str) hash at most.)
>
> In case of broken or compromised TLS, nothing prevents the adversary 
> to record nonce and
> SHA256 hash and reuse them. I defend against this by using rethash, 
> which is a proof that the
> authorization service is in possession of the nonce for this session 
> (which must not be reused),
> and in possession of the secret key (which we hope the adversary had 
> not learned, for it is
> never transmitted in clear like in PAP.
>
> I thought of maintaining some serial or sequence number for the 
> authorization process which
> would also be protected by the SHA256 hash, incremented and never 
> repeated, but I still
> haven't thought of a good implementation of this feature.
>
> I hope this explains.
>
> I know about sequence numbers in IKE as the protection from the replay 
> attacks (I don't know
> this in detail though), but let me remind you that pam_url doesn't 
> authenticate over IKE nor
> IPsec, so if we do not trust TLS, completely we open a host of issues ...
>
> Kind regards,
> Mirsad
>
-- 
Mirsad Todorovac
CARNet system engineer
Faculty of Graphic Arts | Academy of Fine Arts
University of Zagreb
Republic of Croatia, the European Union
--
CARNet sistem inženjer
Grafički fakultet | Akademija likovnih umjetnosti
Sveučilište u Zagrebu
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.libreswan.org/pipermail/swan/attachments/20220207/c7588250/attachment-0001.htm>


More information about the Swan mailing list