Showing posts with label security. Show all posts
Showing posts with label security. Show all posts

Friday, April 25, 2014

EXECUTE IMMEDIATE: SQL injection prevention / EXECUTE IMMEDIATE: Prevenção SQL injection

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


English version
Recently I was requested to implement some stored procedures on a customer site. The request was urgent and I must admit I didn't pay enough attention. The procedures needed to accept some values that would be used inside some SQL statements. Basic best practices mandate that we must check all parameters and in such cases use prepared statements. These principles provide for better interaction with the calling layer and more important, help us avoid a classic and nasty security flaw called SQL injection.
The idea behind SQL injection is terribly simple and efficient: By supplying specially crafted parameters we explore the developer's mistake of not sanitizing those parameters and blindly use them to construct SQL statements. By doing so we may be able to change the meaning of those statements or eventually terminate them and include our own. A couple of examples:

  1. An authentication page uses the SQL statement:
    SELECT
        COUNT(*)
    FROM
        user_table
    WHERE
        user_name = $USER AND user_pass = '$PASS'
    

    and we supply these arguments:
    'john_doe'
    'dummy'' OR ( user_name = ''john_doe'' )'
    

  2. An online banking uses the SQL statement:
    UPDATE
        balance_account
    SET
        ammount = ammount - $VALUE
    WHERE
        account_number = $SOME_NUMBER
    

    and we supply these arguments:
    - 1000
    our_account_number
    

Well, after the procedures were created and some basic testing was done I had a bit more time to look at the code and I noticed I used the SQL instruction "EXECUTE IMMEDIATE..." where I was just concatenating some SQL with the provided arguments. Some of them were being slightly checked, but others were not checked at all. I was alarmed and ashamed by that piece of code and I decided to prove it was crappy... I tried to send weird parameters which included a semi-colon and a full statement following it. As an example consider something like:

CREATE PROCEDURE test_proc(table_name VARCHAR(20))
    EXECUTE IMMEDIATE "DROP TABLE " || table_name
END PROCEDURE;

And I tried to send "some_table;DROP DATABASE d"

(consider that some_table is an existent table name and "d" and existing database)
It raised error

26058: EXECUTE IMMEDIATE and PREPARE in SPL cannot allow multiple SQL statements

Which was a bit of a surprise. I tried other variations and they all failed.
The error description is clear. And the manual too:

http://www-01.ibm.com/support/knowledgecenter/SSGU8G_12.1.0/com.ibm.sqls.doc/ids_sqs_0768.htm?lang=en

As a security measure or not, EXECUTE IMMEDIATE and PREPARE, inside stored procedures don't allow the execution of more than one instruction. A reason for that can be to avoid this specific type of SQL injection exploitation. So it was a good surprise to see that the good people in R&D have thought about something that I failed to (at my first attempt). This is a good security measure. But don't get too enthusiastic.... Terminating a statement and including another on in a variable or argument is just one of the SQL injection attacks. It will be useless on the two examples above. So be aware and never do the same error I was about to do: Always check your parameters and if possible PREPARE your statements. That's the way to avoid these security issues.
For the so called "cursory" statements we can use PREPARE, DECLARE, OPEN, FETCH, CLOSE and FREE. For non-cursory statements we need to use EXECUTE IMMEDIATE. In any case, it's essential to validate all inputs!





Versão Portuguesa
Recentemente foi-me pedido que implementasse algumas stored procedures num cliente. O pedido era urgente e devo admitir que não prestei a devida atenção. Os procedimentos necessitavam de aceitar alguns valores que seriam depois usados em instruções SQL. As boas práticas mais simples ditam que temos sempre de validar os inputs e sempre que possível usar prepared statements. Estes princípios permitem uma melhor interação com a camada que executa os procedimentos (ou genericamente funções), e mais importante, ajudam-nos a evitar uma falha de segurança bastante má, chamada SQL injection.
A ideia base do SQL injection é terrivelmente simples mas eficiente: Fornecendo argumentos "construidos", exploramos falhas dos programadores que não validam nem purificam os argumentos que lhes são passados, usando-os cegamente na construção de instruções SQL. Ao fazê-lo podemos alterar o sentido dessas instruções e eventualmente até terminá-las e escrevendo outras imediatamente a seguir. Um par de exemplos:
  1. Uma página de autenticação usa a seguinte instrução:
    SELECT
        COUNT(*)
    FROM
        tabela_utilizadores
    WHERE
        nome_utilizador = $USER AND pass_utilizador = '$PASS'
    

    e fonecemos estes argumentos:
    'john_doe'
    'dummy'' OR ( nome_utilizador = ''john_doe'' )'
    

  2. Uma aplicação de banco online utiliza esta instrução SQL:
    UPDATE
        tabela_contas
    SET
        saldo = saldo - $VALUE
    WHERE
        numero_conta = $UM_NUMERO
    

    e fornecemos estes argumentos:
    - 1000
    our_account_number
    

Bom, depois de ter criado os procedimentos, e após alguns testes básicos de funcionalidade, tive um pouco mais de tempo para examinar o código e verifiquei que estava a usar a instrução "EXECUTE IMMEDIATE...", e estava apenas a concatenar um pedaço de SQL com os argumentos fornecidos. Alguns tinham algum tipo de validação muito básica, mas outros não eram validados de todo. Fiquei alarmado e envergonhado com algum daquele código e decidi provar a mim mesmo que aquilo não prestava.... Tentei enviar parâmetros compostos de forma específica, incluind um ";" e uma instrução completa em seguida. A título de exemplo considere algo como:

CREATE PROCEDURE test_proc(table_name VARCHAR(20))
    EXECUTE IMMEDIATE "DROP TABLE " || table_name
END PROCEDURE;
E tentei usar como argumento "tabela_teste;DROP DATABASE d"

(considere que "tabela_teste" era o nome de uma tabela existente e que "d" era o nome de uma base de dados também existente)
Obtive um erro:

26058: EXECUTE IMMEDIATE and PREPARE in SPL cannot allow multiple SQL statements
Isto foi um pouco surpreendente. Tentei outras variações e todas falharam.
A descrição do erro parecia clara. E o manual também:

http://www-01.ibm.com/support/knowledgecenter/SSGU8G_12.1.0/com.ibm.sqls.doc/ids_sqs_0768.htm?lang=en

Por medida de segurança ou não, o EXECUTE IMMEDIATE e o PREPARE dentro de procedimentos SPL não permitem a execução de mais que uma instrução. Uma razão para tal pode ser evitar este tipo específico de exploração de SQL injection. A ser assim foi uma surpresa agradável que o pessoal de I&D se tenha lembrado de algo que eu me esqueci (na primeira versão). Isto constitui uma boa medida de segurança. Mas não fique demasiado entusiasmado.... Terminar uma instrução e incluir outra é apenas uma das variantes do SQL injection. É inútil nos dois exemplos acima. Portanto esteja atento e nunca cometa o mesmo erro que estive á beira de cometer: verifique sempre os parâmetros e se possível utilize o PREPARE das suas instruções SQL. Essa é ainda a melhor forma de evitar estes problemas.
Para as instruções baseadas em cursores podemos usar o PREPARE, DECLARE, OPEN, FETCH, CLOSE e FREE. Para instruções que não retornem um conjunto de resultados necessitamos de usar o EXECUTE IMMEDIATE. Em qualquer caso é essencial validar todos os inputs!

Monday, April 14, 2014

Heart bleed bug

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



English version
If you have the slightest security concern, you've certainly noticed that everybody is very concerned with a security issue found on OpenSSL which was named Heart Bleed bug (a kind of joke because the bug attacks a functionality of SSL/TLS known as the Hearbeet extension). And this is perfectly justifiable. This has been considered one of the most serious security threat of the latest years. Why? Because it can be used to steal cryptographic sensitive information from sites like private keys. With these information the attackers can do all sorts of things, specifically impersonating those sites, and after that they can steal user information, passwords etc. The issue per si, is terrible, but the worst is that OpenSSL is used by many products, which means that this is not "just" a vendor specific bug.
Given the wide impact and seriousness of this issue most vendors hurried up to make sure if their products were affected or not by the bug. The OpenSSL versions affected by this bug are between 1.0.1 and 1.0.1f. Previous versions are safe (because they didn't implement the specific extension) and 1.0.1g includes the fix. So the question translate into: Do we use OpenSSL? And if yes, do we use an unsafe version?
In the immediate days after the public disclosure of this bug we received several questions from customers inquiring if our products were safe or not. And IBM hurried up to create alerts and information about this.
The relevant links are:


So, the good news is that Informix is safe from this bug. The SSL/TLS functionality used by Informix is provided by the generic IBM Global Security Kit which is not affected by the Heart Bleed bug




Versão Portuguesa
Se tem algum tipo de preocupação com a segurança, então certamente reparou que anda toda a gente preocupada com um problema de segurança descoberto no OpesSSL que recebeu o nome de Heart Bleed bug (uma piada ao facto de a falha estar na implementação de uma funcionalidade do SSL/TLS conhecida como extensão Heart Beet). E todo este alarme é perfeitamente compreensível e justificável. Este problema foi considerado como um dos mais sérios problemas de segurança descobertos nos últimos anos. Porquê? Porque pode ser usado para obtenção ilícita de informação sensível de criptografia, nomeadamente chaves privadas. Com esta informação os atacantes podem fazer uma série de coisas, entre as quais apresentarem-se como os sites legítimos e a partir daí obter informação sensível dos utilizadores como as suas palavras-chave. O assunto por si só já é terrível, mas o pior é que o OpenSSL é usado em muitos produtos de software, o que faz com que isto não esteja limitado a um fornecedor e/ou produto.
Dado o impacto alargado e a gravidade da falhar, a maioria dos vendedores apressaram-se a verificar se os seus produtos estariam afetados pelo problema. As versões do OpenSSL afetadas são as 1.0.1 até à 1.0.1.f. As versões anteriores não são afetadas pois não implementavam a extensão em causa, e a 1.0.1g incluí a correção para o problema. Portanto a questão resume-se a: Usamos OpenSSL? Se sim, usamos uma versão afetada?
Nos dias imediatamente a seguir aos anúncios públicos desta falha, recebemos várias questões de clientes a interrogarem-se se os nossos produtos estavam a salvo ou não deste bug. E a IBM apressou-se a criar alertas com informação sobre o tema. Os endereços relevantes para se entender o tema são:

Portanto, as boas notícias é que o Informix está a salvo desta falha de segurança. A funcionalidade SSL/TLS usada pelo Informix é fornecida pelo IBM  Global Security Kit, que não foi afetado pelo bug Heart Bleed.

Saturday, August 10, 2013

Google's two factor authenticator / Autenticação com 2 factores da Google

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


English version:

Introduction

It should not be a surprise to anyone that I'm a big fan of PAM (plugable authentication modules). I've written several articles about it in this blog. This time I'll pick up a Google project and show you how to glue it together with Informix to achieve more security for your connections. And this happens thanks to PAM obviously.
Google authenticator is a project that implements one time password (OTP) generators for several mobile platforms. In practice you'll use your smartphone as a token generator. This avoids the need of a specific piece of hardware and has the advantages of being open source and making available a server side PAM library. Meaning you can integrate it with anything that supports PAM. This means your SSH daemon and naturally your favorite database server software.
Google authenticator can be seen and used as a component of a multi-factor authentication mechanism. This implies that a user must present (and I quote) "two or more of the three authentication factors: a knowledge factor ("something the user knows"), a possession factor ("something the user has"), and an inherence factor ("something the user is"). This article will show you how to configure Informix for two factor authentication. In our scenario we'll use a traditional password (something the user knows) and Google's authenticator as the second factor (something the user has). In the future, if the rumors about the introduction of biometric readers (like finger print readers) on mobile phones becomes a reality, it may be possible to extend this to three factors (something the user is).
It's possible that we'll see more services using this kind of technology. Just recently Twitter introduces two factor authentication by sending a request to their app when you try to login in their web site. The user will need to authorize that connection by using the twitter app on an authorized phone. Essentially this implements the same concept, but in an easier way.

The code

To start with this, we need to get the code for the PAM library. We can find it in Google's authenticator website. So I downloaded the package into a Linux VM, and uncompressed it:
[root@kimball google_authenticator]# pwd;ls -lia;tar -xjvf libpam-google-authenticator-1.0-source.tar.bz2
/usr/local/google_authenticator
total 44
1671484 drwxr-xr-x  2 root root  4096 Aug  6 11:43 .
1605654 drwxr-xr-x 14 root root  4096 Aug  6 09:46 ..
 327870 -rw-r--r--  1 root root 32708 Aug  6 11:40 libpam-google-authenticator-1.0-source.tar.bz2
libpam-google-authenticator-1.0/base32.c
libpam-google-authenticator-1.0/demo.c
libpam-google-authenticator-1.0/google-authenticator.c
libpam-google-authenticator-1.0/hmac.c
libpam-google-authenticator-1.0/pam_google_authenticator.c
libpam-google-authenticator-1.0/pam_google_authenticator_unittest.c
libpam-google-authenticator-1.0/sha1.c
libpam-google-authenticator-1.0/base32.h
libpam-google-authenticator-1.0/hmac.h
libpam-google-authenticator-1.0/sha1.h
libpam-google-authenticator-1.0/totp.html
libpam-google-authenticator-1.0/Makefile
libpam-google-authenticator-1.0/FILEFORMAT
libpam-google-authenticator-1.0/README
libpam-google-authenticator-1.0/utc-time/
libpam-google-authenticator-1.0/utc-time/app.yaml
libpam-google-authenticator-1.0/utc-time/utc-time.py

After this, following very simple instructions, I type "make install":

[root@kimball libpam-google-authenticator-1.0]# make install
gcc --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden  -o google-authenticator.o google-authenticator.c
gcc --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden  -o base32.o base32.c
gcc --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden  -o hmac.o hmac.c
gcc --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden  -o sha1.o sha1.c
gcc -g   -o google-authenticator google-authenticator.o base32.o hmac.o sha1.o  -ldl
gcc --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden  -o pam_google_authenticator.o pam_google_authenticator.c
gcc -shared -g   -o pam_google_authenticator.so pam_google_authenticator.o base32.o hmac.o sha1.o -lpam
gcc --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden  -o demo.o demo.c
gcc -DDEMO --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden  -o pam_google_authenticator_demo.o pam_google_authenticator.c
gcc -g   -rdynamic -o demo demo.o pam_google_authenticator_demo.o base32.o hmac.o sha1.o  -ldl
gcc -DTESTING --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden        \
              -o pam_google_authenticator_testing.o pam_google_authenticator.c
gcc -shared -g   -o pam_google_authenticator_testing.so pam_google_authenticator_testing.o base32.o hmac.o sha1.o -lpam
gcc --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden  -o pam_google_authenticator_unittest.o pam_google_authenticator_unittest.c
gcc -g   -rdynamic -o pam_google_authenticator_unittest pam_google_authenticator_unittest.o base32.o hmac.o sha1.o -lc  -ldl
cp pam_google_authenticator.so /lib64/security
cp google-authenticator /usr/local/bin
[root@kimball libpam-google-authenticator-1.0]#

and we can verify that the two components were installed correctly:

[root@kimball libpam-google-authenticator-1.0]# ls -lia /lib64/security/pam_google* /usr/local/bin/google*
1212428 -rwxr-xr-x 1 root root 116777 Aug  6 11:50 /lib64/security/pam_google_authenticator.so
1613197 -rwxr-xr-x 1 root root  55498 Aug  6 11:50 /usr/local/bin/google-authenticator
[root@kimball libpam-google-authenticator-1.0]#

Setup

We have the PAM library and the google-authenticator binary that we'll use to generate a secret key for our user. So this is the next step. I'll not do this with user "informix", because the engine will always ignore PAM for informix user when connecting locally. But before, let's check the other fundamental component of the solution: Your smartphone. Currently I use Android, but the app is available to iOS and Blackberry. You should know how to find and install the app. For Android devices it's available on GooglePlay store. The app for Android and iOS supports both reading a QR code or manual introduction of the secret key generated by the google-authenticator binary. For Blackbery, according to the Google Authenticator website, it only supports manual introduction.

Assuming the app is properly installed, we can proceed with the server side configuration. As mentioned above, the next step is to generate the secret key between the server account and the mobile app. For that we use the google-authenticator binary installed before. When we run it it outputs the information needed and also makes some questions. I will not dig into those as you can find out more in the documentation:

-bash-3.2$ google-authenticator

Do you want authentication tokens to be time-based (y/n) y
https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/fnunes@kimball%3Fsecret%3D6LWH24ZIUDY46HJU
Your new secret key is: 6LWH24ZIUDY46HJU
Your verification code is
649019
Your emergency scratch codes are:
  38268905
  24335468
  11497220
  81653596
  89796862

Do you want me to update your "/opt/fnunes/.google_authenticator" file (y/n) y

Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) n

By default, tokens are good for 30 seconds and in order to compensate for
possible time-skew between the client and the server, we allow an extra
token before and after the current time. If you experience problems with poor
time synchronization, you can increase the window from its default
size of 1:30min to about 4min. Do you want to do so (y/n) n

If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting (y/n) y
-bash-3.2$

It shows us a URL and the key. If we have a library installed (libqrencode) it will also show a QR code that we can use directly with the phone. If we open the URL in a browser it will show a QR code that we can use in the app to install the key. Otherwise we need to introduce the key manually. Either way it's pretty simple and in a minute you'll have a token (or one time password) generator on your mobile phone:

After this we need to go and configure our informix instance to take advantage of this. For that I'll create a new listener port using PAM. As you probably already know, this is done by altering the $INFORMIXSQLHOSTS file and add a line similar to this:

tpch_pam     onsoctcp             kimball.onlinedomus.net     1527   s=4,pam_serv=(ids_pam_service),pamauth=(challenge)


Field by field:
  • tpch_pam
    The INFORMIXSERVER name for this alias
  • onsoctcp
    The protocol to be used
  • kimball.onlinedomus.net
    The hostname
  • 1527
    The unique TCP port number for this listener
  • s=4,pam_serv=(ids_pam_service),pamauth=(challenge)
    options field. s=4 forces PAM usage. The PAM service name is "ids_pam_service" and the PAM mode will be "challenge" 
After the $INFORMIXSQLHOSTS we must make sure that the name "ids_pam_service" is configured in the PAM configuration. Being Linux, this means having a file called ids_pam_service in /etc/pam.d. The content of the file will be:

auth       required     pam_unix.so
auth       required     pam_google_authenticator.so
account    required     pam_unix.so

This will be used for our "basic by the book" configuration. Later I'll explain some issues with it, and will show you a better way to use it. As mentioned in other articles we just need "auth" and "account" configuration lines. And we're "stacking" modules (Google and pam_unix) so that we achieve the "two" factor authentication. First we'll test the unix password and then the Google's authenticator token or code (the one time password)

Next we need to make sure "tpch_pam" is configured in the $INFORMIXDIR/etc/$ONCONFIG file in the parameter DBSERVERALIAS and after that we can start the listener with:

onmode -P start tpch_pam

Testing

So, if we have everything setup, it should work. Let's test it with dbaccess:

tpch@kimball:fnunes-> id;hostname;echo $INFORMIXSERVER
uid=1002(fnunes) gid=1002(fnunes) groups=1002(fnunes)
kimball
tpch
tpch@kimball:fnunes-> dbaccess - -
> CONNECT TO 'sysmaster@tpch_pam' user 'fnunes';
   ENTER PASSWORD:
Password:
Verification code:

Connected.

> SELECT USER, * FROM syscfgtab WHERE cf_name LIKE 'DBSERVER%';



(expression)  fnunes
cf_id         2
cf_name       DBSERVERNAME
cf_flags      0
cf_original   tpch
cf_effective  tpch
cf_default    kimball

(expression)  fnunes
cf_id         77
cf_name       DBSERVERALIASES
cf_flags      0
cf_original   tpch_pam
cf_effective  tpch_pam
cf_default

2 row(s) retrieved.

> tpch@kimball:fnunes->

Firts impression is good. It works. But there's something weird. If you take a close look you'll see that once I do a CONNECT, dbaccess asks me for "ENTER PASSWORD:". Then it asks me for "Password:" and finally for "Verification code:". Three prompts... I hope you understand two of them but not the repeated request for password. Let me explain. By default, whenever we try to CONNECT dbaccess will ask for a password. That's the first prompt. Actually, with this configuration (challenge mode) we can enter whatever we want here... it will be ignored. Than we start the PAM stack layer. And the first module is pam_unix.so. Since we're not sending it any password (more about this later) it asks us for the password ("Password:" prompt). Here you have to write the system user password. After that we move to the second module, the Google authenticator module and it behaves the same way. Since it doesn't have a password it asks for one ("Verification code:" prompt). And here we need to take a look into our smartphone and copy the current generated token (or one time password). After that we're logged in. Both passwords were verified (one for each module in the stack).
This behavior justifies the "challenge" mode. The module sends a challenge back to the engine, and the engine send it back to the client (dbaccess in our case) and the client must have registered a callback function to handle the challenges and user responses. Basically what it does in dbaccess is to echo the challenge and read the response, and finally sending it back to the engine which sends it back to the module for verification.
Although it works, it's a bit ackward and needs a client side function to handle the challenges. This means we could possibly have to change the application. dbaccess already knows how to handle it, but other clients wouldn't know what to do.
Informix APIs have functions to register a callback function. But again, code changes are never welcome. So, let's see what we can do...

Improvement

As you probably know (I mentioned it in previous PAM articles), Informix pam can be configured in "password" or "challenge" mode. The documentation sometimes leads us to think that for this kind of usage we need to use challenge. But in fact it really depends on the modules you use and the options they provide. In our case I noticed that Google's authenticator module supports two interesting options:
  • try_first_pass
    makes it check the PAM framework for a previously supplied password
  • forward_pass
    makes it smart... if you provide a password composed by the concatenation of the system password and the verification token, it will try to split them, verify the code and send the rest through the PAM stack of modules
I also noticed pam_unix.so supports both try_first_pass and use_first_pass. So, what all this suggests is that assuming we have a system password like "mypasswd" and a verification code like "096712" we could use a composed password "mypasswd096712", give it to the dbaccess password request, and don't be bothered by each module prompts. let's change the configuration and test again. The file /etc/pam.d/ids_pam_service becomes this:

