Thursday, September 04, 2014

PAM revisited / PAM revisitado

This article is written in English and Portuguese (original version here)
Este artigo está escrito em Inglês e Português (versão original aqui)

English version:

Introduction
I covered PAM authentication in Informix a few years ago. I'd say most of what is written in those articles is still true. But I recently had to run some tests due to a customer situation, that may render some previous facts as not true anymore. To be more specific, I believe there is the generalized idea that implicit connections are hard or impossible to setup using PAM. This article will show that's not true. Nevertheless there are a few considerations to keep in mind.
In a recent customer situation we faced the following issue. The customer is implementing a third party product that will act as a proxy for the Informix connections. The product is being installed on the database servers and remote clients connect to a TCP port under control of this software (let's imagine 1500 on external TCP interface). The software will make a connection to the real Informix port (let's assume 1501 on localhost). The customer is using implicit (trusted) and explicit (password) connections. For explicit connections this architecture doesn't raise any issue. But for implicit connections there was (3rd party provider apparently already solved it) a big security issue. Informix checks the trust relation against the socket origin. But now, instead of the remote client, the socket is originating in the database server itself by the 3rd party application. So, every implicit connection was being trusted by Informix, independently of the real origin.
This led me to do some tests with PAM. As you know PAM (Plugin Authentication Modules) is a framework that allows applications to use different authentication modules to accomplish the authentication of a user. Different platforms come with different modules already pre-installed. What I'm going to demonstrate was tested with Linux and AIX. But it should work with any other operation system that implements PAM (HP-UX and Solaris). But the catch is that one of the modules may not be installed by default. In the worst case scenario you'd need to grab the module source code and adapt it to your platform, and compile. That work is outside the scope of this article.

Goal
So, what I was trying to achieve was the simulation of the default Informix authentication but using PAM. By default, and depending on the $INFORMIXSQLHOSTS configuration, Informix accepts both password and trusted connections on the TCP ports. In order to achieve that I need to setup a PAM service that uses two different mechanisms. One for password and the other for trusts. The module for password is easy. I think every OS has one to do that. In Linux it's pam_unix.so, in AIX is pam_aix etc. To get the trusted hosts functionality we need another module, and this one will be pam_rhosts.so (on Linux) and pam_rhosts_auth on AIX. I couldn't find a ready module for HP-UX to do this, but it's possible to adapt the module from Linux. There are some changes that must be done, because Linux-PAM has some extensions over the standard PAM framework.
Once we identify the modules we need to define how to set them up.

Configuration
As mentioned in the goal section, the idea is to configure a PAM listener that will accept both types of connections. If you check on previous articles you'll find that PAM modules can be configured with a module type and a control flag.
The module type can be auth, account, session and password.
I explained also that Informix only uses auth and account. For each type we can specify more than one module. Auth module type will check the user identity (is the password correct, is the host trusted) and account will check if the user can access the service, if the user account and/or password is valid and so on.
The control flag defines the role of the module in the overall module stack. Since each module type can use more than one module, we should specify for each module if it's return is critical or not for the overall result. The options we can use are required, requisite, sufficient and optional. LinuxPAM is more feature reach, but you should be able to get information about all these by checking the pam or pam.conf manual on your system.
So, we already know we'll need two modules, the module type, the control flags and finnaly we'll need a service name. The service name is whatever we choose, and makes the bridge between Informix configuration and the PAM framework.
I'll start the configuration in the Informix level and I'll create a new listener port for this. I'll use the name cheetah_pam as the INFORMIXSERVER (DBSERVERALIAS to be more exact). As such I need a new entry in $INFORMIXSQLHOSTS:

cheetah_pam  onsoctcp     primary 1533 s=4,pam_serv=(pam_informix),pamauth=(password)

