Mailing List Archive

Set rhost for PAM authentication
Hello,

I'm using the PAM authentication in Exim together with a custom PAM
module that validates the password. I noticed that the rhost (remote
host) field is not set for PAM requests from Exim (Dovecot sets this
field though and sshd also seems to do so).

My Exim config looks like this:

> begin authenticators
>
> fixed_plain:
> driver = plaintext
> public_name = PLAIN
> server_prompts = :
> # Check password in $auth3 for user in $auth2
> server_condition = ${if pam{$auth2:${sg{$auth3}{:}{::}}}}
> server_set_id = $auth2
>
> login:
> driver = plaintext
> public_name = LOGIN
> server_prompts = Username:: : Password::
> # Check password in $auth2 for user in $auth1
> server_condition = ${if pam{$auth1:${sg{$auth2}{:}{::}}}}
> server_set_id = $auth1

Is there anything I can do to also pass the remote IP address to the PAM
module? The Exim manual suggests passing more parameters to the PAM
function, but it's unclear what to do here.

> pam {<string1>:<string2>:...}
My PAM module includes this code to retrieve the requested data:

> // Get the service
> retval = pam_get_item(pamh, PAM_SERVICE, (void*)&service);
> if (retval != PAM_SUCCESS || service == NULL)
> {
> pam_syslog(pamh, LOG_ERR, "cannot get service");
> return PAM_SERVICE_ERR;
> }
>
> // Get the remote user
> retval = pam_get_item(pamh, PAM_RUSER, (void*)&ruser);
> if (retval != PAM_SUCCESS || ruser == NULL)
> {
> ruser = "";
> }
>
> // Get the remote host
> retval = pam_get_item(pamh, PAM_RHOST, (void*)&rhost);
> if (retval != PAM_SUCCESS || rhost == NULL)
> {
> rhost = "";
> }
>
> // Get the username
> retval = pam_get_user(pamh, &username, NULL);
> if (retval != PAM_SUCCESS)
> {
> pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s", pam_strerror(pamh, retval));
> return PAM_SERVICE_ERR;
> }
>
> // Converse to obtain a password
> retval = obtain_authtok(pamh);
> if (retval != PAM_SUCCESS)
> {
> pam_syslog(pamh, LOG_ERR, "cannot obtain password from user");
> return retval;
> }

-Yves

--
## List details at https://lists.exim.org/mailman/listinfo/exim-users
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/
Re: Set rhost for PAM authentication [ In reply to ]
On 07/01/2021 20:09, Yves Goergen via Exim-users wrote:
> I'm using the PAM authentication in Exim together with a custom PAM module that validates the password. I noticed that the rhost (remote host) field is not set for PAM requests from Exim (Dovecot sets this field though and sshd also seems to do so).
>
> My Exim config looks like this:
>
>> begin authenticators
>>
>> fixed_plain:
>>     driver = plaintext
>>     public_name = PLAIN
>>     server_prompts = :
>>     # Check password in $auth3 for user in $auth2
>>     server_condition = ${if pam{$auth2:${sg{$auth3}{:}{::}}}}
>>     server_set_id = $auth2
>>
>> login:
>>     driver = plaintext
>>     public_name = LOGIN
>>     server_prompts = Username:: : Password::
>>     # Check password in $auth2 for user in $auth1
>>     server_condition = ${if pam{$auth1:${sg{$auth2}{:}{::}}}}
>>     server_set_id = $auth1
>
> Is there anything I can do to also pass the remote IP address to the PAM module? The Exim manual suggests passing more parameters to the PAM function, but it's unclear what to do here.

You'll need to add more items to that colon-separated list.
The first item on the list is special, and given to pam as the user.
Any further ones are just handed to pam, in sequence, when pam ask
for another item - Exim does not know what their meanings are when
handing them over.

I assume that the sequence will match your pam module code sequence
of pam_get_item() calls.

>
>>  pam {<string1>:<string2>:...}
> My PAM module includes this code to retrieve the requested data:
>
>> // Get the service
>> retval = pam_get_item(pamh, PAM_SERVICE, (void*)&service);
>> if (retval != PAM_SUCCESS || service == NULL)
>> {
>>     pam_syslog(pamh, LOG_ERR, "cannot get service");
>>     return PAM_SERVICE_ERR;
>> }

Presumbly you get "exim" for this; we give that as a hardcoded
string to pam_start()

>>
>> // Get the remote user
>> retval = pam_get_item(pamh, PAM_RUSER, (void*)&ruser);
>> if (retval != PAM_SUCCESS || ruser == NULL)
>> {
>>     ruser = "";
>> }

I have no idea what this would need to be. Perhaps a duplicate
of the "user" ? If so, you'll need to duplicate that first
"special" list element.