auth       required     pam_google_authenticator.so try_first_pass forward_pass
auth       required     pam_unix.so use_first_pass
account    required     pam_unix.so

And in INFORMIXSQLHOSTS we'll change "challenge" for "password":

tpch_pam     onsoctcp             kimball.onlinedomus.net     1527   s=4,pam_serv=(ids_pam_service),pamauth=(password)

And now let's restart the listener and repeat the test:

tpch@kimball:root-> onmode -P restart tpch_pam


tpch@kimball:fnunes-> dbaccess - -
> CONNECT TO 'sysmaster@tpch_pam' user 'fnunes';
   ENTER PASSWORD:

Connected.

> SELECT USER, * FROM syscfgtab WHERE cf_name LIKE 'DBSERVER%';

(expression)  fnunes
cf_id         2
cf_name       DBSERVERNAME
cf_flags      0
cf_original   tpch
cf_effective  tpch
cf_default    kimball

(expression)  fnunes
cf_id         77
cf_name       DBSERVERALIASES
cf_flags      0
cf_original   tpch_pam
cf_effective  tpch_pam
cf_default

2 row(s) retrieved.

> tpch@kimball:fnunes->

And voilá.... The first module now is Google authenticator. It gets the double password from the stack, extracts it's part (it knows it's the last 6 digits), verifies it, and sends the rest to the second module (pam_unix) that validates the password in the system

Now... we've seen that dbaccess does some magic... Because it knows we're dealing with a PAM port. So, to be absolutely sure this is transparent for the applications, let's try an external JDBC connection that has no knowledge that it's a PAM enabled port:

Success!
There not much more we can do. It's basic and very simple to setup. It shows Informix flexibility. And because it's PAM you can of course enrich it with additional modules if you like

Considerations

There are many things to note about this subject. First we could wonder about the usage cases for something like this. A few ideas come to mind:

  • added security for privilege users. You could assume that applications only connect trough  a safer network, using normal authentication, but DBSAs may need to connect from the "external" world and it requires added security
  • You can use it to construct a "double" password and have part of if available to the application code and let the user introduce the verification code. This would prevent a user to authenticate from outside the application (because the user would never know first password components, but on the other hand an application manager would not be able to impersonate the user even if he got to know the user password)
  • Other extended uses could be achieved by tweaking the module code.
It's important to keep in mind that as with any security related component you should consider carefully what you need and discuss the possibilities with security conscious people. I did a sort of brain storming with two ex-Informix DBAs that now work in the security team and some interesting points were raised. Among them, here are a few:

  1. This sort of token generator as opposed to specialized hardware like RSA tokens
    • Possible advantages of this method:
      • You probably notice you lost your smartphone faster than if you lost a specialized token
      • It's cheaper
      • If the device needs renewal, this method looks simpler (a user can do it once he gets the new phone)
      • You don't depend on any external supplier
    • Possible disadvantages of this method:
      • It seems easier to remotely "hack" a smartphone than to compromise the security of  a specialized hardware token
      • The application could provide some security measure to prevent unauthorized access to the generated codes to anybody who has physical access to the phone. Note this also happens on the hardware token, and with a phone you could always protect it with PIN or pattern code. This is not exactly a disadvantage comparing to hardware tokens, but could be something to improve
  2.  This would be hard to use for non-interactive processes. Unless, and I believe this could be a possibility, that we work the other way around... Meaning we have the code generator inside the applications servers, and that we setup a callback function to answer the module challenge. This would possibly avoid the usage of the application user from outside the application server environment
  3. A generic issue with any two factor authentication mechanism is that ideally the second factor should use a different communication channel from the first. That doesn't happen here, and this allows for man in the middle attacks
  4. Another point, which can be related to the one before, is the possibility to use, or not, the same token in a certain time interval (even a short one). The codes generated by Google authenticator are valid by default during 30 seconds (to allow some time for the user to introduce the code and also to compensate for small clock differences between client and server). The module allows that the code before and after the correct one to be used, so the time interval becomes 1m30s. But all this can be configured, and we even have the possibility of not allowing the same code to be used twice. This however will limit the ability of making more than one login each 30 seconds

Issues found

During the preparation and testing for this article I've faced two main issues:
  1. On first attempts I got the "Invalid verification code" error from the module. As the documentation says this is usually caused by clock synchronization issues between the server side and the mobile app side. After some checking I've found that I was mixing clock time with timezone offsets and it caused too much difference (the module allows configuration for some small difference)
  2. As usual with PAM and PAM modules the hardest part was debugging. Most modules tend to be very quiet about the errors. Not sure why, but on first attempts of concatenating user password with the verification code I was attempting the wrong order (code + password) instead of the proper order (password + code). I ended up looking into the code and changing it to be much more verbose.
Another possible issue is that at the moment, there is no App for Windows phones. And apparently they are grwoing in the market (according to recent studies, they shipped more units than Blackberry)

Acknowledgements

It's not unusual that I discuss some aspects while working on new articles. I recall having some help from several IBM colleagues, some of them already mentioned here once or twice. In this case I had the pleasure to discuss this subject with two ex-Informix DBAs, with whom I worked for several years on a customer team. Now they're working in the security team and the first time I heard about Google authenticator was during a chat with one of them. During the writing of this article and before it was published we interacted a couple of times. Several aspects of the post should be credited to them. So for that, and for the good time we spent working together (we still do occasionally because they have a long history in this customer and there are always subjects where we can work together) a big "Thank you!" to Daniel Valente and Rui Mourão.


Versão Portuguesa:

Introducão 

Não será uma surpresa para ninguém se disser que sou um grande fã do PAM (plugable authentication modules). Já escrevi vários artigos sobre o tema aqui no blog. Desta vez vou partir de um projeto da Google e mostrar como o usar em conjunto com o Informix para aumentar a segurança das conexões. E tudo graças ao PAM naturalmente.
O Google authenticator é um projeto que implementa um gerador de senhas descartáveis (one time passwords - OTP) para várias plataformas móveis. Na prática, transforma um smartphone num gerador se senhas (tokens). Isto evita a necessidade de uma peça física e tem as vantagens de ser open source e disponibilizar um módulo PAM sob a forma de uma biblioteca dinâmica. Isto permite que seja integrável com qualquer sistema que suporte PAM. Tanto pode ser um servidor de SSH como a sua base de dados favorita.
O Google authenticator pode ser visto como um componente de um mecanismo de autenticação multifatorial. Traduzindo, isto significa que o utilizador deve apresentar (e cito traduzindo) "dois ou mais dos três fatores de autenticação: um fator de conhecimento ("algo que o utilizador sabe"), um fator de posse ("algo que o utilizador tem") e um fator de inerência ("algo que o utilizador é"). Este artigo mostrará como configurar o Informix para uma autenticação de dois fatores. No nosso cenário usaremos uma senha tradicional (algo que o utilizador sabe) e o Google authenticator como segundo fator (algo que o utilizador tem). No futuro, se os rumores sobre a introdução de leitores de dados biométricos (impressão digital por exemplo) em dispositivos móveis se tornar uma realidade, poderá ser possível estender para três fatores (algo que o utilizador é).
É possível que vejamos mais serviços a usar este tipo de tecnologia. Ainda recentemente o Twitter introduziu a autenticação de dois fatores, usando o envio de um pedido à sua App quando tenta fazer login no seu site. O utilizador tem de autorizar a ligação usando a aplicação do Twitter num telefone autorizado. Na essência é o mesmo conceito, mas talvez de uma forma ainda mais fácil para o utilizador.

O código

Para começar, necessitamos da biblioteca PAM. Podemos encontrá-la no website do Google authenticator. Fiz a transferência do pacote e coloquei-o numa máquina virtual Linux, tendo de seguida descomprimido:
[root@kimball google_authenticator]# pwd;ls -lia;tar -xjvf libpam-google-authenticator-1.0-source.tar.bz2
/usr/local/google_authenticator
total 44
1671484 drwxr-xr-x  2 root root  4096 Aug  6 11:43 .
1605654 drwxr-xr-x 14 root root  4096 Aug  6 09:46 ..
 327870 -rw-r--r--  1 root root 32708 Aug  6 11:40 libpam-google-authenticator-1.0-source.tar.bz2
libpam-google-authenticator-1.0/base32.c
libpam-google-authenticator-1.0/demo.c
libpam-google-authenticator-1.0/google-authenticator.c
libpam-google-authenticator-1.0/hmac.c
libpam-google-authenticator-1.0/pam_google_authenticator.c
libpam-google-authenticator-1.0/pam_google_authenticator_unittest.c
libpam-google-authenticator-1.0/sha1.c
libpam-google-authenticator-1.0/base32.h
libpam-google-authenticator-1.0/hmac.h
libpam-google-authenticator-1.0/sha1.h
libpam-google-authenticator-1.0/totp.html
libpam-google-authenticator-1.0/Makefile
libpam-google-authenticator-1.0/FILEFORMAT
libpam-google-authenticator-1.0/README
libpam-google-authenticator-1.0/utc-time/
libpam-google-authenticator-1.0/utc-time/app.yaml
libpam-google-authenticator-1.0/utc-time/utc-time.py

Depois disso, seguindo instruções muito simples basta fazer "make install":

[root@kimball libpam-google-authenticator-1.0]# make install
gcc --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden  -o google-authenticator.o google-authenticator.c
gcc --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden  -o base32.o base32.c
gcc --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden  -o hmac.o hmac.c
gcc --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden  -o sha1.o sha1.c
gcc -g   -o google-authenticator google-authenticator.o base32.o hmac.o sha1.o  -ldl
gcc --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden  -o pam_google_authenticator.o pam_google_authenticator.c
gcc -shared -g   -o pam_google_authenticator.so pam_google_authenticator.o base32.o hmac.o sha1.o -lpam
gcc --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden  -o demo.o demo.c
gcc -DDEMO --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden  -o pam_google_authenticator_demo.o pam_google_authenticator.c
gcc -g   -rdynamic -o demo demo.o pam_google_authenticator_demo.o base32.o hmac.o sha1.o  -ldl
gcc -DTESTING --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden        \
              -o pam_google_authenticator_testing.o pam_google_authenticator.c
gcc -shared -g   -o pam_google_authenticator_testing.so pam_google_authenticator_testing.o base32.o hmac.o sha1.o -lpam
gcc --std=gnu99 -Wall -O2 -g -fPIC -c  -fvisibility=hidden  -o pam_google_authenticator_unittest.o pam_google_authenticator_unittest.c
gcc -g   -rdynamic -o pam_google_authenticator_unittest pam_google_authenticator_unittest.o base32.o hmac.o sha1.o -lc  -ldl
cp pam_google_authenticator.so /lib64/security
cp google-authenticator /usr/local/bin
[root@kimball libpam-google-authenticator-1.0]#

e podemos verificar que os dois componentes foram instalados corretamente:

[root@kimball libpam-google-authenticator-1.0]# ls -lia /lib64/security/pam_google* /usr/local/bin/google*
1212428 -rwxr-xr-x 1 root root 116777 Aug  6 11:50 /lib64/security/pam_google_authenticator.so
1613197 -rwxr-xr-x 1 root root  55498 Aug  6 11:50 /usr/local/bin/google-authenticator
[root@kimball libpam-google-authenticator-1.0]#

Configuração

Temos a biblioteca PAM e o binário google-authenticator que iremos usar para gerar uma chave secreta para cada utilizador. Portanto este será o próximo passo. Não irei usar o utilizador "informix" porque o motor ignora sempre o PAM para conexões locais desse utilizador. Mas antes vamos apenas verificar o outro componente fundamental da solução: O seu smartphone. Atualmente utilizo Android, mas a aplicação está também disponível para iOS e Blackberry. Deverá saber como encontrar e instalar a App. Para dispositivos Android está disponível na loja Google Play. A App para Android e iOS suporta a introdução manual da chave secreta bem como a leitura de um código QR. Para Blackberry, segundo o website apenas suporta a introdução manual.
Assumindo que a App está devidamente instalada, podemos prosseguir com a configuração do lado do servidor. Como mencionado anteriormente o próximo passo é a geração da chave secreta que será partilhada entre o servidor e a aplicação móvel. Para isso usamos o binário google-authenticator instalado acima. Quando o executamos mostra a informação necessária e coloca também algumas questões. Não me vou debruçar sobre as mesmas, pois a documentação tem explicações completas:
-bash-3.2$ google-authenticator

Do you want authentication tokens to be time-based (y/n) y
https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/fnunes@kimball%3Fsecret%3D6LWH24ZIUDY46HJU
Your new secret key is: 6LWH24ZIUDY46HJU
Your verification code is 649019

Your emergency scratch codes are:
  38268905
  24335468
  11497220
  81653596
  89796862

Do you want me to update your "/home/fnunes/.google_authenticator" file (y/n) y

Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) n

By default, tokens are good for 30 seconds and in order to compensate for
possible time-skew between the client and the server, we allow an extra
token before and after the current time. If you experience problems with poor
time synchronization, you can increase the window from its default
size of 1:30min to about 4min. Do you want to do so (y/n) n

If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting (y/n) y
-bash-3.2$

Como vemos mostra tanto um URL como a chave. Se tivermos uma biblioteca instalada (libqrencode) irá mostrar logo um código QR que podemos usar directamente com o telefone.
Se abrirmos o URL num browser a página obtida mostrará o mesmo código QR, que podemos usar na aplicação móvel para instalar a chave secreta. Alternativamente podemos introduzi-la manualmente. Qualquer das formas é bastante fácil e num instante terá um gerador de senhas descartáveis no seu telefone móvel:

Depois disto temos de configurar a nossa instância Informix para tirar proveito do mecanismo. Para tal irei criar um listener que use PAM. Isto é feito adicionando uma linha semelhante a esta ao $INFORMIXSQLHOSTS:

tpch_pam     onsoctcp             kimball.onlinedomus.net     1527   s=4,pam_serv=(ids_pam_service),pamauth=(challenge)

Campo por campo:
  • tpch_pam
    O INFORMIXSERVER para este alias
  • onsoctcp
    O protocolo de comunicação a usar
  • kimball.onlinedomus.net
    O nome da máquina
  • 1527
    O porto TCP único que será usado por este listener
  • s=4,pam_serv=(ids_pam_service),pamauth=(challenge)
    Campos de opções: s=4 indica a utilização de PAM. O nome do serviço PAM será "ids_pam_service" e o modo de funcionamento PAM será "challenge" 
Depois de alterado o $INFORMIXSQLHOSTS  temos de configurar um serviço PAM na máquina com o nome "ids_pam_service". Sendo Linux isto é feito criando um ficheiro com o mesmo nome em /etc/pam.d/ cujo conteúdo será:

auth       required     pam_unix.so
auth       required     pam_google_authenticator.so
account    required     pam_unix.so

Isto será usado para a nossa configuração "básica seguindo as regras". Mais tarde vou explicar alguns problemas deste método e mostrarei uma melhor forma de configurar. Como já mencionei noutros artigos apenas precisamos das linhas de configuração para "auth" e "account". Estamos a "empilhar módulos" (Google e pam_unix) para que tenhamos os "dois" fatores de autenticação. Primeiro iremos testar a senha Unix e depois o código de verificação Google (a senha descartável)

De seguida temos de garantir que "tpch_pam" está configurado no ficheiro $INFORMIXDIR/etc/$ONCONFIG no parâmetro DBSERVERALIAS e após isso podemos iniciar o listener com:

onmode -P start tpch_pam

Testes

Portanto, se tudo estiver bem configurado deverá funcionar. Vamos testar com o dbaccess:

tpch@kimball:fnunes-> id;hostname;echo $INFORMIXSERVER
uid=1002(fnunes) gid=1002(fnunes) groups=1002(fnunes)
kimball
tpch
tpch@kimball:fnunes-> dbaccess - -
> CONNECT TO 'sysmaster@tpch_pam' user 'fnunes';
   ENTER PASSWORD:
Password:
Verification code:

Connected.

> SELECT USER, * FROM syscfgtab WHERE cf_name LIKE 'DBSERVER%';



(expression)  fnunes
cf_id         2
cf_name       DBSERVERNAME
cf_flags      0
cf_original   tpch
cf_effective  tpch
cf_default    kimball

(expression)  fnunes
cf_id         77
cf_name       DBSERVERALIASES
cf_flags      0
cf_original   tpch_pam
cf_effective  tpch_pam
cf_default

2 row(s) retrieved.

> tpch@kimball:fnunes->

A primeira impressão é boa. Funciona. Mas há algo estranho. Se olharmos com cuidado vemos que assim que fazemos o CONNECT, o dbaccess pede-me para "ENTER PASSWORD:". Depois pede "Password:" e finalmente pede "Verification code:". Três pedidos... Espero que dois deles sejam óbvios, mas não o pedido de password repetido. Deixe-me explicar. Por omissão, sempre que tentamos um CONNECT o dbaccess pede uma senha. Esse é o primeiro pedido. Na verdade, com esta configuração (challenge mode) podemos introduzir o que quisermos... será ignorado. Depois entramos na pilha de módulos PAM. E o primeiro módulo que temos configurado é o pam_unix.so. Como não lhe estamos a passar nenhuma senha "internamente" (mais sobre isto adiante), ele pede-nos uma senha (pedido "Password:"). Aqui temos de inserir a senha de sistema do utilizador. Depois passamos para o segundo módulo, o do Google authenticator, que se comporta da mesma forma. Como não lhe é disponibilizada uma senha pede-a (pedido "Verification code:"). Nesta fase temos de olhar para o smartphone e transcrever o número que foi gerado para esta conta (a nossa senha descartável). Após isto, completa-se o login. Ambas as senhas foram validadas (uma por cada módulo).
Este comportamento justifica o challenge mode. O(s) módulo envia um desafio ao motor que o encaminha para o cliente (no nosso caso o dbaccess) e o cliente tem de ter uma função para lidar com estes desafios e as respostas do utilizador. Essa função designa-se por função de callback. No caso do dbaccess a função genérica apenas recebe o desafio, imprime-o, lê a resposta do utilizador e envia-a para o motor que depois responde ao módulo para verificação.
Embora isto funcione, é um pouco estranho e necessita que o cliente tenha uma função que lide com os desafios. Isto quer dizer que poderá ser necessário alterar a aplicação. O dbaccess já sabe o que fazer, mas outros clientes não saberiam.
As APIs do Informix têm funções para registar a função de callback. Mas de qualquer forma, alterações nunca são bem vindas... Vejamos o que podemos fazer.

Melhorias

Como já saberá (já o mencionei noutros artigos sobre PAM), o Informix pode ser configurado em modo "password" ou "challenge". A documentação por vezes leva-nos a pensar que para este tipo de implementação temos de usar challenge. Mas na realidade isto depende dos módulos que vamos usar e das opções que disponibilizam. No nosso caso verifiquei que o Google authenticator suporta duas opções interessantes:
  • try_first_pass
    obriga-o a verificar na infra-estrutura PAM se foi passada anteriormente uma senha
  • forward_pass
    torna-o inteligente... Se fornecermos uma senha que seja a concatenação da senha de sistema com o código de verificação, o módulo vai separá-los, verifica a parte dele, e passa o resto para os módulos seguintes
Notei igualmente que o módulo pam_unix.so também suporta as opções try_first_pass e use_first_pass. Portanto o que tudo isto sugere é que se tivermos uma senha de sistema "MinhaPass" e um código como "096712" poderia-mos usar uma senha composta "MinhaPass096712", dá-la como senha ao dbaccess e não nos preocupamos mais com os pedidos (desafios) de cada módulo. Vamos mudar a configuração e testar novamente. O ficheiro /etc/pam.d/ids_pam_service passa a ter:

auth       required     pam_google_authenticator.so try_first_pass forward_pass
auth       required     pam_unix.so use_first_pass
account    required     pam_unix.so

E no ficheiuro $INFORMIXSQLHOSTS mudamos "challenge" para "password":

tpch_pam     onsoctcp             kimball.onlinedomus.net     1527   s=4,pam_serv=(ids_pam_service),pamauth=(password)

E vamos re-iniciar o listener e testar:

tpch@kimball:root-> onmode -P restart tpch_pam


tpch@kimball:fnunes-> dbaccess - -
> CONNECT TO 'sysmaster@tpch_pam' user 'fnunes';
   ENTER PASSWORD:

Connected.

> SELECT USER, * FROM syscfgtab WHERE cf_name LIKE 'DBSERVER%';

(expression)  fnunes
cf_id         2
cf_name       DBSERVERNAME
cf_flags      0
cf_original   tpch
cf_effective  tpch
cf_default    kimball

(expression)  fnunes
cf_id         77
cf_name       DBSERVERALIASES
cf_flags      0
cf_original   tpch_pam
cf_effective  tpch_pam
cf_default

2 row(s) retrieved.

> tpch@kimball:fnunes->

E voilá....  O primeiro módulo agora passou a ser o Google authenticator. Recebe a senha "dupla" internamente pelo PAM (inicializado pelo motor), extrai a parte que lhe diz respeito (últimos seis caracteres), verifica esse código como senha descartável, e envia o resto para o segundo módulo (pam_unix) que valida a senha no sistema.
Entretanto... nós vimos que o dbaccess faz alguma mágica... Porque sabe que está a lidar com um porto PAM. Para ficarmos absolutamente seguros que este método é transparente para a aplicação, vamos tentar uma ligação externa com JDBC, que não terá qualquer noção que está a falar com um porto PAM:

Sucesso!
E não há muito mais a fazer. É básico e muito simples de configurar. Mostra a flexibilidade do Informix. E como usa PAM, podemos enriquecer a solução adicionando outros módulos se desejarmos

Considerações

Existem muitos aspetos a considerar neste assunto. Primeiro podemos imaginar os cenários de utilização para isto. Vêm-me algumas ideias à cabeça:
  • Segurança adicional para utilizadores priveligiados. Podemos assumir que as aplicações se conectam apenas por uma rede mais segura, utilizando a autenticação normal, mas os DBSAs podem ter de se ligar através de uma interface mais exposta que por isso justifique segurança adicional
  • Pode usar isto para trabalhar com uma senha "dupla" em que parte esteja disponível internamente na aplicação e os utilizadores apenas fornecem o código de verificação. Isto impediria os utilizadores de se autenticarem fora da aplicação (porque o utilizador não teria conhecimento sobre o outro componente da senha, ao mesmo tempo que o gestor aplicacional não poderia assumir a identidade do utilizador pois não teria o código de verificação)
  • Podem criar-se outros cenários bem mais complexos se estivermos dispostos a mexer no código do módulo
É importante ter sempre presente que como em qualquer assunto relacionado com segurança, temos de avaliar cuidadosamente as necessidades e discutir as possibilidades com pessoas que entendam bem os mecanismos de segurança. Tive uma espécie de brain storming com dois ex-DBAs Informix que agora trabalham em segurança e alguns pontos interessantes foram levantados. Entre eles aqui ficam alguns:
  1. Este tipo de gerador de senhas descartáveis em oposição aos dispositivos especializados como os tokens RSA
    • Possíveis vantagens deste método:
      • Provavelmente mais rapidamente se apercebe da perda de um telemóvel que de um equipamento especializado
      • É mais barato
      • Se o dispositivo necessitar de substituição, este método parece mais simples (o próprio utilizador pode fazê-lo após ter um novo telefone na sua posse)
      • Não existe dependência de um fornecedor externo
    • Possíveis desvantagens deste método:
      • Parece mais fácil atacar remotamente um smartphone que comprometer a segurança de um aparelho específico
      • A própria App poderia disponibilizar algum mecanismo para dificultar o acesso não autorizado aos códigos por parte de quem tenha acesso físico ao aparelho. Note-se que isto também é um problema para os dispositivos específicos, e num telefone este pode ser protegido por um PIN ou pela introdução de um padrão de autenticação. Não será necessariamente uma desvantagem deste método, mas algo que poderia ser melhorado
      • Um telefone está mais sujeito a problemas que afetem o funcionamento do gerador que um dispositivo específico
  2. Seria difícil utilizar este método em processos não interativos. A menos, e acredito que isto fosse possível, que se trabalhe ao contrário... Ou seja, que tenhamos o gerador de códigos dentro dos servidores aplicacionais, e que se configure uma função de callback que responda ao desafio do módulo. Isto poderia evitar a utilização do utilizador aplicacional fora do ambiente dos servidores aplicacionais
  3. Um problema genérico da autenticaçao de dois fatores é que idealmente o segundo fator deverá utilizar um canal diferente do primeiro. Tal não acontece aqui e isso permite que um ataque do tipo man in the middle possa ser feito.
  4. Outra questão que pode estar relacionada com a de cima é a possibilidade ou não de reutilizar o mesmo código (senha descartável) num determinado período de tempo ainda que curto. As senhas geradas pelo Google authenticator têm por omissão uma duração de 30 segundos (para permitir que o utilizador tenha tempo de a introduzir e também para compensar algum desfazamento de relógios). O módulo permite ainda usar o código imediatemente antes e depois, passando o intervalo para 1m30. Mas tudo isto pode ser controlado, e inclusivamente podemos limitar cada código a apenas uma utilização. Isto no entanto limitará o utilizador a não fazer mais que um login em cada 30 segundos

Problemas encontrados

Durante a preparação e testes destes artigo encontrei dois problemas principais:
  1. Nas primeiras tentativas obtive o erro "Invalid verification code" gerado pelo módulo. Como é referido na documentação, isto é habitualmente causado por problemas de sincronização de relógios entre o servidor e o lado da aplicação móvel. Após algumas verificações percebi que estava a fazer confusão entre a hora do relógio e a timezone. Isto causava uma diferença de relógios demasiado grande (o módulo permite alguma configuração para pequenas diferenças)
  2. Como é habitual com PAM e os seus módulos, a parte mais difícil é perceber os erros. A maioria dos módulos tendem a ser bastante "secretos" em relação às causas dos erros. Por exemplo, e não sei bem porquê, nas primeiras tentativas de concatenar as duas senhas, assumi que a ordem era a contrária (código + senha de sistema) em vez da correta (senha de sistema + código). Naturalmente não funcionava mas foi difícil perceber porquê. Acabei por alterar o código do módulo para introduzir mensagens que permitissem o debug
Outro possível problema é que actualmente não há versão da App para telefones Windows que aparentemente estão em crescimento no mercado (segundo números recentes venderam-se recentemente mais telefones Windows que Blackberry)

Agradecimentos

Não é inédito que eu discuta alguns aspetos enquanto escrevo estes artigos. Lembro-me de já ter tido ajuda de vários colegas da IBM, alguns já aqui mencionados algumas vezes. Neste caso tive o prazer de discutir este assunto com dois ex-DBAs Informix, com quem já trabalhei durante vários anos numa equipa de um cliente. Agora ambos trabalham na equipa de segurança e a primeira vez que o tema do Google authenticator me chamou a atenção foi durante uma conversa com um deles. Enquanto escrevia este artigo interagi com eles várias vezes. Vários aspetos deste artigo devem-se a essas conversas. Portanto, por isso e pelos bons tempos em que trabalhámos juntos (ocasionalmente ainda o fazemos, pois ambos têm uma longa história nesse cliente e é frequente os assuntos cruzarem-se), um grande "Obrigado!" ao Daniel Valente e Rui Mourão

Tuesday, December 18, 2012

11.7.FC7 is out... / versão 11.7.FC7...

This article is written in English and Portuguese
Este artigo está escrito em Inglês e Português

English version