So, I'm using "pam_informix" as the service name and I'm defining the PAM Informix option "password" as the pamauth option. The values allowed here are "password" and "challenge".
"password" should be used by services where the clients connect to using a user and password and challenge for anything else. But as we shall see, there's more to it than what's explained in the manual
So, after adding it to the $INFORMIXSQLHOSTS we should also add the new name (cheetah_pam) to $INFORMIXDIR/etc/$ONCONFIG option DBSERVERALIAS.

Next step it to configure the PAM stack for this service. This is a bit different depending if you're on Linux, AIX or others. Basically Linux uses a file for each service in /etc/pam.d. So we would create a file named /etc/pam.d/pam_informix. This file would contain several lines specifying the module type, the control flag, the module location and the module flags.
On other systems like AIX, there is only one file, called /etc/pam.conf and the service name is added as the first column in the file. Although these two ways are very similar, I tend to like better the way Linux works. Why? Because either /etc/pam.conf or /etc/pam.d/* are controlled by root and a sysadmin will not grant you, the Informix DBA, control over the /etc/pam.conf file. But he may allow you to control a single file (called pam_informix) inside the /etc/pam.d folder.
Anyway.... Given what I mentioned above, this is the service configuration:
auth        sufficient  pam_rhosts.so debug
auth        sufficient  pam_unix.so
account     required    pam_unix.so

What am I saying here? As you can see, the "auth" module type included two modules, with the control flag "sufficient". This means that either of them is enough to make the stack accept the identity. If one fails and the other is ok that's enough. If both fail than the auth type will fail.
As for the account, we're using the module pam_unix.so which validates if the account is valid (not blocked etc.). For AIX the following lines would have to be added for /etc/pam.conf
pam_informix auth      sufficient /usr/lib/security/pam_rhosts_auth debug
pam_informix auth      sufficient /usr/lib/security/pam_aix
pam_informix account   sufficient /usr/lib/security/pam_aix

The differences are that we added the service name as the first column, and the modules names are slightly different.

Testing
I have run several tests to check this implementation. I used a virtual machine where I'm running the cheetah_pam service and used the native OS as a client. My client hostname is "PTxxxx" and I created a user with the same name on the Linux box. I also setup an Informix instance on an AIX machine and  used the Linux server as a client in order to test 4GL connectivity.
First scenario is Informix instance on Linux and client on PC (windows). I have two SQL files:
C:\Programas\InformixClientSDK_370_tc7_c86>type *.sql

test_explicit.sql

CONNECT TO 'stores' USER 'PTxxxxxx' USING 'mypassword';
SELECT USER FROM sysmaster:sysdual;

test_implicit.sql

CONNECT TO 'stores';
SELECT USER FROM sysmaster:sysdual;

C:\Programas\InformixClientSDK_370_tc7_c86>

Let's see what happens when I run these both tests:
C:\Programas\InformixClientSDK_370_tc7_c86>dbaccess - test_implicit.sql

 1809: Server rejected the connection.
Error in line 1
Near character position 1

C:\Programas\InformixClientSDK_370_tc7_c86>dbaccess - test_explicit.sql

Connected.

(expression)

PTxxxxxx
1 row(s) retrieved.

Disconnected.

C:\Programas\InformixClientSDK_370_tc7_c86>

Ok. Implicit connections are failing, but user and password are working. This is expected because I didn't create the trusted relation on the Linux machine. Let's check the syslog on that machine:
tail -20 /var/log/secure
[...]
Sep  4 06:52:07 primary oninit: pam_rhosts(pam_informix:auth): denied access to PTxxxxxx@192.168.142.1 as PTxxxxxx
Sep  4 06:52:07 primary oninit: pam_unix(pam_informix:auth): authentication failure; logname= uid=1002 euid=0 tty= ruser=PTxxxxxx rhost=192.168.142.1  user=PTxxxxxx
Sep  4 06:52:09 primary oninit: pam_rhosts(pam_informix:auth): denied access to PTxxxxxx@192.168.142.1 as PTxxxxxx
Sep  4 06:52:09 primary oninit: pam_unix(pam_informix:auth): authentication failure; logname= uid=1002 euid=0 tty= ruser=PTxxxxxx rhost=192.168.142.1  user=PTxxxxxx
Sep  4 06:52:12 primary oninit: pam_rhosts(pam_informix:auth): denied access to PTxxxxxx@192.168.142.1 as PTxxxxxx
Sep  4 06:52:12 primary oninit: pam_unix(pam_informix:auth): authentication failure; logname= uid=1002 euid=0 tty= ruser=PTxxxxxx rhost=192.168.142.1  user=PTxxxxxx
Sep  4 06:52:14 primary oninit: pam_rhosts(pam_informix:auth): denied access to PTxxxxxx@192.168.142.1 as PTxxxxxx
Sep  4 06:52:14 primary oninit: pam_unix(pam_informix:auth): authentication failure; logname= uid=1002 euid=0 tty= ruser=PTxxxxxx rhost=192.168.142.1  user=PTxxxxxx
Sep  4 06:52:17 primary oninit: pam_rhosts(pam_informix:auth): denied access to PTxxxxxx@192.168.142.1 as PTxxxxxx
Sep  4 06:52:17 primary oninit: pam_unix(pam_informix:auth): authentication failure; logname= uid=1002 euid=0 tty= ruser=PTxxxxxx rhost=192.168.142.1  user=PTxxxxxx
Sep  4 06:52:32 primary oninit: pam_rhosts(pam_informix:auth): denied access to PTxxxxxx@192.168.142.1 as PTxxxxxx

These lines belong to the implicit attempt and the explicit one (last line). As you can see, for the implicit attempt both modules report failure, but for the explicit attempt only the pam_rhosts reports a failure, because the pam_unix succeeded (and since we used "sufficient", that's enough to accept the connection.
Now, let's configure the trust relation. For that I'll try to add "PTxxxxxx   PTxxxxxx" to /etc/hosts.equiv. Let's repeat the test:
C:\Programas\InformixClientSDK_370_tc7_c86>dbaccess - test_implicit.sql

Connected.

(expression)

PTxxxxxx

1 row(s) retrieved.

Disconnected.

C:\Programas\InformixClientSDK_370_tc7_c86>

and now, I'll remove that line from /etc/hosts.equiv and I'll add it to ~PCxxxxxx/.rhosts
As expected:
C:\Programas\InformixClientSDK_370_tc7_c86>dbaccess - test_implicit.sql

Connected.

(expression)

PCxxxxxx

1 row(s) retrieved.

Disconnected.

C:\Programas\InformixClientSDK_370_tc7_c86>

A final test with this setup: Let's remove the trust and try with a wrong password (changed the test_explicit.sql file):
C:\Programas\InformixClientSDK_370_tc7_c86>dbaccess - test_explicit.sql

 1809: Server rejected the connection.
Error in line 1
Near character position 1

C:\Programas\InformixClientSDK_370_tc7_c86>

So, this proves that with dbaccess we can emulate the native behavior but using PAM. But how about for other connectivity layers, like ODBC, .NET, 4GL etc.?
Well... I tried everything I could think off and only OleDB failed:

  • ConnectTestDemo (ok)
    (connectivity test bundled with ClientSDK for windows
  • ODBC (ok)
    (normal ODBC manager bundled with Windows)
  • VBScript that uses ODBC data source (ok)
  • VBScript that uses OleDB (failed!)
  • C# client that uses .NET (ok)
  • Native 4GL 7.50.UC4 (ok)
As mentioned above, OleDB client failed, because it explicitly does not support PAM. In fact when I try to use it I get the following message in the online.log:

10:35:24  listener-thread: err = -1809: oserr = 0: errstr = Client without PAM support connecting to PAM port: Server rejected the connection.

So this is a known product limitation. I believe this could be removed if we just assumed it would work just for password, and not for challenge. We did that very recently for DRDA and PAM. I suspect, but I cannot be sure, that the limitation comes from the fact that in OleDB clients we can't specify a callback function as we do in JDBC or ESQL/C for example. And that may be the source of this limitation. It would be better to support just the use of password if that's the case.

Conclusions and caveats
This little exercise proves that we can simulate (with some differences mentioned below) that we can simulate the engine native behavior while using PAM. But why would we want to do that? Well, I can list a few different motives:
  1. We may want the user/password to validate against a repository different than the native OS (or simply change the encryption mechanism and Informix may be sensitive to that)
  2. We may setup the OS to work with LDAP, and although it may work for OS native utilities, it may turn the way Informix validates the users in the OS unusable. Informix uses getpwnam() and crypt(). If these two functions don't work exactly the same way after LDAP configuration, the Informix native authentication will fail.
  3. We may want to take advantage of the flexibility of PAM. After setting up these two modules in the PAM configuration there is nothing that prevents us from introducing other modules that implement extra functionality. The only thing to consider is that we can't send challenges to the application from those modules because we setup the pamauth to "password" instead of "challenge"

But as mentioned above, there are a few issues:
  1. When using native authentication, if we provide a password, it will validate the password or refuse the connection. In this example, if the password fails, it will try to validate the trust relation. I don't think this is a big issue... typically trust relations are created for users/servers that don't use passwords. And passwords are created for scenarios where the trust relation is not created
  2. By configuring the port with pamauth=(password) we give up the ability to use any module that sends a challenge that we would reply with a function setup with the callback mechanism
  3. The pam_rhosts module checks the trusts in the files /etc/hosts.equiv and ~utilizador/.rhosts
    As we know, starting with version 11.70, Informix can (and should) use it's own files. It would be possible, and not too complex I believe, to get the source code and change the references to these files (accepting the new ones as module parameters for example) to adapt it to version 11.7
  4. On Linux (and this didn't happen on AIX and may not happen on other Linux versions), the name passed by PAM_RHOST to the module pam_rhosts must be resolvable to an IP address. Otherwise the authentication in this module will fail


Versão Portuguesa:
Introdução
Eu abordei a a autenticação via PAM no Informix há uns anos atrás. Diria que a maioria do que está nesses artigos ainda se aplica. Mas recentemente tive de efetuar alguns testes, devido a uma situação num cliente, que podem tornar falsas algumas assunções anteriores. Para ser mais específico, acredito que exista a ideia generalizada que é impossível efetuar ligações implícitas usando PAM (por exemplo em 4GL). Este artigo irá mostrar que isso é falso. Há no entanto algumas considerações a ter em conta.
Numa situação recente ocorrida num cliente enfrentámos a seguinte situação: O cliente está a implementar um produto de terceiros que irá atuar como proxy para as ligações Informix. O produto está a ser instalado nos servidores de bases de dados e os clientes ligam-se a um porto TCP controlado por esse software (imagine o porto 1500 numa interface "externa"). O referido software irá fazer a conexão ao porto real do Informix (imagine 1501 na interface localhost). O cliente está a usar ligações implícitas (trusted connections) e explícitas (utilizador/password). Para ligações explícitas esta arquitetura não levanta qualquer questão. Mas para conexões implícitas existia (aparentemente já terá sido resolvido pelo fornecedor) um grave problema de segurança. O Informix verifica as relações de confiança (trusts) com base na origem do socket estabelecido pelo cliente. Mas agora, em vez de a origem ser a máquina clientes, a origem era a própria máquina da base de dados, feita pelo software que atua como proxy. Assim, todas as tentativas de conexão implícita estavam a ser consideradas como trusted independentemente da origem real.
Isto levou-me  a fazer alguns testes com PAM. Como saberá, o PAM é uma framework que permite às aplicações usar diferentes módulos de autenticação para efetuar a autenticação dos utilizadores. Plataformas diferentes trazem já módulos pré-instalados diferentes. O que vou demonstrar foi testado em Linux e AIX. Mas deverá funcionar com outros sistemas operativos que suportam PAM (HP-UX e Solaris). O problema é que um dos módulos poderá não estar instalado nas configurações base, ou no pior cenário pode não estar disponível para instalação. Nessa situação poderá ser necessário portar o módulo de Linux e adaptar o código fonte. Esse processo está fora do âmbito deste artigo.

Objectivo
Bem, o que se pretende alcançar é a emulação da autenticação nativa do Informix, mas usando PAM. Por omissão, mas dependendo da configuração das opções no $INFORMIXSQLHOSTS, o Informix aceita tanto ligações com utilizador e password como ligações implícitas. Para conseguir o que pretendo com PAM, necessito de configurar um serviço PAM que utilize dois mecanismos diferentes. Um para as relações de confiança e outro para a autenticação mais clássica com utilizador e password. O módulo PAM para utilizador e password é o mais fácil pois existe em todos os sistemas. Em Linux será o pam_unix e em AIX será o pam_aix. Para conseguir a funcionalidade das relações de confiança será o pam_rhosts (Linux) e pam_rhosts_auth (AIX). Não consegui encontrar este módulo para HP-UX, mas é possível adaptar a partir da versão para Linux. Existem algumas alterações que têm de ser feitas, pois o PAM em Linux tem algumas extensões ao PAM standard.
Após identificarmos os módulos temos de definir a sua configuração.

Configuração
Conforme mencionado na secção anterior, a ideia é configurar um listener usando PAM que aceite ambos os tipos de conexões. Se verificar os artigos anteriores, verifica que os módulos PAM podem ser configurados com um tipo e flag de controlo. O tipo pode ser auth, account, session ou password.
Foi também explicado que o Informix só utiliza o account e auth. Para cada tipo podemos especificar mais que um módulo. O tipo auth irá servir para verificar a identidade do utilizador (se a password está correta, se o par máquina/utilizar está trusted etc.) e o tipo account irá validar se a conta está válida etc.
A flag de controlo define o papel de cada módulo no resultado final da pilha de módulos para o mesmo tipo. Como cada tipo pode conter mais que um módulo, conseguimos assim definir por módulo de que forma o seu resultado afeta o resultado final. As opções que podemos usar são requisite, required, sufficient e optional. LinuxPAM é mais rico nas opções, mas irei manter-me no standard. Pode obter mais informações sobre tudo isto consultando as páginas do manual referentes a "pam" e "pam.conf" no seu sistema.
Portanto, já sabemos que necessitamos de módulos, tipo de módulos, flag de controlo e finalmente vamos precisar de um nome de serviço. O nome de serviço é o que quisermos usar e serve como ponte entre a configuração no Informix e no sistema de PAM.
Vou começar com a configuração de Informix e para tal vamos criar um novo porto para um listener. Vou usar o nome "cheetah_pam" como INFORMIXSERVER (ou para ser mais exato como DBSERVERALIAS). Nestes termos, necessitamos de uma nova entrada em $INFORMIXSQLHOSTS:
cheetah_pam  onsoctcp     primary 1533 s=4,pam_serv=(pam_informix),pamauth=(password)

Estou a usar "pam_informix" como nome de serviço e estou a utilizar o tipo de autenticação "password" na opção "pamauth". Os valores possíveis são "password" e "challenge".
"password" deveria ser usado em situações onde os clientes estão preparados para enviar a password e "challenge" para as outras situações (que implicariam a configuração de uma função de callback).
Mas como veremos, há algo mais sobre isto que aquilo que está explicado no manual.
Depois de adicionar o novo listener à configuração no $INFORMIXSQLHOSTS é necessário incluir isto também no $INFORMIXDIR/etc/$ONCONFIG, no parâmetro DBSERVERALIAS.

O próximo passo é configurar o stack PAM para este serviço. Isto é feito de forma ligeiramente diferente conforme estamos em Linux, AIX ou outros. Basicamente em Linux é usado um ficheiro com o nome do serviço criado em /etc/pam.d. Assim criaremos um ficheiro /etc/pam.d/pam_informix. Este ficheiro deverá conter várias linhas indicando o tipo, a flag de controlo, o nome do módulo e respetivos argumentos.
Em outros sistemas como AIX, existe apenas um ficheiro com o nome /etc/pam.conf e o nome de serviço é adicionado como primeira coluna desse ficheiro. Apesar de ambas as formas serem bastante semelhantes, julgo preferir a forma usada em Linux. Porquê? Porque tanto o /etc/pam.conf como os /etc/pam.d/* são controlados por root e um administrador se sistema não irá conceder-lhe a si, administrador de Informix, controlo sobre o ficheiro /etc/pam.conf. Mas poderá conceder controlo sobre um serviço (ficheiro - pam_informix) dentro do diretório /etc/pam.d
Enfim... Dado o que referi acima, esta será a configuração do serviço:
auth      sufficient  pam_rhosts.so debug
auth      sufficient  pam_unix.so
account   required    pam_unix.so

O que estou aqui a dizer? Como se pode ver, o tipo auth incluí dois módulos, ambos usando a flag de controlo sufficient. Isto significa que qualquer deles são suficientes para fazer o stack aceitar a autenticação. Se um falhar e o outro validar será suficiente. Se ambos falharem, então a autenticação falha.
Em relação ao account, estamos a usar o módulo pam_unix que valida se a conta é válida (não está bloqueada etc.). Para AIX as linhas seguintes teriam de ser adicionadas ao /etc/pam.conf:
pam_informix auth    sufficient /usr/lib/security/pam_rhosts_auth debug
pam_informix auth    sufficient /usr/lib/security/pam_aix
pam_informix account sufficient /usr/lib/security/pam_aix

TA diferença é que adicionámos o nome do serviço como primeira coluna do ficheiro, e os módulos são ligeiramente diferentes no nome.

Testes
Executei vários testes para validar esta implementação. Utilizei uma máquina virtual onde estou a correr a instância onde adicionei o listener cheetah_pam, e utilizei o SO nativo como cliente. O nome do meu cliente é "PTxxxxxx" e criei um utilizador com o mesmo nome no ambiente Linux. Também configurei uma instância numa máquina AIX e usei o Linux como cliente para executar alguns testes com conectividade 4GL.
O primeiro cenário é a instância Informix no Linux e o cliente no PC (Windows). Tenho dois ficheiros SQL:
C:\Programas\InformixClientSDK_370_tc7_c86>type *.sql

test_explicit.sql

CONNECT TO 'stores' USER 'PCxxxxxx' USING 'mypassword';
SELECT USER FROM sysmaster:sysdual;

test_implicit.sql

CONNECT TO 'stores';
SELECT USER FROM sysmaster:sysdual;

C:\Programas\InformixClientSDK_370_tc7_c86>

Vejamos o que acontece quando executo ambos os scripts:
C:\Programas\InformixClientSDK_370_tc7_c86>dbaccess - test_implicit.sql

 1809: Server rejected the connection.
Error in line 1
Near character position 1

C:\Programas\InformixClientSDK_370_tc7_c86>dbaccess - test_explicit.sql

Connected.

(expression)

PTxxxxxx
1 row(s) retrieved.

Disconnected.

C:\Programas\InformixClientSDK_370_tc7_c86>

Ok.As ligações implícitas estão a falhar, mas as que usam utilizador e password estão a funcionar. Isto é esperado porque não criei as relações de confiança na máquina Linux. Verifiquemos o syslog dessa máquina:
tail -20 /var/log/secure

[...]
Sep  4 06:52:07 primary oninit: pam_rhosts(pam_informix:auth): denied access to PTxxxxxx@192.168.142.1 as PTxxxxxx
Sep  4 06:52:07 primary oninit: pam_unix(pam_informix:auth): authentication failure; logname= uid=1002 euid=0 tty= ruser=PTxxxxxx rhost=192.168.142.1  user=PTxxxxxx
Sep  4 06:52:09 primary oninit: pam_rhosts(pam_informix:auth): denied access to PTxxxxxx@192.168.142.1 as PTxxxxxx
Sep  4 06:52:09 primary oninit: pam_unix(pam_informix:auth): authentication failure; logname= uid=1002 euid=0 tty= ruser=PTxxxxxx rhost=192.168.142.1  user=PTxxxxxx
Sep  4 06:52:12 primary oninit: pam_rhosts(pam_informix:auth): denied access to PTxxxxxx@192.168.142.1 as PTxxxxxx
Sep  4 06:52:12 primary oninit: pam_unix(pam_informix:auth): authentication failure; logname= uid=1002 euid=0 tty= ruser=PTxxxxxx rhost=192.168.142.1  user=PTxxxxxx
Sep  4 06:52:14 primary oninit: pam_rhosts(pam_informix:auth): denied access to PTxxxxxx@192.168.142.1 as PTxxxxxx
Sep  4 06:52:14 primary oninit: pam_unix(pam_informix:auth): authentication failure; logname= uid=1002 euid=0 tty= ruser=PTxxxxxx rhost=192.168.142.1  user=PTxxxxxx
Sep  4 06:52:17 primary oninit: pam_rhosts(pam_informix:auth): denied access to PTxxxxxx@192.168.142.1 as PTxxxxxx
Sep  4 06:52:17 primary oninit: pam_unix(pam_informix:auth): authentication failure; logname= uid=1002 euid=0 tty= ruser=PTxxxxxx rhost=192.168.142.1  user=PTxxxxxx
Sep  4 06:52:32 primary oninit: pam_rhosts(pam_informix:auth): denied access to PTxxxxxx@192.168.142.1 as PTxxxxxx
As primeiras pertencem à tentativa implícita e a última à explícita. Como pode ver, para a ligação implícita ambos os módulos reportam falha na autenticação, mas para a explícita apenas o pam_rhosts reporta falha. O pam_unix teve sucesso e como usamos sufficient isso basta para aceitar a conexão.
Agora vamos configurar a relação de confiânça. Para tal vou adicionar"PTxxxxxx   PTxxxxxx" ao ficheiro /etc/hosts.equiv.Vamos repetir o teste:

C:\Programas\InformixClientSDK_370_tc7_c86>dbaccess - test_implicit.sql

Connected.

(expression)

PCxxxxxx

1 row(s) retrieved.

Disconnected.

C:\Programas\InformixClientSDK_370_tc7_c86>

e agora vamos remover essa linha do /etc/hosts.equiv e adicioná-la ao ~PTxxxxxx/.rhosts
Conforme esperado:

C:\Programas\InformixClientSDK_370_tc7_c86>dbaccess - test_implicit.sql

Connected.

(expression)

PTxxxxxx

1 row(s) retrieved.

Disconnected.

C:\Programas\InformixClientSDK_370_tc7_c86>

Um teste final com este ambiente: Vamos remover a relação de confiança e tentar com uma password errada (modificando o ficheiro test_explicit.sql file):

C:\Programas\InformixClientSDK_370_tc7_c86>dbaccess - test_explicit.sql

 1809: Server rejected the connection.
Error in line 1
Near character position 1

C:\Programas\InformixClientSDK_370_tc7_c86>

Portanto, isto prova que no dbaccess podemos emular o comportamento nativo usando PAM. Mas e em relação às outras camadas de conectividade, como ODBC, .NET, 4GL etc.?
Bom... Tentei com tudo o que me ocorreu e só o OleDB falhou:
  • ConnectTestDemo (ok)
    (ferramenta de conectividade fornecida com o ClientSDK para Windows
  • ODBC (ok)
    (o habitual gestor de ODBC fornecido com o Windows)
  • VBScript que usa um data source ODBC (ok)
  • VBScript que usa OleDB (failhou!)
  • Ferramenta Java que usa JDBC (ok)
  • Cliente C# que usa  .NET (ok)
  • 4GL 7.50.UC4 nativo (ok)
Como referido acima, o cliente OleDB falhou, porque explicitamente não suporta PAM. Na verdade quando se tenta usar obtemos a seguinte mensagem no online.log:

10:35:24  listener-thread: err = -1809: oserr = 0: errstr = Client without PAM support connecting to PAM port: Server rejected the connection.

Portanto isto é declaradamente uma limitação no produto (atualmente). Acredito que isto poderia ser removido se assumirmos que passa a funcionar em modo de password e não em challenge. Fizémos isso muito recentemente para DRDA com PAM. Suspeito, mas não consigo ter a certeza, que a limitação deriva do facto de os clientes OleDB não podere especificar uma função de callback como fazemos em JDBC ou ESQL/C. E isso pode ser a fonte desta limitação. Seria melhor limitar o uso de PAM com OleDB a configurações exclusivamente com password.

Conclusões e problemas
Este pequeno exercício prova que podemos simular (com algumas diferenças mencionadas abaixo) o comportamento nativo usando PAM. Mas porque quereríamos fazer isso? Bom, consigo listar alguns motivos:
  1. Podemos querer validar o utilizador/password contra um repositório diferente do SO nativo (ou mais simplesmente mudar o mecanismo de encriptação e o Informix pode ser sensível a isso)
  2. Podemos configurar o SO para trabalhar com LDAP, e ainda que isso possa funcionar bem com utilitários nativos, pode quebrar a forma como o Informix valida os utilizadores no SO. O Informix utiliza as funções getpwnam() e crypt(). Se estas funções não funcionarem exatamente da mesma forma que antes da configuração para LDAP a autenticação Informix pode falhar
  3. Podemos querer aproveitar a flexibilidade do PAM. Depois de configurar estes dois módulos PAM, não há nada que nos impeça de introduzir outros módulos que forneçam outras funcionalidades. A única coisa que temos de manter em mente é que não podemos aceitar "challenges" desses módulos, dado que configurámos o serviço com a opção pamauth=(password) em vez de "challenge"
Mas como escrevi antes, existem alguns problemas:
  1. Ao usar a autenticação nativa, se fornecermos uma password, ira validar a password ou recusar a conexão. Neste exemplo, se a validação cotnra a password falhar vai tentar verificar a relação de confiança. Não me parece que isto seja um grande problema... Normalmente as relações de confiança são criadas para utilizadores que não têm password. E vice-versa, os utilizadores que usam passwords não costumam ter relações de confiança criadas
  2. Ao configurar o porto com pamauth=(password) abdicamos da possibilidade de usar algum módulo que envie um challenge, ao qual poderíamos responder com uma função configurada com o mecanismo de callback
  3. O módulo pam_rhosts valida as relações de confiança nos ficheiros /etc/hosts.equiv e ~utilizador/.rhosts
    Como sabemos, a partir da versão 11.70 o Informix pode (e deve) usar os seus próprios ficheiros. Seria possível e não demasiado complicado obter o código fonte do módulo, alterar estes ficheiros (aceitando-os como parâmetro do módulo por exemplo) para o adaptar à versão 11.7
  4. Em Linux (e isto não aconteceu em AIX e pode não acontecer noutras versões de Linux), o nome que é passado no PAM_RHOST ao módulo pam_rhosts tem de ser passível de resolução para endereço IP ou a autenticação neste módulo falhará

No comments: