Header And Logo

PostgreSQL
| The world's most advanced open source database.

crypt.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * crypt.c
00004  *    Look into the password file and check the encrypted password with
00005  *    the one passed in from the frontend.
00006  *
00007  * Original coding by Todd A. Brandys
00008  *
00009  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00010  * Portions Copyright (c) 1994, Regents of the University of California
00011  *
00012  * src/backend/libpq/crypt.c
00013  *
00014  *-------------------------------------------------------------------------
00015  */
00016 #include "postgres.h"
00017 
00018 #include <unistd.h>
00019 #ifdef HAVE_CRYPT_H
00020 #include <crypt.h>
00021 #endif
00022 
00023 #include "catalog/pg_authid.h"
00024 #include "libpq/crypt.h"
00025 #include "libpq/md5.h"
00026 #include "miscadmin.h"
00027 #include "utils/builtins.h"
00028 #include "utils/syscache.h"
00029 #include "utils/timestamp.h"
00030 
00031 
00032 int
00033 md5_crypt_verify(const Port *port, const char *role, char *client_pass)
00034 {
00035     int         retval = STATUS_ERROR;
00036     char       *shadow_pass,
00037                *crypt_pwd;
00038     TimestampTz vuntil = 0;
00039     char       *crypt_client_pass = client_pass;
00040     HeapTuple   roleTup;
00041     Datum       datum;
00042     bool        isnull;
00043 
00044     /*
00045      * Disable immediate interrupts while doing database access.  (Note we
00046      * don't bother to turn this back on if we hit one of the failure
00047      * conditions, since we can expect we'll just exit right away anyway.)
00048      */
00049     ImmediateInterruptOK = false;
00050 
00051     /* Get role info from pg_authid */
00052     roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
00053     if (!HeapTupleIsValid(roleTup))
00054         return STATUS_ERROR;    /* no such user */
00055 
00056     datum = SysCacheGetAttr(AUTHNAME, roleTup,
00057                             Anum_pg_authid_rolpassword, &isnull);
00058     if (isnull)
00059     {
00060         ReleaseSysCache(roleTup);
00061         return STATUS_ERROR;    /* user has no password */
00062     }
00063     shadow_pass = TextDatumGetCString(datum);
00064 
00065     datum = SysCacheGetAttr(AUTHNAME, roleTup,
00066                             Anum_pg_authid_rolvaliduntil, &isnull);
00067     if (!isnull)
00068         vuntil = DatumGetTimestampTz(datum);
00069 
00070     ReleaseSysCache(roleTup);
00071 
00072     if (*shadow_pass == '\0')
00073         return STATUS_ERROR;    /* empty password */
00074 
00075     /* Re-enable immediate response to SIGTERM/SIGINT/timeout interrupts */
00076     ImmediateInterruptOK = true;
00077     /* And don't forget to detect one that already arrived */
00078     CHECK_FOR_INTERRUPTS();
00079 
00080     /*
00081      * Compare with the encrypted or plain password depending on the
00082      * authentication method being used for this connection.
00083      */
00084     switch (port->hba->auth_method)
00085     {
00086         case uaMD5:
00087             crypt_pwd = palloc(MD5_PASSWD_LEN + 1);
00088             if (isMD5(shadow_pass))
00089             {
00090                 /* stored password already encrypted, only do salt */
00091                 if (!pg_md5_encrypt(shadow_pass + strlen("md5"),
00092                                     port->md5Salt,
00093                                     sizeof(port->md5Salt), crypt_pwd))
00094                 {
00095                     pfree(crypt_pwd);
00096                     return STATUS_ERROR;
00097                 }
00098             }
00099             else
00100             {
00101                 /* stored password is plain, double-encrypt */
00102                 char       *crypt_pwd2 = palloc(MD5_PASSWD_LEN + 1);
00103 
00104                 if (!pg_md5_encrypt(shadow_pass,
00105                                     port->user_name,
00106                                     strlen(port->user_name),
00107                                     crypt_pwd2))
00108                 {
00109                     pfree(crypt_pwd);
00110                     pfree(crypt_pwd2);
00111                     return STATUS_ERROR;
00112                 }
00113                 if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"),
00114                                     port->md5Salt,
00115                                     sizeof(port->md5Salt),
00116                                     crypt_pwd))
00117                 {
00118                     pfree(crypt_pwd);
00119                     pfree(crypt_pwd2);
00120                     return STATUS_ERROR;
00121                 }
00122                 pfree(crypt_pwd2);
00123             }
00124             break;
00125         default:
00126             if (isMD5(shadow_pass))
00127             {
00128                 /* Encrypt user-supplied password to match stored MD5 */
00129                 crypt_client_pass = palloc(MD5_PASSWD_LEN + 1);
00130                 if (!pg_md5_encrypt(client_pass,
00131                                     port->user_name,
00132                                     strlen(port->user_name),
00133                                     crypt_client_pass))
00134                 {
00135                     pfree(crypt_client_pass);
00136                     return STATUS_ERROR;
00137                 }
00138             }
00139             crypt_pwd = shadow_pass;
00140             break;
00141     }
00142 
00143     if (strcmp(crypt_client_pass, crypt_pwd) == 0)
00144     {
00145         /*
00146          * Password OK, now check to be sure we are not past rolvaliduntil
00147          */
00148         if (isnull)
00149             retval = STATUS_OK;
00150         else if (vuntil < GetCurrentTimestamp())
00151             retval = STATUS_ERROR;
00152         else
00153             retval = STATUS_OK;
00154     }
00155 
00156     if (port->hba->auth_method == uaMD5)
00157         pfree(crypt_pwd);
00158     if (crypt_client_pass != client_pass)
00159         pfree(crypt_client_pass);
00160 
00161     return retval;
00162 }