Thursday, February 13, 2014

DNS changes? Ok... / Mudanças no DNS? Ok...

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:
For the blog followers, you probably know I have a special feeling for PAM. I've written several articles about it, and I've been working on other two articles which I hope will be very interesting. I just love PAM flexibility and what we can do with it. During this work, an idea popped up and not even me would have thought that I could be using PAM for something that is not related to authentication. Confused? So was I.
As I mentioned I was messing around with PAM tests that involve a customized module and suddenly I realized I could solve one of the most annoying problems we have in Informix by using PAM!
On a previous article (DNS impact on Informix - a very extensive and detailed article that received a lot of positive feedback) I showed that we cannot change DNS configuration without stopping Informix to assume the new configuration. This is a natural consequence of how the operating system functions work, but applications can overcome that. Informix currently doesn't. That's why I created RFE 33797 (Request For Enhancement). I urge you to vote for it. It's currently classified as "Under consideration".
On that same article I showed that if we managed to get the MSC VP(s) to run a standard C function, it would solve the problem, as the next connection would require the application (Informix) to re-read the resolver configuration. Well... guess what MSC processors also run? Yep... The PAM module's functions are run inside the MSC VP process. So... What's this crazy idea? Simple: create a dummy PAM authentication module that when called runs the referred function. This will clear that process cache and next authentication requests will follow the new DNS configuration. What we need:

  • The PAM module source (I provide that)
  • A C compiler and a few header files
  • Administrator (root) privilege to configure the PAM service
  • Configure a new engine listener (DBSERVERALIAS) for PAM authentication.
So let's start with the source code. It's listed below, and I'll just explain a few aspects referring to their line numbers:
  • Let's start by lines 64 till the end. These are just functions that every PAM module needs to contain. But as we're not going to use them (at least seriously) we just create empty ones that return "ok"
  • The only function that we'll really need is pam_sm_authenticate() which will be called when we try to authenticate against a listener using this port. It starts on line 10
  • Lines 24-34 just serve to get a module parameter that represents a log file. If that parameter is not used, or if it contains a file name that cannot be written to or created, then we'll just use a default in "/tmp". The use for this log file will become clearer ahead
  • Line 39 is the really important stuff. This will clear all the resolver configuration that is cached inside each process that calls resolver routines. I should test the result of calling it and adjust the logging... but keep in mind this is a demo...
  • Lines 45-57 are just used to get a timestamp and the PID of the MSC VP where we're running, and write it to the log file. Again the importance of this will be discussed ahead
  • Finally on line 62 we return an error. As I mentioned, this is meant to be a dummy module which purpose is to solve a limitation. But as it must be configured as an authentication mechanism for a listener I think it's safer if it always refuses the authentication
So, assuming we have this in a source file called pam_clear_dns_cache.c we can compile it with:


pam@primary:informix-> gcc -shared -fPIC -o pam_clear_dns_cache.so pam_clear_dns_cache.c
pam@primary:informix-> ls -lia pam_clear_dns_cache.so
81342 -rwxr-xr-x 1 informix informix 8092 Feb 12 17:40 pam_clear_dns_cache.so
pam@primary:informix->


Then we need to copy it to the system PAM module location. My test environment is a 64bit Linux so this will be:


pam@primary:informix-> cp -p pam_clear_dns_cache.so /lib64/security/
pam@primary:informix-> ls -lia /lib64/security/pam_clear_dns_cache.so
1212609 -rwxr-xr-x 1 informix informix 8092 Feb 12 17:40 /lib64/security/pam_clear_dns_cache.so
pam@primary:informix->


Then we need to setup $INFORMIXSQLHOSTS with a new entry:


pam_clear_dns_cache onsoctcp 127.0.0.1 20002 s=4,pam_serv=(ids_pam_clear_dns_cache),pamauth=(challenge),k=1


I used localhost for added security (in case the database server can only be accessed by DBAs and SysAdmins). Then create the service file in /etc/pam.d:

pam@primary:root-> cat ids_pam_clear_dns_cache
auth required pam_clear_dns_cache.so /tmp/ids_pam_clear_dns_cache.txt
account required
pam_clear_dns_cache.so
pam@primary:root->


As usual we just need two entries. "auth" where we use our module, and "account" where for simplicity I used the same module (remember those other functions that return "success"?)

Ok... so now let's check if it works.

Step 1

I have my system configured to use DNS and then the files. It's not a typical setup, but it suites our test needs. In /etc/resolv.conf I have a DNS address where there is no DNS (192.168.142.2).
I'll start the engine and I'll find out the MSC VP PID with onstat -g glo:


Individual virtual processors:
vp pid class usercpu syscpu total Thread Eff
1 26703 cpu 0.42 0.43 0.85 1.52 55%
2 26704 adm 0.00 0.02 0.02 0.00 0%
3 26705 lio 0.00 0.00 0.00 0.02 0%
4 26706 pio 0.00 0.00 0.00 0.00 0%
5 26707 aio 0.00 0.01 0.01 0.07 13%
6 26708 msc 0.00 0.00 0.00 19.76 0%
7 26709 fifo 0.00 0.00 0.00 0.02 0%
8 26712 soc 0.00 0.00 0.00 NA NA
tot 0.42 0.46 0.88


on another session I'll trace the MSC VP with:


strace -T -o strace_step1.txt -p 26708


while I try to connect from a remote server. The trace includes this:


pam@primary:root-> head -20 strace_step1.txt
semop(2818050, 0x1c39a90, 1)            = 0 <18.944722>
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 3 <0.000022>
connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("192.168.142.2")}, 28) = 0 <0.000018>
fcntl(3, F_GETFL)                       = 0x2 (flags O_RDWR) <0.000011>
fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK)    = 0 <0.000011>
poll([{fd=3, events=POLLOUT}], 1, 0)    = 1 ([{fd=3, revents=POLLOUT}]) <0.000012>
sendto(3, "\376m\1\0\0\1\0\0\0\0\0\0\003115\003142\003168\003192\7in-"..., 46, MSG_NOSIGNAL, NULL, 0) = 46 <0.000081>
poll([{fd=3, events=POLLIN}], 1, 5000)  = 0 (Timeout) <4.990392>
poll([{fd=3, events=POLLOUT}], 1, 0)    = 1 ([{fd=3, revents=POLLOUT}]) <0.000019>
sendto(3, "\376m\1\0\0\1\0\0\0\0\0\0\003115\003142\003168\003192\7in-"..., 46, MSG_NOSIGNAL, NULL, 0) = 46 <0.000540>
poll([{fd=3, events=POLLIN}], 1, 5000)  = 0 (Timeout) <4.998902>
close(3)                                = 0 <0.000024>
open("/etc/hosts", O_RDONLY)            = 3 <0.000025>


Notice the connect to the server I have defined. And notice the poll() call takes around 5s to return by timeout. So, we're in a situation where our DNS stopped working and we want to fix it. This situation (5s delay) would happen to any request.

Step 2

Now, assuming we want to exchange our DNS servers we would edit the /etc/resolv.conf. I'll put the working DNS (192.168.142.1) there and will repeat the trace. The new trace is identical:

pam@primary:root-> strace -T -o strace_step2.txt -p 26708
Process 26708 attached - interrupt to quit
Process 26708 detached
pam@primary:root-> head -20 strace_step2.txt
semop(2818050, 0x1c39a90, 1)            = 0 <11.093218>
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 3 <0.000019>
connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("192.168.142.2")}, 28) = 0 <0.000110>
fcntl(3, F_GETFL)                       = 0x2 (flags O_RDWR) <0.000009>
fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK)    = 0 <0.000009>
poll([{fd=3, events=POLLOUT}], 1, 0)    = 1 ([{fd=3, revents=POLLOUT}]) <0.000011>
sendto(3, "\254/\1\0\0\1\0\0\0\0\0\0\003115\003142\003168\003192\7in-"..., 46, MSG_NOSIGNAL, NULL, 0) = 46 <0.000017>
poll([{fd=3, events=POLLIN}], 1, 5000)  = 0 (Timeout) <5.002838>
poll([{fd=3, events=POLLOUT}], 1, 0)    = 1 ([{fd=3, revents=POLLOUT}]) <0.000099>
sendto(3, "\254/\1\0\0\1\0\0\0\0\0\0\003115\003142\003168\003192\7in-"..., 46, MSG_NOSIGNAL, NULL, 0) = 46 <0.000076>
poll([{fd=3, events=POLLIN}], 1, 5000)  = 0 (Timeout) <4.999880>
close(3)                                = 0 <0.000058>
open("/etc/hosts", O_RDONLY)            = 3 <0.000013>

Step 3

So, let's do some magic.... I'm going to start the listener that uses our customized "dummy" PAM module, and I'll try to connect to it:


pam@primary:informix-> onmode -P start pam_clear_dns_cache
pam@primary:informix-> onstat -m | tail -3
19:38:19 Starting listen thread for sqlhosts server pam_clear_dns_cache
19:38:29 Listen thread init SUCCESS

pam@primary:informix-> dbaccess - -
> CONNECT TO "@pam_clear_dns_cache" USER "fnunes";
ENTER PASSWORD:

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


As expected, the authentication fails. But that's not the purpose of... Let's check the log file we defined in the module configuration:


pam@primary:root-> cat /tmp/ids_pam_clear_dns_cache.txt
2014-02-12 19:42:10 resolver caches cleared for PID (26708)
pam@primary:root->

Good... It says it cleared the resolver caches for the PID we know it's the MSC VP.
So let's try to connect now and see what happens:


pam@primary:root-> strace -T -o strace_step3.txt -p 26708
Process 26708 attached - interrupt to quit
^CProcess 26708 detached
pam@primary:root-> head -20 strace_step3.txt
semop(2818050, 0x1c39a90, 1)            = 0 <12.664204>
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4 <0.000031>
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("192.168.142.1")}, 28) = 0 <0.000033>
fcntl(4, F_GETFL)                       = 0x2 (flags O_RDWR) <0.000014>
fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK)    = 0 <0.000016>
poll([{fd=4, events=POLLOUT}], 1, 0)    = 1 ([{fd=4, revents=POLLOUT}]) <0.000231>
sendto(4, "=B\1\0\0\1\0\0\0\0\0\0\003115\003142\003168\003192\7in-"..., 46, MSG_NOSIGNAL, NULL, 0) = 46 <0.000195>
poll([{fd=4, events=POLLIN}], 1, 5000)  = 1 ([{fd=4, revents=POLLIN}]) <0.106848>
ioctl(4, FIONREAD, [123])               = 0 <0.000022>
recvfrom(4, "=B\201\203\0\1\0\0\0\1\0\0\003115\003142\003168\003192\7in-"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("
192.168.142.1")}, [16]) = 123 <0.000022>
close(4)                                = 0 <0.000030>
open("/etc/hosts", O_RDONLY)            = 4 <0.000031>


As you can see, it opened the socket to the new DNS, got the answer pretty quick (this one is working), but as it does not recognize the client it still went to look into the /etc/hosts file.

Conclusions

So, what have we done? We effectively overcome an Informix limitation by using a PAM module. The trick here is that we managed to execute the function res_init() inside the MSC VP process. We took advantage of the fact that PAM module functions are executed in this virtual processor. But there is a catch... We can have more than one MSC VP and we can't control to which one our connection attempt will be scheduled to. So, if you happen to have more than one MSC VP processor you may have to do several connection attempts so that you get all of them to run the function. I'd say most customers are running just one but you should keep this in mind.

Keep in mind that this is a workaround. I hope the request for enhancement will be implemented by IBM. But if/when it does, it will probably be implemented only in the latest version. So if you have an older one you can use this. The decision to leave the listener running, or even start it by default on every instance is up to the reader. It will not accept any connections. And it can be just a local listener (127.0.0.1).

This has been fun to implement and it may be helpful. But as usual, the code and setup comes with a standard disclaimer. This is not IBM official information. Test it if you think about using it. And use at your own risk.


Versão Portuguesa:

Quem segue o blog já deve ter percebido que tenho um sentimento especial pelo PAM. Já escrevi vários artigos sobre o tema e tenho estado a trabalhar em mais dois que espero sejam interessantes. Simplesmente adoro o PAM pela flexibilidade e pelo que se pode fazer com ele.. Durante este trabalho surgiu-me uma ideia, e nem eu me lembraria que um dia iria usar o PAM para algo que não está relacionado com autenticação. Confuso? Também eu fiquei!
Como referi estava a "brincar" um pouco com testes de PAM que envolvem um módulo costumizado e de repente apercebi-me que poderia resolver um dos problemas mais irritantes que temos no Informix através do PAM. Num artigo anterior (impacto do DNS no Informix - um artigo extenso e detalhado que recebeu bastante feedback positivo) eu mostrei que não podemos mudar as configurações de DNS sem parar o Informix para assumir a nova configuração. Isto é uma consequência natural da forma como as funções de resolução de nomes do sistema operativo funcionam, mas as aplcações podem contornar o problema. O Informix atualmente não o faz. Daí ter registado o RFE 33797 (request for enhancement). Apelo a que vote nele. Está neste momento classificado como "Under Consideration).
Nesse mesmo artigo, mostrei que se conseguirmos que os MSC VP(s) executem uma função standard (res_init) isso resolveria o problema, pois a próxima tentativa de conexão obrigaria o Informix a reler as configurações do sistema de resolução de nomes. Bom... adivinhe o que é que os MSC VPs também executam? Sim... as funções dos módulos PAM configurados no motor são executadas pelos processos da classe MSC. Então... Qual é a ideia maluca? Simples: criar um módulo PAM "dummy" que quando chamado, execute a referida função. Isto irá limpar a cache que esse processo fez das configurações e no próximo pedido de autenticação as novas configurações serão lidas e ativadas. O que precisamos:
  • O código fonte do módulo PAM (eu forneço isto)
  • Um compilador de C e alguns header files
  • Privilégios de administrador (root) para configurar o serviço PAM
  • Configurar um novo listener do motor (DBSERVERALIAS) para autenticação PAM
Vamos começar com o código fonte do módulo. Está listado abaixo e irei explicar alguns aspectos com recurso às linhas apresentadas:
  • Vamos começar pela linha 64, até ao fim. Todas estas funções são obrigatórias num módulo, mas como as não vamos usar (pelo menos de forma séria), vamos criá-las apenas retornando "ok"
  • A única função que vamos realmente usar é a pam_sm_authenticate() que será chamada cada vez que nos tentarmos autenticar por este módulo. Começa na linha 10
  • As linhas 24-34 servem apenas para obter um parâmetro do módulo que deve representar um ficheiro de log. Se o parâmetro não for dado ou se contiver um caminho que não possa ser escrito ou criado, então usaremos um ficheiro em /tmp. A vantagem deste ficheiro ficará clara mais adiante
  • A linha 39 é o mais importante. Esta chamada deverá limpar do processo a "memória" das configurações do sistema de resolução de nomes. Deveria testar o resultado da chamada e ajustar a informação do log de acordo... mas isto é apenas uma demonstração
  • As linhas 45-57 permitem apenas recolher um timestamp e o PID do processo em que estamos a executar para escrever no log. Mais uma vez a importância de o fazer ficará mais clara adiante
  • Finalmente, na linha 62 retornamos um erro standard do PAM. Como mencionei, isto pretende ser um módulo dummy cujo único propósito é resolver uma limitação.Mas dado que tem de ser configurado como mecanismo de autenticação, parece-me mais seguro que recuse sempre a autenticação
Assim, assumindo que temos um ficheiro com o código fonte chamado pam_clear_dns_cache.c podemos compilar o módulo com:

pam@primary:informix-> gcc -shared -fPIC -o pam_clear_dns_cache.so pam_clear_dns_cache.c
pam@primary:informix-> ls -lia pam_clear_dns_cache.so
81342 -rwxr-xr-x 1 informix informix 8092 Feb 12 17:40 pam_clear_dns_cache.so
pam@primary:informix->


Depois necessitamos de o copiar para a localização standard dos módulos PAM na nossa plataforma. No meu caso estou a usar um Linux de 64bits, portanto será:


pam@primary:informix-> cp -p pam_clear_dns_cache.so /lib64/security/
pam@primary:informix-> ls -lia /lib64/security/pam_clear_dns_cache.so
1212609 -rwxr-xr-x 1 informix informix 8092 Feb 12 17:40 /lib64/security/pam_clear_dns_cache.so
pam@primary:informix->


Prosseguimos, com a configuração do $INFORMIXSQLHOSTS com uma nova entrada:


pam_clear_dns_cache onsoctcp 127.0.0.1 20002 s=4,pam_serv=(ids_pam_clear_dns_cache),pamauth=(challenge),k=1


Usei o localhost para segurança reforçada (caso o servidor de base de dados só possa ser acedido por DBAs e administradores de sistema). Depois configuramos o ficheiros do serviço PAM em /etc/pam.d:

pam@primary:root-> cat ids_pam_clear_dns_cache
auth required pam_clear_dns_cache.so /tmp/ids_pam_clear_dns_cache.txt
account required
pam_clear_dns_cache.so
pam@primary:root->


Como sempre necessitamos de duas entradas:  "auth" onde usamos o nosso módulo e "account" onde por simplicidade utilizei o mesmo módulo (lembra-se daquelas funções "inúteis" que retornavam "sucesso"?)

Bom... Mas temos de verificar se funciona

Passo 1

Tenho o meu sistema configurado para procurar primeiro nos DNS e depois nos ficheiros. Não é uma configuração habitual, mas ajusta-se aos testes que queremos fazer. No ficheiro /etc/resolv.conf tenho um endereço de DNS onde não existe DNS ativo (192.168.142.2)
Vou iniciar o motor e ver o PID do MSC VP com o comando onstat -g glo:


Individual virtual processors:
vp pid class usercpu syscpu total Thread Eff
1 26703 cpu 0.42 0.43 0.85 1.52 55%
2 26704 adm 0.00 0.02 0.02 0.00 0%
3 26705 lio 0.00 0.00 0.00 0.02 0%
4 26706 pio 0.00 0.00 0.00 0.00 0%
5 26707 aio 0.00 0.01 0.01 0.07 13%
6 26708 msc 0.00 0.00 0.00 19.76 0%
7 26709 fifo 0.00 0.00 0.00 0.02 0%
8 26712 soc 0.00 0.00 0.00 NA NA
tot 0.42 0.46 0.88


numa outra sessão faço um trace ao MSC VP com:


strace -T -o strace_step1.txt -p 26708


enquanto tento conectar-me de um servidor remoto. O trace incluí isto:


pam@primary:root-> head -20 strace_step1.txt
semop(2818050, 0x1c39a90, 1)            = 0 <18.944722>
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 3 <0.000022>
connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("192.168.142.2")}, 28) = 0 <0.000018>
fcntl(3, F_GETFL)                       = 0x2 (flags O_RDWR) <0.000011>
fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK)    = 0 <0.000011>
poll([{fd=3, events=POLLOUT}], 1, 0)    = 1 ([{fd=3, revents=POLLOUT}]) <0.000012>
sendto(3, "\376m\1\0\0\1\0\0\0\0\0\0\003115\003142\003168\003192\7in-"..., 46, MSG_NOSIGNAL, NULL, 0) = 46 <0.000081>
poll([{fd=3, events=POLLIN}], 1, 5000)  = 0 (Timeout) <4.990392>
poll([{fd=3, events=POLLOUT}], 1, 0)    = 1 ([{fd=3, revents=POLLOUT}]) <0.000019>
sendto(3, "\376m\1\0\0\1\0\0\0\0\0\0\003115\003142\003168\003192\7in-"..., 46, MSG_NOSIGNAL, NULL, 0) = 46 <0.000540>
poll([{fd=3, events=POLLIN}], 1, 5000)  = 0 (Timeout) <4.998902>
close(3)                                = 0 <0.000024>
open("/etc/hosts", O_RDONLY)            = 3 <0.000025>