>>
>> // Get the remote host
>> retval = pam_get_item(pamh, PAM_RHOST, (void*)&rhost);
>> if (retval != PAM_SUCCESS || rhost == NULL)
>> {
>>     rhost = "";
>> }

What are you expecting here? A string with an rDNS name? A string
with representation of an IP address? I hope not some binary blob...

The first two are available as exim variables. Just add to the list.

>>
>> // Get the username
>> retval = pam_get_user(pamh, &username, NULL);
>> if (retval != PAM_SUCCESS)
>> {
>>     pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s", pam_strerror(pamh, retval));
>>     return PAM_SERVICE_ERR;
>> }

Presumably this gets the "special" first item from our list.

>>
>> // Converse to obtain a password
>> retval = obtain_authtok(pamh);
>> if (retval != PAM_SUCCESS)
>> {
>>     pam_syslog(pamh, LOG_ERR, "cannot obtain password from user");
>>     return retval;
>> }

And presumably this gets the "next" item off the list, after the
ones above.


--
Cheers,
Jeremy

--
## List details at https://lists.exim.org/mailman/listinfo/exim-users
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/
Re: Set rhost for PAM authentication [ In reply to ]
To be honest, I have no idea how PAM communicates internally. This code
is largely based on another module that comes with Linux and does
something similar, pam_userdb.c.

I see "exim" as the service name. I don't know what the remote_user
field is and don't regard it. Dovecot sends the IP address string as
remote host so I'd like to see that here as well. This allows me to
enable rate limiting per host (not just per user as it is now for Exim).

From what I've read, I believed that these fields are more or less
defined in PAM, just like the return codes and stuff. Let's see...
Here's the code of dovecot:

https://github.com/dovecot/core/blob/master/src/auth/passdb-pam.c

It contains the PAM_RHOST constant. Looks like PAM knows what that field
means. And there is a pam_set_item function that looks like it should be
called by the application. What happens if I just add more values to the
Exim pam function?

This looks like the corresponding Exim code:

https://github.com/Exim/exim/blob/master/src/src/auths/call_pam.c

I can't see the use of pam_set_item here at all. But again, I don't know
how a PAM client must be implemented, I'm lucky that my PAM module is
working. This entire API seems pretty messy. But since there's no other
standard, that's what we'll have to use.

-Yves


-------- Ursprüngliche Nachricht --------
Von: Jeremy Harris via Exim-users <exim-users@exim.org>
Gesendet: Donnerstag, 7. Januar 2021, 21:58 MEZ
Betreff: [exim] Set rhost for PAM authentication

On 07/01/2021 20:09, Yves Goergen via Exim-users wrote:
I'm using the PAM authentication in Exim together with a custom PAM
module that validates the password. I noticed that the rhost (remote
host) field is not set for PAM requests from Exim (Dovecot sets this
field though and sshd also seems to do so).

My Exim config looks like this:

begin authenticators

fixed_plain:
    driver = plaintext
    public_name = PLAIN
    server_prompts = :
    # Check password in $auth3 for user in $auth2
    server_condition = ${if pam{$auth2:${sg{$auth3}{:}{::}}}}
    server_set_id = $auth2

login:
    driver = plaintext
    public_name = LOGIN
    server_prompts = Username:: : Password::
    # Check password in $auth2 for user in $auth1
    server_condition = ${if pam{$auth1:${sg{$auth2}{:}{::}}}}
    server_set_id = $auth1

Is there anything I can do to also pass the remote IP address to the PAM
module? The Exim manual suggests passing more parameters to the PAM
function, but it's unclear what to do here.

You'll need to add more items to that colon-separated list.
The first item on the list is special, and given to pam as the user.
Any further ones are just handed to pam, in sequence, when pam ask
for another item - Exim does not know what their meanings are when
handing them over.

I assume that the sequence will match your pam module code sequence
of pam_get_item() calls.


 pam {<string1>:<string2>:...}
My PAM module includes this code to retrieve the requested data:

// Get the service
retval = pam_get_item(pamh, PAM_SERVICE, (void*)&service);
if (retval != PAM_SUCCESS || service == NULL)
{
    pam_syslog(pamh, LOG_ERR, "cannot get service");
    return PAM_SERVICE_ERR;
}

Presumbly you get "exim" for this; we give that as a hardcoded
string to pam_start()


// Get the remote user
retval = pam_get_item(pamh, PAM_RUSER, (void*)&ruser);
if (retval != PAM_SUCCESS || ruser == NULL)
{
    ruser = "";
}

I have no idea what this would need to be. Perhaps a duplicate
of the "user" ? If so, you'll need to duplicate that first
"special" list element.