I usually post an article about each Informix fixpack. This time we have 11.70.FC7, but this is a different situation. The latest fixpack is just a security release. You may have noticed the official announcements from IBM (https://www.ibm.com/support/docview.wss?uid=swg21618994) and CVE
(http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-4857).
The problem detected relates to a stack overflow issue. Note that on October another issue also caused a warning recommending the upgrade to 11.70.FC5.
So now, to be safe you should consider moving to 11.70.FC7. If you`re still using 11.50 than you can obtain a special build on top of 11.50.FC9W2. These special fixes are available on Fix Central

Versão Portuguesa

Habitualmente publico um artigo sobre cada fixpack do Informix. Desta feita temos a 11.70.FC7, mas esta é uma situação diferente. Este último fixpack é apenas uma atualização de segurança. Poderá ter verificado os anúncios da IBM (https://www.ibm.com/support/docview.wss?uid=swg21618994) e CVE (http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-4857).
O problema detetado é do tipo stack overflow. Note que em Outubro um outro problema também causou um alerta e recomendava a atualização para a versão 11.70.FC5.
Agora,  para estar seguro deve considerar atualizar para a 11.70.FC7. Se ainda estiver a usar a 11.50 então pode obter um build especial sobre a 11.50.FC9W2. Estes builds estão disponíveis no Fix Central

Sunday, February 05, 2012

Procedures / Procedimentos Owner vs Restricted

This article is written in English and Portuguese
Este artigo está escrito em Inglês e Português

English version:

Introduction

This article focus on a little known aspect of stored procedures or functions. That probably explains why it was the less voted in a recent poll I've conducted. Nonetheless it's (from my point of view) a very interesting topic. During this article I'll be referring to procedures, but I could use the term functions.
If we take a look at the sysprocedures table we'll see a field called mode. This field is just one character and the values it can contain are:

  • D or d
    DBA
  • O or o
    Owner
  • P or p
    Protected
  • R or r
    Restricted
  • T or t
    Trigger
I'm not interested in all of these, but the lower case letters mean "protected" (created by the system), D is for DBA procedures. P is an old nomenclature for protected procedures. T is used for procedures defined as Trigger procedures. And then we have O and R. O for owner mode and R for restricted mode. What is the difference between them? Assume you're using informix user and you run:
CREATE PROCEDURE test()
END PROCEDURE
You'll have an OWNER mode procedure, owned by informix user. But if instead you run:
CREATE PROCEDURE myuser.test()
END PROCEDURE
You'll have a RESTRICTED mode procedure owned by myuser.
You need to have DBA privilege to create a procedure on behalfwith another user name.

Why RESTRICTED?

The reasons why the restricted mode procedures/functions were created are based on security. Let's imagine the following scenario:
  1. You have two databases called db1 and db2
  2. You have a user myuser with connect privileges on db1 and db2 and another user mydba with DBA privileges on db1
  3. User myuser needs to be connected to db1 and run a distributed query to db2
  4. The db2's DBA grants the required privileges on db2 to user myuser
Now, without the RESTRICTED mode procedures, mydba could create a local db1 procedure on behalf of myuser, and with that it could remotely access the data on db2. Note that the db2 DBA did not intend to give the privileges to anyone else beside myuser. So a local DBA could use it's privileges to abuse some of the remote privileges granted to some of the local users.
This is why the RESTRICTED mode was created. Every time we create a procedure on behalf of another user, it will be created as a RESTRICTED mode procedure. And as such any remote operation will be done using the currently logged user and not with the identity of the procedure owner (as it happens with OWNER mode procedures).

Other implications

So, the reasons for the creation of this new mode are explained and are good reasons. But there can be another implication. Note that I'll be referencing a product issue, but it's highly probable that you'd never notice it. But the fix for that bug introduced new limits and a new error so it can be interesting to dig a bit deeper on this.
Whenever we make a remote connection inside a statement we need to open a new database. And we need to keep a record of the current opened ones. The structure of the opened databases used to be an array of "only" 8 positions. And in certain conditions we could wrap around it without raising an error. And this could lead to a nasty situation where the "current" database was not the one it should be. I noticed this on a customer environment when we started to get error -674 (procedure not found) on a procedure called from a trigger. Why is this related to the restricted vs owner mode procedures? Because with the mixed use of restricted and owner mode procedures we raise the possibility of having the same database opened with different users (the owners and our current user).
Please don't be scared with this problem. The situation I got involved around 60 objects (tables and procedures) linked together by a complex sequence of triggers that called procedures, that made INSERTs/UPDATEs/DELETEs which in turn called other procedures etc..
This sequence was started by a simple INSERT. And it involved 5 databases. The array I mentioned earlier had 8 positions.
Since then, we fixed several things and now (11.50.xC9 and 11.70.xC3):
  1. The array was increased to 32 positions
  2. If we still achieve that limit a proper error will be raised (-26600)
  3. The documentation was improved (it didn't mention any limit and it still mentions 8, but it should be fixed soon)
Versão Portuguesa:

Introdução  

Este artigo foca um aspecto pouco conhecido das stored procedures (ou funções). O facto de ser desconhecido deve ajudar a explicar porque foi o menos votado para artigos num inquérito que realizei há pouco tempo. Apesar disso, é um assunto interessante (do meu ponto de vista). Durante este artigo irei referir na maior parte das vezes "procedimentos". Mas podemos assumir "funções".
Se dermos uma vista de olhos à tabela sysprocedures podemos reparar que contém uma coluna com o nome mode. É apenas um caracter e os valores que pode conter são:
  • D or d
    DBA
  • O or o
    Owner
  • P or p
    Protected
  • R or r
    Restricted
  • T or t
    Trigger
Não estou interessado nestes todos, mas para melhor entendimento, as letras minúsculas significam que o prodedimento (ou função) é "protegido" (criado pelo sistema). D é para procedimentos DBA. P é uma nomenclatura antiga para procedimentos protegidos. T é usado para procedimentos definidos como Trigger procedures. E depois temos os O e R. O para modo owner e R para modo restricted. Qual é a diferença entre ambos? Assuma que estamos a usar o utilizador informix e corremos:
CREATE PROCEDURE teste()
END PROCEDURE
Ficaremos com um procedimento em modo OWNER, cujo dono é o informix. Mas se em vez disso fizermos:
CREATE PROCEDURE myuser.teste()
END PROCEDURE
Ficaremos com um procedimento em modo RESTRICTED cujo dono é o myuser.
É necessário ter privilégios de DBA para criar procedimentos em nome de outro utilizador.

Porquê RESTRICTED?

As razões que levaram à criação do modo RESTRICTED para funções e procedimentos prendem-se com segurança. Vamos imaginar o seguinte cenário:
  1. Temos duas bases de dados chamadas bd1 e bd2
  2. Temos um utilizador myuser com privilégios de CONNECT em bd1 e bd2 e outro utilizador mydba com privilégios de DBA na bd1
  3. O utilizador myuser necessita de, estando conectado à bd1, correr uma query distribuída à bd2
  4. O DBA da bd2 faz o GRANT dos privilégios necessários na bd2 ao utilizador myuser
Ora, sem o modo RESTRICTED dos procedimentos, o utilizador mydba poderia criar um procedimento na bd1, em nome do utilizador myuser e nesse procedimento poderia aceder à bd2 usando a identidade do myuser (que tem privilégios na bd2). Note-se que o DBA da bd2 não tencionava dar os privilégios a mais ninguém que não o myuser. Portanto um DBA local poderia usar os seus privilégios para usufruir de privilégios remotos dados a utilizadores da sua base de dados.
Esta foi a razão que levou à criação deste novo modo. Em termos práticos, um procedimento criado como RESTRICTED executa todas as operações remotas com a identidade do utilizador que a está a executar e não com a identidade do utilizador que está definido como dono (que pode ser diferente de quem a criou).

Outras implicações

Portanto, as razões para a introdução deste novo modo estão apresentadas e são boas razões. Mas podem existir outras implicações. De seguida irei referir um bug do produto, mas é altamente improvável que venha a encontrá-lo. Mas a correcção introduziu algumas alterações que são dignas de nota e que valerão a pena gastar algum tempo com elas.
Cada vez que fazemos uma conexão remota, dentro de uma instrução SQL, temos de abrir a base de dados remota. E necessitamos de manter um registo das bases de dados abertas em cada momento. A estrutura que mantém essa informação era um array de "apenas" 8 posições. E em determinadas situações poderíamos "dar a volta" sem despoletar um erro apropriado. E isto poderia dar origem a uma situação onde a base de dados "actual" não era a que deveria ser (devido à forma como eram abertas e fechadas as ligações durante a execução de uma instrução SQL). Deparei-me com isto num ambiente de um cliente onde começamos a obter o erro -674 (procedure not found) num procedimento despoletado por um trigger. Como é que isto se relaciona com o tema deste artigo? Porque o uso misto de procedimentos em modo RESTRICTED e OWNER potencia um maior número de bases de dados abertas em simultâneo (cada conexão tem um utilizador específico associado que conforme o modo pode ser o dono dos procedimentos ou o utilizador da sessão).
Não fique assustado com este problema. Para melhor enquadrar, na situação que encontrei existiam cerca de 60 objectos (tabelas e procedimentos) ligados por uma complexa teia de triggers e procedimentos (triggers que chamavam procedimentos que fazia INSERTs, UPDATEs e DELETEs, que por sua vez faziam disparar outros triggers e assim sucessivamente).
A sequência era despoletada por um simples INSERT e envolvia 5 bases de dados distintas. O array mencionado anteriormente tinha apenas 8 posições.
Isto levou a várias correcções e agora (11.50.xC9 e 11.70.xC3):
  1. O array foi incrementado para 32 posições
  2. Se alguma vez atingirmos este limite (espero sinceramente que não) um erro apropriado será retornado (-26600)
  3. A documentação foi melhorada (não mencionada qualquer limite, sendo que de momento ainda refere 8... Deve ser corrigido brevemente)