Repare no connect() ao servidor que tenho configurado. E depois o poll() demora cerca de 5s a retornar por timeout. Portanto estamos a reproduzir uma situação onde o nosso DNS tenha deixado de funcionar, e queremos "arranjar" isso. Esta situação (atraso de 5s) aconteceria em cada tentativa de conexão.

Passo 2

Agora, assumindo que queremos trocar os nossos servidores DNS, editaríamos o ficheiro /etc/resolv.conf. Vou colocar o endereço do servidor DNS activo (192.168.142.1) e vou repeitr o trace durante uma nova conexão. O novo trace está igual:

pam@primary:root-> strace -T -o strace_step2.txt -p 26708
Process 26708 attached - interrupt to quit
Process 26708 detached
pam@primary:root-> head -20 strace_step2.txt
semop(2818050, 0x1c39a90, 1)            = 0 <11.093218>
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 3 <0.000019>
connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("192.168.142.2")}, 28) = 0 <0.000110>
fcntl(3, F_GETFL)                       = 0x2 (flags O_RDWR) <0.000009>
fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK)    = 0 <0.000009>
poll([{fd=3, events=POLLOUT}], 1, 0)    = 1 ([{fd=3, revents=POLLOUT}]) <0.000011>
sendto(3, "\254/\1\0\0\1\0\0\0\0\0\0\003115\003142\003168\003192\7in-"..., 46, MSG_NOSIGNAL, NULL, 0) = 46 <0.000017>
poll([{fd=3, events=POLLIN}], 1, 5000)  = 0 (Timeout) <5.002838>
poll([{fd=3, events=POLLOUT}], 1, 0)    = 1 ([{fd=3, revents=POLLOUT}]) <0.000099>
sendto(3, "\254/\1\0\0\1\0\0\0\0\0\0\003115\003142\003168\003192\7in-"..., 46, MSG_NOSIGNAL, NULL, 0) = 46 <0.000076>
poll([{fd=3, events=POLLIN}], 1, 5000)  = 0 (Timeout) <4.999880>
close(3)                                = 0 <0.000058>
open("/etc/hosts", O_RDONLY)            = 3 <0.000013>

Passo 3

Vamos então fazer a magia.... Vou iniciar um listener que usa o nosso módulo PAM, e vou tentar ligar-me ao mesmo:

pam@primary:informix-> onmode -P start pam_clear_dns_cache
pam@primary:informix-> onstat -m | tail -3
19:38:19 Starting listen thread for sqlhosts server pam_clear_dns_cache
19:38:29 Listen thread init SUCCESS

pam@primary:informix-> dbaccess - -
> CONNECT TO "@pam_clear_dns_cache" USER "fnunes";
ENTER PASSWORD:

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


Tal como esperado, a autenticação falha. Mas não era esse o seu propósito... Vamos verificar o ficheiro de log que definimos na configuração do módulo:


pam@primary:root-> cat /tmp/ids_pam_clear_dns_cache.txt
2014-02-12 19:42:10 resolver caches cleared for PID (26708)
pam@primary:root->

Bom... Diz que limpou a cache das funções para o PID que sabemos ser o do nosso MSC VP.
Portanto vamos tentar nova ligação e ver o que acontece com o trace:

pam@primary:root-> strace -T -o strace_step3.txt -p 26708
Process 26708 attached - interrupt to quit
^CProcess 26708 detached
pam@primary:root-> head -20 strace_step3.txt
semop(2818050, 0x1c39a90, 1)            = 0 <12.664204>
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4 <0.000031>
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("192.168.142.1")}, 28) = 0 <0.000033>
fcntl(4, F_GETFL)                       = 0x2 (flags O_RDWR) <0.000014>
fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK)    = 0 <0.000016>
poll([{fd=4, events=POLLOUT}], 1, 0)    = 1 ([{fd=4, revents=POLLOUT}]) <0.000231>
sendto(4, "=B\1\0\0\1\0\0\0\0\0\0\003115\003142\003168\003192\7in-"..., 46, MSG_NOSIGNAL, NULL, 0) = 46 <0.000195>
poll([{fd=4, events=POLLIN}], 1, 5000)  = 1 ([{fd=4, revents=POLLIN}]) <0.106848>
ioctl(4, FIONREAD, [123])               = 0 <0.000022>
recvfrom(4, "=B\201\203\0\1\0\0\0\1\0\0\003115\003142\003168\003192\7in-"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("
192.168.142.1")}, [16]) = 123 <0.000022>
close(4)                                = 0 <0.000030>
open("/etc/hosts", O_RDONLY)            = 4 <0.000031>


Como se pode verificar, abriu um socket para o novo DNS, obteve a resposta como era de esperar e rapidamente (este está activo), mas como o cliente nao é conhecido no servvidor prossegiu para pesquisar no ficheiro /etc/hosts.

Conclusões

Então, o que fizemos? Efectivamente superámos uma limitação do Informix através de um módulo PAM.
O truque é que conseguimos que a função res_init() fosse executada dentro do processo de um MSC VP. Tirámos proveito do faco de os módulos PAM serem executados neste processador virtual. Mas há um senão.... Podemos ter mais que um MSC VP, e não podemos controlar para qual é que a nossa tentativa de conexão vai parar. Portanto se por acaso usar mais de um MSC VP poderá ter de repetir as tentativas de autenticação até que todos eles executem a função. Diria que a maioria dos clientes apenas usam um MSC VP, mas terá de ter este ponto em mente. O log é particularmente útil para isto.

Considere isto um workaround. Espero que o pedido de melhoria venha a ser implementado pela IBM. Mas se/quando o for será muito provavelmente apenas na última versão. Portanto se usar uma mais antiga pode optar por este mecanismo. A decisão de deixar o novo listener a correr ou até mesmo de o levantar logo com o motor será uma decisão do leitor. Não autorizará nenhuma autenticação. E pode ser configurado como local (127.0.0.1)

Isto foi divertido de implementar, e pode ser útil. Mas como é habitual, o código e a configuração vêm com um termo de desresponsabilização. Isto não é informação oficial da IBM. Teste se pensar em usar. E use por sua conta e risco

The code / O código

1  #include <time.h>
2  #include <string.h>
3  #include <stdio.h>
4  #include <security/pam_modules.h>
5  #include <resolv.h>

1 comment:

Unknown said...

Excellent article, and what a great workaround! Good work!