// Get the remote host
retval = pam_get_item(pamh, PAM_RHOST, (void*)&rhost);
if (retval != PAM_SUCCESS || rhost == NULL)
{
    rhost = "";
}

What are you expecting here? A string with an rDNS name? A string
with representation of an IP address? I hope not some binary blob...

The first two are available as exim variables. Just add to the list.


// Get the username
retval = pam_get_user(pamh, &username, NULL);
if (retval != PAM_SUCCESS)
{
    pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s",
pam_strerror(pamh, retval));
    return PAM_SERVICE_ERR;
}

Presumably this gets the "special" first item from our list.


// Converse to obtain a password
retval = obtain_authtok(pamh);
if (retval != PAM_SUCCESS)
{
    pam_syslog(pamh, LOG_ERR, "cannot obtain password from user");
    return retval;
}

And presumably this gets the "next" item off the list, after the
ones above.




--
## List details at https://lists.exim.org/mailman/listinfo/exim-users
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/
Re: Set rhost for PAM authentication [ In reply to ]
On 07/01/2021 22:50, Yves Goergen via Exim-users wrote:
> To be honest, I have no idea how PAM communicates internally. This code is largely based on another module that comes with Linux and does something similar, pam_userdb.c.
>
> I see "exim" as the service name. I don't know what the remote_user field is and don't regard it. Dovecot sends the IP address string as remote host

So, you'll want to use the exim variable $sender_host_address for
an element in your list of fields.

>
> From what I've read, I believed that these fields are more or less defined in PAM, just like the return codes and stuff. Let's see... Here's the code of dovecot:
>
> https://github.com/dovecot/core/blob/master/src/auth/passdb-pam.c
>
> It contains the PAM_RHOST constant. Looks like PAM knows what that field means. And there is a pam_set_item function that looks like it should be called by the application. What happens if I just add more values to the Exim pam function?
>
> This looks like the corresponding Exim code:
>
> https://github.com/Exim/exim/blob/master/src/src/auths/call_pam.c

The pam library calls back to exim, calling our pam_converse() routine.
Exim supplies as many fields as it asks for, each time it is called -
without knowing what they might be used for. Exim gets fields to
supply by walking the list that your config-file use of the "pam"
expansion condition supplied.

--
Cheers,
Jeremy

--
## List details at https://lists.exim.org/mailman/listinfo/exim-users
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/
Re: Set rhost for PAM authentication [ In reply to ]
Okay, I added that variable as third parameter to the pam function in
Exim and modified my PAM module so that it tries a second conversation
to fetch that rhost value. It's a special case for Exim only. Now the
logs are complete and the attacker's IP address is available to log
consumers like firewall tools.

It seems like Exim wants to provide two conversation answers but the PAM
module only required one. So whatever is provided as the first
conversation answer seems to be taken as the user name implicitly by
PAM. I cannot explain the observed behaviour otherwise.

Besides these conversation answers, there must also exist special
well-known items, like the rhost. Dovecot sets them like this, Exim
could very likely just do the same, but simply doesn't. Again, someone
with more experience in PAM would be a better advisor here. These are
just my limited observations.

-Yves


-------- Ursprüngliche Nachricht --------
Von: Jeremy Harris via Exim-users <exim-users@exim.org>
Gesendet: Freitag, 8. Januar 2021, 00:15 MEZ
Betreff: [exim] Set rhost for PAM authentication

On 07/01/2021 22:50, Yves Goergen via Exim-users wrote:
To be honest, I have no idea how PAM communicates internally. This code
is largely based on another module that comes with Linux and does
something similar, pam_userdb.c.

I see "exim" as the service name. I don't know what the remote_user
field is and don't regard it. Dovecot sends the IP address string as
remote host

So, you'll want to use the exim variable $sender_host_address for
an element in your list of fields.


From what I've read, I believed that these fields are more or less
defined in PAM, just like the return codes and stuff. Let's see...
Here's the code of dovecot:

https://github.com/dovecot/core/blob/master/src/auth/passdb-pam.c

It contains the PAM_RHOST constant. Looks like PAM knows what that field
means. And there is a pam_set_item function that looks like it should be
called by the application. What happens if I just add more values to the
Exim pam function?

This looks like the corresponding Exim code:

https://github.com/Exim/exim/blob/master/src/src/auths/call_pam.c

The pam library calls back to exim, calling our pam_converse() routine.
Exim supplies as many fields as it asks for, each time it is called -
without knowing what they might be used for. Exim gets fields to
supply by walking the list that your config-file use of the "pam"
expansion condition supplied.



--
## List details at https://lists.exim.org/mailman/listinfo/exim-users
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/